FreeRTOS实时操作系统空闲任务的阻塞延时实现

目录

  • 什么是阻塞延时、为什么需要空闲任务
  • 空闲任务的实现
  • 阻塞延时的实现
  • xTicksToDelay 递减
  • SysTick初始化
  • 仿真

什么是阻塞延时、为什么需要空闲任务 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(); }

【FreeRTOS实时操作系统空闲任务的阻塞延时实现】由于引入了阻塞延时,所以任务切换函数需要改写,因为当所有任务阻塞后,需要切换至空闲任务运行
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
FreeRTOS实时操作系统空闲任务的阻塞延时实现
文章图片

以上就是FreeRTOS实时操作系统空闲任务的阻塞延时实现的详细内容,更多关于FreeRTOS空闲任务阻塞延时的资料请关注脚本之家其它相关文章!

    推荐阅读