首页 > 其他分享 >【FreeRTOS】两个Delay函数

【FreeRTOS】两个Delay函数

时间:2024-07-01 12:57:13浏览次数:3  
标签:count vTaskDelay 函数 FreeRTOS length Delay pInfo LCD Tick

目录

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时刻,需要做什么呢?

  1. 更新preTime = T2
  2. 让任务进入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

相关文章

  • 内联函数(inline)
    ①概念引入多用于复用性强的函数(代码量较少具有特定功能的函数)坏处:让你的代码上下文很臃肿(冗余)好处:在复用性强的函数调用的时候直接插入函数代码段而不是进行函数跳转(编译器弄的),减少性能消耗提高运行效率。减少函数调用开销:函数调用涉及压栈、跳转等操作,会产生......
  • 53、Flink 测试工具测试用户自定义函数详解
    1.测试用户自定义函数a)单元测试无状态、无时间限制的UDF示例:无状态的MapFunction。publicclassIncrementMapFunctionimplementsMapFunction<Long,Long>{@OverridepublicLongmap(Longrecord)throwsException{returnrecord+1;}......
  • Oracle PL / SQL 函数
    FUNCTION是返回值的PL/SQL块或方法,因此它可以在赋值的右侧使用。这里是一个例子:n_value:=to_number('123.45');由于FUNCTION返回一个值,因此也可以在SQL语句中使用它,如下例所示:selectto_number('1')fromdual;创建函数让我们创建一个无错的to_number()函数,而不......
  • 【AI 大模型】大模型应用架构 ( 业务架构 - AI Embedded、AI Copilot、AI Agent | 技
    文章目录一、大模型技术方向-大模型训练/大模型应用二、大模型应用-业务架构1、AIEmbedded模式2、AICopilot模式3、AIAgent模式三、大模型应用-技术架构1、提示词技术架构2、Agent+FunctionCalling技术架构3、RAG技术架构4、Fine-tuning微调技术......
  • 【Effective Python教程】(90个有效方法)笔记——第3章:函数——24:用None和docstring来描
    文章目录第3章:函数第24条用None和docstring来描述默认值会变的参数函数默认值的坑(函数的默认参数值只在定义函数时计算)解决方法:将函数默认参数值设为None,然后再在函数体中判断并初始化函数默认参数值设置为None的其他应用示例“函数默认参数值设置为None”与“函数参数......
  • Linux---open和close函数
    open:这是对文件权限的说明。注意:返回上一个工作目录:cd-close函数:关闭文件注意:在对C语言代码进行了修改时,必须要都运行的文件重新编译,然后在重新运行。不然,输出的结果不会发生改变。今日标语“努力不一定成功,但不努力一定不会成功。”......
  • 关于函数指针和结构体一起的用法
    想到单片机中的中断处理不好多样化,一直通过函数指针传递,今天想通过函数指针实现多样化,在中断中放一个要执行的函数指针,在外面可以改变此指针指向的函数。配合结构体的使用,感受到面向对象中class的存在了。typedefint(*pFunc)(int);//定义一个函数指针类型intadd(inta){......
  • c语言malloc、calloc 和 realloc动态分配内存函数的区别
    c语言malloc、calloc和realloc动态分配内存函数的区别malloc、calloc和realloc是C语言中用于动态内存分配的三个重要函数,它们之间有一些关键的区别。以下是这三个函数的区别,以分点表示和归纳的形式进行解释:内存来源和初始化:malloc:在堆上分配指定大小的内存块,但不进行初始化......
  • C++ : 如何用C语言实现C++的虚函数机制?
    前言在 googletest的源码中,看到gtest-matchers.h中实现的MatcherBase 类自定义了一个VTable,这种设计实现了一种类似于C++虚函数的机制。C++中的虚函数机制实质上就是通过这种方式实现的,本文用c语言自定义虚函数表VTable实现了一下virtual的功能,来深刻理解其机制。我们通过创......
  • NzN的C++之路--拷贝构造函数&&赋值运算符重载
    目录Part1拷贝构造函数一、概念二、特征Part2赋值运算符重载一、运算符重载二、赋值运算符重载三、前置++和后置++重载Part3const成员Part4 取地址及const取地址操作符重载 Part1拷贝构造函数一、概念        拷贝构造函数:只有单个形参,该形参......