文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2023-06-29 21:38

关注

这篇文章主要介绍“FreeRTOS实时操作系统空闲任务的阻塞延时怎么实现”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“FreeRTOS实时操作系统空闲任务的阻塞延时怎么实现”文章能帮助大家解决问题。

什么是阻塞延时、为什么需要空闲任务

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; 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 ){    //重装载计数器值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实时操作系统空闲任务的阻塞延时怎么实现”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-人工智能
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯