一、基础概念
作用:队列是为了任务与任务、任务与中断之间的通信而准备的,可以在任务与任务、任务与中断之间传递消息,队列中可以存储有限的、大小固定的数据项目。
二、运行机制
在脑海中想像不同任务之间,其需要交互数据的时候,需要的一种通信机制,这种机制在该操作系统就是“消息队列”其特点是任何建立的任务都可以向队列中发送消息和取信息。
1、首先就是创建队列
- 创建消息队列时 FreeRTOS 会先给消息队列分配一块内存空间,这块内存的大小等于消息队列控制块大小加上(单个消息空间大小与消息队列长度的乘积),接着再初始化消息队列,此时消息队列为空。
- 初始化消息队列的时候同时会初始内存空间,用于保存消息队列的一些信息如消息的存储位置,头指针 pcHead、尾指针pcTail、消息大小 uxItemSize 以及队列长度 uxLength 等。同时每个消息队列都与消息空间在同一段连续的内存空间中(消息队列是消息空间的别名)。在创建成功的时候,这些内存就被占用了,只有删除了消息队列的时候,这段内存才会被释放掉。其消息队列发送拷贝的数据不能超过其创建时候设置的大小。
2、队列发送消息(入队机制)
- 分三种情况
对应场景: 如果队列未满或者允许覆盖入队,FreeRTOS 会将消息拷贝到消息队列队尾
对应场景①:在设置的阻塞超时时间中,如果队列一直不允许入队,该任务将保持阻塞状态以等待队列允许入队。 对应场景①:当其它任务从其等待的队列中读取入了数据(队列未满),该任务将自动由阻塞态转移为就绪态。 也就是在等待过程中队列会一直按照设定时间入队,但是当队列中提前有消息被读走时候会提前入队。
-超过设置阻塞时间
即使队列中还不允许入队,任务也会自动从阻塞态转移为就绪态,此时发送消息的任务或者中断程序会收到一个错误码 errQUEUE_FULLAPI系统自动发出。
发送紧急消息的过程与发送消息几乎一样,唯一的不同是,当发送紧急消息时,发送的位置是消息队列队头而非队尾,这样,接收者就能够优先接收到紧急消息,从而及时进行消息处理。
3、队列接收消息(出队机制)
- 读取消息时候,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。
- 当其它任务或中断服务程序往其等待的队列中写入了数据,该任务将自动由阻塞态转移为就绪态。任务程序继续执行下去。
- 当等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转移为就绪态。继续执行程序。
注意事项:
- FreeRtos只有在任务中发送消息使用队列,才允许进行阻塞状态,而在中断中发送消息不允许带有阻塞机制的,需要调用在中断中发送消息的API 函数接口,因为发送消息的上下文环境是在中断中,不允许有阻塞的情况。
- 假如有多个任务阻塞在一个消息队列中,那么这些阻塞的任务将按照任务优先级进行排序优先级高的任务将优先获得队列的访问权。
三、 API函数
使用队列模块的典型流程如下:
四、实验代码分析
野火的很简单就任务间的收发,发采用立即发,收阻塞收没有实战使用价值
static void Send_Task(void* parameter)
{
BaseType_t xReturn = pdPASS;
uint32_t send_data1 = 1;
uint32_t send_data2 = 2;
while (1)
{
if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
{
printf("发送消息send_data1!\n");
xReturn = xQueueSend( Test_Queue,
&send_data1,
0 );
if(pdPASS == xReturn)
printf("消息send_data1发送成功!\n\n");
}
if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )
{
printf("发送消息send_data2!\n");
xReturn = xQueueSend( Test_Queue,
&send_data2,
0 );
if(pdPASS == xReturn)
printf("消息send_data2发送成功!\n\n");
}
vTaskDelay(20);
}
}
static void Receive_Task(void* parameter)
{
BaseType_t xReturn = pdTRUE;
uint32_t r_queue;
while (1)
{
xReturn = xQueueReceive( Test_Queue,
&r_queue,
portMAX_DELAY);
if(pdTRUE == xReturn)
printf("本次接收到的数据是%d\n\n",r_queue);
else
printf("数据接收出错,错误代码0x%lx\n",xReturn);
}
}
正点的 涉及了中断与任务,任务与任务。
任务与任务之间没什么好分析的就是阻塞时间为10的发送,与创建一个任务阻塞等待接收处理事件。定时器接收队列消息
void TIM2_IRQHandler(void)
{
u8 *buffer;
BaseType_t xTaskWokenByReceive=pdFALSE;
BaseType_t err;
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
{
buffer=mymalloc(SRAMIN,USART_REC_LEN);
if(Message_Queue!=NULL)
{
memset(buffer,0,USART_REC_LEN);
err=xQueueReceiveFromISR(Message_Queue,buffer,&xTaskWokenByReceive);
if(err==pdTRUE)
{
disp_str(buffer);
}
}
myfree(SRAMIN,buffer);
portYIELD_FROM_ISR(xTaskWokenByReceive);
}
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
队列接收中断函数解释
if((USART_RX_STA&0x8000)&&(Message_Queue!=NULL))
{
xQueueSendFromISR(Message_Queue,USART_RX_BUF,&xHigherPriorityTaskWoken);
USART_RX_STA=0;
memset(USART_RX_BUF,0,USART_REC_LEN);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
待验证:xHigherPriorityTaskWoken这个参数官方解释,估计是返回值的由来,设置好这个参数会自动返回函数值与执行任务切换的选择
|