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();
}

由于引入了阻塞延时,所以任务切换函数需要改写,因为当所有任务阻塞后,需要切换至空闲任务运行

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空闲任务阻塞延时的资料请关注我们其它相关文章!

(0)

相关推荐

  • FreeRTOS实时操作系统的任务创建与任务切换

    目录 任务控制块数据结构 任务创建函数 定义就绪表 就绪表初始化 启动调度器 任务切换 任务控制块数据结构 任务控制块数据结构在task.c声明 typedef struct tskTaskControlBlock { volatile StackType_t * pxTopOfStack; //栈顶指针 ListItem_t xStateListItem; //任务节点 StackType_t * pxStack; //任务栈起始地址 char pcTaskName[configMAX_TAS

  • FreeRTOS实时操作系统的任务概要讲解

    目录 1. 任务和协程(Co-routines) 1.1任务的特性 1.2任务概要 2. 任务状态 3.任务优先级 4.实现一个任务 5.空闲任务和空闲任务钩子(idle task和Idle Task hook) 5.1空闲任务 5.2空闲任务钩子 1. 任务和协程(Co-routines) 应用程序可以使用任务也可以使用协程,或者两者混合使用,但是任务和协程使用不同的API函数,因此在任务和协程之间不能使用同一个队列或信号量传递数据. 通常情况下,协程仅用在资源非常少的微处理器中,特别是RAM

  • FreeRTOS实时操作系统多任务管理基础知识

    目录 什么是多任务系统? FreeRTOS  任务与协程 1.任务(Task) 的特性 2.协程(Co-routine)的特性 任务状态 运行态 就绪态 阻塞态 挂起态 任务优先级 任务实现 任务控制块 任务堆栈 RTOS 系统的核心就是任务管理,FreeRTOS 也不例外,而且大多数学习 RTOS 系统的工程师或者学生主要就是为了使用 RTOS 的多任务处理功能,初步上手 RTOS 系统首先必须掌握的也是任务的创建.删除.挂起和恢复等操作,由此可见任务管理的重要性. 什么是多任务系统? 回想一

  • FreeRTOS实时操作系统的任务创建和删除

    目录 前言 1.任务创建 1.1函数描述 1.2参数描述 1.3返回值 1.4用法举例 2.任务删除 2.1任务描述 2.2参数描述 前言 在FreeRTOS移植到Cortex-M3硬件平台的文章中,我们已经见过任务创建API,但那篇文章的重点在于如何移植FreeRTOS,本文将重点放在任务的创建和删除API函数上面. 任务创建和删除API函数位于文件task.c中,需要包含task.h头文件. 1.任务创建 1.1函数描述 BaseType_t xTaskCreate( TaskFunctio

  • FreeRTOS任务控制API函数的功能分析

    目录 1.相对延时 1.1函数描述 1.2参数描述 1.3用法举例 2.绝对延时 2.1函数描述 2.2参数描述 2.3用法举例 3.获取任务优先级 3.1函数描述 3.2参数描述 3.3返回值 3.4用法举例 4.设置任务优先级 4.1函数描述 4.2参数描述 4.3用法举例 5.任务挂起 5.1函数描述 5.2参数描述 5.3用法举例 6.恢复挂起的任务 6.1函数描述 6.2参数描述 7.恢复挂起的任务(在中断服务函数中使用) 7.1函数描述 7.2参数描述 7.3返回值 7.4用法举例

  • freertos实时操作系统空闲任务阻塞延时示例解析

    阻塞态:如果一个任务当前正在等待某个外部事件,则称它处于阻塞态. rtos中的延时叫阻塞延时,即任务需要延时的时候,会放弃CPU的使用权,进入阻塞状态.在任务阻塞的这段时间,CPU可以去执行其它的任务(如果其它的任务也在延时状态,那么 CPU 就将运行空闲任务),当任务延时时间到,重新获取 CPU 使用权,任务继续运行. 空闲任务:处理器空闲的时候,运行的任务.当系统中没有其他就绪任务时,空闲任务开始运行,空闲任务的优先级是最低的. 空闲任务 定义空闲任务: #define portSTACK_

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

    目录 什么是阻塞延时.为什么需要空闲任务 空闲任务的实现 阻塞延时的实现 xTicksToDelay 递减 SysTick初始化 仿真 什么是阻塞延时.为什么需要空闲任务 RTOS中的延时叫阻塞延时,即任务需要延时时,任务会放弃cpu使用权,cpu转而去做其他的事,当任务延时时间到后,任务重新请求获得cpu使用权.但当所有的任务都处于阻塞后,为了不让cpu空闲没事干就需要一个空闲任务让cpu干活. 空闲任务的实现 空闲任务实现和创建普通任务没区别,空闲任务在调用vTaskStartSchedul

  • FreeRTOS实时操作系统队列的API函数讲解

    目录 FreeRTOS为操作队列提供了非常丰富的API函数,包括队列的创建.删除,灵活的入队和出队方式.带中断保护的入队和出队等等.下面就来详细讲述这些API函数. 1.获取队列入队信息数目1.1函数描述 UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue ); 返回队列中存储的信息数目.具有中断保护的版本为uxQueueMessagesWaitingFromISR(),原型为:UBaseType_t uxQueueMessagesW

  • freertos实时操作系统临界段保护开关中断及进入退出

    目录 中断的基础知识 嵌套: 优先级: 中断的悬起与解悬: 咬尾中断Tail‐Chaining: 晚到的高优先级异常: 进入临界段和退出临界段 中断的基础知识 嵌套: 嵌套向量中断控制器 NVIC(Nested Vectored Interrupt Controller与内核是紧耦合的.提供如下的功能:可嵌套中断支持.向量中断支持.动态优先级调整支持.中断延迟大大缩短. 中断可屏蔽. 所有的外部中断和绝大多数系统异常均支持可嵌套中断.异常都可以被赋予不同的优先级,当前优先级被存储在 xPSR的专

  • FreeRTOS实时操作系统信号量基础

    目录 前言 1.信号量简介 2.二进制信号量 3.计数信号量 4.互斥量 5.递归互斥量 前言 本文介绍信号量的基础知识,详细源码分析见<FreeRTOS进阶FreeRTOS信号量分析> 1.信号量简介 FreeRTOS的信号量包括二进制信号量.计数信号量.互斥信号量(以后简称互斥量)和递归互斥信号量(以后简称递归互斥量). 我们可以把互斥量和递归互斥量看成特殊的信号量.互斥量和信号量在用法上不同: 信号量用于同步,任务间或者任务和中断间同步:互斥量用于互锁,用于保护同时只能有一个任务访问的资

  • FreeRTOS实时操作系统队列基础

    目录 本文介绍队列的基本知识,详细源码分析见<FreeRTOS高级篇5---FreeRTOS队列分析> 1.FreeRTOS队列 队列是主要的任务间通讯方式.可以在任务与任务间.中断和任务间传送信息.大多数情况下,队列用于具有线程保护的FIFO(先进先出)缓冲区:新数据放在队列的后面.当然,数据也可以放在队列的前面,在下一篇讲队列API函数时,会涉及到数据的存放位置. 图1-1:读写队列 图1-1所示的队列中,最多能保存5个项目,并且假设队列永远不会满.任务A使用API函数xQueueSend

  • FreeRTOS实时操作系统特点介绍

    目录 1.什么是FreeRTOS? 2.为什么择 选择 FreeRTOS ? 3.FreeRTOS 特点 FreeRTOS资料与源码下载 FreeRTOS源码文件介绍 1.什么是FreeRTOS? Free 即免费的,RTOS 全称是 Real Time Operating System,中文就是实时操作系统.注意,RTOS 不是指某一个确定的系统,而是指一类系统.比如 uC/OS,FreeRTOS,RTX,RT-Thread 等这些都是 RTOS 类操作系统. 操作系统允许多个任务同时运行,这

  • FreeRTOS实时操作系统临界段保护场合示例

    目录 临界段保护场合 非中断场合 中断场合 临界段保护场合 FreeRTOS中临界段保护有2种场合,中断和非中断,通过关中断(或者关部分中断)来实现临界保护. 非中断场合 task.h 中 #define taskENTER_CRITICAL()portENTER_CRITICAL() #define taskEXIT_CRITICAL()portEXIT_CRITICAL() portmacro.h中 #define portENTER_CRITICAL()vPortEnterCritical

  • FreeRTOS实时操作系统Cortex-M内核使用注意事项

    前言 在阅读本文之前,有两个定义在FreeRTOSConfig.h中的宏,你必须先明白它们是什么意思,<FreeRTOS内核配置说明>一文中,讲解了这两个宏: configKERNEL_INTERRUPT_PRIORITY configMAX_SYSCALL_INTERRUPT_PRIORITY FreeRTOS与Cortex-M内核可谓是绝配,以至于让移植和使用FreeRTOS都变得更简单起来.根据FreeRTOS官方反馈,在Cortex-M内核上使用FreeRTOS大多数的问题点是由不正确

随机推荐