操作系统 FreeRTOS:延时函数,队列,信号量

系统延时函数

vTaskDelay()与vTaskDelayUnitl()。vTaskDelay()是相对模式(相对延时函数),函数 vTaskDelayUntil()是绝对模式(绝对延时函数)。函数 vTaskDelay()在文件 tasks.c 中有定义,要使用此函数的话宏 INCLUDE_vTaskDelay 必须为 1

相对延时函数vTaskDelay()

void vTaskDelay( const TickType_t xTicksToDelay )
{
	BaseType_t xAlreadyYielded = pdFALSE;
	//延时时间要大于 0。
	if( xTicksToDelay > ( TickType_t ) 0U ) 						(1)
	{
	configASSERT( uxSchedulerSuspended == 0 );
	vTaskSuspendAll(); 												(2)
	{
	traceTASK_DELAY();
	prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );	    (3)
	}
	xAlreadyYielded = xTaskResumeAll(); 							(4)
	}
	else
	{
	mtCOVERAGE_TEST_MARKER();
	}
	if( xAlreadyYielded == pdFALSE )							    (5)
	{
	portYIELD_WITHIN_API(); 										(6)
	}
	else
	{
	mtCOVERAGE_TEST_MARKER();
	}
}

(1)、延时时间由参数 xTicksToDelay 来确定,为要延时的时间节拍数,延时时间肯定要大于 0。否则的话相当于直接调用函数 portYIELD()进行任务切换。

(2)、调用函数 vTaskSuspendAll()挂起任务调度器。

(3) 、 调 用 函 数 prvAddCurrentTaskToDelayedList() 将 要 延 时 的 任 务 添 加 到 延 时 列 表pxDelayedTaskList 或 者 pxOverflowDelayedTaskList() 中 。 后 面 会 具 体 分 析 函 数prvAddCurrentTaskToDelayedList()。

(4)、调用函数 xTaskResumeAll()恢复任务调度器。

(5)、如果函数 xTaskResumeAll()没有进行任务调度的话那么在这里就得进行任务调度。

(6)、调用函数 portYIELD_WITHIN_API()进行一次任务调度。

在prvAddCurrentTaskToDelayedList中,加入到一个延时列表中,prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE )一个是要延时的时间,另一个是。把当前任务移除出就绪列表(就绪位清零)。代码具体功能:获取系统节拍之后,把要delay的任务从就绪状态列表中移除,就绪位清0,判断条件是否满足(所需要延时的时间是不是最大值)需要放到挂起列表中,也就是说这个任务直接相当于挂起了(此功能需要在宏中开启)。在不是最大值的时候,会定义一个变量timeToWake设置唤醒时间

TimeToWake =  xConstTickCount+xTickToWait//进入的系统时间+要唤醒的时间

之后,根据列表的原理,设置列表项大小的数值为TimeToWake进行排序。会存在延时溢出列表(之前提到的是延时溢出列表)来存放溢出太多的时间

while (1)
{
	funtion();
	vTaskDelay(100)
}

如上函数的调用了function以后进行延时,延时的时间是100ms,但是整个任务的时间并不是固定的,因为function读取寄存器的时间是不确定的所以整个任务的时间也不能确定。相对性就是体现在这里。

绝对延时函数vTaskDelayUntil()

函数 vTaskDelayUntil()会阻塞任务,阻塞时间是一个绝对时间,那些需要按照一定的频率运行的任务可以使用函数 vTaskDelayUntil()。

while (1)
{
	funtion();
	vTaskDelayUntil(100)
}

上述代码就可以延迟固定时间。当然也存在溢出的可能。

void TestTask( void * pvParameters )
	{
		TickType_t PreviousWakeTime;
		//延时 50ms,但是函数 vTaskDelayUntil()的参数需要设置的是延时的节拍数,不能直接
		//设置延时时间,因此使用函数 pdMS_TO_TICKS 将时间转换为节拍数。
		const TickType_t TimeIncrement = pdMS_TO_TICKS( 50 );
		PreviousWakeTime = xTaskGetTickCount(); //获取当前的系统节拍值
		for( ;; )
		{
		/*************************任务主体*********************************/
		//调用函数 vTaskDelayUntil 进行延时
		vTaskDelayUntil( &PreviousWakeTime, TimeIncrement);
		}
}

滴答定时器

又名系统时钟节拍计数器。不管是什么系统,运行都需要有个系统时钟节拍,前面已经提到多次了,xTickCount 就是FreeRTOS 的系统时钟节拍计数器。每个滴答定时器中断中 xTickCount 就会加一,xTickCount 的具体操作过程是在函数 xTaskIncrementTick()中进行的。Systick直属于CortexM内核。实际作用类似于正常定时器
操作系统 FreeRTOS:延时函数,队列,信号量_第1张图片
具体Systick细节在CortexM权威指南中有,重点关注前三个寄存器。

void delay_init()
{
	u32 reload;
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//选择外部时钟  HCLK
	fac_us=SystemCoreClock/1000000;				//不论是否使用OS,fac_us都需要使用
	reload=SystemCoreClock/1000000;				//每秒钟的计数次数 单位为M  
	reload*=1000000/configTICK_RATE_HZ;			//根据configTICK_RATE_HZ设定溢出时间
												//reload为24位寄存器,最大值:16777216,在72M下,约合0.233s左右	
	fac_ms=1000/configTICK_RATE_HZ;				//代表OS可以延时的最少单位	   

	SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;   	//开启SYSTICK中断
	SysTick->LOAD=reload; 						//每1/configTICK_RATE_HZ秒中断一次	
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;   	//开启SYSTICK    
}								    

每个系统都需要一个系统运行时钟节拍。xTickCount就是FreeRtOS的系统时钟节拍。每产生一个节拍,滴答定时器中断+1。具体操作过程是在xTaskIncrementTick中进行的。

//systick中断服务函数,使用ucos时用到
void SysTick_Handler(void)
{	
    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
    {
        xPortSysTickHandler();	
    

你可能感兴趣的:(操作系统 FreeRTOS:延时函数,队列,信号量)