学习目标
- 能够读取按键操作
-
学习内容
原理图
按键消抖
软件延时法:在按键按下时,使用软件延时一段时间,例如10毫秒,然后再检测按键是否还处于按下状态,如果是,则认为按键有效。这种方法简单易行,但会浪费一定的处理器时间,同时需要根据实际情况调整延时时间。
- 硬件滤波法:在按键输入引脚上添加RC滤波电路,可以有效地去除按键信号上的瞬间噪声。这种方法对于高频噪声的去除效果较好,但需要一定的电路设计能力。
- 程序消抖法:在程序中记录按键前后两次的状态,如果两次状态不同,则认为按键有效。这种方法可以根据需要调整检测时间,消抖效果较好,但需要额外的程序设计。
软件设计
要求
当用户按下,或者松开按键时,捕获到这个事件。将事件通过串口发出来。
分析
监控引脚的高低电平变化。记录状态,比对实时状态。
- 监控:死循环去读取电平信息
- 记录与比对:通过变量记录,实时拿到当前状态,与记录的上一次进行比对。
实现单个按钮
```cinclude “Config.h”
include “Delay.h”
include “GPIO.h”
include “UART.h”
include “NVIC.h”
include “Switch.h”
define KEY1 P51
void 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_BRTx COMx_InitStructure.UART_BRT_Use = BRT_Timer1; //选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2) COMx_InitStructure.UART_BaudRate = 115200ul; //波特率, 一般 110 ~ 115200 COMx_InitStructure.UART_RxEnable = ENABLE; //接收允许, ENABLE或DISABLE COMx_InitStructure.BaudRateDouble = DISABLE; //波特率加倍, ENABLE或DISABLE UART_Configuration(UART1, &COMx_InitStructure); //初始化串口1 UART1,UART2,UART3,UART4
NVIC_UART1_Init(ENABLE,Priority_1); //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
UART1_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 0
printf("KEY1 up\n");
last_key_state = 1;
}else if(KEY1 == 0 && last_key_state == 1){// 当前是按下Down 0, 上一次是抬起Up 1
printf("KEY1 down\n");
last_key_state = 0;
}
delay_ms(20);
}
}
<a name="jAsDH"></a>
#### 实现多个按钮
```c
#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 P54
void 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_BRTx
COMx_InitStructure.UART_BRT_Use = BRT_Timer1; //选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)
COMx_InitStructure.UART_BaudRate = 115200ul; //波特率, 一般 110 ~ 115200
COMx_InitStructure.UART_RxEnable = ENABLE; //接收允许, ENABLE或DISABLE
COMx_InitStructure.BaudRateDouble = DISABLE; //波特率加倍, ENABLE或DISABLE
UART_Configuration(UART1, &COMx_InitStructure); //初始化串口1 UART1,UART2,UART3,UART4
NVIC_UART1_Init(ENABLE,Priority_1); //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
UART1_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 1
u8 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] = UP
void main(){
GPIO_config();
UART_config();
EA = 1;
while(1){
if(KEY1 && IS_KEY_DOWN(0)){ // 这次是抬起Up 1, 上一次是按下Down 0
printf("KEY1 up\n");
SET_KEY_UP(0);
}else if(!KEY1 && IS_KEY_UP(0)){// 这次是按下Down 0, 上一次是抬起Up 1
printf("KEY1 down\n");
SET_KEY_DOWN(0);
}
if(KEY2 && IS_KEY_DOWN(1)){ // 这次是抬起Up 1, 上一次是按下Down 0
printf("KEY2 up\n");
SET_KEY_UP(1);
}else if(!KEY2 && IS_KEY_UP(1)){// 这次是按下Down 0, 上一次是抬起Up 1
printf("KEY2 down\n");
SET_KEY_DOWN(1);
}
if(KEY3 && IS_KEY_DOWN(2)){ // 这次是抬起Up 1, 上一次是按下Down 0
printf("KEY3 up\n");
SET_KEY_UP(2);
}else if(!KEY3 && IS_KEY_UP(2)){// 这次是按下Down 0, 上一次是抬起Up 1
printf("KEY3 down\n");
SET_KEY_DOWN(2);
}
if(KEY4 && IS_KEY_DOWN(3)){ // 这次是抬起Up 1, 上一次是按下Down 0
printf("KEY4 up\n");
SET_KEY_UP(3);
}else if(!KEY4 && IS_KEY_UP(3)){// 这次是按下Down 0, 上一次是抬起Up 1
printf("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位
练习题
- 实现按键操作
- 通过按键控制震动马达震动