首页 > 代码库 > 任务内建消息队列——OSTaskQ???()

任务内建消息队列——OSTaskQ???()

一、知识背景

实际应用中,多个任务同时等待一个消息队列的情况很少见,也就是说OSQ???()用的并不多,因此,在uC/OS-III 中,每一个任务都有其内建的消息队列。用户可以通过外部消息队列直接发送消息给任务。 这个特性不仅简化了代码, 还提高了效率。如下示意图,

 

 uC/OS-III 中与任务消息队列相关的服务都是以 OSTask???()开头的。设置 OS_CFG.H 中的 OS_CFG_TASK_EN 使能任务的消息队列服务。与任务消息队列相关的代码在 OS_TASK.C 中。

 当用户清楚消息需要发布到哪个任务的时候,可以使用向任务直接发布消息的功能,举个例子,当收到以太网控制器中断时,用户可以将数据包的地址直接发布给负责处理接收数据包的任务。这是因为在中断里面我们尽可能说短时间,数据处理交给任务来完成。

二,应用实例

1、将串口中断接收到的数据发送给串口接收数据处理任务

 1 //串口1中断处理程序 2 void USART1_IRQHandler(void)                    //串口1中断服务程序 3 { 4     OS_ERR err;     5     OSIntEnter();    //通知UCOS进入中断 6     //发送缓冲区空中断 7     if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)  { 8         USART_SendData(USART1, *pTxBufRead);//向串口发送缓冲区写入一个字节 9         if((pTxBufRead++) == &Usart1TxBuf[USART1_TX_BUFFER_LEN - 1]){ //读到最后一个字节10             pTxBufRead = Usart1TxBuf;  //移动读指针到第首地址11         }        12         if(pTxBufRead == pTxBufWrite){ //若读写指针相等,表明本次缓冲区数据已经读完13             USART_ITConfig(USART1, USART_IT_TXE, DISABLE);//关闭中断14         }15         OSSemPost(&Usart1TxBufSem, OS_OPT_POST_1, &err); //释放缓冲区信号量16     }    17     //串口接收到数据中断18     if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){19         *pRxBufWrite = USART_ReceiveData(USART1); //读取一个字节到缓冲区20         OSTaskQPost(&Usart1RxTaskTCB, pRxBufWrite, 1, OS_OPT_POST_FIFO, &err);   //发送该字节所在缓冲区的地址到消息队列,等待任务处理21         if((pRxBufWrite++) == &Usart1RxBuf[USART1_RX_BUFFER_LEN - 1]){ //若当前写指针写到缓冲区最后一个地址22             pRxBufWrite = Usart1RxBuf; //写指针更新为缓冲区第一个地址,环形队列23         }24     }25     OSIntExit();      //通知UCOS退出中断                                     26 } 
USART1_IRQHandler.c

我们仅来分析串口1中断处理程序,这里将接收到的数据传递给指针,再用任务内建消息将指针地址传给任务。

 1 static  void  Usart1RxTask (void *p_arg) 2 { 3     OS_ERR      err;     4     CPU_TS ts; 5     void *p_msg; 6     CPU_INT08U ch;  7     OS_MSG_SIZE msg_size; 8   (void)p_arg; 9      while (DEF_TRUE) {                                10       p_msg = OSTaskQPend(0, OS_OPT_PEND_BLOCKING, &msg_size, &ts, &err);11         if(err == OS_ERR_NONE)12             {13             ch = *((CPU_INT08U *)p_msg);14             usart1SendData(ch);    //STM32接收到串口发送到32上的数据,并通过串口发送发送到电脑上显示!            15           }16     }17 }
Usart1RxTask.c

可以看出,我们从OSTaskQPend取出消息地址赋给空指针p_msg,再将空指针强制类型转换指向要指向的字节,并将其取出来赋给ch。这样就完成了任务消息传递。

2、DMA中断数据传输给指定任务处理

 1 void DMA1_Channel1_IRQHandler() 2 { 3   OS_ERR err; 4     OSIntEnter();    //通知UCOS进入中断 5     if(DMA_GetITStatus(DMA1_IT_TC1))  //通道1传输完成中断 6     { 7         TIM_Cmd(TIM2, DISABLE); 8         DMA_ClearITPendingBit(DMA1_IT_GL1); //清除全部中断标志   通道1全局中断 9         OSTaskQPost(&maintaskTCB,Single_ADC_RegularConvertedValueTab,2, OS_OPT_POST_FIFO, &err);   //发送该字节所在缓冲区的地址到消息队列,等待任务处理10         TIM_Cmd(TIM2, ENABLE);11    }12     OSIntExit();      //通知UCOS退出中断        13  }
DMA1_Channel1_IRQHandler.c

这里将DMA数组的首地址进行传输给相应任务。

 1 static void Maintask(void *p_arg) 2 { 3        OS_ERR      err;     4       u16 i = 0; 5       u32 total = 0; 6         static u16 index_count,index_count1; 7 //  static u16 start_offset_voltage; 8       static u8 Up_Target_Flag; 9       static u16 Count_Up,Count_Down;10       CPU_TS ts;11       CPU_INT16U *p_msg;12       OS_MSG_SIZE msg_size;13     (void)p_arg;14         while (DEF_TRUE) 15       {                                16           p_msg = (u16 *)OSTaskQPend(0, OS_OPT_PEND_BLOCKING, &msg_size, &ts, &err);17             if(err == OS_ERR_NONE)18               {                19                     for(i=0;i < ADC_TAB_LENGTH_ONE;i++)20              {21                  printf("%d\n",*p_msg);22                  total += *p_msg++;23              }24                  total=0;25                  Single_ADC_Average = total / ADC_TAB_LENGTH_ONE; 26                  printf("%d\n",Single_ADC_Average);
void Maintask.c

首先将OSTaskQPend(0, OS_OPT_PEND_BLOCKING, &msg_size, &ts, &err)返回的空指针强制类型转换成u16类型赋给指针变量p_msg,再将指针自加取出所有数据。

总结:比较实例1和实例2,我们能发现很多C语言的基础知识,实例1中只传送一个数据的地址,所以将OSTaskQPend(0, OS_OPT_PEND_BLOCKING, &msg_size, &ts, &err)接收到的空指针赋给空指针 p_msg,再将空指针 p_msg强制类型转换成具体格式的数据赋给ch,而实例2万万不可以采用此种方式,原因是我们要得到的是一组数据,空指针是不能自加的,所以我们必须将传递过来的空地址首先进行强制类型转换成指向数据类型明确的指针后,才能让指针自加。切记切记。

这两个实例的共同特点是,我们在中断里接收到的数据,我们仅仅通过任务内建消息传递数据地址数组的首地址即可,那为什么传递过来的地址是空类型的指针呢,原因是空类型指针接收任何数据地址类型,而我们接收后需要什么类型的数据再将空类型指针转换成相应的指向数据格式即可,这也就让OSTaskQPend(0, OS_OPT_PEND_BLOCKING, &msg_size, &ts, &err)可以接收任何类型的指针了,仔细想想,这也是设计ucos的强大之处。同时C语言的基础知识还要仔细体会啊!

任务内建消息队列——OSTaskQ???()