引自免费微信小程序:皆我百晓生
在FreeRTOS环境下,STM32的GPIO按键处理需要考虑中断与任务同步的问题。上述代码片段通过轮询方式检测PB1引脚上的按键状态,这种实现方式可能导致按键响应不灵敏或系统卡死,原因有以下几点:
-
任务调度延迟:vTaskDelay(20)
设置了20个系统ticks的延时,但在实时操作系统中,其他更高优先级的任务可能抢占当前任务,导致按键事件未被及时捕获。
-
按键抖动:物理按键按下和释放时会有电气信号的抖动,直接在主循环中连续读取两次可能会错过有效电平变化,建议使用硬件滤波或者软件去抖动方法。
-
卡死问题:while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1) == 0);
这句代码会导致任务在按键一直按下的情况下阻塞,无法执行其他任何任务,从而导致系统“卡死”。
为解决这些问题,推荐采用中断的方式来处理按键事件,同时配合信号量、事件组等机制进行任务间同步。以下是改进后的示例代码:
#include "stm32fxxx_hal.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
SemaphoreHandle_t xKeySemphr = NULL;
void EXTI1_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1) == 0)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(xKeySemphr, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
void vButtonTask(void *pvParameters)
{
for (;;)
{
xSemaphoreTake(xKeySemphr, portMAX_DELAY);
// 按键按下后的工作
key_num = 2;
// 延迟或其他操作...
}
}
int main(void)
{
// 初始化HAL库、FreeRTOS及GPIO
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
// 创建信号量
xKeySemphr = xSemaphoreCreateBinary();
// 设置EXTI中断线,关联到GPIO_PIN_1
HAL_NVIC_SetPriority(EXTI1_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
HAL_GPIO_EXTI_Callback(GPIO_PIN_1, EXTI1_IRQHandler);
// 创建并启动按键处理任务
xTaskCreate(vButtonTask, "ButtonTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
vTaskStartScheduler();
while (1);
}
这段代码首先创建了一个二值信号量(Semaphore)用于按键事件通知,然后在EXTI中断服务程序中,当检测到PB1引脚按键按下时,给予信号量,并触发任务切换。在按键处理任务中,通过等待信号量来获取按键事件,这样即使按键长时间按下也不会导致任务阻塞和系统卡死。