温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

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

发布时间:2022-04-07 13:43:01 来源:亿速云 阅读:424 作者:iii 栏目:开发技术

这篇文章主要介绍“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实时操作系统空闲任务的阻塞延时怎么实现”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注亿速云行业资讯频道,小编每天都会为大家更新不同的知识点。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI