網站首頁 編程語言 正文
什么是阻塞延時、為什么需要空閑任務
RTOS中的延時叫阻塞延時,即任務需要延時時,任務會放棄cpu使用權,cpu轉而去做其他的事,當任務延時時間到后,任務重新請求獲得cpu使用權。
但當所有的任務都處于阻塞后,為了不讓cpu空閑沒事干就需要一個空閑任務讓cpu干活。
空閑任務的實現
空閑任務實現和創建普通任務沒區別,空閑任務在調用vTaskStartScheduler
函數內部創建,如下
//定義空閑棧 #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 ) StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE]; //空閑任務任務控制塊 TCB_t IdleTaskTCB; //設置空閑任務的參數 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; //空閑任務棧大小 //設置空閑任務參數 vApplicationGetIdleTaskMemory(&pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize); //創建空閑任務 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; //啟動調度器 if(xPortStartScheduler()!=pdFALSE) { //啟動成功則不會運行到這里 } }
阻塞延時的實現
阻塞延時需要用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;
所以阻塞延時就是這樣實現
void vTaskDelay(const TickType_t xTicksToDelay) { TCB_t *pxTCB = NULL; pxTCB = pxCurrentTCB; //設置延時時間 pxTCB->xTicksToDelay = xTicksToDelay; //進行一次任務切換 taskYIELD(); }
由于引入了阻塞延時,所以任務切換函數需要改寫,因為當所有任務阻塞后,需要切換至空閑任務運行
void vTaskSwitchContext( void ) { //如果當前時空閑任務,嘗試去執行任務1或任務2,如果他們延時時間都沒到則繼續執行空閑任務 if( pxCurrentTCB == &IdleTaskTCB ) { if(Task1TCB.xTicksToDelay == 0) { pxCurrentTCB =&Task1TCB; } else if(Task2TCB.xTicksToDelay == 0) { pxCurrentTCB =&Task2TCB; } else { return; } } else //當前任務不是空閑任務會執行到這里 { //當前任務時任務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中斷來實現遞減操作的
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; ixTicksToDelay > 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 ) { //重裝載計數器值 portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; //設置systick時鐘使用內核時鐘 //使能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
原文鏈接:https://blog.csdn.net/weixin_41572450/article/details/105101447
相關推薦
- 2022-02-17 npm run serve Syntax Error: Error: Node Sass versi
- 2022-03-29 C語言中的盜賊(小偷)問題詳解_C 語言
- 2022-08-02 Python+Selenium實現瀏覽器標簽頁的切換_python
- 2022-04-02 C語言實現二叉樹層次遍歷介紹_C 語言
- 2023-05-13 python中數字列表轉化為數字字符串的實例代碼_python
- 2022-06-07 Selenium瀏覽器自動化如何上傳文件_python
- 2022-04-02 Python數據結構之雙向鏈表詳解_python
- 2022-09-12 IOS開發自定義view方法規范示例_IOS
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支