云计算百科
云计算领域专业知识百科平台

FreeRTOS(12):事件标志组实验

实验现象

实验步骤:

实验现象:

项目创建

复制Freertos_二值信号量实验文件,粘贴并且重命名为Freertos_事件标志组实验,进入keil,删除无关项,包括二值信号量的创建,task1和task2中的操作,在文件头上加上#include "event_groups.h",将task1与task2的任务优先级互换,下方是删除完后的代码:

#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "led.h"
#include "key.h"
#include "serial.h"
#include "semphr.h"
#include "event_groups.h"

/* 任务参数 */
#define START_TASK_SIZE 128
#define START_TASK_PRIO 1
#define TASK1_SIZE 256
#define TASK1_PRIO 3
#define TASK2_SIZE 256
#define TASK2_PRIO 2

TaskHandle_t start_task_handle;
TaskHandle_t task1_handle;
TaskHandle_t task2_handle;

void Start_task(void *pvParameters);
void task_1(void *pvParameters);
void task_2(void *pvParameters);

void FreeRTOS_Task(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
Key_Init();
LED_Init();
Serial_Init();

xTaskCreate((TaskFunction_t)Start_task, "Start_task", START_TASK_SIZE, NULL, START_TASK_PRIO, &start_task_handle);
vTaskStartScheduler();
}

void Start_task(void *pvParameters)
{
taskENTER_CRITICAL(); // 进入临界区,确保任务创建过程不被中断
xTaskCreate((TaskFunction_t)task_1, "task_1", TASK1_SIZE, NULL, TASK1_PRIO, &task1_handle);
xTaskCreate((TaskFunction_t)task_2, "task_2", TASK2_SIZE, NULL, TASK2_PRIO, &task2_handle);
taskEXIT_CRITICAL();
vTaskDelete(NULL); // 删除启动任务自身
}

void task_1(void *pvParameters)
{

while(1)
{

}
}

void task_2(void *pvParameters)
{
while(1)
{

}
}

来到start_task函数,在这个函数这里创建事件标志组,在创建之前应该在文件头定义事件标志组的句柄:EventGroupHandle_t eventgroup_handle,随后在start_task函数这里创建事件标志组,具体代码如下:

/**
* @brief 启动任务:创建事件组和两个应用任务,然后删除自身
*/
void Start_task(void *pvParameters)
{
taskENTER_CRITICAL();

/* 创建事件组 */
eventgroup_handle = xEventGroupCreate();
if (eventgroup_handle == NULL)
{
Serial_Sendstring("event group create failed\\r\\n");
}
else
{
Serial_Sendstring("event group create successful\\r\\n");
}

xTaskCreate((TaskFunction_t)task_1, "task_1",
TASK1_SIZE, NULL, TASK1_PRIO, &task1_handle);
xTaskCreate((TaskFunction_t)task_2, "task_2",
TASK2_SIZE, NULL, TASK2_PRIO, &task2_handle);

taskEXIT_CRITICAL();
vTaskDelete(NULL);
}

随后来到task1,在task1中应该完成按键扫描,消抖后将按键事件通知给事件组,KEY1 (keynum == 1) 设置位0 (0x01),KEY2 (keynum == 2) 设置位1 (0x02),下方是具体代码:

/**
* @brief 任务1:按键扫描,消抖后将按键事件通知给事件组
* – KEY1 (keynum == 1) 设置位0 (0x01)
* – KEY2 (keynum == 2) 设置位1 (0x02)
* 包含完整的上升沿消抖逻辑,确保单次按键只响应一次
*/
void task_1(void *pvParameters)
{
uint8_t keynum;
uint8_t last_key = 0; // 记录上一次的按键状态,检测上升沿

while (1)
{
keynum = Key_GetNum(); // 读取当前按键值

/* 按键消抖:仅当按键从未按下变为按下时(上升沿)才处理 */
if (keynum != 0 && last_key == 0)
{
vTaskDelay(30); // 消抖延时 30 ms
if (Key_GetNum() == keynum) // 再次读取确认按键仍按下
{
if (keynum == 1) // PB1 按下
{
xEventGroupSetBits(eventgroup_handle, 0x01);
Serial_Sendstring("PB1 pressed, set bit 0\\r\\n");
}
else if (keynum == 2) // PB2 按下
{
xEventGroupSetBits(eventgroup_handle, 0x02);
Serial_Sendstring("PB2 pressed, set bit 1\\r\\n");
}
}
}

last_key = keynum; // 更新上一次状态
vTaskDelay(10); // 10 ms 扫描一次,降低 CPU 占用
}
}

随后来到task2,等待事件组中的所有事件位被设置,读取后清除该位,并打印当前触发的事件位,下方是task2的代码:

/**
* @brief 任务2:等待事件组中的所有事件位被设置,
* 读取后清除该位,并打印当前触发的事件位
*/
void task_2(void *pvParameters)
{
EventBits_t event_bit = 0;

while (1)
{
/*
* 等待事件位 0x01 和 0x02 都被置一
* 参数说明:
* – 第三参数 pdTRUE:函数退出时会清除触发等待的那些位(这里是0x01或0x02)
* – 第四参数 pdTRUE:等待所有位置一
*/
event_bit = xEventGroupWaitBits(eventgroup_handle,
0x01 | 0x02, /* 感兴趣的事件位 */
pdTRUE, /* 退出时清除这些位 */
pdTRUE, /* 等待所有位 */
portMAX_DELAY);/* 永久阻塞等待 */
/*
* event_bit 返回值是成功读取时整个事件组的当前值(清除前),
* 与等待条件匹配的位会被置位。
*/
Serial_Sendstring("Event triggered: ");
Serial_SendHex(event_bit);
Serial_Sendstring("\\r\\n");
}
}

下方是完整的代码:

#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "led.h"
#include "key.h"
#include "serial.h"
#include "semphr.h"
#include "event_groups.h"

/* 任务参数 */
#define START_TASK_SIZE 128
#define START_TASK_PRIO 1
#define TASK1_SIZE 256
#define TASK1_PRIO 3
#define TASK2_SIZE 256
#define TASK2_PRIO 2

/* 任务句柄 */
TaskHandle_t start_task_handle;
TaskHandle_t task1_handle;
TaskHandle_t task2_handle;

/* 事件组句柄 */
EventGroupHandle_t eventgroup_handle;

void Start_task(void *pvParameters);
void task_1(void *pvParameters);
void task_2(void *pvParameters);

/**
* @brief 系统入口,创建启动任务并开启调度器
*/
void FreeRTOS_Task(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
Key_Init();
LED_Init();
Serial_Init();

xTaskCreate((TaskFunction_t)Start_task, "Start_task",
START_TASK_SIZE, NULL, START_TASK_PRIO, &start_task_handle);
vTaskStartScheduler();
}

/**
* @brief 启动任务:创建事件组和两个应用任务,然后删除自身
*/
void Start_task(void *pvParameters)
{
taskENTER_CRITICAL();

/* 创建事件组 */
eventgroup_handle = xEventGroupCreate();
if (eventgroup_handle == NULL)
{
Serial_Sendstring("event group create failed\\r\\n");
}
else
{
Serial_Sendstring("event group create successful\\r\\n");
}

xTaskCreate((TaskFunction_t)task_1, "task_1",
TASK1_SIZE, NULL, TASK1_PRIO, &task1_handle);
xTaskCreate((TaskFunction_t)task_2, "task_2",
TASK2_SIZE, NULL, TASK2_PRIO, &task2_handle);

taskEXIT_CRITICAL();
vTaskDelete(NULL);
}

/**
* @brief 任务1:按键扫描,消抖后将按键事件通知给事件组
* – KEY1 (keynum == 1) 设置位0 (0x01)
* – KEY2 (keynum == 2) 设置位1 (0x02)
* 包含完整的上升沿消抖逻辑,确保单次按键只响应一次
*/
void task_1(void *pvParameters)
{
uint8_t keynum;
uint8_t last_key = 0; // 记录上一次的按键状态,检测上升沿

while (1)
{
keynum = Key_GetNum(); // 读取当前按键值

/* 按键消抖:仅当按键从未按下变为按下时(上升沿)才处理 */
if (keynum != 0 && last_key == 0)
{
vTaskDelay(30); // 消抖延时 30 ms
if (Key_GetNum() == keynum) // 再次读取确认按键仍按下
{
if (keynum == 1) // PB1 按下
{
xEventGroupSetBits(eventgroup_handle, 0x01);
Serial_Sendstring("PB1 pressed, set bit 0\\r\\n");
}
else if (keynum == 2) // PB2 按下
{
xEventGroupSetBits(eventgroup_handle, 0x02);
Serial_Sendstring("PB2 pressed, set bit 1\\r\\n");
}
}
}

last_key = keynum; // 更新上一次状态
vTaskDelay(10); // 10 ms 扫描一次,降低 CPU 占用
}
}

/**
* @brief 任务2:等待事件组中的所有事件位被设置,
* 读取后清除该位,并打印当前触发的事件位
*/
void task_2(void *pvParameters)
{
EventBits_t event_bit = 0;

while (1)
{
/*
* 等待事件位 0x01 和 0x02 都被置一
* 参数说明:
* – 第三参数 pdTRUE:函数退出时会清除触发等待的那些位(这里是0x01或0x02)
* – 第四参数 pdTRUE:等待所有位置一
*/
event_bit = xEventGroupWaitBits(eventgroup_handle,
0x01 | 0x02, /* 感兴趣的事件位 */
pdTRUE, /* 退出时清除这些位 */
pdTRUE, /* 等待所有位 */
portMAX_DELAY);/* 永久阻塞等待 */
/*
* event_bit 返回值是成功读取时整个事件组的当前值(清除前),
* 与等待条件匹配的位会被置位。
*/
Serial_Sendstring("Event triggered: ");
Serial_SendHex(event_bit);
Serial_Sendstring("\\r\\n");
}
}

若有错误,还望海涵。

赞(0)
未经允许不得转载:网硕互联帮助中心 » FreeRTOS(12):事件标志组实验
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!