学习目标

  1. 能够读取按键操作
  2. 能够处理按键消抖

    学习内容

    原理图

    61.png

    按键消抖

  3. 软件延时法:在按键按下时,使用软件延时一段时间,例如10毫秒,然后再检测按键是否还处于按下状态,如果是,则认为按键有效。这种方法简单易行,但会浪费一定的处理器时间,同时需要根据实际情况调整延时时间。

  4. 硬件滤波法:在按键输入引脚上添加RC滤波电路,可以有效地去除按键信号上的瞬间噪声。这种方法对于高频噪声的去除效果较好,但需要一定的电路设计能力。
  5. 程序消抖法:在程序中记录按键前后两次的状态,如果两次状态不同,则认为按键有效。这种方法可以根据需要调整检测时间,消抖效果较好,但需要额外的程序设计。

我们采用程序消抖法。

软件设计

要求

当用户按下,或者松开按键时,捕获到这个事件。将事件通过串口发出来。

分析

监控引脚的高低电平变化。记录状态,比对实时状态。

  • 监控:死循环去读取电平信息
  • 记录与比对:通过变量记录,实时拿到当前状态,与记录的上一次进行比对。

    实现单个按钮

    ```c

    include “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

  1. NVIC_UART1_Init(ENABLE,Priority_1); //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
  2. 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(){

  1. GPIO_config();
  2. UART_config();
  3. EA = 1;
  4. while(1){
  5. if(KEY1 == 1 && last_key_state == 0){ // 当前是抬起Up 1, 上一次是按下Down 0
  6. printf("KEY1 up\n");
  7. last_key_state = 1;
  8. }else if(KEY1 == 0 && last_key_state == 1){// 当前是按下Down 0, 上一次是抬起Up 1
  9. printf("KEY1 down\n");
  10. last_key_state = 0;
  11. }
  12. delay_ms(20);
  13. }

}

  1. <a name="jAsDH"></a>
  2. #### 实现多个按钮
  3. ```c
  4. #include "Config.h"
  5. #include "Delay.h"
  6. #include "GPIO.h"
  7. #include "UART.h"
  8. #include "NVIC.h"
  9. #include "Switch.h"
  10. #define KEY1 P51
  11. #define KEY2 P52
  12. #define KEY3 P53
  13. #define KEY4 P54
  14. void GPIO_config(void) {
  15. P5_MODE_IO_PU(GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4);
  16. }
  17. void UART_config(void) {
  18. COMx_InitDefine COMx_InitStructure; //结构定义
  19. COMx_InitStructure.UART_Mode = UART_8bit_BRTx; //模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTx
  20. COMx_InitStructure.UART_BRT_Use = BRT_Timer1; //选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)
  21. COMx_InitStructure.UART_BaudRate = 115200ul; //波特率, 一般 110 ~ 115200
  22. COMx_InitStructure.UART_RxEnable = ENABLE; //接收允许, ENABLE或DISABLE
  23. COMx_InitStructure.BaudRateDouble = DISABLE; //波特率加倍, ENABLE或DISABLE
  24. UART_Configuration(UART1, &COMx_InitStructure); //初始化串口1 UART1,UART2,UART3,UART4
  25. NVIC_UART1_Init(ENABLE,Priority_1); //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
  26. UART1_SW(UART1_SW_P30_P31); // 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
  27. }
  28. #define DOWN 0
  29. #define UP 1
  30. u8 last_key_states[] = {UP, UP, UP, UP}; // key的最后一次状态
  31. // 判断指定位置【是否是】按下或抬起
  32. #define IS_KEY_DOWN(i) last_key_states[i] == DOWN
  33. #define IS_KEY_UP(i) last_key_states[i] == UP
  34. // 将指定位置值【设置】为按下或抬起
  35. #define SET_KEY_DOWN(i) last_key_states[i] = DOWN
  36. #define SET_KEY_UP(i) last_key_states[i] = UP
  37. void main(){
  38. GPIO_config();
  39. UART_config();
  40. EA = 1;
  41. while(1){
  42. if(KEY1 && IS_KEY_DOWN(0)){ // 这次是抬起Up 1, 上一次是按下Down 0
  43. printf("KEY1 up\n");
  44. SET_KEY_UP(0);
  45. }else if(!KEY1 && IS_KEY_UP(0)){// 这次是按下Down 0, 上一次是抬起Up 1
  46. printf("KEY1 down\n");
  47. SET_KEY_DOWN(0);
  48. }
  49. if(KEY2 && IS_KEY_DOWN(1)){ // 这次是抬起Up 1, 上一次是按下Down 0
  50. printf("KEY2 up\n");
  51. SET_KEY_UP(1);
  52. }else if(!KEY2 && IS_KEY_UP(1)){// 这次是按下Down 0, 上一次是抬起Up 1
  53. printf("KEY2 down\n");
  54. SET_KEY_DOWN(1);
  55. }
  56. if(KEY3 && IS_KEY_DOWN(2)){ // 这次是抬起Up 1, 上一次是按下Down 0
  57. printf("KEY3 up\n");
  58. SET_KEY_UP(2);
  59. }else if(!KEY3 && IS_KEY_UP(2)){// 这次是按下Down 0, 上一次是抬起Up 1
  60. printf("KEY3 down\n");
  61. SET_KEY_DOWN(2);
  62. }
  63. if(KEY4 && IS_KEY_DOWN(3)){ // 这次是抬起Up 1, 上一次是按下Down 0
  64. printf("KEY4 up\n");
  65. SET_KEY_UP(3);
  66. }else if(!KEY4 && IS_KEY_UP(3)){// 这次是按下Down 0, 上一次是抬起Up 1
  67. printf("KEY4 down\n");
  68. SET_KEY_DOWN(3);
  69. }
  70. delay_ms(20);
  71. }
  72. }

使用位操作存储状态

  1. // P51, P52, P53, P54
  2. //u8 last_key_states[] = {UP, UP, UP, UP};
  3. // 0b 0 0 0 0 - 1 1 1 1
  4. u8 last_key_states = 0x0F; // KEY最后一次状态的8个位(只使用低4位)
  5. // 判断指定位置【是否】是按下
  6. // 0b 0 0 0 0 - 0 0 0 0
  7. //& 0b 0 0 0 0 - 0 1 0 0 ----- 判断指定位i=2是否是0
  8. // 0b 0 0 0 0 - 0 0 0 0 == 0
  9. #define IS_KEY_DOWN(i) (last_key_states & (1 << i)) == 0
  10. // 判断指定位置【是否】是抬起
  11. // 0b 0 0 0 0 - 1 1 0 0
  12. //& 0b 0 0 0 0 - 1 0 0 0 ----- 判断指定位i=3是否是1
  13. // 0b 0 0 0 0 - 1 0 0 0 > 0
  14. #define IS_KEY_UP(i) (last_key_states & (1 << i)) > 0
  15. // 将指定位置值【设置】为按下
  16. // 0b 0 0 0 0 - 1 1 0 0
  17. //&= 0b 1 1 1 1 - 1 0 1 1 ------ 将指定位i=2设置为0,按下
  18. //&=~0b 0 0 0 0 - 0 1 0 0
  19. // 0b 0 0 0 0 - 1 0 0 0
  20. #define SET_KEY_DOWN(i) last_key_states &= ~(1 << i)
  21. // 将指定位置值【设置】为抬起
  22. // 0b 0 0 0 0 - 1 1 0 0
  23. //|= 0b 0 0 0 0 - 0 0 1 0 ------ 将指定位i=1设置为1,抬起
  24. // 0b 0 0 0 0 - 1 1 1 0
  25. #define SET_KEY_UP(i) last_key_states |= (1 << i)
  • u16存储状态: 16个 1 << i 只能存 16 位
  • u32存储状态: 32个, 1 << i要改成 1L << i 能存32位

练习题

  1. 实现按键操作
  2. 通过按键控制震动马达震动