队列主要完成任务与任务、任务与中断之间的消息传递。
本文使用的软件为STM32CubeMX5.3.0 、Keil5.25, 硬件平台为Nucleo-L476
STM32CubeMX的工程创建和最基本配置本文不再赘述,
详见:Nucleo-L476运行FreeRTOS学习1-环境搭建Demo: https://blog.csdn.net/Paul_Yu_Zhang/article/details/104233028
只进行有关队列部分的描述。
本例子使用 STM32CubeMX 配置创建两个任务,一个任务每隔一定时间发送一个消息到队列,另一个等待消息并根据消息的内容控制 LED 的闪烁次数。
①消息队列发送任务
②等待消息任务
点击 Queues 栏的 Add 按钮,增加一个任务队列 myQueue01, 数据大小为 16, 每个单元数据类型是 uint16_t
之后点击生成代码
可以看到创建消息队列和句柄赋值的函数:
/* Create the queue(s) */
/* definition and creation of myQueue01 */
osMessageQDef(myQueue01, 16, uint16_t);
myQueue01Handle = osMessageCreate(osMessageQ(myQueue01), NULL);
和消息队列有关的函数主要有:
我们在发送消息队列的任务中使用函数osMessagePut()来发送消息队列,依次发送1,2,3
osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec);
有三个参数:
/// 消息队列句柄 queue_id : message queue ID obtained with \ref osMessageCreate.
/// 消息值 info : message information.
/// 超时时间 millisec : timeout value or 0 in case of no time-out.
/* USER CODE END Header_TaskMsgProducer */
void TaskMsgProducer(void const * argument)
{
/* USER CODE BEGIN TaskMsgProducer */
/* Infinite loop */
for(;;)
{
osDelay(1000);
osMessagePut(myQueue01Handle,1,osWaitForever);
osDelay(1000);
osMessagePut(myQueue01Handle,2,osWaitForever);
osDelay(1000);
osMessagePut(myQueue01Handle,3,osWaitForever);
osDelay(1000);
}
/* USER CODE END TaskMsgProducer */
}
等待消息队列任务中,使用函数osMessageGet读取消息值:
/* USER CODE END Header_TaskMsgConsumer */
void TaskMsgConsumer(void const * argument)
{
/* USER CODE BEGIN TaskMsgConsumer */
osEvent event;
/* Infinite loop */
for(;;)
{
event = osMessageGet(myQueue01Handle,osWaitForever);
if(event.status == osEventMessage)
{
while(event.value.v--)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5,0);
osDelay(100);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5,1);
osDelay(100);
}
}
}
/* USER CODE END TaskMsgConsumer */
}
返回的数据由变量event接收,数据类型为osEvent,osEvent结构体含义如下:
/// 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;
修改之后编译下载观察实验结果:
第1s闪1次,第2s闪2次, 第3s闪3次, 常亮1s;依次循环
如果队列存储的数据单元尺寸较大,那最好是利用队列来传递数据的指针而不是数据本身在队列上一字节一字节地拷贝进或拷贝出。传递指针无论是在处理速度上还是 内存空间利用上都更有效。这个实现方式就是邮箱队列,它传递的是数据的指针。
CubeMX5.3目前还不支持配置生成邮箱队列,直接用写代码方式学习使用邮箱队列。
本例子使用 STM32CubeMX 配置创建两个任务,一个任务每隔一定时间发送一个消息到队列,另一个等待消息并根据消息的内容控制 LED 的闪烁次数。
①邮箱队列发送任务
②等待消息任务
创建完成之后生成代码。
邮箱队列主要函数有这些:
参考官方的 Mail 例程,代码参考官方Demo,STM32CubeL4 的支持包 stm32cube_FW_L4 V1.14.0.zip 解压
先申明一个结构体,和一个osMailQId句柄
typedef struct
{ /* Mail object structure */
uint32_t var1; /* var1 is a uint32_t */
uint32_t var2; /* var2 is a uint32_t */
uint8_t var3; /* var3 is a uint8_t */
} Amail_TypeDef;
osMailQId mailId;
邮箱队列创建
/* Create the mail queue used by the two tasks to pass the struct Amail_TypeDef */
osMailQDef(mail, MAIL_SIZE, Amail_TypeDef); /* Define mail queue */
mailId = osMailCreate(osMailQ(mail), NULL); /* create mail queue */
发送邮箱队列任务:
/* USER CODE BEGIN Header_TaskMailProducer */
/**
* @brief Function implementing the MailProducer thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_TaskMailProducer */
void TaskMailProducer(void const * argument)
{
/* USER CODE BEGIN TaskMailProducer */
Amail_TypeDef pTMail[3];
pTMail[0].var1 = 1;
pTMail[0].var2 = 2;
pTMail[0].var3 = 3;
pTMail[1].var1 = 2;
pTMail[1].var2 = 1;
pTMail[1].var3 = 3;
pTMail[2].var1 = 3;
pTMail[2].var2 = 2;
pTMail[2].var3 = 1;
/* Infinite loop */
for(;;)
{
osMailPut(mailId, &pTMail[0]);
osDelay(1000);
osMailPut(mailId, &pTMail[1]);
osDelay(1000);
osMailPut(mailId, &pTMail[2]);
osDelay(1000);
}
/* USER CODE END TaskMailProducer */
}
邮箱队列读取:
/* USER CODE BEGIN Header_TaskMailConsumer */
/**
* @brief Function implementing the MailConsumer thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_TaskMailConsumer */
void TaskMailConsumer(void const * argument)
{
/* USER CODE BEGIN TaskMailConsumer */
osEvent event;
Amail_TypeDef *pRMail;
uint32_t val;
/* Infinite loop */
for(;;)
{
/* Get the message from the queue */
event = osMailGet(mailId, osWaitForever); /* wait for mail */
if(event.status == osEventMail)
{
pRMail = event.value.p; //获取邮箱指针
val = pRMail->var1; //提取邮箱队列中var1的值
while(val--)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, 1);
osDelay(100);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, 0);
osDelay(100);
}
}
}
/* USER CODE END TaskMailConsumer */
}
编译下载到板子中看运行效果。
第1s闪1次,第2s闪2次, 第3s闪3次;闪烁次数实际上是根据var1的值大小改变