文章目录
-
目录
文章目录
前言
目标
内容
原理图
按键消抖
软件设计
要求
分析
实现单个按钮
实现多个按钮
使用位操作存储状态
总结
前言
本次学习旨在探索按键操作及按键消抖的原理和实现方法。通过学习原理图、按键消抖的三种方法以及软件设计的要求和分析,我们将实现单个按钮和多个按钮的按键状态监控,并通过串口将按键事件发送出来。在这个过程中,我们将运用软件延时法、硬件滤波法和程序消抖法,以及位操作存储状态的方法来实现按键操作的稳定性和准确性
目标
- 能够读取按键操作
- 能够处理按键消抖
内容
原理图
按键消抖
- 软件延时法:在按键按下时,使用软件延时一段时间,例如10毫秒,然后再检测按键是否还处于按下状态,如果是,则认为按键有效。这种方法简单易行,但会浪费一定的处理器时间,同时需要根据实际情况调整延时时间。
- 硬件滤波法:在按键输入引脚上添加RC滤波电路,可以有效地去除按键信号上的瞬间噪声。这种方法对于高频噪声的去除效果较好,但需要一定的电路设计能力。
- 程序消抖法:在程序中记录按键前后两次的状态,如果两次状态不同,则认为按键有效。这种方法可以根据需要调整检测时间,消抖效果较好,但需要额外的程序设计。
我们采用程序消抖法。
软件设计
要求
当用户按下,或者松开按键时,捕获到这个事件。将事件通过串口发出来。
分析
监控引脚的高低电平变化。记录状态,比对实时状态。
- 监控:死循环去读取电平信息
- 记录与比对:通过变量记录,实时拿到当前状态,与记录的上一次进行比对。
实现单个按钮
#include "Config.h"
#include "Delay.h"
#include "GPIO.h"
#include "UART.h"
#include "NVIC.h"
#include "Switch.h"#define KEY1 P51void GPIO_config(void) {P5_MODE_IO_PU(GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4);
}void UART_config(void) {COMx_InitDefine COMx_InitStructure; //结构定义COMx_InitStructure.UART_Mode = UART_8bit_BRTx; //模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTxCOMx_InitStructure.UART_BRT_Use = BRT_Timer1; //选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)COMx_InitStructure.UART_BaudRate = 115200ul; //波特率, 一般 110 ~ 115200COMx_InitStructure.UART_RxEnable = ENABLE; //接收允许, ENABLE或DISABLECOMx_InitStructure.BaudRateDouble = DISABLE; //波特率加倍, ENABLE或DISABLEUART_Configuration(UART1, &COMx_InitStructure); //初始化串口1 UART1,UART2,UART3,UART4NVIC_UART1_Init(ENABLE,Priority_1); //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3UART1_SW(UART1_SW_P30_P31); // 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}u8 last_key_state = 1; // 抬起void main(){GPIO_config();UART_config();EA = 1;while(1){if(KEY1 == 1 && last_key_state == 0){ // 当前是抬起Up 1, 上一次是按下Down 0printf("KEY1 up\n");last_key_state = 1;}else if(KEY1 == 0 && last_key_state == 1){// 当前是按下Down 0, 上一次是抬起Up 1printf("KEY1 down\n"); last_key_state = 0;}delay_ms(20);}
}
实现多个按钮
#include "Config.h"
#include "Delay.h"
#include "GPIO.h"
#include "UART.h"
#include "NVIC.h"
#include "Switch.h"#define KEY1 P51
#define KEY2 P52
#define KEY3 P53
#define KEY4 P54void GPIO_config(void) {P5_MODE_IO_PU(GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4);
}void UART_config(void) {COMx_InitDefine COMx_InitStructure; //结构定义COMx_InitStructure.UART_Mode = UART_8bit_BRTx; //模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTxCOMx_InitStructure.UART_BRT_Use = BRT_Timer1; //选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)COMx_InitStructure.UART_BaudRate = 115200ul; //波特率, 一般 110 ~ 115200COMx_InitStructure.UART_RxEnable = ENABLE; //接收允许, ENABLE或DISABLECOMx_InitStructure.BaudRateDouble = DISABLE; //波特率加倍, ENABLE或DISABLEUART_Configuration(UART1, &COMx_InitStructure); //初始化串口1 UART1,UART2,UART3,UART4NVIC_UART1_Init(ENABLE,Priority_1); //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3UART1_SW(UART1_SW_P30_P31); // 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}#define DOWN 0
#define UP 1u8 last_key_states[] = {UP, UP, UP, UP}; // key的最后一次状态// 判断指定位置【是否是】按下或抬起
#define IS_KEY_DOWN(i) last_key_states[i] == DOWN
#define IS_KEY_UP(i) last_key_states[i] == UP// 将指定位置值【设置】为按下或抬起
#define SET_KEY_DOWN(i) last_key_states[i] = DOWN
#define SET_KEY_UP(i) last_key_states[i] = UPvoid main(){GPIO_config();UART_config();EA = 1;while(1){if(KEY1 && IS_KEY_DOWN(0)){ // 这次是抬起Up 1, 上一次是按下Down 0printf("KEY1 up\n");SET_KEY_UP(0);}else if(!KEY1 && IS_KEY_UP(0)){// 这次是按下Down 0, 上一次是抬起Up 1printf("KEY1 down\n"); SET_KEY_DOWN(0);}if(KEY2 && IS_KEY_DOWN(1)){ // 这次是抬起Up 1, 上一次是按下Down 0printf("KEY2 up\n");SET_KEY_UP(1);}else if(!KEY2 && IS_KEY_UP(1)){// 这次是按下Down 0, 上一次是抬起Up 1printf("KEY2 down\n"); SET_KEY_DOWN(1);}if(KEY3 && IS_KEY_DOWN(2)){ // 这次是抬起Up 1, 上一次是按下Down 0printf("KEY3 up\n");SET_KEY_UP(2);}else if(!KEY3 && IS_KEY_UP(2)){// 这次是按下Down 0, 上一次是抬起Up 1printf("KEY3 down\n"); SET_KEY_DOWN(2);}if(KEY4 && IS_KEY_DOWN(3)){ // 这次是抬起Up 1, 上一次是按下Down 0printf("KEY4 up\n");SET_KEY_UP(3);}else if(!KEY4 && IS_KEY_UP(3)){// 这次是按下Down 0, 上一次是抬起Up 1printf("KEY4 down\n"); SET_KEY_DOWN(3);}delay_ms(20);}
}
使用位操作存储状态
// P51, P52, P53, P54
//u8 last_key_states[] = {UP, UP, UP, UP};
// 0b 0 0 0 0 - 1 1 1 1
u8 last_key_states = 0x0F; // KEY最后一次状态的8个位(只使用低4位)// 判断指定位置【是否】是按下
// 0b 0 0 0 0 - 0 0 0 0
//& 0b 0 0 0 0 - 0 1 0 0 ----- 判断指定位i=2是否是0
// 0b 0 0 0 0 - 0 0 0 0 == 0
#define IS_KEY_DOWN(i) (last_key_states & (1 << i)) == 0// 判断指定位置【是否】是抬起
// 0b 0 0 0 0 - 1 1 0 0
//& 0b 0 0 0 0 - 1 0 0 0 ----- 判断指定位i=3是否是1
// 0b 0 0 0 0 - 1 0 0 0 > 0
#define IS_KEY_UP(i) (last_key_states & (1 << i)) > 0// 将指定位置值【设置】为按下
// 0b 0 0 0 0 - 1 1 0 0
//&= 0b 1 1 1 1 - 1 0 1 1 ------ 将指定位i=2设置为0,按下
//&=~0b 0 0 0 0 - 0 1 0 0
// 0b 0 0 0 0 - 1 0 0 0
#define SET_KEY_DOWN(i) last_key_states &= ~(1 << i)// 将指定位置值【设置】为抬起
// 0b 0 0 0 0 - 1 1 0 0
//|= 0b 0 0 0 0 - 0 0 1 0 ------ 将指定位i=1设置为1,抬起
// 0b 0 0 0 0 - 1 1 1 0
#define SET_KEY_UP(i) last_key_states |= (1 << i)
- u16存储状态: 16个
1 << i
只能存 16 位 - u32存储状态: 32个,
1 << i
要改成1L << i
能存32位
总结
通过本次学习,我们深入了解了按键操作的原理及按键消抖的多种方法。从简单的软件延时法到更高效的程序消抖法,我们掌握了不同的实现技巧,并学会了如何利用位操作来存储按键状态。通过实现单个按钮和多个按钮的按键监控系统,我们进一步加深了对按键操作的理解,并提升了对嵌入式系统设计的能力。这些知识和技能将为我们在实际项目中处理按键操作提供重要的参考和指导