FreeRTOS 事件
实验概述
本次实验主要使用 FreeRTOS 实现了一个简单的按键检测和事件处理系统。通过创建多个任务(Key_Task、Event_Task 和 LED_Task),实现了按键状态的检测、事件的设置与等待以及 LED 的切换。
代码结构
主函数 main
int main(void)
{
/* 初始化硬件 */
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
/* 创建初始化任务 */
xTaskCreate(InitializeTasks, "init_Task", configMINIMAL_STACK_SIZE, NULL, 1, &InitializeTasks_Handle);
/* 启动调度器 */
vTaskStartScheduler();
while (1)
{
// 无限循环,实际不会执行到这里
}
}
主要步骤
- 初始化硬件:调用
HAL_Init()初始化 HAL 库,SystemClock_Config()配置系统时钟,MX_GPIO_Init()初始化 GPIO,MX_USART2_UART_Init()初始化 UART。 - 创建初始化任务
InitializeTasks,并将其放入任务队列。 - 启动 FreeRTOS 调度器,开始多任务调度。
初始化任务 InitializeTasks
void InitializeTasks(void *pvParameters)
{
taskENTER_CRITICAL();
eventGroup = xEventGroupCreate(); // 创建事件组
// 创建三个任务:Key_Task, Event_Task, LED_Task
xTaskCreate(Key_Task, "Key_Task", configMINIMAL_STACK_SIZE, NULL, 3, &Key_Task_Handle);
xTaskCreate(Event_Task, "Event_Task", configMINIMAL_STACK_SIZE, NULL, 1, &Event_Task_Handle);
xTaskCreate(LED_Task, "LED_Task", configMINIMAL_STACK_SIZE, NULL, 2, &LED_Task_Handle);
// 删除初始化任务
vTaskDelete(InitializeTasks_Handle);
taskEXIT_CRITICAL();
}
主要步骤
- 进入临界区,防止中断打断任务创建过程。
- 使用
xEventGroupCreate()创建一个事件组,用于任务间通信。 - 使用
xTaskCreate()创建三个任务:Key_Task、Event_Task和LED_Task,并设置各自的优先级。 - 删除初始化任务,释放资源。
按键检测任务 Key_Task
void Key_Task(void *pvParameters)
{
while (1)
{
if (Key_Scan(GPIOA, GPIO_PIN_10) == 1)
{
xEventGroupSetBits(eventGroup, KEY1_EVENT_BIT); // 设置按键1事件位
printf("Key 1 Pressed\r\n");
}
if (Key_Scan(GPIOB, GPIO_PIN_4) == 1)
{
xEventGroupSetBits(eventGroup, KEY2_EVENT_BIT); // 设置按键2事件位
printf("Key 2 Pressed\r\n");
}
vTaskDelay(pdMS_TO_TICKS(200)); // 延时200毫秒
}
}
主要步骤
- 在无限循环中不断检测按键状态。
- 使用
Key_Scan()函数检测按键 GPIO 引脚的状态,如果按键按下,则使用xEventGroupSetBits()设置相应的事件位,并打印按键信息。 - 使用
vTaskDelay()添加延时,避免频繁检测导致 CPU 占用过高。
按键事件处理任务 Event_Task
void Event_Task(void *pvParameters)
{
while (1)
{
EventBits_t eventBits = xEventGroupWaitBits(eventGroup, KEY1_EVENT_BIT | KEY2_EVENT_BIT, pdTRUE, pdTRUE, portMAX_DELAY); // 等待按键事件
if (eventBits & (KEY1_EVENT_BIT | KEY2_EVENT_BIT) == (KEY1_EVENT_BIT | KEY2_EVENT_BIT))
{
printf("Both Key 1 and Key 2 Pressed\r\n");
}
}
}
主要步骤
- 在无限循环中等待按键事件。
- 使用
xEventGroupWaitBits()等待KEY1_EVENT_BIT和KEY2_EVENT_BIT两个事件位同时被设置。 - 如果两个按键同时按下,则打印相关信息。
LED 切换任务 LED_Task
void LED_Task(void *pvParameters)
{
while (1)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 切换LED状态
vTaskDelay(pdMS_TO_TICKS(500)); // 延时500毫秒
}
}
主要步骤
- 在无限循环中不断切换 LED 状态。
- 使用
HAL_GPIO_TogglePin()切换 LED 的 GPIO 引脚状态。 - 使用
vTaskDelay()添加延时,控制 LED 的闪烁频率。
按键扫描函数 Key_Scan
uint8_t Key_Scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
// 读取当前按键状态
uint8_t keyState = (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET) ? 1 : 0;
if (keyState == 1)
{
vTaskDelay(pdMS_TO_TICKS(50));
// 再次读取按键状态,确认是否真的变化
uint8_t keyState_new = (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET) ? 1 : 0;
if (1 == keyState_new)
{
return 1; // 按键按下
}
}
return 0; // 按键未按下
}
主要步骤
- 读取指定 GPIO 引脚的按键状态。
- 如果按键状态为按下(
GPIO_PIN_RESET),则添加延时以去抖动。 - 再次读取按键状态,确认按键确实被按下。
- 返回按键状态:1 表示按键按下,0 表示按键未按下。
标准输出重定向函数 fputc
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
主要步骤
- 将字符
ch通过 UART 发送到串口。 - 使用
HAL_UART_Transmit()发送数据。 - 返回发送的字符。
实验结果
根据实验结果,系统能够正确检测按键状态,并在按键按下时设置相应的事件位。具体输出如下:
FreeRTOS STM32F1xx Example
Key 1 Pressed
Key 2 Pressed
Both Key 1 and Key 2 Pressed
Key 2 Pressed
Key 1 Pressed
Both Key 1 and Key 2 Pressed
总结
本次实验通过 FreeRTOS 实现了按键检测和事件处理,展示了如何使用事件组进行任务间的通信。通过创建多个任务并设置不同的优先级,实现了系统的多任务调度。此外,还展示了如何重定向标准输出,将调试信息通过串口发送到终端。
参考资料
- FreeRTOS 官方文档
- STM32 HAL 库文档
FreeRTOS实现按键检测与事件处理实验
258

被折叠的 条评论
为什么被折叠?



