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

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

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

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


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


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