FreeRTOS 事件

FreeRTOS实现按键检测与事件处理实验

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)
    {
        // 无限循环,实际不会执行到这里
    }
}
主要步骤
  1. 初始化硬件:调用 HAL_Init() 初始化 HAL 库,SystemClock_Config() 配置系统时钟,MX_GPIO_Init() 初始化 GPIO,MX_USART2_UART_Init() 初始化 UART。
  2. 创建初始化任务 InitializeTasks,并将其放入任务队列。
  3. 启动 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();
}
主要步骤
  1. 进入临界区,防止中断打断任务创建过程。
  2. 使用 xEventGroupCreate() 创建一个事件组,用于任务间通信。
  3. 使用 xTaskCreate() 创建三个任务:Key_TaskEvent_TaskLED_Task,并设置各自的优先级。
  4. 删除初始化任务,释放资源。

按键检测任务 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毫秒
    }
}
主要步骤
  1. 在无限循环中不断检测按键状态。
  2. 使用 Key_Scan() 函数检测按键 GPIO 引脚的状态,如果按键按下,则使用 xEventGroupSetBits() 设置相应的事件位,并打印按键信息。
  3. 使用 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");
        }
    }
}
主要步骤
  1. 在无限循环中等待按键事件。
  2. 使用 xEventGroupWaitBits() 等待 KEY1_EVENT_BITKEY2_EVENT_BIT 两个事件位同时被设置。
  3. 如果两个按键同时按下,则打印相关信息。

LED 切换任务 LED_Task

void LED_Task(void *pvParameters)
{
    while (1)
    {
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 切换LED状态
        vTaskDelay(pdMS_TO_TICKS(500));         // 延时500毫秒
    }
}
主要步骤
  1. 在无限循环中不断切换 LED 状态。
  2. 使用 HAL_GPIO_TogglePin() 切换 LED 的 GPIO 引脚状态。
  3. 使用 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; // 按键未按下
}
主要步骤
  1. 读取指定 GPIO 引脚的按键状态。
  2. 如果按键状态为按下(GPIO_PIN_RESET),则添加延时以去抖动。
  3. 再次读取按键状态,确认按键确实被按下。
  4. 返回按键状态:1 表示按键按下,0 表示按键未按下。

标准输出重定向函数 fputc

int fputc(int ch, FILE *f)
{
    HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
    return ch;
}
主要步骤
  1. 将字符 ch 通过 UART 发送到串口。
  2. 使用 HAL_UART_Transmit() 发送数据。
  3. 返回发送的字符。

实验结果

根据实验结果,系统能够正确检测按键状态,并在按键按下时设置相应的事件位。具体输出如下:

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 事件处理机制 FreeRTOS 中的事件主要用于事件类型的通讯,无数据传输。这意味着可以利用事件作为标志位来判断某些事件是否已经发生并据此做出相应的处理[^1]。 #### 使用场景对比分析 在裸机编程环境中,使用全局变量作为标志位是一种常见做法;然而,在引入操作系统的情况下,这种方法存在诸多弊端: - **资源消耗**:任务需不断轮询检查全局变量状态,造成不必要的 CPU 占用。 - **并发冲突**:多任务环境下难以确保对共享资源的安全访问。 - **复杂度增加**:开发者还需自行实现诸如等待超时等功能[^3]。 相比之下,采用 FreeRTOS 提供的事件机制能够简化开发流程,提高程序健壮性和效率。 #### 多重条件下的协调工作 对于需要依赖多项前置条件才能继续执行的情况(例如设备初始化过程中涉及多个参数校验),可以通过组合不同事件的方式轻松达成目标——只有当所有预期中的事件都被触发后才会放行后续动作。 ### 技术细节与实践指南 为了支持更灵活的任务间协作模式,特别是针对那些可能涉及到同多个对象交互的需求,FreeRTOS 引入了「事件标志组」的概念。这一特性允许单个接收方监听来自若干源头的消息集合,并依据预设逻辑作出响应[^4]。 以下是基于 C 语言的一个简易实例展示如何创建和运用此类结构体成员完成基本功能: ```c // 定义必要的头文件包含语句以及配置选项设定 #include "FreeRTOS.h" #include "event_groups.h" EventGroupHandle_t xCreatedEventGroup; void setup_events(void *pvParameters){ // 创建一个新的事件群组 xCreatedEventGroup = xEventGroupCreate(); while(1){ // 模拟外部输入改变导致特定事件被激活的过程... vTaskDelay(pdMS_TO_TICKS(500)); if(/* some condition */){ // 设置某项具体事务对应的标记位 xEventGroupSetBits(xCreatedEventGroup, bit_0); } if(/* another condition */){ // 设定另一项关联事项的状态指示符 xEventGroupSetBits(xCreatedEventGroup, bit_1); } } } void wait_for_events(void *pvParameters){ EventBits_t uxReturn; while (1) { // 阻塞直到指定的一系列事件均已完成为止 uxReturn = xEventGroupWaitBits( xCreatedEventGroup, bit_0 | bit_1, /* 等待这两者 */ pdTRUE, /* 自动清除匹配上的bit*/ pdFALSE, /* 不强制要求全部满足即可返回 */ portMAX_DELAY ); /* 无限期等待 */ if((uxReturn & (bit_0|bit_1)) == (bit_0|bit_1)){ // 当两个事件都已准备好时采取行动 printf("Both events occurred.\n"); // 清除相关联的事件标记以便下次循环正常使用 xEventGroupClearBits(xCreatedEventGroup, bit_0 | bit_1 ); }else{ // 如果有其他未预见的情形发生,则在此处添加额外处理分支 } } } ``` 此代码片段展示了怎样定义、初始化一个 `EventGroup` 类型的对象,并通过调用 API 接口函数对其进行操作以达到跨线程间的高效沟通目的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值