1. 程式人生 > >【UCOSIII】UCOSIII的訊號量

【UCOSIII】UCOSIII的訊號量

訊號量

訊號量像是一種上鎖機制,程式碼必須獲得對應的鑰匙才能繼續執行,一旦獲得了鑰匙,也就意味著該任務具有進入被鎖部分程式碼的許可權。一旦執行至被鎖程式碼段,則任務一直等待,直到對應被鎖部分程式碼的鑰匙被再次釋放才能繼續執行。

訊號量用於控制對共享資源的保護,但是現在基本用來做任務同步用。

要想獲取資源的任務必須執行“等待”操作,如果該資源對應的訊號量有效值大於1,則任務可以獲得該資源,任務繼續執行。如果該訊號量的有效值為0,則任務加入等待訊號量的任務表中。如果等待時間超過某一個設定值,該訊號量仍然沒有被釋放掉,則等待訊號量的任務就進入就緒態,如果將等待時間設定為0的話任務就將一直等待該訊號量。

這裡就涉及到一個請求訊號量(Pend)、釋放訊號量(Post)。

訊號量分類

訊號量分為兩種:二進位制訊號量與計數型訊號量,這是由訊號量的取值範圍進行區分的。在共享資源中只有任務可以使用訊號量,中斷服務程式則不能使用。

二進位制訊號量只能取0和1兩個值;計數型訊號量的訊號量值大於1,計數型訊號量的範圍由OS_SEM_CTR決定,OS_SEM_CTR可以為8位,16位和32位,取值範圍分別為:0~255,0~65535和0~4294967295。

可以很感性地這樣認為:訊號量的值代表著能夠再次申請該訊號量的數目;當訊號量的值為0的時候,表示該訊號量此時不能被申請、被等待了,只有等其他的訊號量釋放了之後,才能夠再次獲取。

二值訊號量用於那些一次只能一個任務使用的資源,比如I/O裝置,印表機計,數型訊號量用於某些資源可以同時被幾個任務所使用,比如一個快取池有10個快取塊,那麼同時最多可以支援10個任務來使用記憶體池。

訊號量API函式

常見訊號量API函式
函式說明
OSSemCreate()建立一個訊號量
OSSemDel()刪除一個訊號量
OSSemPend()等待一個訊號量
OSSemPendAbort()取消等待
OSSemPost()釋放一個訊號量
OSSemSet()強制設定一個訊號量的值

建立一個訊號量

要想使用訊號量,肯定需要先建立一個訊號量,我們使用函式OSSemCreate()來建立訊號量,函式原型如下:

void  OSSemCreate (OS_SEM      *p_sem,                  //指向訊號量控制塊
                   CPU_CHAR    *p_name,                 //指向訊號量的名字
                   OS_SEM_CTR   cnt,                    //設定訊號量的初始值,1(代表二進位制訊號量),大於1(代表計數型訊號量)
                   OS_ERR      *p_err)
{
    CPU_SR_ALLOC();

    OS_CRITICAL_ENTER();
    p_sem->Type    = OS_OBJ_TYPE_SEM;                       /* Mark the data structure as a semaphore                 */
    p_sem->Ctr     = cnt;                                   /* Set semaphore value                                    */
    p_sem->TS      = (CPU_TS)0;
    p_sem->NamePtr = p_name;                                /* Save the name of the semaphore                         */
    OS_PendListInit(&p_sem->PendList);                      /* Initialize the waiting list                            */

    OSSemQty++;

    OS_CRITICAL_EXIT_NO_SCHED();
   *p_err = OS_ERR_NONE;
}

訊號量控制塊是什麼結構呢?

struct  os_sem {                                            /* Semaphore                                              */
                                                            /* ------------------ GENERIC  MEMBERS ------------------ */
    OS_OBJ_TYPE          Type;                              /* Should be set to OS_OBJ_TYPE_SEM                       */
    CPU_CHAR            *NamePtr;                           /* Pointer to Semaphore Name (NUL terminated ASCII)       */
    OS_PEND_LIST         PendList;                          /* List of tasks waiting on semaphore                     */
#if OS_CFG_DBG_EN > 0u
    OS_SEM              *DbgPrevPtr;
    OS_SEM              *DbgNextPtr;
    CPU_CHAR            *DbgNamePtr;
#endif
                                                            /* ------------------ SPECIFIC MEMBERS ------------------ */
    OS_SEM_CTR           Ctr;                            //訊號量當前的取值
    CPU_TS               TS;
};

請求訊號量

當一個任務需要獨佔式的訪問某個特定的系統資源時,需要與其他任務或中斷服務程式同步,或者需要等待某個事件的發生,應該呼叫函式OSSemPend(),函式原型如下:

OS_SEM_CTR  OSSemPend (OS_SEM   *p_sem,                        //指向訊號量控制塊
                       OS_TICK   timeout,                        //指定等待訊號量的超時時間(時鐘節拍數)
                       OS_OPT    opt,                    //用於設定是否使用阻塞模式
                       CPU_TS   *p_ts,            //指向一個時間戳,記錄接收到訊號量的時刻,NULL表示不記錄
                       OS_ERR   *p_err)
{
    OS_SEM_CTR    ctr;
    OS_PEND_DATA  pend_data;
    CPU_SR_ALLOC();

    if (p_ts != (CPU_TS *)0) {
       *p_ts  = (CPU_TS)0;                                  /* Initialize the returned timestamp                      */
    }
    CPU_CRITICAL_ENTER();
    if (p_sem->Ctr > (OS_SEM_CTR)0) {                       /* Resource available?                                    */
        p_sem->Ctr--;                                       /* Yes, caller may proceed                                */
        if (p_ts != (CPU_TS *)0) {
           *p_ts  = p_sem->TS;                              /*      get timestamp of last post                        */
        }
        ctr   = p_sem->Ctr;
        CPU_CRITICAL_EXIT();
       *p_err = OS_ERR_NONE;
        return (ctr);
    }

    if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) {    /* Caller wants to block if not available?                */
        ctr   = p_sem->Ctr;                                 /* No                                                     */
        CPU_CRITICAL_EXIT();
       *p_err = OS_ERR_PEND_WOULD_BLOCK;
        return (ctr);
    } else {                                                /* Yes                                                    */
        if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {    /* Can't pend when the scheduler is locked                */
            CPU_CRITICAL_EXIT();
           *p_err = OS_ERR_SCHED_LOCKED;
            return ((OS_SEM_CTR)0);
        }
    }

    OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();                  /* Lock the scheduler/re-enable interrupts                */
    OS_Pend(&pend_data,                                     /* Block task pending on Semaphore                        */
            (OS_PEND_OBJ *)((void *)p_sem),
            OS_TASK_PEND_ON_SEM,
            timeout);

    OS_CRITICAL_EXIT_NO_SCHED();

    OSSched();                                              /* Find the next highest priority task ready to run       */

    CPU_CRITICAL_ENTER();
    switch (OSTCBCurPtr->PendStatus) {
        case OS_STATUS_PEND_OK:                             /* We got the semaphore                                   */
             if (p_ts != (CPU_TS *)0) {
                *p_ts  =  OSTCBCurPtr->TS;
             }
            *p_err = OS_ERR_NONE;
             break;

        case OS_STATUS_PEND_ABORT:                          /* Indicate that we aborted                               */
             if (p_ts != (CPU_TS *)0) {
                *p_ts  =  OSTCBCurPtr->TS;
             }
            *p_err = OS_ERR_PEND_ABORT;
             break;

        case OS_STATUS_PEND_TIMEOUT:                        /* Indicate that we didn't get semaphore within timeout   */
             if (p_ts != (CPU_TS *)0) {
                *p_ts  = (CPU_TS  )0;
             }
            *p_err = OS_ERR_TIMEOUT;
             break;

        case OS_STATUS_PEND_DEL:                            /* Indicate that object pended on has been deleted        */
             if (p_ts != (CPU_TS *)0) {
                *p_ts  =  OSTCBCurPtr->TS;
             }
            *p_err = OS_ERR_OBJ_DEL;
             break;

        default:
            *p_err = OS_ERR_STATUS_INVALID;
             CPU_CRITICAL_EXIT();
             return ((OS_SEM_CTR)0);
    }
    ctr = p_sem->Ctr;
    CPU_CRITICAL_EXIT();
    return (ctr);
}

timeout:指定等待訊號量的超時時間(時鐘節拍數),如果在指定時間內沒有等到訊號量則允許任務恢復執行。如果指定時間為0的話任務就會一直等待下去,直到等到訊號量。

opt:用於設定是否使用阻塞模式,有下面兩個選項。OS_OPT_PEND_BLOCKING:指定訊號量無效時,任務掛起以等待訊號量;OS_OPT_PEND_NON_BLOCKING:訊號量無效時,任務直接返回。

傳送訊號量

任務獲得訊號量以後就可以訪問共享資源了,在任務訪問完共享資源以後必須釋放訊號量,釋放訊號量也叫傳送訊號量,使用函式OSSemPost()傳送訊號量。

如果沒有任務在等待該訊號量的話則OSSemPost()函式只是簡單的將訊號量加1,然後返回到呼叫該函式的任務中繼續執行。如果有一個或者多個任務在等待這個訊號量,則優先順序最高的任務將獲得這個訊號量,然後由排程器來判定剛獲得訊號量的任務是否為系統中優先順序最高的就緒任務,如果是,則系統將進行任務切換,執行這個就緒任務,OSSemPost()函式原型如下:

OS_SEM_CTR  OSSemPost (OS_SEM  *p_sem,                    //指向一個訊號的指標
                       OS_OPT   opt,                    //用來選擇訊號量傳送的方式
                       OS_ERR  *p_err)
{
    OS_SEM_CTR  ctr;
    CPU_TS      ts;

    ts = OS_TS_GET();                                       /* Get timestamp                                          */

    ctr = OS_SemPost(p_sem,                                 /* Post to semaphore                                      */
                     opt,
                     ts,
                     p_err);

    return (ctr);
}

opt:用來選擇訊號量傳送的方式。OS_OPT_POST_1:僅向等待該訊號量的優先順序最高的任務傳送訊號量;OS_OPT_POST_ALL:向等待該訊號量的所有任務傳送訊號量;OS_OPT_POST_NO_SCHED:該選項禁止在本函式內執行任務排程操作。即使該函式使得更高優先順序的任務結束掛起進入就緒狀態,也不會執行任務排程,而是會在其他後續函式中完成任務排程。

UCOSIII實際例程

上文說到:訊號量用於控制對共享資源的保護,但是現在基本用來做任務同步用。

使用訊號量訪問共享資源區實驗

例程要求:建立3個任務,任務A用於建立其他兩個任務,任務A執行一次後就會被刪除掉。任務B和任務C都可以訪問作為共享資源D,任務B和C對於共享資源D是使用訊號量訪問的。

例子:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
#include "includes.h"

//UCOSIII中以下優先順序使用者程式不能使用,ALIENTEK
//將這些優先順序分配給了UCOSIII的5個系統內部任務
//優先順序0:中斷服務服務管理任務 OS_IntQTask()
//優先順序1:時鐘節拍任務 OS_TickTask()
//優先順序2:定時任務 OS_TmrTask()
//優先順序OS_CFG_PRIO_MAX-2:統計任務 OS_StatTask()
//優先順序OS_CFG_PRIO_MAX-1:空閒任務 OS_IdleTask()

//任務優先順序
#define START_TASK_PRIO		3
//任務堆疊大小	
#define START_STK_SIZE 		128
//任務控制塊
OS_TCB StartTaskTCB;
//任務堆疊	
CPU_STK START_TASK_STK[START_STK_SIZE];
//任務函式
void start_task(void *p_arg);

//任務優先順序
#define TASK1_TASK_PRIO		4
//任務堆疊大小	
#define TASK1_STK_SIZE 		128
//任務控制塊
OS_TCB Task1_TaskTCB;
//任務堆疊	
CPU_STK TASK1_TASK_STK[TASK1_STK_SIZE];
void task1_task(void *p_arg);

//任務優先順序
#define TASK2_TASK_PRIO		5
//任務堆疊大小	
#define TASK2_STK_SIZE 		128
//任務控制塊
OS_TCB Task2_TaskTCB;
//任務堆疊	
CPU_STK TASK2_TASK_STK[TASK2_STK_SIZE];
void task2_task(void *p_arg);

u8 share_resource[30];               //共享資源區

OS_SEM	MY_SEM;		            //定義一個訊號量,用於訪問共享資源

int main(void)                        //主函式
{
	OS_ERR err;
	CPU_SR_ALLOC();
	
	delay_init();  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	uart_init(115200);
	LED_Init();
	LCD_Init();		
	KEY_Init();	
	
	POINT_COLOR = RED;
	LCD_ShowString(30,10,200,16,16,"ALIENTEK STM32F1");	
	LCD_ShowString(30,30,200,16,16,"UCOSIII Examp 10-2");
	LCD_ShowString(30,50,200,16,16,"Sem Share Resource");
	LCD_ShowString(30,70,200,16,16,"[email protected]");
	LCD_ShowString(30,90,200,16,16,"2013/3/19");
	
	OSInit(&err);		    	    //初始化UCOSIII
	OS_CRITICAL_ENTER();	        //進入臨界區			 

	OSTaskCreate((OS_TCB 	* )&StartTaskTCB,		//任務控制塊
				 (CPU_CHAR	* )"start task", 		//任務名字
                 (OS_TASK_PTR )start_task, 			//任務函式
                 (void		* )0,					//傳遞給任務函式的引數
                 (OS_PRIO	  )START_TASK_PRIO,     //任務優先順序
                 (CPU_STK   * )&START_TASK_STK[0],	//任務堆疊基地址
                 (CPU_STK_SIZE)START_STK_SIZE/10,	//任務堆疊深度限位
                 (CPU_STK_SIZE)START_STK_SIZE,		//任務堆疊大小
                 (OS_MSG_QTY  )0,					//任務內部訊息佇列能夠接收的最大訊息數目,為0時禁止接收訊息
                 (OS_TICK	  )0,					//當使能時間片輪轉時的時間片長度,為0時為預設長度,
                 (void   	* )0,					//使用者補充的儲存區
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任務選項
                 (OS_ERR 	* )&err);				//存放該函式錯誤時的返回值
	OS_CRITICAL_EXIT();	//退出臨界區	 
	OSStart(&err);      //開啟UCOSIII
}

void start_task(void *p_arg)                //開始任務函式
{
	OS_ERR err;
	CPU_SR_ALLOC();
	p_arg = p_arg;
	
	CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
   OSStatTaskCPUUsageInit(&err);  	//統計任務                
#endif
	
#ifdef CPU_CFG_INT_DIS_MEAS_EN		//如果使能了測量中斷關閉時間
    CPU_IntDisMeasMaxCurReset();	
#endif
	
#if	OS_CFG_SCHED_ROUND_ROBIN_EN  //當使用時間片輪轉的時候
	 //使能時間片輪轉排程功能,時間片長度為1個系統時鐘節拍,既1*5=5ms
	OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);  
#endif	
		
	OS_CRITICAL_ENTER();	//進入臨界區

	OSSemCreate ((OS_SEM*	)&MY_SEM,        //建立一個訊號量
                 (CPU_CHAR*	)"MY_SEM",
                 (OS_SEM_CTR)1,		
                 (OS_ERR*	)&err);

	OSTaskCreate((OS_TCB 	* )&Task1_TaskTCB,	                //建立TASK1任務	
				 (CPU_CHAR	* )"Task1 task", 		
                 (OS_TASK_PTR )task1_task, 			
                 (void		* )0,					
                 (OS_PRIO	  )TASK1_TASK_PRIO,     
                 (CPU_STK   * )&TASK1_TASK_STK[0],	
                 (CPU_STK_SIZE)TASK1_STK_SIZE/10,	
                 (CPU_STK_SIZE)TASK1_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,  					
                 (void   	* )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR 	* )&err);			

	OSTaskCreate((OS_TCB 	* )&Task2_TaskTCB,	                    //建立TASK2任務	
				 (CPU_CHAR	* )"Task2 task", 		
                 (OS_TASK_PTR )task2_task, 			
                 (void		* )0,					
                 (OS_PRIO	  )TASK2_TASK_PRIO,     
                 (CPU_STK   * )&TASK2_TASK_STK[0],	
                 (CPU_STK_SIZE)TASK2_STK_SIZE/10,	
                 (CPU_STK_SIZE)TASK2_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,  					
                 (void   	* )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR 	* )&err);				 
	OS_CRITICAL_EXIT();	//退出臨界區
	OSTaskDel((OS_TCB*)0,&err);	//刪除start_task任務自身
}

void task1_task(void *p_arg)                //任務1的任務函式
{
	OS_ERR err;
	u8 task1_str[]="First task Running!";
	while(1)
	{
		printf("\r\n任務1:\r\n");
		LCD_Fill(0,110,239,319,CYAN);
		OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); 	//請求訊號量
		memcpy(share_resource,task1_str,sizeof(task1_str));     //向共享資源區拷貝資料
		delay_ms(300);                                //任務切換
		printf("%s\r\n",share_resource);	    //串列埠輸出共享資源區資料	
		OSSemPost (&MY_SEM,OS_OPT_POST_1,&err);				//傳送訊號量
		LED0 = ~LED0;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);   //延時1s
	}
}

void task2_task(void *p_arg)                    //任務2的任務函式
{	
	OS_ERR err;
	u8 task2_str[]="Second task Running!";
	while(1)
	{
		printf("\r\n任務2:\r\n");
		LCD_Fill(0,110,239,319,BROWN);
		OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); 	//請求訊號量
		memcpy(share_resource,task2_str,sizeof(task2_str));	//向共享資源區拷貝資料
		delay_ms(300);
		printf("%s\r\n",share_resource);	//串列埠輸出共享資源區資料		
		OSSemPost (&MY_SEM,OS_OPT_POST_1,&err);				//傳送訊號量
		LED1 = ~LED1;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);   //延時1s
	}
}

任務同步實驗

訊號量現在更多的被用來實現任務的同步以及任務和ISR間的同步,訊號量用於任務同步如下圖所示:


上圖中用一個小旗子代表訊號量,小旗子旁邊的數值N為訊號量計數值,表示釋出訊號量的次數累積值,ISR可以多次釋出訊號量,釋出的次數會記錄為N。一般情況下,N的初始值是0,表示事件還沒有發生過。在初始化時,也可以將N的初值設為大於零的某個值,來表示初始情況下有多少訊號量可用。

等待訊號量的任務旁邊的小沙漏表示等待任務可以設定超時時間。超時的意思是該任務只會等待一定時間的訊號量,如果在這段時間內沒有等到訊號量,UCOSIII就會將任務置於就緒表中,並返回錯誤碼。

例程要求:建立3個任務,任務A用於建立其他兩個任務和一個初始值為0的訊號量,任務C必須徵得任務B的同意才能執行一次操作。

例子:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
#include "includes.h"

//UCOSIII中以下優先順序使用者程式不能使用,ALIENTEK
//將這些優先順序分配給了UCOSIII的5個系統內部任務
//優先順序0:中斷服務服務管理任務 OS_IntQTask()
//優先順序1:時鐘節拍任務 OS_TickTask()
//優先順序2:定時任務 OS_TmrTask()
//優先順序OS_CFG_PRIO_MAX-2:統計任務 OS_StatTask()
//優先順序OS_CFG_PRIO_MAX-1:空閒任務 OS_IdleTask()

//任務優先順序
#define START_TASK_PRIO		3
//任務堆疊大小	
#define START_STK_SIZE 		128
//任務控制塊
OS_TCB StartTaskTCB;
//任務堆疊	
CPU_STK START_TASK_STK[START_STK_SIZE];
//任務函式
void start_task(void *p_arg);

//任務優先順序
#define TASK1_TASK_PRIO		4
//任務堆疊大小	
#define TASK1_STK_SIZE 		128
//任務控制塊
OS_TCB Task1_TaskTCB;
//任務堆疊	
CPU_STK TASK1_TASK_STK[TASK1_STK_SIZE];
void task1_task(void *p_arg);

//任務優先順序
#define TASK2_TASK_PRIO		5
//任務堆疊大小	
#define TASK2_STK_SIZE 		128
//任務控制塊
OS_TCB Task2_TaskTCB;
//任務堆疊	
CPU_STK TASK2_TASK_STK[TASK2_STK_SIZE];
void task2_task(void *p_arg);

//LCD刷屏時使用的顏色
int lcd_discolor[14]={	WHITE, BLACK, BLUE,  BRED,      
						GRED,  GBLUE, RED,   MAGENTA,       	 
						GREEN, CYAN,  YELLOW,BROWN, 			
						BRRED, GRAY };

OS_SEM	SYNC_SEM;		//定義一個訊號量,用於任務同步

int main(void)                //主函式
{
	OS_ERR err;
	CPU_SR_ALLOC();
	
	delay_init();  //時鐘初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中斷分組配置
	uart_init(115200);   //串列埠初始化
	LED_Init();         //LED初始化	
	LCD_Init();			//LCD初始化	
	KEY_Init();			//按鍵初始化
	
	POINT_COLOR = RED;
	LCD_ShowString(30,10,200,16,16,"ALIENTEK STM32F1");	
	LCD_ShowString(30,30,200,16,16,"UCOSIII Examp 10-3");
	LCD_ShowString(30,50,200,16,16,"Sem Sync");
	LCD_ShowString(30,70,200,16,16,"[email protected]");
	LCD_ShowString(30,90,200,16,16,"2015/3/19");
	
	POINT_COLOR = BLACK;
	LCD_DrawRectangle(5,110,234,314);	
	LCD_DrawLine(5,130,234,130);
	POINT_COLOR = RED;
	LCD_ShowString(30,111,200,16,16,"SYNC_SEM Value:  0");
	POINT_COLOR = BLUE;
	
	OSInit(&err);		    	//初始化UCOSIII
	OS_CRITICAL_ENTER();	//進入臨界區			 
	//建立開始任務
	OSTaskCreate((OS_TCB 	* )&StartTaskTCB,		//任務控制塊
				 (CPU_CHAR	* )"start task", 		//任務名字
                 (OS_TASK_PTR )start_task, 			//任務函式
                 (void		* )0,					//傳遞給任務函式的引數
                 (OS_PRIO	  )START_TASK_PRIO,     //任務優先順序
                 (CPU_STK   * )&START_TASK_STK[0],	//任務堆疊基地址
                 (CPU_STK_SIZE)START_STK_SIZE/10,	//任務堆疊深度限位
                 (CPU_STK_SIZE)START_STK_SIZE,		//任務堆疊大小
                 (OS_MSG_QTY  )0,					//任務內部訊息佇列能夠接收的最大訊息數目,為0時禁止接收訊息
                 (OS_TICK	  )0,					//當使能時間片輪轉時的時間片長度,為0時為預設長度,
                 (void   	* )0,					//使用者補充的儲存區
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任務選項
                 (OS_ERR 	* )&err);				//存放該函式錯誤時的返回值
	OS_CRITICAL_EXIT();	//退出臨界區	 
	OSStart(&err);      //開啟UCOSIII
}

void start_task(void *p_arg)                //開始任務函式
{
	OS_ERR err;
	CPU_SR_ALLOC();
	p_arg = p_arg;
	
	CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
   OSStatTaskCPUUsageInit(&err);  	//統計任務                
#endif
	
#ifdef CPU_CFG_INT_DIS_MEAS_EN		//如果使能了測量中斷關閉時間
    CPU_IntDisMeasMaxCurReset();	
#endif
	
#if	OS_CFG_SCHED_ROUND_ROBIN_EN  //當使用時間片輪轉的時候
	 //使能時間片輪轉排程功能,時間片長度為1個系統時鐘節拍,既1*5=5ms
	OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);  
#endif	
		
	OS_CRITICAL_ENTER();	//進入臨界區

	OSSemCreate ((OS_SEM*	)&SYNC_SEM,                    //建立一個訊號量
                 (CPU_CHAR*	)"SYNC_SEM",
                 (OS_SEM_CTR)0,		
                 (OS_ERR*	)&err);

	OSTaskCreate((OS_TCB 	* )&Task1_TaskTCB,	                //建立TASK1任務	
				 (CPU_CHAR	* )"Task1 task", 		
                 (OS_TASK_PTR )task1_task, 			
                 (void		* )0,					
                 (OS_PRIO	  )TASK1_TASK_PRIO,     
                 (CPU_STK   * )&TASK1_TASK_STK[0],	
                 (CPU_STK_SIZE)TASK1_STK_SIZE/10,	
                 (CPU_STK_SIZE)TASK1_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,  					
                 (void   	* )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR 	* )&err);			

	OSTaskCreate((OS_TCB 	* )&Task2_TaskTCB,		        //建立TASK2任務
				 (CPU_CHAR	* )"Task2 task", 		
                 (OS_TASK_PTR )task2_task, 			
                 (void		* )0,					
                 (OS_PRIO	  )TASK2_TASK_PRIO,     
                 (CPU_STK   * )&TASK2_TASK_STK[0],	
                 (CPU_STK_SIZE)TASK2_STK_SIZE/10,	
                 (CPU_STK_SIZE)TASK2_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,  					
                 (void   	* )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR 	* )&err);				 
	OS_CRITICAL_EXIT();	//退出臨界區
	OSTaskDel((OS_TCB*)0,&err);	//刪除start_task任務自身
}

void task1_task(void *p_arg)                //任務1的任務函式
{
	u8 key;
	OS_ERR err;
	while(1)
	{
		key = KEY_Scan(0);  //掃描按鍵
		if(key==WKUP_PRES)	
		{
			OSSemPost(&SYNC_SEM,OS_OPT_POST_1,&err);    //傳送訊號量
			LCD_ShowxNum(150,111,SYNC_SEM.Ctr,3,16,0);	//顯示訊號量值
		}
		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err);   //延時10ms
	}
}

void task2_task(void *p_arg)                //任務2的任務函式
{	
	u8 num;
	OS_ERR err;
	while(1)
	{
		OSSemPend(&SYNC_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); //請求訊號量
		num++;
		LCD_ShowxNum(150,111,SYNC_SEM.Ctr,3,16,0);			//顯示訊號量值
		LCD_Fill(6,131,233,313,lcd_discolor[num%14]);		//刷屏
		LED1 = ~LED1;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);   //延時1s
	}
}

相關推薦

LinuxPOSIX訊號

POSIX訊號量和SystemV訊號量作⽤相同,都是⽤於同步操作,達到⽆衝突的訪問共享資源⺫的。 但POSIX可以⽤於執行緒間同步。 一個計數器+等待佇列,計數器用來標記當前是否有資源可供操作,等待佇列則是沒有資源就將pcb加入佇列中,等有資源的時候喚醒佇列中的執行緒。 訊號量的操作就是對計

UCOSIIIUCOSIII訊號

訊號量訊號量像是一種上鎖機制,程式碼必須獲得對應的鑰匙才能繼續執行,一旦獲得了鑰匙,也就意味著該任務具有進入被鎖部分程式碼的許可權。一旦執行至被鎖程式碼段,則任務一直等待,直到對應被鎖部分程式碼的鑰匙被再次釋放才能繼續執行。訊號量用於控制對共享資源的保護,但是現在基本用來做任

UCOSIIIUCOSIII的互斥訊號

訊號量用於控制對共享資源的保護,但是現在基本用來做任務同步用(不太清楚的可以參考連結:【UCOSIII】UCOSIII的訊號量)。優先順序反轉優先順序反轉在可剝奪核心中是非常常見的,在實時系統中不允許出現這種現象,這樣會破壞任務的預期順序,可能會導致嚴重的後果,下圖就是一個優

UCOSIIIUCOSIII的任務排程和切換

UCOSIII任務排程可剝奪型任務排程任務排程就是中止當前正在執行的任務轉而去執行其他的任務。UCOSIII是可剝奪型核心,因此當一個高優先順序的任務準備就緒,並且此時發生了任務排程,那麼這個高優先順序的任務就會獲得CPU的使用權!UCOSIII中的任務排程是由任務排程器來完

UCOSIIIUCOSIII的同時等待多個核心物件

UCOSIII同時等待多個核心物件前面講述了UCOSIII的訊號量(一個任務與另一個任務同步)、事件標誌組(一個任務與多個任務同步),它們都可以完成任務的同步。同時,訊號量(保證全域性變數)、訊息佇列,它們都可以完成訊息的傳遞。但是,它們描述的情況都是任務如何等待單個物件,比

UCOSIIIUCOSIII的儲存管理

UCOSIII記憶體管理簡介作為一個RTOS作業系統,記憶體管理是必備的功能,因此UCOSIII也就記憶體管理能力。通常應用程式可以呼叫ANSI C編譯器的malloc()和free()函式來動態的分配和釋放記憶體,但是在嵌入式事實作業系統中最好不要這麼做,多次這樣的操作會把

UCOSIIIUCOSIII系統內部任務

之前講到UCOSIII預設有5個系統任務:空閒任務:UCOSIII建立的第一個任務,UCOSIII必須建立的任務,此任務有UCOSIII自動建立,不需要使用者手動建立;時鐘節拍任務:此任務也是必須建立的任務;統計任務:可選任務,用來統計CPU使用率和各個任務的堆疊使用量。此任

UCOSIIIUCOSIII軟體定時器

在學習STM32的時候會使用定時器來做很多定時任務,這個定時器是微控制器自帶的,也就是硬體定時器,在UCOSIII中提供了軟體定時器,我們可以使用這些軟體定時器完成一些功能,本文我們就講解一下UCOSIII軟體定時器。UCOSIII軟體定時器簡介定時器其實就是一個遞減計數器,

ARM&LinuxLinux訊號互斥程式設計

《訊號量互斥程式設計示例演示》 /**************************************************************************************** * 檔名: a_pro.c * 建立者:

QT觸發訊號時獲取控制元件物件

snapshotBox = new CustomCheckBox [sys_info.max_cameras]; for (int i(0); i < sys_info.max_cameras && i < MAX_CAMERA; i++) { (this->

非常好的一篇對linux訊號(signal)的解析 (轉載) Linux訊號(signal) 機制分析

轉自:https://blog.csdn.net/return_cc/article/details/78845346 Linux訊號(signal) 機制分析 轉載至:https://www.cnblogs.com/hoys/archive/2012/08/19/2646377.html

Linux利用訊號實現sleep函式

在另一篇文章Linux訊號中,介紹了訊號的產生與處理方式,以及一系列訊號集函式的使用。 本文使用訊號機制,模擬實現sleep函式並瞭解競態條件。 在此之前先介紹一波需要用到的函式。 sigaction函式 #include <signal.h>

Golang關於訊號signal處理

我們在生產環境下執行的系統要求優雅退出,即程式接收退出通知後,會有機會先執行一段清理程式碼,將收尾工作做完後再真正退出。我們採用系統Signal來 通知系統退出,即kill pragram-pid。我們在程式中針對一些系統訊號設定了處理函式,當收到訊號後,會執行

QtQt訊號與槽使用不當,使程式崩潰

問題描述 跨執行緒使用Qt訊號和槽,訊號傳送時間間隔大於槽函式處理時間時,造成程式崩潰。 原因分析 跨執行緒使用Qt訊號和槽時,connect預設是QueuedConnection,佇列連線方式。 訊號傳遞給槽函式的引數,分配記憶體後放入佇列,如果槽

MOOC數字訊號處理-電子科技大學-第二週

2.1連續時間訊號的取樣 Concepts of sampling 取樣(sampling):指把時間域或空間域的連續量轉化成離散量的過程。 取樣流程 實際的連續時間訊號的數字化處理過程: 將上述過程的一些部分理想化後,可得 理想的連續時間

專欄 - 數字訊號處理的 MATLAB 實現

數字訊號處理的 MATLAB 實現 MATLAB 是一個強大的數學工具,對於數字訊號處理而言,應用MATLAB 程式設計去實現數字訊號處理,無論對於MATLAB·的使用還是對於數字訊號處理的理解都大有裨益。 更多內容,見我的部落

Nginx訊號

TERM, INT Quick shutdown     QUIT Graceful shutdown 優雅的關閉程序,即等請求結束後再關閉 KILL

Qt開發QThread中的互斥、讀寫鎖、訊號、條件變數

在gemfield的《從pthread到QThread》一文中我們瞭解了執行緒的基本使用,但是有一大部分的內容當時說要放到這片文章裡討論,那就是執行緒的同步問題。關於這個問題,gemfield在《從進 程到執行緒》中有一個比喻,有必要重新放在下面溫習下: ***************

OS訊號機制

儲存一下自己看,侵刪。 原文地址:http://blog.csdn.net/speedme/article/details/17597373 上篇部落格中(程序同步之臨界區域問題及Peterson演算法),我們對臨界區,臨界資源,鎖機制詳細解讀了下,留下了一個問題,就是鎖機制只能判斷臨界資源是否被佔用,所

Windows原理執行緒同步-訊號

#include "stdafx.h" #include <windows.h> int g_num = 0; HANDLE g_hSemaphore = nullptr; DWORD WINAPI ThreadProc(LPVOID lpParam) { for