FreeRTOS實時操作系統(tǒng)空閑任務的阻塞延時實現(xiàn)
什么是阻塞延時、為什么需要空閑任務
RTOS中的延時叫阻塞延時,即任務需要延時時,任務會放棄cpu使用權(quán),cpu轉(zhuǎn)而去做其他的事,當任務延時時間到后,任務重新請求獲得cpu使用權(quán)。
但當所有的任務都處于阻塞后,為了不讓cpu空閑沒事干就需要一個空閑任務讓cpu干活。
空閑任務的實現(xiàn)
空閑任務實現(xiàn)和創(chuàng)建普通任務沒區(qū)別,空閑任務在調(diào)用vTaskStartScheduler
函數(shù)內(nèi)部創(chuàng)建,如下
//定義空閑棧 #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 ) StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE]; //空閑任務任務控制塊 TCB_t IdleTaskTCB; //設置空閑任務的參數(shù) void vApplicationGetIdleTaskMemory( TCB_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ) { *ppxIdleTaskTCBBuffer=&IdleTaskTCB; *ppxIdleTaskStackBuffer=IdleTaskStack; *pulIdleTaskStackSize=configMINIMAL_STACK_SIZE; } void vTaskStartScheduler(void) { TCB_t *pxIdleTaskTCBBuffer = NULL;//空閑任務控制塊指針 StackType_t *pxIdleTaskStackBuffer = NULL;//空閑任務棧指針 uint32_t ulIdleTaskStackSize; //空閑任務棧大小 //設置空閑任務參數(shù) vApplicationGetIdleTaskMemory(&pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize); //創(chuàng)建空閑任務 xIdleTaskHandle = xTaskCreateStatic((TaskFunction_t)prvIdleTask, (char *)"IDLE", (uint32_t)ulIdleTaskStackSize, (void*)NULL, (StackType_t*)pxIdleTaskStackBuffer, (TCB_t*)pxIdleTaskTCBBuffer); //將空閑任務添加到就緒列表 vListInsertEnd(&(pxReadyTasksLists[0]),&(((TCB_t *)pxIdleTaskTCBBuffer)->xStateListItem)); //手動指定第一個要運行的任務 pxCurrentTCB = &Task1TCB; //啟動調(diào)度器 if(xPortStartScheduler()!=pdFALSE) { //啟動成功則不會運行到這里 } }
阻塞延時的實現(xiàn)
阻塞延時需要用xTicksToDelay
,這個時TCB中的一個成員,用于記錄還要阻塞多久。
typedef struct tskTaskControlBlock { volatile StackType_t * pxTopOfStack; ListItem_t xStateListItem; StackType_t * pxStack; · char pcTaskName[configMAX_TASK_NAME_LEN]; TickType_t xTicksToDelay; //用于延時 }tskTCB;
所以阻塞延時就是這樣實現(xiàn)
void vTaskDelay(const TickType_t xTicksToDelay) { TCB_t *pxTCB = NULL; pxTCB = pxCurrentTCB; //設置延時時間 pxTCB->xTicksToDelay = xTicksToDelay; //進行一次任務切換 taskYIELD(); }
由于引入了阻塞延時,所以任務切換函數(shù)需要改寫,因為當所有任務阻塞后,需要切換至空閑任務運行
void vTaskSwitchContext( void ) { //如果當前時空閑任務,嘗試去執(zhí)行任務1或任務2,如果他們延時時間都沒到則繼續(xù)執(zhí)行空閑任務 if( pxCurrentTCB == &IdleTaskTCB ) { if(Task1TCB.xTicksToDelay == 0) { pxCurrentTCB =&Task1TCB; } else if(Task2TCB.xTicksToDelay == 0) { pxCurrentTCB =&Task2TCB; } else { return; } } else //當前任務不是空閑任務會執(zhí)行到這里 { //當前任務時任務1或任務2的話,檢查另一個任務 //如果另外的任務不在延時中,會切換到該任務 //否則,判斷當前任務是否在延時中,是則切換到空閑任務, //否則,不進行任何切換 if (pxCurrentTCB == &Task1TCB) { if (Task2TCB.xTicksToDelay == 0) { pxCurrentTCB =&Task2TCB; } else if (pxCurrentTCB->xTicksToDelay != 0) { pxCurrentTCB = &IdleTaskTCB; } else { return; } } else if (pxCurrentTCB == &Task2TCB) { if (Task1TCB.xTicksToDelay == 0) { pxCurrentTCB =&Task1TCB; } else if (pxCurrentTCB->xTicksToDelay != 0) { pxCurrentTCB = &IdleTaskTCB; } else { return; } } } }
xTicksToDelay 遞減
vTaskDelay中設置了xTicksToDelay成員后,是通過SystTick中斷來實現(xiàn)遞減操作的
void xPortSysTickHandler( void ) { int x = portSET_INTERRUPT_MASK_FROM_ISR(); xTaskIncrementTick(); portCLEAR_INTERRUPT_MASK_FROM_ISR(x); } void xTaskIncrementTick( void ) { TCB_t *pxTCB = NULL; BaseType_t i = 0; const TickType_t xConstTickCount = xTickCount + 1; xTickCount = xConstTickCount; for (i=0; i<configMAX_PRIORITIES; i++) { pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &pxReadyTasksLists[i] ) ); if (pxTCB->xTicksToDelay > 0) { pxTCB->xTicksToDelay --; //這里遞減 } } portYIELD(); }
SysTick初始化
//systick控制寄存器 #define portNVIC_SYSTICK_CTRL_REG (*((volatile uint32_t *) 0xe000e010 )) //systick重裝載寄存器 #define portNVIC_SYSTICK_LOAD_REG (*((volatile uint32_t *) 0xe000e014 )) //systick時鐘源選擇 #ifndef configSYSTICK_CLOCK_HZ #define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ #define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) #else #define portNVIC_SYSTICK_CLK_BIT ( 0 ) #endif #define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) #define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) void vPortSetupTimerInterrupt( void ) { //重裝載計數(shù)器值 portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; //設置systick時鐘使用內(nèi)核時鐘 //使能systick定時器中斷 //使能systick定時器 portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); }
在FreeRTOSConfig.h
中
#define configCPU_CLOCK_HZ (( unsigned long ) 25000000) #define configTICK_RATE_HZ (( TickType_t ) 100)
configSYSTICK_CLOCK_HZ是沒有定義的,所以configSYSTICK_CLOCK_HZ使用的是configCPU_CLOCK_HZ
仿真
portCHAR flag1; portCHAR flag2; TaskHandle_t Task1_Handle; StackType_t Task1Stack[128]; TCB_t Task1TCB; TaskHandle_t Task2_Handle; StackType_t Task2Stack[128]; TCB_t Task2TCB; void Task1_Fntry(void *arg) { while(1) { flag1=1; vTaskDelay( 2 ); flag1=0; vTaskDelay( 2 ); } } void Task2_Fntry(void *arg) { while(1) { flag2=1; vTaskDelay( 2 ); flag2=0; vTaskDelay( 2 ); } } int main(void) { prvInitialiseTaskLists(); Task1_Handle = xTaskCreateStatic(Task1_Fntry,"task1",128,NULL,Task1Stack,&Task1TCB); vListInsertEnd(&pxReadyTasksLists[1],&((&Task1TCB)->xStateListItem)); Task2_Handle = xTaskCreateStatic(Task2_Fntry,"task2",128,NULL,Task2Stack,&Task2TCB); vListInsertEnd(&pxReadyTasksLists[2],&((&Task2TCB)->xStateListItem)); vTaskStartScheduler(); for(;;) {} }
可以看到2個task是同步運行的,且延時是20ms
以上就是FreeRTOS實時操作系統(tǒng)空閑任務的阻塞延時實現(xiàn)的詳細內(nèi)容,更多關(guān)于FreeRTOS空閑任務阻塞延時的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
FreeRTOS實時操作系統(tǒng)之可視化追蹤調(diào)試
這篇文章主要為大家介紹了FreeRTOS實時操作系統(tǒng)之可視化追蹤調(diào)試的示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04FreeRTOS動態(tài)內(nèi)存分配管理heap_5示例
這篇文章主要為大家介紹了FreeRTOS動態(tài)內(nèi)存分配管理heap_5示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04freertos實時操作系統(tǒng)臨界段保護開關(guān)中斷及進入退出
這篇文章主要介紹了freertos實時操作系統(tǒng)臨界段保護開關(guān)中斷及進入退出,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04FreeRTOS實時操作系統(tǒng)空閑任務的阻塞延時實現(xiàn)
這篇文章主要為大家介紹了FreeRTOS實時操作系統(tǒng)空閑任務的阻塞延時實現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04FreeRTOS軟件定時器apollo中斷狀態(tài)判斷
這篇文章主要為大家介紹了FreeRTOS軟件定時器apollo中斷狀態(tài)的判斷,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04