【前言】
如果需要通过按键来实现用户的交互,就需要创建一个高效的按键处理。这一篇我在FreeRTOS的多任务系统下,实现一个基于状态机的按键处理程序。
【实现目标】
在MAX32625MBED开发板中,有两个按键SW2与SW3,需要实现按下按键并松开后,在串口打印出按键按下的信息。
【原理图】
在MAX32625MBED的原理图中,两个按键分别接到了P2.2与P2.3中,并有上拉电阻,在松开状态时,为高电平。
【注】 在MAX32625PICO开发板上是没有这两个物理按键的,如果实现需要自行拉入两个按键。
【程序代码】
1、在工程中添加两个文件,分别为key.c/h,key.c代码如下:
#include "key.h" #include "gpio.h" #include "max32625.h" #include "FreeRTOS.h" #include "task.h" #include "stdio.h" // 按键引脚定义 #define SW2_PIN PIN_2 #define SW2_PORT PORT_2 #define SW3_PIN PIN_3 #define SW3_PORT PORT_2 // 按键状态机 typedef enum { KEY_STATE_IDLE, KEY_STATE_PRESSED, KEY_STATE_RELEASED } key_state_t; static key_state_t sw2_state = KEY_STATE_IDLE; static key_state_t sw3_state = KEY_STATE_IDLE; // 按键消抖时间(毫秒) #define DEBOUNCE_TIME_MS 20 gpio_cfg_t sw2_cfg = {SW2_PORT, SW2_PIN, GPIO_FUNC_GPIO, GPIO_PAD_INPUT_PULLUP}; gpio_cfg_t sw3_cfg = {SW3_PORT, SW3_PIN, GPIO_FUNC_GPIO, GPIO_PAD_INPUT_PULLUP}; void key_init(void) { GPIO_Config(&sw2_cfg); GPIO_Config(&sw3_cfg); } // 获取按键单击状态(非阻塞式) key_event_t key_get_click(void) { static TickType_t last_sw2_time = 0; static TickType_t last_sw3_time = 0; static uint8_t sw2_last = 1; static uint8_t sw3_last = 1; uint8_t sw2_current = GPIO_InGet(&sw2_cfg); uint8_t sw3_current = GPIO_InGet(&sw3_cfg); TickType_t now = xTaskGetTickCount(); key_event_t event = KEY_NONE; // SW2状态处理 if (sw2_current != sw2_last) { if ((now - last_sw2_time) > pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) { if (sw2_current == 0) { // 按下 sw2_state = KEY_STATE_PRESSED; } else { // 松开 if (sw2_state == KEY_STATE_PRESSED) { sw2_state = KEY_STATE_RELEASED; event = KEY_SW2_CLICK; } } last_sw2_time = now; } sw2_last = sw2_current; } // SW3状态处理 if (sw3_current != sw3_last) { if ((now - last_sw3_time) > pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) { if (sw3_current == 0) { // 按下 sw3_state = KEY_STATE_PRESSED; } else { // 松开 if (sw3_state == KEY_STATE_PRESSED) { sw3_state = KEY_STATE_RELEASED; event = KEY_SW3_CLICK; } } last_sw3_time = now; } sw3_last = sw3_current; } return event; } // 示例使用(在任务中调用) void vTaskKeyScan(void *pvParameters) { key_init(); while (1) { key_event_t event = key_get_click(); switch (event) { case KEY_SW2_CLICK: printf("SW2 Clicked\n"); break; case KEY_SW3_CLICK: printf("SW3 Clicked\n"); break; default: break; } vTaskDelay(pdMS_TO_TICKS(10)); // 10ms扫描间隔 } } // 创建任务函数 void vCreateKeyScanTask(void) { xTaskCreate(vTaskKeyScan, "KeyScanTask", 512, NULL, tskIDLE_PRIORITY + 1, NULL); }
key.h
#ifndef __KEY_H #define __KEY_H typedef enum { KEY_NONE, KEY_SW2_CLICK, KEY_SW3_CLICK } key_event_t; void key_init(void); key_event_t key_get_click(void); void vCreateKeyScanTask(void); #endif
最后在main.c的任务创建中调用vCreateKeyScanTask。就完成任务的创建。
【实现效果】
分别按下两个按键,在串口中输出内容如下:
【总结】
结合freertos的多任务操作系统,实现多按键的状态获取,可以为今后的用户交互做好准备。