一、知識背景
實際應用中,多個任務同時等待一個消息隊列的情況很少見,也就是說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 }
我們僅來分析串口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 }
可以看出,我們從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 }
這里將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);
首先將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語言的基礎知識還要仔細體會啊!