UCOS是一個實時的多任務任務基於優先級的操作系統,要運行多任務的話,在啓動多任務之後,需要在每隔一段時間(一個滴答)內進行任務調度和相關數據的更新,以確保滿足運行要求的最高優先級任務以便在下一次任務調度切換的時候能夠得到一個及時的響應。
這個時間間隔是由時間中斷來實現,而且每個中斷的中斷函數需要對相關的數據進行一個週期性更新。
在STM32中時間的中斷由SysTick定時器(是cortexM3內核的定時器)實現。
系統時間中斷函數OS_CPU_SysTickHandle 函數實現。
系統時鐘中斷
SysTick定時器被捆綁在NVIC中,用於產生SysTick異常(詳情請見《CM3權威指南》8.7節)。
在SysTick定時器開始工作之前,需要對定時器進行初始化,確定其中斷時間間隔,以及相關寄存器的配置。其功能由OS_CPU_SysTickInit(os_cpu_c.c)函數實現。
這裏配置沒有用到st的官方庫,而是直接使用的寄存器操作。
代碼如下:
//對應寄存器的地址
#define OS_CPU_CM3_NVIC_ST_CTRL (*((volatile INT32U *)0xE000E010)) /* SysTick Ctrl & Status Reg. */
#define OS_CPU_CM3_NVIC_ST_RELOAD (*((volatile INT32U *)0xE000E014)) /* SysTick Reload Value Reg. */
#define OS_CPU_CM3_NVIC_ST_CURRENT (*((volatile INT32U *)0xE000E018)) /* SysTick Current Value Reg. */
#define OS_CPU_CM3_NVIC_ST_CAL (*((volatile INT32U *)0xE000E01C)) /* SysTick Cal Value Reg. */
#define OS_CPU_CM3_NVIC_PRIO_ST (*((volatile INT8U *)0xE000ED23)) /* SysTick Handler Prio Reg. */
#define OS_CPU_CM3_NVIC_ST_CTRL_COUNT 0x00010000 /* Count flag. */
#define OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC 0x00000004 /* Clock Source. */
#define OS_CPU_CM3_NVIC_ST_CTRL_INTEN 0x00000002 /* Interrupt enable. */
#define OS_CPU_CM3_NVIC_ST_CTRL_ENABLE 0x00000001 /* Counter mode. */
#define OS_CPU_CM3_NVIC_PRIO_MIN 0xFF /* Min handler prio. */
void OS_CPU_SysTickInit (void)
{
INT32U cnts;
cnts = OS_CPU_SysTickClkFreq() / OS_TICKS_PER_SEC; //OS_CPU_SysTickClkFreq()是得到systick的時鐘頻率。則也就是說,計數器一秒鐘能計數的次數。OS_TICKS_PER_SEC(os_cfg.h)表示的是一秒鐘計數器需要清零的次數,這裏配置爲1000(可以根據系統的需求進行配置),也就是說1s/1000 = 1ms進行一次systick時間中斷。那麼1ms內定時器需要計數次數爲OS_CPU_SysTickClkFreq()
/ OS_TICKS_PER_SEC
OS_CPU_CM3_NVIC_ST_RELOAD = (cnts - 1);//將cnts的值填進RELOAD寄存器中
OS_CPU_CM3_NVIC_PRIO_ST = OS_CPU_CM3_NVIC_PRIO_MIN; //設置systick中斷的優先級
OS_CPU_CM3_NVIC_ST_CTRL |= OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC | OS_CPU_CM3_NVIC_ST_CTRL_ENABLE;//定時器使能,開啓內核時鐘
OS_CPU_CM3_NVIC_ST_CTRL |= OS_CPU_CM3_NVIC_ST_CTRL_INTEN; //定時器中斷使能
}
系統時鐘中斷中斷函數
在系統時間中斷後,便會執行相應的中斷函數void OS_CPU_SysTickHandler (void)(os_cpu_c.c)
該函數的主要功能就是將中斷狀態標誌全局變量OSIntNesting加1,然後調用OSTimeTick()進行調度。
在退出中斷程序之前,調用OSIntExit,完成退出之前的最後處理。該函數在之後的中斷管理說明。
時間中斷任務調度器OSTimeTick
OSTimeTick函數(os_core.c)的主要作用是在系統時鐘中斷的時候,由中斷程序調用,對任務的時延、任務的狀態進行修改,設置就緒狀態,但是不會實現任務的切換。
函數的核心代碼部分如下:
ptcb = OSTCBList; //指向就緒任務控制塊鏈表
while (ptcb->OSTCBPrio != OS_TASK_IDLE_PRIO) { //遍歷鏈表
OS_ENTER_CRITICAL();
if (ptcb->OSTCBDly != 0u) { //如果設置了任務時延或者等待事件時延
ptcb->OSTCBDly--; //將時延時間減1
if (ptcb->OSTCBDly == 0u) { //若是時延時間爲0
if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) { //如果任務等待事件有一個發生了
ptcb->OSTCBStat &= (INT8U)~(INT8U)OS_STAT_PEND_ANY; //清除事件等待標誌,因爲時間已經到了,不管有沒有事件發生,都得清零
ptcb->OSTCBStatPend = OS_STAT_PEND_TO; //表示等待事件以爲時間超時不在等待
} else {
ptcb->OSTCBStatPend = OS_STAT_PEND_OK; //任務等待完成,或是因爲是簡單的任務延時,或者是因爲任務等待事件已經發生
}
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { //任務處於就緒狀態,而不是阻塞狀態(就緒鏈表中的任務狀態只有三種,就緒,阻塞和運行態)
OSRdyGrp |= ptcb->OSTCBBitY; //更新就緒任務表和就緒任務數組
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
}
}
ptcb = ptcb->OSTCBNext; /* Point at next TCB in TCB list */
OS_EXIT_CRITICAL();
}
在就緒任務鏈表中,若ptcb->OSTCBDly != 0u,說明該任務處於時延狀態(任務時延或者等待時間時延),爲阻塞態。(兩者等價)