內核對象和各種內核機制的函數接口都在os.h里聲明,實現在各自的.c文件,比如os_tmr.c和os_time.c。
C語言全局變量一般會默認初始化;局部變量如若不初始化,會分配垃圾數據的;建議使用時都手動初始化。
其實使用內核對象時,就類似與使用任務,只不過在創建對象之前,要先聲明一個內核對象。
好了,上面閑聊了幾句,今天來說說ucosiii的幾個內核對象。
首先說“軟件定時器”,其實單純的講就是定時作用,這里我們要注意的就是,使用它方法和使用任務類似;那么我們就先來分析分析任務的執行過程:
- main會初始化ucos;調用一個起始任務創建函數(它的參數就是就是任務的工作模式,任務信息,任務函數指針)來創建任務;啟動多任務管理。
- 任務函數指針指向起始任務函數會初始化操作系統;調用若干普通任務創建函數(與上一致)來創建任務;刪除起始任務本身。
- 任務函數就會完成具體的操作。
那繼續說軟件定時器,在上面的3里任務函數會聲明一個定時器對象,然后調用一個定時器創建函數(它的參數為定時器信息,工作模式,回調函數指針等)來創建任務,回調函數完成一些自定義的操作(每次定時完成會調用此回調函數),最后調用OSTmrStart()啟動軟件定時器;接下來就可以使用它了。
我們可以知道,用OSTimeDly是將任務置為等待態,CPU的使用權暫時被剝奪,開啟定時器之后,該任務還是可以使用CPU。
通過debug我們也可以發現,運行完OSTimeDly之后(也可以說定時了指定時間之后),會完成一次回調函數里面的操作。也就驗證了這個結論“定時完成之后會調用回調函數”。
下面我們就具體看一下源碼,聲明:這是秉火例程里的代碼,我只是做了稍加修改。
#include <includes.h> CPU_TS ts_start; //時間戳變量 CPU_TS ts_end; static OS_TCB AppTaskStartTCB; //任務控制塊 static OS_TCB AppTaskTmrTCB; static CPU_STK AppTaskStartStk[APP_TASK_START_STK_SIZE]; //任務堆棧 static CPU_STK AppTaskTmrStk [ APP_TASK_TMR_STK_SIZE ]; static void AppTaskStart (void *p_arg); //任務函數聲明 static void AppTaskTmr ( void * p_arg ); int main (void) { OS_ERR err; OSInit(&err); //初始化 uC/OS-III /* 創建起始任務 */ OSTaskCreate((OS_TCB *)&AppTaskStartTCB, //任務控制塊地址 (CPU_CHAR *)"App Task Start", //任務名稱 (OS_TASK_PTR ) AppTaskStart, //任務函數 (void *) 0, //傳遞給任務函數(形參p_arg)的實參 (OS_PRIO ) APP_TASK_START_PRIO, //任務的優先級 (CPU_STK *)&AppTaskStartStk[0], //任務堆棧的基地址 (CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10, //任務堆棧空間剩下1/10時限制其增長 (CPU_STK_SIZE) APP_TASK_START_STK_SIZE, //任務堆棧空間(單位:sizeof(CPU_STK)) (OS_MSG_QTY ) 5u, //任務可接收的最大消息數 (OS_TICK ) 0u, //任務的時間片節拍數(0表默認值OSCfg_TickRate_Hz/10) (void *) 0, //任務擴展(0表不擴展) (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任務選項 (OS_ERR *)&err); //返回錯誤類型 OSStart(&err); //啟動多任務管理(交由uC/OS-III控制) } static void AppTaskStart (void *p_arg) { CPU_INT32U cpu_clk_freq; CPU_INT32U cnts; OS_ERR err; (void)p_arg; BSP_Init(); //板級初始化 CPU_Init(); //初始化 CPU 組件(時間戳、關中斷時間測量和主機名) cpu_clk_freq = BSP_CPU_ClkFreq(); //獲取 CPU 內核時鍾頻率(SysTick 工作時鍾) cnts = cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz; //根據用戶設定的時鍾節拍頻率計算 SysTick 定時器的計數值 OS_CPU_SysTickInit(cnts); //調用 SysTick 初始化函數,設置定時器計數值和啟動定時器 Mem_Init(); //初始化內存管理組件(堆內存池和內存池表) #if OS_CFG_STAT_TASK_EN > 0u //如果使能(默認使能)了統計任務 OSStatTaskCPUUsageInit(&err); //計算沒有應用任務(只有空閑任務)運行時 CPU 的(最大) #endif //容量(決定 OS_Stat_IdleCtrMax 的值,為后面計算 CPU //使用率使用)。 CPU_IntDisMeasMaxCurReset(); //復位(清零)當前最大關中斷時間 /* 創建 AppTaskTmr 任務 */ OSTaskCreate((OS_TCB *)&AppTaskTmrTCB, //任務控制塊地址 (CPU_CHAR *)"App Task Tmr", //任務名稱 (OS_TASK_PTR ) AppTaskTmr, //任務函數 (void *) 0, //傳遞給任務函數(形參p_arg)的實參 (OS_PRIO ) APP_TASK_TMR_PRIO, //任務的優先級 (CPU_STK *)&AppTaskTmrStk[0], //任務堆棧的基地址 (CPU_STK_SIZE) APP_TASK_TMR_STK_SIZE / 10, //任務堆棧空間剩下1/10時限制其增長 (CPU_STK_SIZE) APP_TASK_TMR_STK_SIZE, //任務堆棧空間(單位:sizeof(CPU_STK)) (OS_MSG_QTY ) 5u, //任務可接收的最大消息數 (OS_TICK ) 0u, //任務的時間片節拍數(0表默認值OSCfg_TickRate_Hz/10) (void *) 0, //任務擴展(0表不擴展) (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任務選項 (OS_ERR *)&err); //返回錯誤類型 OSTaskDel ( & AppTaskStartTCB, & err ); //刪除起始任務本身,該任務不再運行 } /* ********************************************************************************************************* * TMR TASK ********************************************************************************************************* */ void TmrCallback (OS_TMR *p_tmr, void *p_arg) //軟件定時器MyTmr的回調函數 { CPU_INT32U cpu_clk_freq; CPU_SR_ALLOC(); //使用到臨界段(在關/開中斷時)時必需該宏,該宏聲明和定義一個局部變 //量,用於保存關中斷前的 CPU 狀態寄存器 SR(臨界段關中斷只需保存SR) //,開中斷時將該值還原。 printf ( "%s", ( char * ) p_arg ); cpu_clk_freq = BSP_CPU_ClkFreq(); //獲取CPU時鍾,時間戳是以該時鍾計數 macLED1_TOGGLE (); ts_end = OS_TS_GET() - ts_start; //獲取定時后的時間戳(以CPU時鍾進行計數的一個計數值) //,並計算定時時間。 OS_CRITICAL_ENTER(); //進入臨界段,不希望下面串口打印遭到中斷 printf ( "\r\n定時1s,通過時間戳測得定時 %07d us,即 %04d ms。\r\n", ts_end / ( cpu_clk_freq / 1000000 ), //將定時時間折算成 us ts_end / ( cpu_clk_freq / 1000 ) ); //將定時時間折算成 ms OS_CRITICAL_EXIT(); ts_start = OS_TS_GET(); //獲取定時前時間戳 } static void AppTaskTmr ( void * p_arg ) { OS_ERR err; OS_TMR my_tmr; //聲明軟件定時器對象 (void)p_arg; /* 創建軟件定時器 */ OSTmrCreate ((OS_TMR *)&my_tmr, //軟件定時器對象 (CPU_CHAR *)"MySoftTimer", //命名軟件定時器 (OS_TICK )10, //定時器初始值,依10Hz時基計算,即為1s (OS_TICK )10, //定時器周期重載值,依10Hz時基計算,即為1s (OS_OPT )OS_OPT_TMR_PERIODIC, //周期性定時 (OS_TMR_CALLBACK_PTR )TmrCallback, //回調函數 (void *)"Timer Over!", //傳遞實參給回調函數 (OS_ERR *)err); //返回錯誤類型 /* 啟動軟件定時器 */ OSTmrStart ((OS_TMR *)&my_tmr, //軟件定時器對象 (OS_ERR *)err); //返回錯誤類型 ts_start = OS_TS_GET(); //獲取定時前時間戳 while (DEF_TRUE) { //任務體,通常寫成一個死循環DEF_TRUE printf("將任務置為等待態1s"); OSTimeDly ( 1000, OS_OPT_TIME_DLY, & err ); //給任務提供定時 } }
那再啰嗦一下回調函數,上面這個回調函數的類型聲明如下:
typedef void (*OS_TMR_CALLBACK_PTR)(void *p_tmr, void *p_arg);
顯而易見的是,我們自定義的回調函數的返回類型OS_TMR_CALLBACK_PTR其實就是一個void指針(而這個指針是一個函數指針),為了避免晦澀的基礎類型,所以typedef一下,便於理解。
那再說一下void
1.當函數返回為空或參數列表為空使用。
2.void*指向任何類型,只不過使用時要轉換成具體類型,才能操作。(上面的回調函數的實現有應用到)。
仔細將這些話與代碼對應起來體會,很有意思的。。。