STM32F1与STM32CubeIDE编程实例-CMSIS-RTOS V2-事件标志(Event Flags)

事件标志(Event Flags)

CMSIS-RTOS v2 (CMSIS-RTOS2) 为基于 Arm® Cortex® 处理器的设备提供通用 RTOS 接口。 它为需要 RTOS 功能的软件组件提供标准化 API,从而为用户和软件行业带来巨大的好处。

本文将详细介绍在CMSIS-RTOS V2中如何使用事件标志(Event Flags)。

1、事件标志介绍

CMSIS-RTOS 中的事件标志管理功能允许您控制或等待事件标志。 每个信号最多有 31 个事件标志。

线程对事件有如下操作:

  • 可以等待设置事件标志(使用 osEventFlagsWait)。 使用此功能,它进入 BLOCKED 状态。
  • 可以在任何其他给定线程中设置一个或多个标志(使用 osEventFlagsSet)。
  • 可以清除自己的信号或其他线程的信号(使用 osEventFlagsClear)。

当线程唤醒并恢复执行时,其信号标志会自动清除(除非指定了事件标志选项 osFlagsNoClear)。

1
2
注意:函数 osEventFlagsSet、osEventFlagsClear、osEventFlagsGet 和 osEventFlagsWait 可以从中断服务程序中调用。

事件操作流程如下:

在这里插入图片描述

一个简单的事件标志示例如下:

  • 在向发送 id 为 sig1_id 的事件的线程中,调用 set 函数:
1
2
3
osDelay(1000U);                                           // wait for 1 second
osEventFlagsSet(sig1_id, 0x0001U); // set the flag 0x0001U for event sig1\_id

  • 在应该等待事件的另一个线程(或多个线程)中,调用等待函数:
1
2
osEventFlagsWait(sig1_id, 0x0001U, NULL, osWaitForever);  // wait forever for any flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include "cmsis\_os2.h" // CMSIS RTOS header file

/\*----------------------------------------------------------------------------
\* Event Flags creation & usage
\*---------------------------------------------------------------------------\*/

#define FLAGS\_MSK1 0x00000001U

osEventFlagsId\_t evt_id; // event flags id

osThreadId\_t tid_Thread_EventSender; // thread id 1
osThreadId\_t tid_Thread_EventReceiver; // thread id 2

void Thread\_EventSender (void \*argument); // thread function 1
void Thread\_EventReceiver (void \*argument); // thread function 2

int Init\_Events (void) {

evt_id = osEventFlagsNew(NULL);
if (evt_id == NULL) {
; // Event Flags object not created, handle failure
}

tid_Thread_EventSender = osThreadNew(Thread_EventSender, NULL, NULL);
if (tid_Thread_EventSender == NULL) {
return(-1);
}
tid_Thread_EventReceiver = osThreadNew(Thread_EventReceiver, NULL, NULL);
if (tid_Thread_EventReceiver == NULL) {
return(-1);
}
return(0);
}

void Thread\_EventSender (void \*argument) {

while (1) {
osEventFlagsSet(evt_id, FLAGS_MSK1);
osThreadYield(); // suspend thread
}
}

void Thread\_EventReceiver (void \*argument) {
uint32\_t flags;

while (1) {
flags = osEventFlagsWait(evt_id, FLAGS_MSK1, osFlagsWaitAny, osWaitForever);
//handle event
}
}

2、CMSIS-RTOS V2的事件标志API

在这里插入图片描述

事件标志属性osEventFlagsAttr_t的定义如下:

1
2
3
4
5
6
7
8
/// Attributes structure for event flags.
typedef struct {
const char \*name; ///< name of the event flags
uint32\_t attr_bits; ///< attribute bits
void \*cb_mem; ///< memory for control block
uint32\_t cb_size; ///< size of provided memory for control block
} osEventFlagsAttr\_t;

  • **osEventFlagsId_t osEventFlagsNew(const osEventFlagsAttr_t * attr)**:创建一个新的事件标志对象,该对象用于跨线程发送事件并返回指向事件标志对象标识符的指针或在发生错误时返回 NULL。 它可以在 RTOS 启动之前安全地调用(调用 osKernelStart),但不能在初始化之前调用(调用 osKernelInitialize)。

参数attr 设置事件标志属性(参考osEventFlagsAttr_t)。 如果设置为 NULL,将使用默认属性,即内核内存分配用于事件控制块。

注意,该函数不能在中断函数中调用

简单示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "cmsis\_os2.h" // CMSIS RTOS header file

osEventFlagsId\_t evt_id; // message queue id

int Init\_Events (void) {

evt_id = osEventFlagsNew(NULL);
if (evt_id == NULL) {
; // Event Flags object not created, handle failure
return(-1);
}
return(0);
}

  • **uint32_t osEventFlagsSet(osEventFlagsId_t ef_id,uint32_t flags)**:函数 osEventFlagsSet 在由参数 ef_id 指定的事件标志对象中设置由参数标志指定的事件标志。等待标志集的具有最高优先级的线程将被通知从 BLOCKED 状态恢复。 该函数返回存储在事件控制块中的事件标志或错误代码(设置了最高位,请参阅标志函数错误代码)。 当向 osEventFlagsWait 调用提供选项 osFlagsNoClear 时,可以按优先级顺序唤醒更多线程。

可能的标志函数错误代码返回值如下:

+ **osFlagsErrorUnknown**:未指定的错误。
+ **osFlagsErrorParameter**:参数 ef\_id 未识别有效的事件标志对象或标志设置了最高位。
+ **osFlagsErrorResource**:事件标志对象处于无效状态。`注意:该函数支持在中断函数中调用`

简单示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
#include "cmsis\_os2.h" // CMSIS RTOS header file

osEventFlagsId\_t evt_id; // event flags id

void Thread\_EventSender (void \*argument) {

while (1) {
osEventFlagsSet(evt_id, 0x00000001U);
osThreadYield(); // suspend thread
}
}

  • **uint32_t osEventFlagsWait(osEventFlagsId_t ef_id,uint32_t flags,uint32_t options,uint32_t timeout)**:函数 osEventFlagsWait 暂停当前 RUNNING 线程的执行,直到设置了由参数 ef_id 指定的事件对象中的参数 flags 指定的任何或所有事件标志。 当这些事件标志已经设置时,函数会立即返回。 否则,线程将进入 BLOCKED 状态。

options 参数指定等待条件:

+ **osFlagsWaitAny**:等待任何标志(默认)
+ **osFlagsWaitAll**:等待所有标志。
+ **osFlagsNoClear**:不要清除已指定等待的标志。如果在选项 osEventFlagsClear 中设置了 osFlagsNoClear 可用于手动清除标志。

参数 timeout 指定系统等待事件标志的时间。 在系统等待期间,调用此函数的线程将进入 BLOCKED 状态。 参数 timeout 可以有以下值:

+ 当 timeout 为 0 时,函数立即返回(即尝试语义)。
+ 当超时设置为 osWaitForever 时,该函数将等待无限时间,直到事件标志可用(即等待语义)。
+ 所有其他值在内核滴答中指定超时时间(即定时等待语义)。该函数返回清除前的事件标志或错误代码(设置了最高位,请参阅标志函数错误代码)。

返回的错误代码如下:

+ **osFlagsErrorUnknown**:未指定的错误。
+ **osFlagsErrorTimeout**:在给定时间内未设置等待的标志。
+ **osFlagsErrorResource**:未指定超时时未设置等待标志。
+ **osFlagsErrorParameter**:参数 ef\_id 未识别有效的事件标志对象或标志设置了最高位。`注意:如果参数 timeout 设置为 0,则可以从中断服务程序调用。`

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include "cmsis\_os2.h" // CMSIS RTOS header file

osEventFlagsId\_t evt_id; // event flasg id

void Thread\_EventReceiver (void \*argument) {
uint32\_t flags;

while (1) {
flags = osEventFlagsWait(evt_id, 0x00000001U, osFlagsWaitAny, osWaitForever);
//handle event
}
}

其他API函数请参考文档,在这里就不展开描述了。

3、在STM32CubeIDE中配置事件标志

第一步: 新建工程

新建工程,请参考前面文章:

串口配置,请参考前面文章:

第二步: 配置时钟

配置时钟,请参考前面文章:

第三步:配置FreeRTOS

配置FreeRTOS,及FreeRTOS的堆栈,请参考:

第四步:添加线程

创建线程,请参考前面文章:

在本次实例中定义的线程如下:

在这里插入图片描述

定义了两个事件发送线程和一个事件监听线程。

第五步:添加事件标志

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

第六步:保存配置并生成代码

4、事件标志操作逻辑代码实现

main.c文件的main函数中,RTOS启动调用如下:

1
2
3
4
5
6
7
/\* Init scheduler \*/
osKernelInitialize(); /\* Call init function for freertos objects (in freertos.c) \*/
MX\_FREERTOS\_Init();

/\* Start scheduler \*/
osKernelStart();

freertos.c文件中,生成定义的事件标志、线程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/\* Definitions for defaultTask \*/
osThreadId\_t defaultTaskHandle;
const osThreadAttr\_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 128 \* 4,
.priority = (osPriority\_t) osPriorityNormal,
};
/\* Definitions for eventSender1 \*/
osThreadId\_t eventSender1Handle;
const osThreadAttr\_t eventSender1_attributes = {
.name = "eventSender1",
.stack_size = 128 \* 4,
.priority = (osPriority\_t) osPriorityNormal,
};
/\* Definitions for eventSender2 \*/
osThreadId\_t eventSender2Handle;
const osThreadAttr\_t eventSender2_attributes = {
.name = "eventSender2",
.stack_size = 128 \* 4,
.priority = (osPriority\_t) osPriorityNormal,
};
/\* Definitions for eventReceiver \*/
osThreadId\_t eventReceiverHandle;
const osThreadAttr\_t eventReceiver_attributes = {
.name = "eventReceiver",
.stack_size = 128 \* 4,
.priority = (osPriority\_t) osPriorityNormal,
};
/\* Definitions for myEvent01 \*/
osEventFlagsId\_t myEvent01Handle;
const osEventFlagsAttr\_t myEvent01_attributes = {
.name = "myEvent01"
};

1
2
3
4
5
void StartDefaultTask(void \*argument);
void eventSender1Task(void \*argument);
void eventSender2Task(void \*argument);
void eventReceiverTask(void \*argument);

自定义事件如下:

1
2
3
4
5
6
/\* Private define ------------------------------------------------------------\*/
/\* USER CODE BEGIN PD \*/
#define FLAGS\_MSK1 0x00000001U
#define FLAGS\_MSK2 0x00000010U
/\* USER CODE END PD \*/

各线程实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/\* USER CODE BEGIN Header\_StartDefaultTask \*/
/\*\*
\* @brief Function implementing the defaultTask thread.
\* @param argument: Not used
\* @retval None
\*/
/\* USER CODE END Header\_StartDefaultTask \*/
void StartDefaultTask(void \*argument) {
/\* USER CODE BEGIN StartDefaultTask \*/
/\* Infinite loop \*/
for (;;) {
osDelay(1);
}
/\* USER CODE END StartDefaultTask \*/
}

/\* USER CODE BEGIN Header\_eventSender1Task \*/
/\*\*
\* @brief Function implementing the eventSender1 thread.
\* @param argument: Not used
\* @retval None
\*/
/\* USER CODE END Header\_eventSender1Task \*/
void eventSender1Task(void \*argument) {
/\* USER CODE BEGIN eventSender1Task \*/
/\* Infinite loop \*/
for (;;) {
printf("sender:set event flag\r\n");
osDelay(1000);
osEventFlagsSet(myEvent01Handle, FLAGS_MSK1);
osThreadYield(); // suspend thread
}
/\* USER CODE END eventSender1Task \*/
}

/\* USER CODE BEGIN Header\_eventSender2Task \*/
/\*\*
\* @brief Function implementing the eventSender2 thread.
\* @param argument: Not used
\* @retval None
\*/
/\* USER CODE END Header\_eventSender2Task \*/
void eventSender2Task(void \*argument) {
/\* USER CODE BEGIN eventSender2Task \*/
/\* Infinite loop \*/
for (;;) {
printf("sender2:set event flag\r\n");
osDelay(500);
osEventFlagsSet(myEvent01Handle, FLAGS_MSK2);
osThreadYield();
}
/\* USER CODE END eventSender2Task \*/
}

/\* USER CODE BEGIN Header\_eventReceiverTask \*/
/\*\*
\* @brief Function implementing the eventReceiver thread.
\* @param argument: Not used
\* @retval None
\*/
/\* USER CODE END Header\_eventReceiverTask \*/
void eventReceiverTask(void \*argument) {
/\* USER CODE BEGIN eventReceiverTask \*/
/\* Infinite loop \*/
for (;;) {
flags = osEventFlagsWait(myEvent01Handle, FLAGS_MSK1 | FLAGS_MSK2,
osFlagsWaitAny,
osWaitForever);
//handle event

if ((flags & (FLAGS_MSK1 | FLAGS_MSK2)) == (FLAGS_MSK1 | FLAGS_MSK2)) {
printf("received event:FLAGS\_MSK1 | FLAGS\_MSK2\r\n");
} else if ((flags & FLAGS_MSK1) == FLAGS_MSK1) {
printf("received event:FLAGS\_MSK1\r\n");
} else if ((flags & FLAGS_MSK2) == FLAGS_MSK2) {
printf("received event:FLAGS\_MSK2\r\n");
}
}
/\* USER CODE END eventReceiverTask \*/
}

最后,编译生成程序并下载到开板板。

5、CMSIS-RTOS V2.0.0中与FreeRTOS10.0.1中Bug修复

在中断中调用osEventFlagsSet函数,会导致返回osErrorResource的结果,原因是osEventFlagsSet函数的逻辑错误,修复方法如下:

cmsis_os2.c文件中,定位到osEventFlagsSet函数,该函数实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
uint32\_t osEventFlagsSet (osEventFlagsId\_t ef_id, uint32\_t flags) {
EventGroupHandle_t hEventGroup = (EventGroupHandle_t)ef_id;
uint32\_t rflags;
BaseType_t yield;

if ((hEventGroup == NULL) || ((flags & EVENT_FLAGS_INVALID_BITS) != 0U)) {
rflags = (uint32\_t)osErrorParameter;
}
else if (IS\_IRQ()) {
yield = pdFALSE;

if (xEventGroupSetBitsFromISR (hEventGroup, (EventBits_t)flags, &yield) != pdFAIL) {
rflags = (uint32\_t)osErrorResource;
} else {
rflags = flags;
portYIELD\_FROM\_ISR (yield);
}
}
else {
rflags = xEventGroupSetBits (hEventGroup, (EventBits_t)flags);
}

return (rflags);
}

只需将下面代码:

1
2
if (xEventGroupSetBitsFromISR (hEventGroup, (EventBits_t)flags, &yield) != pdFAIL)

修改为:

1
2
if (xEventGroupSetBitsFromISR (hEventGroup, (EventBits_t)flags, &yield) == pdFAIL)

另外,在CMSIS-OS2的源码提交中,请参考该Bug的修改方法

在这里插入图片描述

6、STM32F1与STM32CubeIDE快速入门系列文章

序号 内容
1 STM32F1与STM32CubeIDE快速入门-开发环境搭建
2 STM32F1与STM32CubeIDE快速入门-STM32F1微控制器概述
3 STM32F1与STM32CubeIDE快速入门-GPIO概述与点亮LED
4 STM32F1与STM32CubeIDE快速入门-按键与LED控制
5 STM32F1与STM32CubeIDE快速入门-中断、NVIC与EXTI概述
6 STM32F1与STM32CubeIDE快速入门-外部中断配置与功能实现
7 STM32F1与STM32CubeIDE快速入门-USART/UART串口通信
8 STM32F1与STM32CubeIDE快速入门-定时器(Timer)概述
9 STM32F1与STM32CubeIDE快速入门-定时器定时模式
10 STM32F1与STM32CubeIDE快速入门-定时器计数模式
11 STM32F1与STM32CubeIDE快速入门-定时器PWM模式
12 STM32F1与STM32CubeIDE快速入门-定时器编码(Encoder)模式
13 STM32F1与STM32CubeIDE快速入门-定时器输入捕获模式(Input Capture Mode)实现频率计数
14 STM32F1与STM32CubeIDE快速入门-DMA概述
15 STM32F1与STM32CubeIDE快速入门-USART通过DMA进行数据接收与发送
16 STM32F1与STM32CubeIDE快速入门-ADC概述
17 STM32F1与STM32CubeIDE快速入门-ADC轮询方式实现PWM调光器
18 STM32F1与STM32CubeIDE快速入门-ADC中断方式实现PWM调光器
19 STM32F1与STM32CubeIDE快速入门-ADC通过DMA方式与PWM实现调光器
20 STM32F1与STM32CubeIDE快速入门-DAC概述
21 STM32F1与STM32CubeIDE快速入门-SPI概述
22 STM32F1与STM32CubeIDE快速入门-M25P16串行闪存驱动
23 STM32F1与STM32CubeIDE快速入门-I2C概述
24 STM32F1与STM32CubeIDE快速入门-I2C驱动LCD1602显示屏(基于PCF8574)
25 STM32F1与STM32CubeIDE快速入门-独立看门狗(IWDG)
26 STM32F1与STM32CubeIDE快速入门-OLED-SSD1306-I2C驱动
27 STM32F1与STM32CubeIDE编程实例-CMSIS-RTOS V2配置(基于FreeRTOS)
28 STM32F1与STM32CubeIDE编程实例-CMSIS-RTOS V2-线程管理
29 STM32F1与STM32CubeIDE编程实例-CMSIS-RTOS v2-延时
30 STM32F1与STM32CubeIDE编程实例-CMSIS-RTOS V2-定时器管理
31 STM32F1与STM32CubeIDE编程实例-CMSIS-RTOS V2-互斥(Mutex)管理
32 STM32F1与STM32CubeIDE编程实例-CMSIS-RTOS V2-信号量(Semaphore)
33 STM32F1与STM32CubeIDE编程实例-CMSIS-RTOS V2-消息队列

文章来源: https://iotsmart.blog.csdn.net/article/details/124724560