STM32F1与STM32CubeIDE编程实例-CMSIS-RTOS V2-消息队列 消息队列 CMSIS-RTOS v2 (CMSIS-RTOS2) 为基于 Arm® Cortex® 处理器的设备提供通用 RTOS 接口。 它为需要 RTOS 功能的软件组件提供标准化 API,从而为用户和软件行业带来巨大的好处。
本文将详细介绍在CMSIS-RTOS V2中如何使用队列(Message Queue)。
1、消息队列介绍 一个队列可以容纳有限数量的固定大小的数据项。 队列可以容纳的最大项目数称为“长度”。 每个数据项的长度和大小都是在创建队列时设置的。
队列通常用作先进先出 (FIFO) 缓冲区,其中数据被写入队列的末端(尾部)并从队列的前端(头部)移除。 下图演示了正在用作 FIFO 的队列中写入和读取的数据。 也可以写入队列的前面,并覆盖已经在队列前面的数据。
队列发送的所有数据必须属于同一类型,在队列创建阶段声明。 它可以是简单的变量或结构。
队列长度在创建阶段声明,定义为将通过队列发送的项目数。
队列内的操作在关键部分执行(通过对 BASEPRI 寄存器进行编程来阻止中断,以便在队列上进行操作。
任务可以超时或无限地阻塞队列发送或接收数据。
如果多个任务被阻塞等待从队列接收/发送数据,那么当数据/空间可用时,只有具有最高优先级的任务才会被解除阻塞。 如果两个任务具有相同的优先级,则等待时间最长的任务将被解除阻塞。
1.2 CMSIS-RTOS V2中的消息队列 消息传递是线程之间的另一种基本通信模型。 在消息传递模型中,一个线程显式发送数据,而另一个线程接收数据。 该操作更像是某种 I/O,而不是直接访问要共享的信息。 在 CMSIS-RTOS 中,这种机制称为消息队列。 数据以类似 FIFO 的操作从一个线程传递到另一个线程。 使用消息队列功能,可以控制、发送、接收或等待消息。 要传递的数据可以是整数或指针类型:
与内存池相比,消息队列通常效率较低,但可以解决更广泛的问题。 有时,线程没有公共地址空间或使用共享内存会引发问题,例如互斥。
在 CMSIS-RTOS API 中有两种类型的队列:
只能发送整数类型数据或指针的消息
可以发送内存块的邮件
1 2 注意:函数 osMessageQueuePut、osMessageQueueGet、osMessageQueueGetCapacity、osMessageQueueGetMsgSize、osMessageQueueGetCount、osMessageQueueGetSpace 可以从中断服务程序中调用。
CMSIS-RTOS V2提供了如下API:
**osMessageQueueId_t osMessageQueueNew(uint32_t msg_count,uint32_t msg_size,const osMessageQueueAttr_t * attr)**: 创建并初始化一个消息队列对象。 该函数返回一个消息队列对象标识符或在发生错误时返回 NULL。
该函数可以在内核初始化后使用 osKernelInitialize 调用。 可以在使用 osKernelStart 启动 RTOS 内核之前创建消息队列对象。
消息队列数据所需的内存总量至少为 msg_count * msg_size。 msg_size 向上舍入为双偶数以确保内存块的 32 位对齐。
从消息队列分配的内存块具有使用参数 msg_size 定义的固定大小。
注意:该函数不能在中断服务中调用
**osStatus_t osMessageQueuePut(osMessageQueueId_t mq_id,const void * msg_ptr,uint8_t msg_prio,uint32_t timeout)**:将msg_ptr指向的消息放入参数mq_id指定的消息队列中。
参数 msg_prio 用于在插入时根据消息的优先级(数字越大表示优先级越高)对消息进行排序。
参数 timeout 指定系统等待将消息放入队列的时间。 在系统等待期间,调用此函数的线程将进入 BLOCKED 状态。 参数 timeout 可以有以下值:
当 timeout 为 0 时,函数立即返回(即尝试语义)。
当 timeout 设置为 osWaitForever 时,函数将无限等待直到消息被传递(即等待语义)。
所有其他值在内核滴答中指定超时时间(即定时等待语义)。返回值osStatus_t :
osOK :消息已放入队列。
osErrorTimeout :消息无法在给定时间内放入队列(等待时间语义)。
osErrorResource :队列中没有足够的空间(尝试语义)。
osErrorParameter :参数 mq_id 为 NULL 或无效,在 ISR 中指定的非零超时。注意:如果参数 timeout 设置为 0,则可以从中断服务程序调用。
简单的示例如下:
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 #include "cmsis\_os2.h" // CMSIS RTOS header file /\*---------------------------------------------------------------------------- \* Message Queue creation & usage \*---------------------------------------------------------------------------\*/ #define MSGQUEUE\_OBJECTS 16 // number of Message Queue Objects typedef struct { // object data type uint8\_t Buf[32]; uint8\_t Idx; } MSGQUEUE_OBJ_t; osMessageQueueId\_t mid_MsgQueue; // message queue id osThreadId\_t tid_Thread_MsgQueue1; // thread id 1 osThreadId\_t tid_Thread_MsgQueue2; // thread id 2 void Thread\_MsgQueue1 (void \*argument); // thread function 1 void Thread\_MsgQueue2 (void \*argument); // thread function 2 int Init\_MsgQueue (void) { mid_MsgQueue = osMessageQueueNew(MSGQUEUE_OBJECTS, sizeof(MSGQUEUE_OBJ_t), NULL); if (mid_MsgQueue == NULL) { ; // Message Queue object not created, handle failure } tid_Thread_MsgQueue1 = osThreadNew(Thread_MsgQueue1, NULL, NULL); if (tid_Thread_MsgQueue1 == NULL) { return(-1); } tid_Thread_MsgQueue2 = osThreadNew(Thread_MsgQueue2, NULL, NULL); if (tid_Thread_MsgQueue2 == NULL) { return(-1); } return(0); } void Thread\_MsgQueue1 (void \*argument) { MSGQUEUE_OBJ_t msg; while (1) { ; // Insert thread code here... msg.Buf[0] = 0x55U; // do some work... msg.Idx = 0U; osMessageQueuePut(mid_MsgQueue, &msg, 0U, 0U); osThreadYield(); // suspend thread } } void Thread\_MsgQueue2 (void \*argument) { MSGQUEUE_OBJ_t msg; osStatus\_t status; while (1) { ; // Insert thread code here... status = osMessageQueueGet(mid_MsgQueue, &msg, NULL, 0U); // wait for message if (status == osOK) { ; // process data } } }
**osStatus_t osMessageQueueGet(osMessageQueueId_t mq_id,void * msg_ptr,uint8_t * msg_prio,uint32_t timeout)**:从参数 mq_id 指定的消息队列中检索一条消息,并将其保存到参数 msg_ptr 指向的缓冲区中。 如果不是 token{NULL},则消息优先级存储到参数 msg_prio。
参数 timeout 指定系统等待从队列中检索消息的时间。 在系统等待期间,调用此函数的线程将进入 BLOCKED 状态。
参数 timeout 可以有以下值:
当 timeout 为 0 时,函数立即返回(即尝试语义)。
当超时设置为 osWaitForever 时,该函数将等待无限时间,直到检索到消息(即等待语义)。
所有其他值在内核滴答中指定超时时间(即定时等待语义)。返回值osStatus_t :
osOK :消息已从队列中检索。
osErrorTimeout :在给定时间内无法从队列中检索消息(定时等待语义)。
osErrorResource :没有从队列中得到(尝试语义)。
osErrorParameter :参数 mq_id 为 NULL 或无效,在 ISR 中指定的非零超时。注意:如果参数 timeout 设置为 0,则可以从中断服务程序调用。
**osStatus_t osMessageQueueDelete(osMessageQueueId_t mq_id)**:删除由参数 mq_id 指定的消息队列对象。 它释放用于消息队列处理的内部内存。 在此调用之后,mq_id 不再有效,无法使用。 可以使用函数 osMessageQueueNew 再次创建消息队列。
返回的结果如下:
+ **osOK**:消息队列对象已被删除。
+ **osErrorParameter**:参数 mq\_id 为 NULL 或无效。
+ **osErrorResource**:消息队列处于无效状态。
+ **osErrorISR**:无法从中断服务例程调用 osMessageQueueDelete。
2、在STM32CubeIDE中配置消息队列 第一步: 新建工程
新建工程,请参考前面文章:
串口配置,请参考前面文章:
第二步: 配置时钟
配置时钟,请参考前面文章:
第三步:配置FreeRTOS
配置FreeRTOS,及FreeRTOS的堆栈,请参考:
第四步:添加线程
创建线程,请参考前面文章:
在本次实例中定义的线程如下:
第五步:添加消息队列
第六步:保存生成代码
3、消息队列功能代码实现 在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 osThreadId\_t defaultTaskHandle; const osThreadAttr\_t defaultTask_attributes = { .name = "defaultTask", .stack_size = 128 \* 4, .priority = (osPriority\_t) osPriorityNormal, }; /\* Definitions for senderHandle \*/ osThreadId\_t senderHandleHandle; const osThreadAttr\_t senderHandle_attributes = { .name = "senderHandle", .stack_size = 256 \* 4, .priority = (osPriority\_t) osPriorityNormal, }; /\* Definitions for receiverHandle \*/ osThreadId\_t receiverHandleHandle; const osThreadAttr\_t receiverHandle_attributes = { .name = "receiverHandle", .stack_size = 256 \* 4, .priority = (osPriority\_t) osPriorityNormal, }; /\* Definitions for counterQueue \*/ osMessageQueueId\_t counterQueueHandle; const osMessageQueueAttr\_t counterQueue_attributes = { .name = "counterQueue" };
1 2 3 4 void StartDefaultTask(void \*argument); void senderTask(void \*argument); void receiverTask(void \*argument);
线程执行代码功能实现:
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 /\*\* \* @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\_senderTask \*/ /\*\* \* @brief Function implementing the senderHandle thread. \* @param argument: Not used \* @retval None \*/ /\* USER CODE END Header\_senderTask \*/ void senderTask(void \*argument) { /\* USER CODE BEGIN senderTask \*/ /\* Infinite loop \*/ int counter = 0; for (;;) { osDelay(1000); counter++; osMessageQueuePut(counterQueueHandle, &counter, 0, 100); printf("queue sent:%d\r\n",counter); } /\* USER CODE END senderTask \*/ } /\* USER CODE BEGIN Header\_receiverTask \*/ /\*\* \* @brief Function implementing the receiverHandle thread. \* @param argument: Not used \* @retval None \*/ /\* USER CODE END Header\_receiverTask \*/ void receiverTask(void \*argument) { /\* USER CODE BEGIN receiverTask \*/ /\* Infinite loop \*/ uint16\_t localVa = 0; for(;;){ //osDelay(100); osMessageQueueGet(counterQueueHandle, &localVa, 0, 500); printf("queue received:%d\r\n",localVa); } /\* USER CODE END receiverTask \*/ }
在freertos.c文件中的MX_FREERTOS_Init函数中,创建消息队列:
1 2 3 counterQueueHandle = osMessageQueueNew(16, sizeof(uint16\_t), &counterQueue_attributes);
创建线程:
1 2 3 4 5 6 7 8 9 10 11 defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes); /\* creation of senderHandle \*/ senderHandleHandle = osThreadNew(senderTask, NULL, &senderHandle_attributes); /\* creation of receiverHandle \*/ receiverHandleHandle = osThreadNew(receiverTask, NULL, &receiverHandle_attributes);
最后,编译生成程序并下载到开板板。
4、STM32F1与STM32CubeIDE快速入门系列文章
文章来源: https://iotsmart.blog.csdn.net/article/details/124706653
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!