当前位置: 首页 > article >正文

四、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: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.kler.cn/a/530511.html

相关文章:

  • C语言 --- 分支
  • [权限提升] Windows 提权 维持 — 系统错误配置提权 - Trusted Service Paths 提权
  • 在GPIO控制器中,配置通用输入,读取IO口电平时,上拉和下拉起到什么作用
  • 【python】python油田数据分析与可视化(源码+数据集)【独一无二】
  • 《基于Scapy的综合性网络扫描与通信工具集解析》
  • 《数据可视化新高度:Graphy的AI协作变革》
  • dup函数和dup2函数复制文件描述符区别
  • 小程序设计和开发:如何研究同类型小程序的优点和不足。
  • 初入机器学习
  • 经典游戏红色警戒2之英语
  • MP4基础
  • EF Core与ASP.NET Core的集成
  • 知识库建设与知识管理实践对企业发展的助推作用探索
  • FreeRTOS学习 --- 任务切换
  • 网络工程师 (13)时间管理
  • 【华为OD-E卷 - 磁盘容量排序 100分(python、java、c++、js、c)】
  • IM 即时通讯系统-45-merua0oo0 IM 分布式聊天系统
  • 【Ai】DeepSeek本地部署+Page Assist图形界面
  • 【软件测试项目实战】淘宝网订单管理功能
  • 项目集成Spring Security授权部分
  • 2025年2月2日(range()函数的参数及含义)
  • 「全网最细 + 实战源码案例」设计模式——享元模式
  • 【C++面试题】malloc和new delete和delete[]
  • 在AWS上使用Flume搜集分布在不同EC2实例上的应用程序日志具体流程和代码
  • Golang 并发机制-4:用Mutex管理共享资源
  • 毕业设计:基于卷积神经网络的鲜花花卉种类检测算法研究