四、GPIO中断实现按键功能

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:0 | PIOn_0 ~ PIOn_11的输入/输出数据 |
| 31:12 | 保留 |

4.2.2 方向寄存器 GPIOnDIR

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

4.2.3 中断触发寄存器 GPIOnIS

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

|-------|--------------------------|
| 11:0 | PIOn_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文件里代码如下

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#ifndef _BUTTON_H_
#define _BUTTON_H_

#include <LPC11xx.h>

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

#endif
相关推荐
天天爱吃肉82185 分钟前
【恒流源cc与恒压源cv典型电路解析】
自动化测试·单片机·嵌入式硬件·汽车
九鼎创展科技1 小时前
LGA封装 Z3588开发板,8K视频编解码
arm开发·人工智能·嵌入式硬件
电子艾号哲2 小时前
STC89C52单片机学习——第20节: [8-2]串口向电脑发送数据&电脑通过串口控制LED
单片机·学习·电脑
万能的小裴同学2 小时前
给单片机生成字库的方案
单片机·嵌入式硬件
c-u-r-ry304 小时前
011---UART之RS232通信接口标准(二)
嵌入式硬件·fpga开发
二年级程序员4 小时前
51单片机的工作过程
单片机·嵌入式硬件·51单片机
云山工作室5 小时前
基于单片机的智能电表设计(论文+源码)
单片机·嵌入式硬件·毕业设计
风正豪6 小时前
如何向 Linux 中加入一个 IO 扩展芯片
linux·运维·单片机
小小的代码里面挖呀挖呀挖7 小时前
杰理可视化SDK-手机三方通话控制
笔记·单片机·物联网·iot
Wallace Zhang7 小时前
STM32 - 在机器人领域,LL库相比HAL优势明显
stm32·嵌入式硬件·机器人