四、GPIO中断实现按键功能

news/2025/2/3 12:59:59 标签: 单片机, 嵌入式硬件

4.1 GPIO简介

        输入输出(I/O)是一个非常重要的概念。I/O泛指所有类型的输入输出端口,包括单向的端口如逻辑门电路的输入输出管脚和双向的GPIO端口。而GPIO(General-Purpose Input/Output)则是一个常见的术语,指的是通用输入输出接口。

        下面有请DeepSeek发言

LPC1110系列Cortex-M0微控制器的GPIO口的结构特点:

1端口可由软件配置为输入输出
2引脚默认为输入(所以点灯时需要改下方向)
3端口引脚的读写操作可屏蔽
4每个单独引脚可被用作外部中断输入引脚
5每个GPIO中断可配置为 高、低电平、下降、上升沿或双边沿触发
6可对单独端口的中断级别进行配置

4.2 GPIO口的寄存器

        所有GPIO寄存器都为32位 

        GPIO端口基址为

端口0  0x5000 0000
端口1  0x5001 0000

端口2

 0x5002 0000
端口3 0x5003 0000

4.2.1 数据寄存器  GPIOnDATA

        用于读取输入引脚的状态数据或者配置输出引脚的输出状态

        对应端口位置后四位范围为 0000 ~ 3FFC

11:0PIOn_0 ~ PIOn_11的输入/输出数据
31:12保留

4.2.2 方向寄存器 GPIOnDIR

11:0PIOn_0 ~ PIOn_11的输入/输出方向   0为输入, 1为输出  位数0-11与0-11引脚一一对应
31:12保留

4.2.3 中断触发寄存器 GPIOnIS

        相较于基地址偏移量0x8004 即0x500n 8004

11:0PIOn_x    0为边沿触发,1为电平触发
31:12保留

4.2.4 中断双边沿触发寄存器 GPIOnIBE

        相较于基地址偏移量0x8008 即0x500n 8008

11:0

0为通过4.2.5中寄存器GPIOnIEV控制PIOn_x的中断

1为通过PIOn_x上双边沿触发中断

31:12保留

4.2.5 中断事件寄存器 GPIOnIEV

        相较于基地址偏移量0x800C 即0x500n 800C

11:0

0为上升沿或者高电平触发中断

1为下降沿或者低电平触发中断

具体边沿还是电平 看4.2.3中GPIOnIS的设置

31:12保留

4.2.6 中断屏蔽寄存器 GPIOnIE

        相较于基地址偏移量0x8010 即0x500n 8010

11:0

0为中断被屏蔽

1为中断不被屏蔽

31:12保留

4.2.7 原始中断状态寄存器 GPIOnIRS

        相较于基地址偏移量0x8014 即0x500n 8014

        屏蔽之前的中断状态

11:0

0为无中断

1为满足中断要求

31:12保留

4.2.8 屏蔽中断状态寄存器 GPIOnMIS

        相较于基地址偏移量0x8018 即0x500n 8018

        考虑了屏蔽操作之后是否有中断

11:0

0为无中断,或者中断被屏蔽

1为满足中断要求

31:12保留

4.2.9 中断清除寄存器 GPIOnIC

        相较于基地址偏移量0x801C 即0x500n 801C

11:0

0无操作

1为清除PIOn_x上的边沿检测逻辑

31:12保留

4.3 LPC上的GPIO按键

        按键按下引脚低电平,不按是高电平

4.4 按键控制LED闪烁频率

任务:

1. BUTTON(PIO3_5)按键按下,闪烁频率为1Hz,再次按下,恢复闪烁频率为0.5Hz;

2. WEAKUP(PIO1_4)按键按下,闪烁频率为2Hz,再次按下,恢复闪烁频率为0.5Hz;

3. 适当考虑按键防抖功能。            

思路:

        对于闪烁频率的修改,首先考虑用什么控制LED闪烁,结合上章可以用SysTick,然后按键按下改变SysTick周期即可

        对于按键防抖,由于按键固有的物理结构,按下后弹簧一上一下会影响中断,需要用延时函数过滤抖动。

抖动时间大概10ms这样, 我们可以用个延时函数过滤掉这个抖动过程,延时20ms就足够了

代码:

利用之前写过的函数即可,复制个新工程,然后main文件里代码如下

#include <LPC11xx.h>
#include "LED.h"


//延时ms函数 // 太粗糙了,而且要根据机器指令与时钟周期关系调整,也就防抖延时用一下
__inline void delay_ms(uint32_t a)    //约1ms延时函数 
{                           
    uint32_t i;
    while( a -- != 0)
    {
           for(i = 0; i<5500; i++);
    }             
}


int flag1 = 0, flag2 = 0; // 判断botton 和 wakeup 按键上一次状态
int main()
{
	LED_Init(); 

	// PIO1_4
    LPC_IOCON->PIO1_4 &= ~(0x1F);  // 清除之前的配置
    LPC_IOCON->PIO1_4 |= 0x00;     // 配置为GPIO功能
    LPC_GPIO1->DIR &= ~(1UL << 4);// 设置GPIO方向为输入
	
    LPC_GPIO1->IS &= ~(0x1 << 4); // 清除第 4 位,设置为边沿触发
    LPC_GPIO1->IBE &= ~(0x1 << 4); // 清除第 4 位,设置为单边沿触发
    LPC_GPIO1->IEV &= ~(0x1 << 4); // 清除第 4 位,设置为低电平触发
	LPC_GPIO1 -> IE |= (0x1<<4); // 使能端口中断
	LPC_IOCON->PIO1_4 |= (1UL << 5);          // 使能滞后模式
    LPC_GPIO1->IC |= (1UL << 4); // 清除中断标志位
    NVIC_EnableIRQ(EINT1_IRQn); // 使能GPIO1中断

	// PIO3_5
    LPC_IOCON->PIO3_5 &= ~(0x1F);   // 清除之前的配置
    LPC_IOCON->PIO3_5 |= 0x00;      // 配置为GPIO功能
    LPC_GPIO3->DIR &= ~(1UL << 5);// 设置GPIO方向为输入
	
    LPC_GPIO3->IS &= ~(0x1 << 5); // 清除第 5 位,设置为边沿触发
    LPC_GPIO3->IBE &= ~(0x1 << 5); // 清除第 5 位,设置为单边沿触发
    LPC_GPIO3->IEV &= ~(0x1 << 5); // 清除第 5 位,设置为低电平触发
    LPC_GPIO3 -> IE |= (0x1<<5); // 使能端口中断
	LPC_IOCON->PIO3_5 |= (1UL << 5);  // 使能滞后模式
    LPC_GPIO3->IC |= (1UL << 5); //清除中断标志
    NVIC_EnableIRQ(EINT3_IRQn);
	
	SysTick_Config(SystemCoreClock/100); // 0.01s进一次中断 1s翻转一次 0.5 Hz
	while(1)
	{

	}
}

void SysTick_Handler() /// 系统节拍定时器中断函数
{
	static unsigned long ticks;
	if(ticks++ >= 99)
	{
		ticks = 0;
		LED_Toggle();
	}
}

// GPIO3_5的中断服务函数,处理BUTTON按键按下事件
void PIOINT3_IRQHandler(void)
{
    if((LPC_GPIO3->MIS & (1UL << 5)) == (1UL << 5))// 检查是否是PIO3_5的中断
	{ 
		delay_ms(20); // 消抖
		while((LPC_GPIO3->DATA & (1UL << 5)) == 0);
		delay_ms(20);
	
		if(flag1)
			SysTick_Config(SystemCoreClock/100); // 0.01s进一次中断 1s翻转一次 0.5 Hz
		else 
			SysTick_Config(SystemCoreClock/200); // 0.005s进一次中断 0.5s翻转一次 1 Hz
		flag1 = !flag1;
		
		LPC_GPIO3->IC |= (1UL << 5);          // 清除中断标志
    }
}
// GPIO1_4的中断服务函数,处理WAKEUP按键按下事件
void PIOINT1_IRQHandler(void)
{
    if((LPC_GPIO1->MIS & (1UL << 4)) == (1UL << 4)) // 检查是否是PIO1_4的中断
	{
		delay_ms(20);
		while((LPC_GPIO1->DATA & (1UL << 4)) == 0);
		delay_ms(20);
		if(flag2)
			SysTick_Config(SystemCoreClock/100); // 0.01s进一次中断 1s翻转一次 0.5 Hz
		else 
			SysTick_Config(SystemCoreClock/400); // 0.0025s进一次中断 0.2s翻转一次 2 Hz 
		flag2 = !flag2;
		
        LPC_GPIO1->IC |= (1UL << 4);           // 清除中断标志
    }
}

模块化一下,新建Button.c Button.h文件,便于之后移植工程

main.c

#include <LPC11xx.h>
#include "LED.h"
#include "Button.h"


int main()
{
	LED_Init(); 
	WAKEUP_Init();
	Button_Init();
	
	while(1)
	{

	}
}

void SysTick_Handler() /// 系统节拍定时器中断函数
{
	static unsigned long ticks;
	if(ticks++ >= 99)
	{
		ticks = 0;
		LED_Toggle();
	}
}


Button.c

#include "Button.h"
int flag1 = 0, flag2 = 0; // 判断botton 和 wakeup 按键上一次状态

//延时ms函数 // 太粗糙了,而且要根据机器指令与时钟周期关系调整,也就防抖延时用一下
__inline void delay_ms(uint32_t a)    //约1ms延时函数 
{                           
    uint32_t i;
    while( a -- != 0)
    {
           for(i = 0; i<5500; i++);
    }             
}

void WAKEUP_Init(void)
{
	LPC_SYSCON -> SYSAHBCLKCTRL |= (1UL << 6) | (1UL << 16); // 使能GPIO时钟和IO时钟
	// PIO1_4
    LPC_IOCON->PIO1_4 &= ~(0x1F);  // 清除之前的配置
    LPC_IOCON->PIO1_4 |= 0x00;     // 配置为GPIO功能
    LPC_GPIO1->DIR &= ~(1UL << 4);// 设置GPIO方向为输入
	
    LPC_GPIO1->IS &= ~(0x1 << 4); // 清除第 4 位,设置为边沿触发
    LPC_GPIO1->IBE &= ~(0x1 << 4); // 清除第 4 位,设置为单边沿触发
    LPC_GPIO1->IEV &= ~(0x1 << 4); // 清除第 4 位,设置为低电平触发
	LPC_GPIO1 -> IE |= (0x1<<4); // 使能端口中断
	LPC_IOCON->PIO1_4 |= (1UL << 5);          // 使能滞后模式
    LPC_GPIO1->IC |= (1UL << 4); // 清除中断标志位
    NVIC_EnableIRQ(EINT1_IRQn); // 使能GPIO1中断
}

void Button_Init(void)
{
	LPC_SYSCON -> SYSAHBCLKCTRL |= (1UL << 6) | (1UL << 16); // 使能GPIO时钟和IO时钟
	// PIO3_5
    LPC_IOCON->PIO3_5 &= ~(0x1F);   // 清除之前的配置
    LPC_IOCON->PIO3_5 |= 0x00;      // 配置为GPIO功能
    LPC_GPIO3->DIR &= ~(1UL << 5);// 设置GPIO方向为输入
	
    LPC_GPIO3->IS &= ~(0x1 << 5); // 清除第 5 位,设置为边沿触发
    LPC_GPIO3->IBE &= ~(0x1 << 5); // 清除第 5 位,设置为单边沿触发
    LPC_GPIO3->IEV &= ~(0x1 << 5); // 清除第 5 位,设置为低电平触发
    LPC_GPIO3 -> IE |= (0x1<<5); // 使能端口中断
	LPC_IOCON->PIO3_5 |= (1UL << 5);  // 使能滞后模式
    LPC_GPIO3->IC |= (1UL << 5); //清除中断标志
    NVIC_EnableIRQ(EINT3_IRQn);
}

// GPIO3_5的中断服务函数,处理BUTTON按键按下事件
void PIOINT3_IRQHandler(void)
{
    if((LPC_GPIO3->MIS & (1UL << 5)) == (1UL << 5))// 检查是否是PIO3_5的中断
	{ 
		delay_ms(20); // 消抖
		while((LPC_GPIO3->DATA & (1UL << 5)) == 0);
		delay_ms(20);
	
		if(flag1)
			SysTick_Config(SystemCoreClock/100); // 0.01s进一次中断 1s翻转一次 0.5 Hz
		else 
			SysTick_Config(SystemCoreClock/200); // 0.005s进一次中断 0.5s翻转一次 1 Hz
		flag1 = !flag1;
		
		LPC_GPIO3->IC |= (1UL << 5);          // 清除中断标志
    }
}
// GPIO1_4的中断服务函数,处理WAKEUP按键按下事件
void PIOINT1_IRQHandler(void)
{
    if((LPC_GPIO1->MIS & (1UL << 4)) == (1UL << 4)) // 检查是否是PIO1_4的中断
	{
		delay_ms(20);
		while((LPC_GPIO1->DATA & (1UL << 4)) == 0);
		delay_ms(20);
		if(flag2)
			SysTick_Config(SystemCoreClock/100); // 0.01s进一次中断 1s翻转一次 0.5 Hz
		else 
			SysTick_Config(SystemCoreClock/400); // 0.0025s进一次中断 0.2s翻转一次 2 Hz 
		flag2 = !flag2;
		
        LPC_GPIO1->IC |= (1UL << 4);           // 清除中断标志
    }
}

Button.h

#ifndef _BUTTON_H_
#define _BUTTON_H_

#include <LPC11xx.h>

void WAKEUP_Init(void);
void Button_Init(void);

#endif


http://www.niftyadmin.cn/n/5840827.html

相关文章

第一性原理:游戏开发成本的思考

利润 营收-成本 营收定价x销量x分成比例 销量 曝光量x 点击率x &#xff08;购买率- 退款率&#xff09; 分成比例 100%- 平台抽成- 税- 引擎费- 发行抽成 成本开发成本运营成本 开发成本 人工外包办公地点租金水电设备折旧 人工成本设计成本开发成本迭代修改成本后续内容…

Spring Boot 实例解析:从概念到代码

SpringBoot 简介&#xff1a; 简化 Spring 应用开发的一个框架整合 Spring 技术栈的一个大整合J2EE 开发的一站式解决方案优点&#xff1a;快速创建独立运行的 Spring 项目以及与主流框架集成使用嵌入式的 Servlet 容器&#xff0c;应用无需打成 war 包&#xff0c;内嵌 Tomcat…

XML,WEB项目部署,HTTP

XML&#xff08;配置文件&#xff09; XML和HTML一样都是标记语言&#xff0c;也就是说它们的基本语法都是标签。 常见配置文件类型 1 根标签只能有一个 2 第一行永远是<?xml version"1.0" encoding"UTF-8"?>不允许放任何东西 3 xml是有约束的…

python算法和数据结构刷题[6]:二叉树、堆、BFS\DFS

遍历二叉树 前序遍历NLR&#xff1a;先访问根结点&#xff0c;再前序遍历左子树&#xff0c;最后前序遍历右子树。中序遍历LNR&#xff1a;先中序遍历左子树&#xff0c;再访问根结点&#xff0c;最后中序遍历右子树。后序遍历 LRN&#xff1a;先后序遍历左子树&#xff0c;再…

Unity游戏(Assault空对地打击)开发(3) 摄像机的控制

详细步骤 打开My Assets或者Package Manager。 选择Unity Registry。 搜索Cinemachine&#xff0c;找到 Cinemachine包&#xff0c;点击 Install按钮进行安装。 关闭窗口&#xff0c;新建一个FreeLook Camera&#xff0c;如下。 接着新建一个对象Pos&#xff0c;拖到Player下面…

ubuntu直接运行arm环境qemu-arm-static

qemu-arm-static 嵌入式开发有时会在ARM设备上使用ubuntu文件系统。开发者常常会面临这样一个问题&#xff0c;想预先交叉编译并安装一些应用程序&#xff0c;但是交叉编译的环境配置以及依赖包的安装十分繁琐&#xff0c;并且容易出错。想直接在目标板上进行编译和安装&#x…

css三角图标

案例三角&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><s…

每日 Java 面试题分享【第 19 天】

欢迎来到每日 Java 面试题分享栏目&#xff01; 订阅专栏&#xff0c;不错过每一天的练习 今日分享 3 道面试题目&#xff01; 评论区复述一遍印象更深刻噢~ 目录 问题一&#xff1a;Java Object 类中有什么方法&#xff0c;有什么作用&#xff1f;问题二&#xff1a;Java …