1.什么是信號量
在UCOSIII中,信號量分為兩種:二值信號量和計數信號量
二值信號量就是只有兩個值(0和1)的信號量,當它為1的時候,與它綁定的資源就可以被訪問,當它為0的時候,與它綁定的資源不可以被訪問。試圖訪問一個信號量為0的資源的任務會被放入到等待信號量的任務表中,在等待信號量的時候也可以設置超時處理,如果設定的時間任務沒有等到信號量的話那么該任務就會進入就緒態。可以看出,一個信號量如果為二值信號量的話,一次只能有一個任務可以訪問這個資源,其實和單片機程序中的flag一樣,做標志位的。
計數信號量可以有多種數值,每發送一次信號量時,該信號量+1,每請求一次信號量時,該信號量-1。任務同步使用這個。
2.怎么使用信號量
要使用信號量肯定要先創建一個信號量
2.1創建信號量的函數
void OSSemCreate (OS_SEM *p_sem, //信號量 CPU_CHAR *p_name, //信號量名字 OS_SEM_CTR cnt, //信號量的值 OS_ERR *p_err) //返回值,記錄錯誤信號
第一個參數需要先定義一個OS_SEM的變量
OS_SEM MySem; //定義一個信號量,用於任務同步
第二個參數隨便寫一個字符串
第三個參數用來表示信號量的初始值。有2種寫法, 如果用於共享資源,則應初始化為可用資源數量。如果用於表示事件的發生,則應將其初始化為0。用於任務同步的話,使用事件發生次數。
第四個參數記錄返回值,其他函數也有這樣的一個返回值。
調用OSSemCreate創建一個信號量。創建信號量應該放在第一個任務中(用來創建其他任務的哪個任務)
OSSemCreate ((OS_SEM* )&MySem, (CPU_CHAR* )"SemName", (OS_SEM_CTR)0, //初始化為0,表示事件的發生次數 (OS_ERR* )&err);
2.2發送信號量的函數
OS_SEM_CTR OSSemPost (OS_SEM *p_sem,
OS_OPT opt,
OS_ERR *p_err)
第一個參數是上面定義的信號量結構體變量MySem
第二個參數決定發送模式,
2.3請求信號量的函數
OS_SEM_CTR OSSemPend (OS_SEM *p_sem, OS_TICK timeout, OS_OPT opt, CPU_TS *p_ts, OS_ERR *p_err)
第一個參數是上面定義的信號量結構體變量MySem
第二個參數是設置超時時間,設置為0則一直等待(阻塞)
第三個參數是設置是否阻塞等待這個信號量
第四個參數可以記錄請求到信號量時的時間(時間戳),為空則不需要時間戳
第五個參數記錄返回值,其他函數也有這樣的一個返回值。
3.信號量完成任務同步
信號量現在更多的被用來實現任務的同步以及任務和ISR間的同步,信號量用於任務同步。
圖1 信號量用於任務同步
圖1中用一個小旗子代表信號量,小旗子旁邊的數值N為信號量計數值,表示發布號量的次數累積值,ISR可以多次發布信號量,發布的次數會記錄為N。一般情況下,N的
初始值是0,表示事件還沒有發生過。在初始化時,也可以將N的初值設為大於零的某個值,來表示初始情況下有多少信號量可用。等待信號量的任務旁邊的小沙漏表示等待任務可以設定超時時間。超時的意思是該任務只會等待一定時間的信號量,如果在這段時間內沒有等到信號量,UCOSⅡ就會將任務置於就緒表中,並返回錯誤碼
3.1 使用信號量完成任務同步
任務1每1.5s發送一個信號量。任務2每0.5s請求一次信號量。記錄這個信號量數值的變量是上面定義的的信號量結構體下的一個ctr變量
發送信號量和請求信號量的過程:
每當任務1發送一次信號量是ctr+1,每當任務2請求到一次信號量時ctr-1。當ctr=0時,任務2阻塞等待任務1發送信號量。原理就是只有任務1發送了信號量時,任務2才會請求到一次信號量,任務2才會執行一次。當任務1發送了10次信號量,任務2也就會執行10次。這樣完成任務同步。
//任務1的任務函數 void task1_task(void *p_arg) { u8 key=0; OS_ERR err; //printf("當前任務1進來了 \n" ); while(1) { keey++; if(次數==10){OSTaskDel((OS_TCB *)0,&err);}//任務1發送10次信號量后刪除自己,不再發送信號量 if(key<10){ printf("當前任務1while1 進來了key=%d \n", key); OSSemPost(&SYNC_SEM,OS_OPT_POST_1,&err);//發送信號量 printf("當前任務1的信號量:%d\n",MySem.Ctr); OSTimeDlyHMSM(0,0,1,500,OS_OPT_TIME_PERIODIC,&err); //延時1.5s }} }
//任務2的任務函數 void task2_task(void *p_arg) { u8 num; OS_ERR err; //printf("當前任務2進來了 \n" ); while(1) {printf("當前任務2while1 進來了 \n" ); OSSemPend(&SYNC_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); //請求信號量 num++; printf("當前任務2的信號量:%d\n",MySem.Ctr); OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延時1s } }
結果:
由於任務1 2里面的延時時間不同,串口打印的數據也很錯亂。當任務1發送了9次信號量后,停止發送,任務2也會請求到9次信號量。也就是說任務1和任務2的執行次數是相同的。這樣完成任務同步。