# FreeRTOS操作系统学习
@[TOC](文章目录)
</font>
<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">
# 前言
FreeRTOS操作系统时间管理主要分为三部分:
一、FreeRTOS系统延时函数
二、滴答定时器
三、FreeRTOS系统时钟节拍
这一节主要学习系统延时
# 一、系统延时函数
![<font color=#999AAA >示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。](?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5oiR5LiObmFubw==,size_17,color_FFFFFF,t_70,g_se,x_16)
```c
vTaskDelay(); //相对延时
vTaskDelayUntil(); //绝对延时
```
相对延时与绝对延时的区别
相对延时:vTaskDelay():
相对延时是指每次延时都是从任务执行函数vTaskDelay()开始,延时指定的时间结束
绝对延时:vTaskDelayUntil():
绝对延时是指调用vTaskDelayUntil()的任务每隔x时间运行一次。也就是任务周期运行。
# 二、延时函数详解
![在这里插入图片描述](?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5oiR5LiObmFubw==,size_14,color_FFFFFF,t_70,g_se,x_16)
## 1.相对延时函数vTaskDelay()
在学习vTaskDelay()函数过程中,会使用这个函数很简单。
```c
vTaskDelay(500);//延时500ms
```
相对延时vTaskDelay()是从调用vTaskDelay()这个函数的时候开始延时,但是任务执行的时候,可能发生了中断,导致任务执行时间变长了,但是整个任务的延时时间还是1000个tick,这就不是周期性了
```c
void vTaskA( void * pvParameters )
{
while(1)
{
// ...
// 这里为任务主体代码
// ...
/* 调用相对延时函数,阻塞1000个tick */
vTaskDelay( 1000 );
}
}
```
![在这里插入图片描述](?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5oiR5LiObmFubw==,size_20,color_FFFFFF,t_70,g_se,x_16)
当任务运行的时候,假设被某个高级任务或者是中断打断了,那么任务的执行时间就更长了,然而延时还是延时1000个tick这样子,整个系统的时间就混乱了。
函数 prvAddCurrentTaskToDelayedList()用于将当前任务添加到等待列表中
```c
void vTaskDelay( const TickType_t xTicksToDelay )
{
BaseType_t xAlreadyYielded = pdFALSE;
/* 如果延时时间为0,则不会将当前任务加入延时列表 */
if( xTicksToDelay > ( TickType_t ) 0U )
{
vTaskSuspendAll();
{
/* 将当前任务从就绪列表中移除,并根据当前系统节拍计数器值计算唤醒时间,然后将任务加入延时列表 */
prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
}
xAlreadyYielded = xTaskResumeAll();
}
/* 强制执行一次上下文切换*/
if( xAlreadyYielded == pdFALSE )
{
portYIELD_WITHIN_API();
}
}
```
## 2绝对延时函数vTaskDelayUntil()
调用vTaskDelayUntil()是希望任务以固定频率定期执行,而不受外部的影响,任务从上一次运行开始到下一次运行开始的时间间隔是绝对的,而不是相对的。假设主体任务被打断0.3s,不会影响这个周期性,只是会缩短其它任务的执行时间!
![在这里插入图片描述](?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5oiR5LiObmFubw==,size_16,color_FFFFFF,t_70,g_se,x_16)
```c
void vTaskA( void * pvParameters )
{
/* 用于保存上次时间。调用后系统自动更新 */
static portTickType PreviousWakeTime;
/* 设置延时时间,将时间转为节拍数 */
const portTickType TimeIncrement = pdMS_TO_TICKS(1000);
/* 获取当前系统时间 */
PreviousWakeTime = xTaskGetTickCount();
while(1)
{
/* 调用绝对延时函数,任务时间间隔为1000个tick */
vTaskDelayUntil( &PreviousWakeTime,TimeIncrement );
// ...
// 这里为任务主体代码
// ...
}
}
```
实例
```c
void vTask3(void *pvParam)
{
while(1)
{
/*
任务主体
*/
vTaskDelay(1000);
}
}
void vTask2(void *pvParam)
{
TickType_t lasttick = xTaskGetTickCount();
while(1)
{
vTaskDelayUntil(&lasttick, 1000);
/*
任务主体
*/
}
```
![在这里插入图片描述](?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5oiR5LiObmFubw==,size_20,color_FFFFFF,t_70,g_se,x_16)
在使用的时候要将延时时间转化为系统节拍,在任务主体之前要调用延时函数。任务会先调用vTaskDelayUntil()使任务进入阻塞态,等到时间到了就从阻塞中解除,然后执行主体代码,任务主体代码执行完毕。会继续调用vTaskDelayUntil()使任务进入阻塞态,然后循环执行。即使任务在执行过程中发生中断,那么也不会影响这个任务的运行周期,仅仅是缩短了阻塞的时间而已。
与相对延时函数vTaskDelay不同,本函数增加了一个参数pxPreviousWakeTime用于指向一个变量,变量保存上次任务解除阻塞的时间,此后函数vTaskDelayUntil()在内部自动更新这个变量。由于变量xTickCount可能会溢出,所以程序必须检测各种溢出情况,并且要保证延时周期不得小于任务主体代码执行时间。
```c
xTimeIncrement():任务周期时间
pxPreviousWakeTime():上一次唤醒的时间点
xTimeToWake():下一次唤醒的系统时间点
xConstTickCount():进入延时的时间点
```
# 总结
如果任务调用相对延时,其运行周期完全是不可测的,如果任务的优先级不是最高的话,其误差更大,就好比一个必须要在5ms内相应的任务,假如使用了相对延时1ms,那么很有可能在该任务执行的时候被更高优先级的任务打断,从而错过5ms内的相应,但是调用绝对延时,则任务会周期性将该任务在阻塞列表中解除,但是,任务能不能运行,还得取决于任务的优先级,如果优先级最高的话,任务周期还是比较精确的(相对vTaskDelay来说)
[原文链接](https://blog.csdn.net/jiejiemcu/article/details/81515788)
|