【FreeRtos教程三】STM32 CubeMx——Message Queue(消息队列)
消息队列类似于数据结构中的“队列”,都是一个线性的存储表。可以往队列里面写入数据,也可从队列里面读取数据。
目录
1.消息队列
1.1什么是消息队列
消息队列类似于数据结构中的“队列”,都是一个线性的存储表。可以往队列里面写入数据,也可从队列里面读取数据。队列的简化操作如下图所示,从此图可知:
- 队列可以包含若干个数据:队列中有若干项,这被称为"长度"(length)
- 每个数据大小固定
- 创建队列时就要指定长度、数据大小
- 数据的操作采用先进先出的方法(FIFO,First In First Out):写数据时放到尾部,读数据时从头部读
- 也可以强制写队列头部:覆盖头部数据
1.2 传输数据的两种方法
- 拷贝:把数据或变量的值复制进队列里
- 引用:把数据或变量的地址复制进队列里
1.3 队列的阻塞访问
只要知道队列的句柄,谁都可以读、写该队列。
任务读写队列时,简单地说:如果读写不成功,则阻塞;可以指定超时时间。口语化地说,就是可以定个闹钟:如果能读写了就马上进入就绪态,否则就阻塞直到超时。
跟读队列类似,一个任务要写队列时,如果队列满了,该任务也可以进入阻塞状态:还可以指定阻塞的时间。如果队列有空间了,则该阻塞的任务会变为就绪态。如果一直都没有空间,则时间到之后它也会进入就绪态。
1.4 读取队列的两种方式
- Get(获取):使用函数osMessageGet()读队列的时候,读到一个数据,队列中的改数据会被移除
- Peek(偷窥):使用函数osMessagePeek()读队列的时候,此函数会从队列中复制出数据,但是不移除 数据
2 示例程序
使用队列传递数据的两种方法:
- 拷贝:把数据或变量的值复制进队列里
- 引用:把数据或变量的地址复制进队列里
2.1 以拷贝值的方式传递数据
2.1.1 例程功能
创建一个按键任务用来向队列里写数据,创建两个任务以PEEK和GET的方式从队列里面读取数据并打印出来。
2.1.2 步骤
配置按键的IO口
配置串口1,波特率115200、数据位8、停止位1、奇偶校验none
创建3个动态任务
创建一个队列,队列长度16(能存放16个数据),每个数据占32位
创建按键任务,按键按下即往消息队列里面发送数据
/* USER CODE END Header_StartTask_KEY */
void StartTask_KEY(void const * argument)
{
/* USER CODE BEGIN StartTask_KEY */
/* Infinite loop */
uint32_t ProducerValue=0;
for(;;)
{
if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin)==0)
{
if(osMessagePut(myQueue01Handle,ProducerValue,100)!=osOK)
{
osThreadSuspendAll();
printf("发送失败\n"); //要添加头文件 stdio.h
osThreadResumeAll();
}
else
{
++ProducerValue;
osThreadSuspendAll();
printf("发送成功\n"); //要添加头文件 stdio.h
osThreadResumeAll();
}
while(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin)==0)
{
osDelay(10);
}
}
osDelay(1);
}
/* USER CODE END StartTask_KEY */
}
以PEEK的方式读取消息队列中的数据,并将读取的数据打印出来
/* USER CODE END Header_StartTask01 */
void StartTask01(void const * argument)
{
/* USER CODE BEGIN StartTask01 */
/* Infinite loop */
osEvent event;
for(;;)
{
event=osMessagePeek(myQueue01Handle,osWaitForever);
if(event.status==osEventMessage) //判断是否是消息队列
{
printf("任务1数据: %d\n",event.value.v);
}
osDelay(1);
}
/* USER CODE END StartTask01 */
}
以GET的方式读取消息队列中的数据,并将读取的数据打印出来
/* USER CODE END Header_StartTask02 */
void StartTask02(void const * argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
osEvent event;
for(;;)
{
event=osMessageGet(myQueue01Handle,osWaitForever);
if(event.status==osEventMessage) //判断是否是消息队列
{
printf("任务2数据: %d\n",event.value.v);
}
osDelay(1);
}
/* USER CODE END StartTask02 */
}
2.1.3 实验结果
按键按下,任务Task_01和任务Task_02接受到队列中的数据并打印出来
2.1.4 函数讲解
1 osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec)
/*
@brief Put a Message to a Queue.
@param queue_id message queue ID obtained with \ref osMessageCreate.
@param info message information.
@param millisec timeout value or 0 in case of no time-out.
@retval status code that indicates the execution status of the function.
@note MUST REMAIN UNCHANGED: \b osMessagePut shall be consistent in every CMSIS-RTOS.
*/
osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec)
- 函数功能:往队列中发送消息
- 传入参数:1.queue_id(要写入队列的句柄)2.info(要写入的消息)3.millisec(等待时间)
2 osEvent osMessagePeek (osMessageQId queue_id, uint32_t millisec)
/*
@brief Receive an item from a queue without removing the item from the queue.
@param queue_id message queue ID obtained with \ref osMessageCreate.
@param millisec timeout value or 0 in case of no time-out.
@retval event information that includes status code.
*/
osEvent osMessagePeek (osMessageQId queue_id, uint32_t millisec)
- 函数功能:获取队列中的消息(得到消息后不会将其删除)
- 传入参数:1.queue_id(要获取队列的句柄)2.millisec(等待时间)
3 osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec)
/*
@brief Get a Message or Wait for a Message from a Queue.
@param queue_id message queue ID obtained with \ref osMessageCreate.
@param millisec timeout value or 0 in case of no time-out.
@retval event information that includes status code.
@note MUST REMAIN UNCHANGED: \b osMessageGet shall be consistent in every CMSIS-RTOS.
*/
osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec)
- 函数功能:获取队列中的消息(得到消息后会将消息从队列中删除)
- 传入参数:1.queue_id(要获取队列的句柄)2.millisec(等待时间)
2.1.5 程序源码
github链接:HaoJosephWen/FreeRtos_personal (github.com)
2.2 以拷贝地址的方式传递数据
2.2.1 例程功能
创建一个按键任务用来向队列里写结构体地址,创建两个任务以PEEK和GET的方式从队列里面读取结构体的地址并将结构体里的数据打印出来
2.2.2 步骤
配置按键的IO口
配置串口1,波特率115200、数据位8、停止位1、奇偶校验none
创建3个动态任务
创建一个队列,队列长度16(能存放16个数据),每个数据占32位
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef struct
{
uint32_t var1;
uint32_t var2;
uint32_t var3;
}DataGroup;
/* USER CODE END PTD */
定义一个结构体,结构体里面存放3个数据变量
/* USER CODE END Header_StartTask_KEY */
void StartTask_KEY(void const * argument)
{
/* USER CODE BEGIN StartTask_KEY */
/* Infinite loop */
uint32_t ProducerValue=0;
DataGroup myDataGroup_t={10,20,30};
ProducerValue=(uint32_t)&myDataGroup_t;
for(;;)
{
if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin)==0)
{
osDelay(10);
if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin)==0)
{
if(osMessagePut(myQueue01Handle,ProducerValue,0)!=osOK)
{
osThreadSuspendAll();
printf("发送失败\n"); //要添加头文件 stdio.h
osThreadResumeAll();
}
else
{
osThreadSuspendAll();
printf("发送成功\n"); //要添加头文件 stdio.h
osThreadResumeAll();
}
while(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin)==0)
{
osDelay(10);
}
}
}
osDelay(1);
}
/* USER CODE END StartTask_KEY */
}
创建按键任务,按键按下即往队列里面发送结构体地址
/* USER CODE END Header_StartTask01 */
void StartTask01(void const * argument)
{
/* USER CODE BEGIN StartTask01 */
/* Infinite loop */
osEvent event;
DataGroup *myDataGroup_r;
for(;;)
{
event=osMessagePeek(myQueue01Handle,osWaitForever);
if(event.status==osEventMessage) //判断是否是消息队列
{
myDataGroup_r=event.value.p;
osThreadSuspendAll();
printf("任务1接受的数据: %d\n",myDataGroup_r->var1);
printf("任务1接受的数据: %d\n",myDataGroup_r->var2);
printf("任务1接受的数据: %d\n",myDataGroup_r->var3);
osThreadResumeAll();
}
osDelay(1);
}
/* USER CODE END StartTask01 */
}
以PEEK的方式读取消息队列中的结构体的地址,自定义结构体指针指向队列传输的结构体的地址,并将结构体中的数据打印出来
/* USER CODE END Header_StartTask02 */
void StartTask02(void const * argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
osEvent event;
DataGroup *myDataGroup_r;
for(;;)
{
event=osMessageGet(myQueue01Handle,osWaitForever);
if(event.status==osEventMessage) //判断是否是消息队列
{
myDataGroup_r=event.value.p;
osThreadSuspendAll();
printf("任务2接受的数据: %d\n",myDataGroup_r->var1);
printf("任务2接受的数据: %d\n",myDataGroup_r->var2);
printf("任务2接受的数据: %d\n",myDataGroup_r->var3);
osThreadResumeAll();
}
osDelay(1);
}
/* USER CODE END StartTask02 */
}
以GET的方式读取消息队列中的结构体的地址,自定义结构体指针指向队列传输的结构体的地址,并将结构体中的数据打印出来
2.2.3 实验结果
2.2.4 函数讲解
同上。
这里补充一个结构体:
/// Event structure contains detailed information about an event.
/// \note MUST REMAIN UNCHANGED: \b os_event shall be consistent in every CMSIS-RTOS.
/// However the struct may be extended at the end.
typedef struct {
osStatus status; ///< status code: event or error information
union {
uint32_t v; ///< message as 32-bit value
void *p; ///< message or mail as void pointer
int32_t signals; ///< signal flags
} value; ///< event value
union {
osMailQId mail_id; ///< mail id obtained by \ref osMailCreate
osMessageQId message_id; ///< message id obtained by \ref osMessageCreate
} def; ///< event definition
} osEvent;
2.2.5 程序源码
更多推荐
所有评论(0)