目录
0 前言
-
在我们实际开发过程中,一般都用事件开发
-
不要使用死循环
1 Delay函数
1.1 两个Delay函数
FreeRTOS中有两个Delay函数:
- vTaskDelay:至少等待指定个数的Tick Interrupt才能变为就绪状态。
- vTaskDelayUntil:等待到指定的绝对时刻,才能变为就绪态。
这2个函数原型如下:
void vTaskDelay( const TickType_t xTicksToDelay ); /* xTicksToDelay: 等待多少给Tick */
/* pxPreviousWakeTime: 上一次被唤醒的时间
* xTimeIncrement: 要阻塞到(pxPreviousWakeTime + xTimeIncrement)
* 单位都是Tick Count
*/
BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
const TickType_t xTimeIncrement );
下面画图说明:
Tick中断如下:这里使用vTaskDelay(5)的话,就会等待5个Tick中断,任务再次变成就绪态
如果等到了5个Tick中断,并且查看现在有没有同级别的或者更高优先级的任务,如果没有的话,就会立刻执行这个任务。
现在假设我们运行的任务是这样的
while(1)
{
do_sth(); //这段代码执行的时间长度每次都不相同,假设是1ms、10ms、5ms
vTaskDelay(5); //延时5ms
}
每次执行do_sth();这段代码执行的时间长度每次都不相同,假设是1ms、10ms、5ms
实际的运行流程:
- 先运行1ms的do_sth();,再允许5ms的延时,再运行10ms的do_sth();,再运行5ms的延时,再运行5ms的do_sth();
流程如下图:
- vTaskDelay(5);这个函数运行时间大于等于5个Tick
如果想让这个函数周期运行的话,他们的间隔都是同样一段时间,如下图:
这时候就需要使用领一个函数
BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement );
现在的运行流程就是,先记录Tick的时刻preTime=T1,然后运行do_sth(),直到运行到T1+15=T2这个时刻
运行到T2时刻,需要做什么呢?
- 更新preTime = T2
- 让任务进入Ready状态
T2和T3之间也是15个Tick,这样就能使得do_sth 周期性的运行~
1.2 总结
- 使用vTaskDelay(n)时,进入、退出vTaskDelay的时间间隔至少是n个Tick中断
- 使用xTaskDelayUntil(&Pre, n)时,前后两次退出xTaskDelayUntil的时间至少是n个Tick中断
- 退出xTaskDelayUntil时任务就进入的就绪状态,一般都能得到执行机会
- 所以可以使用xTaskDelayUntil来让任务周期性地运行
2 程序
在06_create_task_use_params的基础上修改,得到11_task_delay
2.1 函数修改
void LCDPrintfTask(void *params)
{
//将传入的参数,转换成 struct TaskPrintInfo 这个结构体
struct TaskPrintInfo *pInfo = params;
uint32_t count = 0; //定义一个计数值
uint8_t length; //长度
BaseType_t preTime; //long
uint64_t t1, t2; //时间变量
LCD_Init();
LCD_Clear(); // 清屏
preTime = xTaskGetTickCount(); // 获取时间,这个时间后面会自动更新
while(1)
{
/* 打印信息 */
if (g_LCDCanUse) //g_LCDCanUse == 1 能使用LCD
{
g_LCDCanUse = 0; // 在这里禁止其他任务使用LCD
length = LCD_PrintString(pInfo->x, pInfo->y, pInfo->name); //在(x,y)处打印name里的内容,返回打印了多少个字符
length += LCD_PrintString(length, pInfo->y, ":"); //从返回的那个长度开始打印一个冒号
LCD_PrintSignedVal(length, pInfo->y, count++); //在冒号的下一个位置开始打印计数值count,并且count++
g_LCDCanUse = 1; //用完再恢复成1,表示可以继续使用LCD
mdelay(count & 0x3); //取出低两位,来添加一个随机的死循环
}
//获取系统时间
t1 = system_get_ns();
vTaskDelay(500); //500ms
//xTaskDelayUntil(&preTime, 500);
t2 = system_get_ns();
//打印 显示
LCD_ClearLine(pInfo->x, pInfo->y+2);
LCD_PrintSignedVal(pInfo->x, pInfo->y+2, (t2-t1)/1e6); //ms为单位
}
}
烧录程序,OLED的第二行显示499,是固定的
修改代码,改成vTaskDelayUntil
void LCDPrintfTask(void *params)
{
//将传入的参数,转换成 struct TaskPrintInfo 这个结构体
struct TaskPrintInfo *pInfo = params;
uint32_t count = 0; //定义一个计数值
uint8_t length; //长度
BaseType_t preTime; //long
uint64_t t1, t2; //时间变量
LCD_Init();
LCD_Clear(); // 清屏
preTime = xTaskGetTickCount(); // 获取时间,这个时间后面会自动更新
while(1)
{
/* 打印信息 */
if (g_LCDCanUse) //g_LCDCanUse == 1 能使用LCD
{
g_LCDCanUse = 0; // 在这里禁止其他任务使用LCD
length = LCD_PrintString(pInfo->x, pInfo->y, pInfo->name); //在(x,y)处打印name里的内容,返回打印了多少个字符
length += LCD_PrintString(length, pInfo->y, ":"); //从返回的那个长度开始打印一个冒号
LCD_PrintSignedVal(length, pInfo->y, count++); //在冒号的下一个位置开始打印计数值count,并且count++
g_LCDCanUse = 1; //用完再恢复成1,表示可以继续使用LCD
mdelay(count & 0x3); //取出低两位,来添加一个随机的死循环
}
//获取系统时间
t1 = system_get_ns();
//vTaskDelay(500); //500ms
vTaskDelayUntil(&preTime, 500);
t2 = system_get_ns();
//打印 显示
LCD_ClearLine(pInfo->x, pInfo->y+2);
LCD_PrintSignedVal(pInfo->x, pInfo->y+2, (t2-t1)/1e6); //ms为单位
}
}
烧写运行
mdelay(count & 0x3); //取出低两位,来添加一个随机的死循环
这行代码,导致程序会随机delay一阵子,那么OLED上显示的内容每次都是不一样的!
随机显示398 397 396 399 404 473 406 405
2.2 总结
- vTaskDelay:函数执行完延时 **ms
- vTaskDelayUntil:整个函数运行**ms(加上Delay)
学习视频:【FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS(FreeRTOS教程 基于STM32,以实际项目为导向)】 【精准空降到 01:50】 https://www.bilibili.com/video/BV1Jw411i7Fz/?p=24&share_source=copy_web&vd_source=8af85e60c2df9af1f0fd23935753a933&t=110
标签:count,vTaskDelay,函数,FreeRTOS,length,Delay,pInfo,LCD,Tick From: https://blog.csdn.net/weixin_63135906/article/details/140043687