任務(wù)間通信 5.4.1 信號(hào)量 信號(hào)量是用來保護(hù)共享資源用的,表示共享資源的個(gè)數(shù)。共享資源被占用一個(gè),信號(hào)量的指會(huì)減1,共享資源被釋放一個(gè),信號(hào)量的值會(huì)加1。 (理解:USB口的占用與釋放,假設(shè)一個(gè)電腦有3個(gè)USB,插入一個(gè)USB設(shè)備,電腦的USB資源會(huì)減少1,此時(shí)電腦的USB資源還有2;拔出USB設(shè)備,電腦的USB資源會(huì)增加1,此時(shí)電腦的USB資源有3個(gè)USB) 其實(shí)信號(hào)量的本質(zhì)就是一個(gè)操作系統(tǒng)計(jì)數(shù)器(0~65535),實(shí)際用于實(shí)現(xiàn)任務(wù)間同步執(zhí)行。 需要掌握的函數(shù)如下: 
實(shí)驗(yàn)代碼如下:
- #include "main.h"
- #include "includes.h"
-
- /** 任務(wù)0 **/
- #define TASK0_PRI 8 //任務(wù)優(yōu)先級
- #define TASK0_STK_SIZE 256 //任務(wù)棧大小
- OS_STK stack0[TASK0_STK_SIZE]; //數(shù)組作為任務(wù)堆棧
- void Task0 (void *p_arg); //函數(shù)聲明
-
- /** 任務(wù)1 **/
- #define TASK1_PRI 9 //任務(wù)優(yōu)先級
- #define TASK1_STK_SIZE 256 //任務(wù)棧大小
- OS_STK stack1[TASK1_STK_SIZE]; //數(shù)組作為任務(wù)堆棧
- void Task1 (void *p_arg); //函數(shù)聲明
-
- /** 任務(wù)2 **/
- #define TASK2_PRI 10 //任務(wù)優(yōu)先級
- #define TASK2_STK_SIZE 256 //任務(wù)棧大小
- OS_STK stack2[TASK2_STK_SIZE]; //數(shù)組作為任務(wù)堆棧
- void Task2 (void *p_arg); //函數(shù)聲明
-
- OS_EVENT *sem;//全局變量,創(chuàng)建信號(hào)量、等待信號(hào)量、發(fā)布信號(hào)量都需要用到該指針。
-
- int main(void)
- {
- OSSysTickInit();//滴答定時(shí)器初始化
- USART1_Init(115200);//初始化串口,用于調(diào)試
-
- OSInit(); //操作系統(tǒng)初始化
-
- sem=OSSemCreate(2);//初始化--默認(rèn)創(chuàng)建2個(gè)信號(hào)量
- OSTaskCreate (Task0,NULL,&stack0[TASK0_STK_SIZE-1],TASK0_PRI);//創(chuàng)建一個(gè)任務(wù),任務(wù)名:Task0
- OSTaskCreate (Task1,NULL,&stack1[TASK1_STK_SIZE-1],TASK1_PRI);//創(chuàng)建一個(gè)任務(wù),任務(wù)名:Task1
- OSTaskCreate (Task2,NULL,&stack2[TASK2_STK_SIZE-1],TASK2_PRI);//創(chuàng)建一個(gè)任務(wù),任務(wù)名:Task2
-
- OSStart(); //啟動(dòng)操作系統(tǒng)
- }
-
- void Task0 (void *p_arg) //實(shí)現(xiàn)任務(wù)Task0
- {
- u8 k=0;//給個(gè)默認(rèn)值0,表示無鍵狀態(tài)
- KEY_Init();//按鍵初始化,放在對應(yīng)任務(wù)中
- while(1)
- {
- k=Key_Scanf(0);//按鍵掃描
- switch (k)
- {
- case KEY_ONE: //按鍵1按下,發(fā)布一個(gè)信號(hào)量
- OSSemPost(sem);//發(fā)送信號(hào)量
- break;
- default:
- break;
- }
- OSTimeDly(1);//高優(yōu)先級釋放CPU,延遲不可太長,延時(shí)太長會(huì)影響按鍵的靈敏度。
- }
- }
-
- void Task1 (void *p_arg) //實(shí)現(xiàn)任務(wù)Task1
- {
- while(1)
- {
- //等待一個(gè)信號(hào)量
- OSSemPend (sem,//信號(hào)量
- 0,//超時(shí)時(shí)間
- 0);//錯(cuò)誤類型--沒有錯(cuò)誤
- printf("任務(wù)1\r\n");
- OSTimeDly(100);//5ms一次滴答,100*5=500ms,打印一次
- }
- }
-
- void Task2 (void *p_arg) //實(shí)現(xiàn)任務(wù)Task2
- {
- while(1)
- {
- //等待一個(gè)信號(hào)量
- OSSemPend (sem,//信號(hào)量
- 0,//超時(shí)時(shí)間
- 0);//錯(cuò)誤類型--沒有錯(cuò)誤
- printf("任務(wù)2\r\n");
- OSTimeDly(100);//5ms一次滴答,100*5=500ms,打印一次
- }
- }
-
復(fù)制代碼
因?yàn)閯?chuàng)建了兩個(gè)信號(hào)量,按下復(fù)位按鍵,同時(shí)打印出任務(wù)1、任務(wù)2。 
按下按鍵1,打印出任務(wù)1,因?yàn)槿蝿?wù)1的優(yōu)先級比較高。 
快速按下按鍵1,能打印出任務(wù)1,和任務(wù)2,因?yàn)樗麄兌荚诘刃盘?hào)量。 
5.4.2 互斥信號(hào)量 用來保護(hù)共享資源,但是這個(gè)共享資源只有一個(gè)。兩個(gè)任務(wù)同時(shí)操作一個(gè)硬件,這時(shí)候需要加互斥信號(hào)量保護(hù)。 (理解:電話亭的使用,假設(shè)電話亭里只有一個(gè)電話,有3個(gè)人想打電話,需要排隊(duì),還需要等待,等待電話亭里面沒有人,排在前面的人就能進(jìn)入電話亭打電話了) 其實(shí)互斥信號(hào)量的本質(zhì)就是一個(gè)操作系統(tǒng)計(jì)數(shù)器(0-1)。 注意:互斥信號(hào)量中需要有一個(gè)空閑的優(yōu)先級作為優(yōu)先級反轉(zhuǎn)用,該優(yōu)先級必須比所有能夠獲得該互斥信號(hào)量的優(yōu)先級還高。 理解:假設(shè)能獲得該互斥信號(hào)量的所有任務(wù)的優(yōu)先級分別為:4、10、11、13,則該空閑優(yōu)先級的取值(0~3);在如,假設(shè)能獲得該互斥信號(hào)量的所有任務(wù)的優(yōu)先級分別為:8、10、11、13,則該空閑優(yōu)先級的取值(0~7)。 需要掌握的函數(shù)如下: 
實(shí)驗(yàn)代碼如下:
- #include "main.h"
- #include "includes.h"
-
- /** 任務(wù)0 **/
- #define TASK0_PRI 8 //任務(wù)優(yōu)先級
- #define TASK0_STK_SIZE 256 //任務(wù)棧大小
- OS_STK stack0[TASK0_STK_SIZE]; //數(shù)組作為任務(wù)堆棧
- void Task0 (void *p_arg); //函數(shù)聲明
-
- /** 任務(wù)1 **/
- #define TASK1_PRI 9 //任務(wù)優(yōu)先級
- #define TASK1_STK_SIZE 256 //任務(wù)棧大小
- OS_STK stack1[TASK1_STK_SIZE]; //數(shù)組作為任務(wù)堆棧
- void Task1 (void *p_arg); //函數(shù)聲明
-
- OS_EVENT *mutex;//全局變量,創(chuàng)建信號(hào)量、等待信號(hào)量、發(fā)布信號(hào)量都需要用到該指針。
-
- int main(void)
- {
- OSSysTickInit();//滴答定時(shí)器初始化
- USART1_Init(115200);//初始化串口,用于調(diào)試
-
- OSInit(); //操作系統(tǒng)初始化
-
- //創(chuàng)建互斥信號(hào)量
- mutex=OSMutexCreate (5,//空閑優(yōu)先級
- 0);//錯(cuò)誤類型
- OSTaskCreate (Task0,NULL,&stack0[TASK0_STK_SIZE-1],TASK0_PRI);//創(chuàng)建一個(gè)任務(wù),任務(wù)名:Task0
- OSTaskCreate (Task1,NULL,&stack1[TASK1_STK_SIZE-1],TASK1_PRI);//創(chuàng)建一個(gè)任務(wù),任務(wù)名:Task1
-
- OSStart(); //啟動(dòng)操作系統(tǒng)
- }
-
- void Task0 (void *p_arg) //實(shí)現(xiàn)任務(wù)Task0
- {
- while(1)
- {
- //獲取互斥信號(hào)量
- OSMutexPend (mutex,//互斥信號(hào)量
- 0,//超時(shí)時(shí)間
- 0);//錯(cuò)誤類型
- for(int i=0;i<5;i++)
- {
- printf("任務(wù)0--%d\r\n",i);//
- OSTimeDly(100);//
- }
- //釋放互斥信號(hào)量
- OSMutexPost (mutex);
- }
- }
-
- void Task1 (void *p_arg) //實(shí)現(xiàn)任務(wù)Task1
- {
- while(1)
- {
- //獲取互斥信號(hào)量
- OSMutexPend (mutex,//互斥信號(hào)量
- 0,//超時(shí)時(shí)間
- 0);//錯(cuò)誤類型
- for(int i=0;i<5;i++)
- {
- printf("任務(wù)1--%d\r\n",i);//
- OSTimeDly(100);//
- }
- //釋放互斥信號(hào)量
- OSMutexPost (mutex);
- }
- }
-
復(fù)制代碼
燒錄代碼,結(jié)果如下圖: 
實(shí)驗(yàn)表明,假設(shè)多個(gè)任務(wù)在訪問同一資源,只有等正在訪問的任務(wù)使用完并釋放資源,下一個(gè)任務(wù)才能訪問使用。 假設(shè)不加互斥信號(hào)量進(jìn)行互斥訪問,代碼如下, 
結(jié)果如下: 
5.4.3 消息郵箱 用于任務(wù)與任務(wù)之間交換數(shù)據(jù)(任務(wù)與任務(wù)之間的通信)。消息郵箱只能存放一則消息,消息的內(nèi)容長短不限制。 需要掌握的函數(shù): 
實(shí)驗(yàn)代碼:
- #include "main.h"
- #include "includes.h"
-
- /** 任務(wù)0 **/
- #define TASK0_PRI 8 //任務(wù)優(yōu)先級
- #define TASK0_STK_SIZE 256 //任務(wù)棧大小
- OS_STK stack0[TASK0_STK_SIZE]; //數(shù)組作為任務(wù)堆棧
- void SendTask (void *p_arg); //函數(shù)聲明
-
- /** 任務(wù)1 **/
- #define TASK1_PRI 9 //任務(wù)優(yōu)先級
- #define TASK1_STK_SIZE 256 //任務(wù)棧大小
- OS_STK stack1[TASK1_STK_SIZE]; //數(shù)組作為任務(wù)堆棧
- void ReceiveTask (void *p_arg); //函數(shù)聲明
-
- OS_EVENT *mbox;//全局變量,創(chuàng)建郵箱、發(fā)送消息、接收消息,都需要用到該指針。
-
- int main(void)
- {
- OSSysTickInit();//滴答定時(shí)器初始化
- USART1_Init(115200);//初始化串口,用于調(diào)試
-
- OSInit(); //操作系統(tǒng)初始化
- //創(chuàng)建一個(gè)郵箱
- mbox=OSMboxCreate (NULL);//初始化消息的地址:NULL
-
- OSTaskCreate (SendTask,NULL,&stack0[TASK0_STK_SIZE-1],TASK0_PRI);//創(chuàng)建發(fā)送任務(wù)
- OSTaskCreate (ReceiveTask,NULL,&stack1[TASK1_STK_SIZE-1],TASK1_PRI);//創(chuàng)建接收任務(wù)
-
- OSStart(); //啟動(dòng)操作系統(tǒng)
- }
-
- void SendTask (void *p_arg) //發(fā)送任務(wù)
- {
- u8 k=0;//給個(gè)默認(rèn)值0,表示無鍵狀態(tài)
- KEY_Init();//按鍵初始化,放在對應(yīng)任務(wù)中
- while(1)
- {
- k=Key_Scanf(0);//按鍵掃描
- switch (k)
- {
- case KEY_ONE: //按鍵1按下,發(fā)布一則消息
- OSMboxPost (mbox,//郵箱
- "hello 51黑");//郵箱內(nèi)容
- break;
- case KEY_TWO: //按鍵2按下,發(fā)布一則消息
-
- OSMboxPost (mbox,//郵箱
- "hello world");//郵箱內(nèi)容
- OSMboxPost (mbox,//郵箱
- "hello xixi");//郵箱內(nèi)容
- OSMboxPost (mbox,//郵箱
- "hello haha");//郵箱內(nèi)容
- break;
- default:
- break;
- }
- OSTimeDly(1);//釋放CPU使用權(quán)
- }
- }
-
- void ReceiveTask (void *p_arg) //接收任務(wù)
- {
- u8 *str;
- while(1)
- {
- //接收一條消息
- str=OSMboxPend (mbox,//郵箱地址
- 0,//死等
- 0);//發(fā)送成功
- printf("接收到的消息:%s\r\n",str);//開始默認(rèn)打印三次,因?yàn)槟J(rèn)開始創(chuàng)建3個(gè)信號(hào)量。
- OSTimeDly(1);//釋放CPU使用權(quán)
- }
- }
-
復(fù)制代碼
實(shí)驗(yàn)結(jié)果如下: 按下按鍵1: 
按下按鍵2: 
原因是接收方接收不過來了,造成了數(shù)據(jù)丟失。這時(shí)需要引入消息隊(duì)列。 5.4.4 消息隊(duì)列 消息郵箱只能發(fā)送一則消息,獲取消息的地方如果處理比較慢就會(huì)丟失消息。消息隊(duì)列能存儲(chǔ)一隊(duì)消息,能很好的避免接收方處理能力弱而丟失消息的問題。隊(duì)列是一種數(shù)據(jù)結(jié)構(gòu),遵循先進(jìn)先出原則。 
需要掌握的函數(shù): 
實(shí)驗(yàn)代碼:
- #include "main.h"
- #include "includes.h"
-
- /** 任務(wù)0 **/
- #define TASK0_PRI 8 //任務(wù)優(yōu)先級
- #define TASK0_STK_SIZE 256 //任務(wù)棧大小
- OS_STK stack0[TASK0_STK_SIZE]; //數(shù)組作為任務(wù)堆棧
- void SendTask (void *p_arg); //函數(shù)聲明
-
- /** 任務(wù)1 **/
- #define TASK1_PRI 9 //任務(wù)優(yōu)先級
- #define TASK1_STK_SIZE 256 //任務(wù)棧大小
- OS_STK stack1[TASK1_STK_SIZE]; //數(shù)組作為任務(wù)堆棧
- void ReceiveTask (void *p_arg); //函數(shù)聲明
-
- OS_EVENT *q;//全局變量,創(chuàng)建隊(duì)列、發(fā)送消息、接收消息,都需要用到該指針。
-
-
- void *queue[10];//隊(duì)列
-
- int main(void)
- {
- OSSysTickInit();//滴答定時(shí)器初始化
- USART1_Init(115200);//初始化串口,用于調(diào)試
-
- OSInit(); //操作系統(tǒng)初始化
-
- //創(chuàng)建一個(gè)隊(duì)列
- q=OSQCreate(queue,//隊(duì)列
- 10);//隊(duì)列的大小
-
- OSTaskCreate (SendTask,NULL,&stack0[TASK0_STK_SIZE-1],TASK0_PRI);//創(chuàng)建發(fā)送任務(wù)
- OSTaskCreate (ReceiveTask,NULL,&stack1[TASK1_STK_SIZE-1],TASK1_PRI);//創(chuàng)建接收任務(wù)
-
- OSStart(); //啟動(dòng)操作系統(tǒng)
- }
-
- void SendTask (void *p_arg) //發(fā)送任務(wù)
- {
- u8 k=0;//給個(gè)默認(rèn)值0,表示無鍵狀態(tài)
- KEY_Init();//按鍵初始化,放在對應(yīng)任務(wù)中
- while(1)
- {
- k=Key_Scanf(0);//按鍵掃描
- switch (k)
- {
- case KEY_ONE: //按鍵1按下,發(fā)布一則消息
- OSQPost (q,//隊(duì)列地址
- "lele");//需要發(fā)送的內(nèi)容
- OSQPost (q,//隊(duì)列地址
- "學(xué)習(xí)");//需要發(fā)送的內(nèi)容
- OSQPost (q,//隊(duì)列地址
- "不可能");//需要發(fā)送的內(nèi)容
- break;
- case KEY_TWO: //按鍵2按下,發(fā)布一則消息
- OSQPost (q,//隊(duì)列地址
- "哈哈");//需要發(fā)送的內(nèi)容
- break;
- default:
- break;
- }
- OSTimeDly(1);//釋放CPU使用權(quán)
- }
- }
-
- void ReceiveTask (void *p_arg) //接收任務(wù)
- {
- char *strs;
- while(1)
- {
- //接收一條消息
- strs=OSQPend(q,//隊(duì)列
- 0,//死等
- 0);//錯(cuò)誤類型
- printf("接收到的消息:%s\r\n",strs);//開始默認(rèn)打印三次,因?yàn)槟J(rèn)開始創(chuàng)建3個(gè)信號(hào)量。
- OSTimeDly(1);//釋放CPU使用權(quán)
- }
- }
復(fù)制代碼
實(shí)驗(yàn)結(jié)果如下: 
發(fā)送三條消息,接收到三條消息,沒有數(shù)據(jù)丟失。 5.4.5 補(bǔ)充 信號(hào)量:就理解成有多個(gè)電話的電話亭,這些電話是共享資源,當(dāng)有很多人使用時(shí),需要排隊(duì)(優(yōu)先級),需要等待(等待信號(hào)量),當(dāng)電話亭的電話空閑時(shí)(有信號(hào)量),就可以讓排在前面的人使用,依次使用。 互斥信號(hào)量:和信號(hào)量基本一致,理解成只用一個(gè)電話的電話亭,多個(gè)用戶要互斥使用這個(gè)電話。 消息郵箱:發(fā)送一條消息。如果消息發(fā)送太快,接收方接收不過來,會(huì)造成數(shù)據(jù)丟失。 消息隊(duì)列:發(fā)送多條消息。實(shí)現(xiàn)原理是把多條消息存放在隊(duì)列中。 最重要的最重要的是: - 概念的理解,領(lǐng)會(huì)
- 代碼的實(shí)現(xiàn)、代碼的流程
- 多實(shí)踐與思考
5.5 其他補(bǔ)充 5.5.1 延時(shí)函數(shù) 這里的延時(shí)與STM32的延遲有不同的含義,對于STM32F407,系統(tǒng)時(shí)鐘為21M,即21 000 000 次脈沖為1秒鐘=21 000 000個(gè)滴答為1秒鐘,UCOS的延時(shí)規(guī)定,200個(gè)脈沖為1秒鐘=200個(gè)滴答為1秒鐘,所以UCOS的一個(gè)滴答為5ms。 

常用第一個(gè)函數(shù),第二函數(shù)用來延時(shí),會(huì)有點(diǎn)誤差,因?yàn)槿蝿?wù)調(diào)度會(huì)消耗一點(diǎn)時(shí)間。 為什么使用第一個(gè)函數(shù),能發(fā)生任務(wù)調(diào)度?看代碼如下: 
因?yàn)楹瘮?shù)的實(shí)現(xiàn)中有任務(wù)調(diào)度函數(shù)。 實(shí)際的任務(wù)調(diào)度代碼源頭在哪?進(jìn)入看看 

在點(diǎn)擊進(jìn)入發(fā)現(xiàn)。進(jìn)不了了。實(shí)際這個(gè)函數(shù)由匯編代碼實(shí)現(xiàn) 

5.5.2 軟件定時(shí)器 實(shí)現(xiàn)UCOS軟件定時(shí)器需要注意兩點(diǎn): - 打開定時(shí)器代碼,會(huì)發(fā)現(xiàn)創(chuàng)建定時(shí)器代碼都是灰色,需要修改一個(gè)宏


第二,定義一個(gè)優(yōu)先級,編譯你就會(huì)發(fā)現(xiàn)這個(gè)優(yōu)先級了,注意優(yōu)先級不能設(shè)置跟其他任務(wù)的有效一樣。 


編寫代碼驗(yàn)證,編寫如下代碼: #include "main.h"
#include "includes.h"
OS_TMR *tmr;//定時(shí)器。
void MyCallback (OS_TMR *ptmr, void *p_arg);//回調(diào)函數(shù)
int main(void)
{
OSSysTickInit();//滴答定時(shí)器初始化
USART1_Init(115200);//初始化串口,用于調(diào)試
OSInit(); //操作系統(tǒng)初始化
//創(chuàng)建定時(shí)器
tmr=OSTmrCreate (50,//第一次使用,規(guī)定10個(gè)滴答為1秒鐘--所以第一次定時(shí)5秒鐘
10,//第二次以后使用--定時(shí)1秒鐘
OS_TMR_OPT_PERIODIC,//循環(huán)模式
(void *)MyCallback,//回調(diào)函數(shù)
0,//回調(diào)函數(shù)參數(shù)
0,//定時(shí)器名字
0);//錯(cuò)誤類型
//啟動(dòng)定時(shí)器
OSTmrStart (tmr,//要啟動(dòng)的定時(shí)器
0);//錯(cuò)誤類型--成功
OSStart();//啟動(dòng)操作系統(tǒng)
}
void MyCallback (OS_TMR *ptmr, void *p_arg)
{
printf("定時(shí)器創(chuàng)建成功\r\n");
} 實(shí)驗(yàn)結(jié)果如下: 
定時(shí)器,一次滴答多少時(shí)間? 
5.5.3 其他函數(shù) 
給調(diào)度器上鎖與解鎖,一般用于初始化任務(wù)。 臨界區(qū):是被保護(hù)的區(qū)域,一般不允許被中斷,所以進(jìn)入之前需要關(guān)閉中斷,出來時(shí),要打開中斷功能。
全部資料51hei下載地址:
程序.7z
(373.07 KB, 下載次數(shù): 11)
2020-6-30 18:18 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
UCOS之任務(wù)間通信、軟件定時(shí)器補(bǔ)充.docx
(4.21 MB, 下載次數(shù): 11)
2020-6-29 09:18 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
|