按钮松开时,读到的值是1;按钮按下时,读到的值是0。
上图,一开始按钮处于松开状态,因此读到引脚的值是1;之后按钮按下,读到的值变为0;再次松开,值有变为1。
因此,要捕捉按钮松开的瞬间,即值从0变为1的瞬间。
编写按钮代码
首先,FreeRTOS需要新建目录,让代码更加易于区分。新建User目录,在User目录下新建Task目录,在Task目录下新建Key_Task.c和Key_Task.h文件。
Key_Task.c
1234567891011121314151617181920 | void KeyTask(void *pvParameters){GPIO_PinState previous = GPIO_PIN_SET;GPIO_PinState current = GPIO_PIN_SET;//类型是GPIO_PinState,先都赋值成GPIO_PIN_SETfor(;;){current = HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin);//读KEY1引脚的值if(previous == GPIO_PIN_RESET && current == GPIO_PIN_SET)//按钮被点击,即上一次为0,这一次为1{extern UART_HandleTypeDef huart1;//huart是全局变量,声明在main文件,要在Key_Task文件用,需要externHAL_UART_Transmit(&huart1, (const uint8_t *)"KEY1 Clicked.\n", strlen("KEY1 Clicked.\n"), HAL_MAX_DELAY);//每次按下按钮,串口发送一个消息}previous = current;//把本次的状态给到上一次,用于下次判断使用。vTaskDelay(pdMS_TO_TICKS(10));//延迟10ms}} |
Key_Task.h
1234 | void KeyTask(void *pvParameters); |
最后,在主程序中新建一个任务
1 | xTaskCreate( KeyTask, "Key", 128, NULL, 3 , NULL ); |
程序现象是,每次按下KEY1按钮,串口会发送一条KEY1 Clicked.
Q1:为什么在KeyTask()方法的for循环中要加vTaskDelay延迟,可以省略吗?
删掉该延迟后,KeyTask()方法无法进入阻塞,按钮任务始终执行,低优先级的任务没有执行的机会,因此不能省略。
Q2:为什么上方的按钮驱动程序没有消抖?
因为每次读取一次按钮状态后,要等10ms,而按钮抖动只有不到1ms,没有必要消抖。
编写通用按钮驱动程序
有时需要使用到多个按钮,为每个按钮单独编写程序很麻烦,而且难以维护。
因此需要编写通用按钮驱动程序。
驱动程序适合存放在Drivers文件夹中,因此在Drivers文件夹中创建key.c和key.h这2个文件。
key.c
12345678910111213141516171819 | void Key_Init(KeyHandle_TypeDef *Handle){ Handle->previous = GPIO_PIN_SET; Handle->current = GPIO_PIN_SET;//类型是GPIO_PinState,先都赋值成1} void Key_Scan(KeyHandle_TypeDef *Handle){ Handle->current = HAL_GPIO_ReadPin(Handle->GPIO_Port, Handle->GPIO_Pin);//读KEY引脚的值 if(Handle->previous == GPIO_PIN_RESET && Handle->current == GPIO_PIN_SET)//按钮被点击,即上一次为0,这一次为1 { Handle->ClickedCallback(); } Handle->previous = Handle->current;//更新值,为下一次做准备} |
key.h
123456789101112131415161718192021 | typedef struct//定义按键句柄结构体,将一个按键相关的信息封装在一起,方便管理多个按键:{ GPIO_PinState previous;//按钮前一时刻的状态 GPIO_PinState current;//按钮当前时刻的状态 GPIO_TypeDef *GPIO_Port;//按钮的端口号 uint16_t GPIO_Pin;//按钮的引脚号 void (*ClickedCallback)(void);//按钮点击后要执行的动作} KeyHandle_TypeDef; void Key_Init(KeyHandle_TypeDef *Handle);void Key_Scan(KeyHandle_TypeDef *Handle); |
Key_Task.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 | static KeyHandle_TypeDef hkey1;static KeyHandle_TypeDef hkey2; static void KEY1_ClickedCallback(void);static void KEY2_ClickedCallback(void); void Key1Task(void *pvParameters){ hkey1.GPIO_Port = KEY1_GPIO_Port; hkey1.GPIO_Pin = KEY1_Pin; hkey1.ClickedCallback = KEY1_ClickedCallback; Key_Init(&hkey1); for(;;) { Key_Scan(&hkey1); vTaskDelay(pdMS_TO_TICKS(10));//延迟10ms }} static void KEY1_ClickedCallback(void){ extern UART_HandleTypeDef huart1; HAL_UART_Transmit(&huart1, (const uint8_t *)"KEY1 Clicked.\n", strlen("KEY1 Clicked.\n"), HAL_MAX_DELAY);} void Key2Task(void *pvParameters){ hkey2.GPIO_Port = KEY2_GPIO_Port; hkey2.GPIO_Pin = KEY2_Pin; hkey2.ClickedCallback = KEY2_ClickedCallback; Key_Init(&hkey2); for(;;) { Key_Scan(&hkey2); vTaskDelay(pdMS_TO_TICKS(10));//延迟10ms }}static void KEY2_ClickedCallback(void){ extern UART_HandleTypeDef huart1; HAL_UART_Transmit(&huart1, (const uint8_t *)"KEY2 Clicked.\n", strlen("KEY1 Clicked.\n"), HAL_MAX_DELAY);} |
Key_Task.h
1234567 | void Key1Task(void *pvParameters);void Key2Task(void *pvParameters); |
程序现象是,按下KEY1按钮,串口会发送一条KEY1 Clicked.
按下KEY2按钮,串口会发送一条KEY2 Clicked.
拆分长代码
按照上面代码的逻辑,如果有多个按钮,可能会导致单个文件的代码过长,不容易维护。
因此需要把长代码拆分为多个文件。对key_task.c文件进行拆分。
例如有6个按钮,因此需要把key_task.c拆分为key1.c、key2.c、key3.c、key4.c、key5.c、key6.c。
在User文件夹下创建keys文件夹,把这些拆分的文件放在里面,专门存放不同按钮的代码。
Drivers -> key.c
1234567891011121314151617181920 | void Key_Init(KeyHandle_TypeDef *Handle){ Handle->previous = GPIO_PIN_SET; Handle->current = GPIO_PIN_SET;//类型是GPIO_PinState,先都赋值成1} void Key_Scan(KeyHandle_TypeDef *Handle){ Handle->current = HAL_GPIO_ReadPin(Handle->GPIO_Port, Handle->GPIO_Pin);//读KEY1引脚的值 if(Handle->previous == GPIO_PIN_RESET && Handle->current == GPIO_PIN_SET)//按钮被点击,即上一次为0,这一次为1 { Handle->ClickedCallback(); } Handle->previous = Handle->current;} |
User -> Tasks -> Key_Task.c
12345678910111213141516171819202122232425262728293031 | void KeyTask(void *pvParameters){ Key1_Init(); Key2_Init(); Key3_Init(); Key4_Init(); Key5_Init(); Key6_Init(); for(;;) { Key1_Scan(); Key2_Scan(); Key3_Scan(); Key4_Scan(); Key5_Scan(); Key6_Scan(); vTaskDelay(pdMS_TO_TICKS(10));//延迟10ms }} |
User -> Keys ->key1.c
12345678910111213141516171819202122232425 | static KeyHandle_TypeDef hkey1; static void KEY1_ClickedCallback(void); void Key1_Init(void)//初始化key1{ hkey1.GPIO_Port = KEY1_GPIO_Port; hkey1.GPIO_Pin = KEY1_Pin; hkey1.ClickedCallback = KEY1_ClickedCallback; Key_Init(&hkey1);} void Key1_Scan(void)//key1扫描{ Key_Scan(&hkey1);} static void KEY1_ClickedCallback(void)//key1的回调函数{ extern UART_HandleTypeDef huart1; HAL_UART_Transmit(&huart1, (const uint8_t *)"KEY1 Clicked.\n", strlen("KEY1 Clicked.\n"), HAL_MAX_DELAY);} |
其他按钮的程序与key1类似,不再赘述。
实验现象: