3. 二值信号量同步中断与任务
任务定义
在STM32F407嵌入式平台上,使用外部中断(PA0引脚,按键触发)与二值信号量实现中断与任务之间的同步。当按键按下产生下降沿中断时,中断服务程序仅负责给出信号量,不执行具体业务逻辑;一个LED控制任务阻塞等待该信号量,获取成功后翻转板载LED(PD14)状态。通过该案例掌握FreeRTOS二值信号量在中断与任务间传递事件的核心用法。
硬件连接与原理
- PA0 — 按键(外部下拉,按下为高电平,释放为低电平,配置下降沿触发)
- PD14 — 板载LED(高电平点亮)
中断服务程序中调用xSemaphoreGiveFromISR给出信号量;LED任务调用xSemaphoreTake阻塞等待。每次按键按下,任务被唤醒并翻转LED状态,实现中断事件到任务处理的同步传递。
代码实现
/* FreeRTOS + STM32F407 二值信号量中断同步示例 */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "stm32f4xx_hal.h"
SemaphoreHandle_t xBinarySemaphore;
/* LED任务:等待信号量,翻转LED */
void vLEDTask(void *pvParameters) {
for (;;) {
if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE) {
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_14);
}
}
}
/* 外部中断回调:给出信号量 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (GPIO_Pin == GPIO_PIN_0) {
xSemaphoreGiveFromISR(xBinarySemaphore, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
/* GPIO初始化:PA0输入下降沿中断,PD14输出 */
static void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
HAL_NVIC_SetPriority(EXTI0_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
/* EXTI0中断服务函数 */
void EXTI0_IRQHandler(void) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}
int main(void) {
HAL_Init();
MX_GPIO_Init();
xBinarySemaphore = xSemaphoreCreateBinary();
xTaskCreate(vLEDTask, "LED", 128, NULL, 1, NULL);
vTaskStartScheduler();
for (;;);
}
关键实现说明
- 二值信号量在创建后初始为“空”状态,任务首次调用
xSemaphoreTake时阻塞等待。 - 中断中使用
xSemaphoreGiveFromISR,并通过portYIELD_FROM_ISR判断是否需要任务切换,保证实时性。 - LED翻转逻辑完全在任务上下文中执行,中断只做“给出信号量”这一最小操作,符合中断服务程序“快进快出”原则。
验证方法
编译并烧录程序后,连接串口调试助手(波特率115200)用于观察系统运行状态。操作步骤与判定标准如下:
- 系统上电后,LED保持熄灭状态(初始为低电平)。
- 每次按下按键(PA0接地),LED状态翻转一次:熄灭→点亮→熄灭→点亮……
- 快速连续按键时,每次按键均对应一次LED翻转,无丢失响应。
- 若在任务中加入串口打印(可选),每次获取信号量后输出一条日志,可进一步确认中断与任务的同步时序。
满足上述现象即可判定二值信号量中断同步功能实现正确。
信号量同步机制示意
工程配置要点
- 在FreeRTOSConfig.h中确保
configUSE_TIMERS未开启(本案例不使用软件定时器),configMAX_PRIORITIES大于等于2。 - 中断优先级分组设置为4位抢占优先级(HAL_Init默认),EXTI0抢占优先级设为5,低于FreeRTOS可管理中断的最高优先级(
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY通常为5),确保GiveFromISR可安全调用。 - 堆栈大小128字(512字节)足够本任务使用,若增加串口打印需适当增大。