FreeRTOS 定时器组

本章节为大家讲解 FreeRTOS 支持的定时器组,或者叫软件定时器,又或者叫用户定时器均可。软件
定时器的功能比较简单,也容易掌握。 被称为定时器组是因为用户可以创建多个定时器,创建的个数是可
配置的。

定时器组介绍
FreeRTOS 软件定时器组的时基是基于系统时钟节拍实现的,之所以叫软件定时器是因为它的实现不
需要使用任何硬件定时器,而且可以创建很多个,综合这些因素,这个功能就被称之为软件定时器组。
既然是定时器,那么它实现的功能与硬件定时器也是类似的。 在硬件定时器中,我们是在定时器中断
中实现需要的功能,而使用软件定时器时,我们是在创建软件定时器时指定软件定时器的回调函数,在回
调函数中实现相应的功能。

单次模式和周期模式
FreeRTOS 提供的软件定时器支持单次模式和周期性模式,单次模式就是用户创建了定时器并启动了
定时器后,定时时间到将不再重新执行,这就是单次模式软件定时器的含义。 周期模式就是此定时器会按
照设置的时间周期重复去执行,这就是周期模式软件定时器的含义。 另外就是单次模式或者周期模式的定
时时间到后会调用定时器的回调函数,用户可以回调函数中加入需要执行的工程代码。

定时器任务(Daemon(守护进程) 任务)
为了更好的管理 FreeRTOS 的定时器组件, 专门创建了一个定时器任务, 或者称之为 Daemon 任务。
关于这个任务,我们上章节在讲解事件标志组的时候有用到。

FreeRTOS 定时器组的大部分 API 函数都是通过消息队列给定时器任务发消息,在定时器任务里面执
行实际的操作。 为了更好的说明这个问题,我们将官方在线版手册中的这个截图贴出来进行说明:

左侧图是用户应用程序,右侧是定时器任务。在用户应用程序里面调用了定时器组API函数xTimerReset,
这个函数会通过消息队列给定时器任务发消息,在定时器任务里面执行实际操作。 消息队列在此处的作用
有一个专门的名字:Timer command queue,即专门发送定时器组命令的队列。

使用软件定时器组注意事项
定时器回调函数是在定时器任务中执行的,实际应用中切不可在定时器回调函数中调用任何将定时
器任务挂起的函数,比如vTaskDelay(), vTaskDelayUntil()以及非零延迟的消息队列和信号量相关的函数。
将定时器任务挂起,会导致定时器任务负责的相关功能都不能正确执行了。

定时器组 API 函数
使用如下 20 个函数可以实现 FreeRTOS 的定时器组:
? xTimerCreate()
? xTimerCreateStatic()
? xTimerIsTimerActive()
? xTimerStart()
? xTimerStop()
? xTimerChangePeriod()

? xTimerDelete()
? xTimerReset()
? xTimerStartFromISR()
? xTimerStopFromISR()
? xTimerChangePeriodFromISR()
? xTimerResetFromISR()
? pvTimerGetTimerID()
? vTimerSetTimerID()
? xTimerGetTimerDaemonTaskHandle()
? xTimerPendFunctionCall()
? xTimerPendFunctionCallFromISR()
? pcTimerGetName()
? xTimerGetPeriod()
? xTimerGetExpiryTime()
关于这 20 个函数的讲解及其使用方法可以看 FreeRTOS 在线版手册 。
这里我们重点的说以下 3 个函数:
? xTimerCreate()
? xTimerStart ()
? pvTimerGetTimerID ()
因为本章节配套的例子使用的是这 3 个函数。

函数 xTimerCreate
函数原型:

函数描述:
函数 xTimerCreate 用于创建软件定时器。
? 第 1 个参数是定时器名字,用于调试目的,方便识别不同的定时器。
? 第 2 个参数是定时器周期,单位系统时钟节拍。
? 第 3 个参数是选择周期模式还是单次模式,若参数为 pdTRUE,则表示选择周期模式,若参数为
pdFALSE,则表示选择单次模式。
? 第 4 个参数是定时器 ID,当创建不同的定时器,但使用相同的回调函数时,在回调函数中通过不同的
ID 号来区分不同的定时器。
? 第 5 个参数是定时器回调函数。
? 返回值,创建成功返回定时器的句柄,由于 FreeRTOSCongfig.h 文件中 heap 空间不足,或者定时器周期设置为 0,会返回 NULL。

使用这个函数要注意以下问题:
1. 在 FreeRTOSConfig.h 文件中使能宏定义:
#define configUSE_TIMERS 1

函数 xTimerStart

函数 xTimerStart 用于启动软件定时器。
? 第 1 个参数是定时器句柄。
? 第 2 个参数是成功启动定时器前的最大等待时间设置,单位系统时钟节拍,定时器组的大部分 API
函数不是直接运行的(见 上图),而是通过消息队列给定时器任务发消息来实现的,此参
数设置的等待时间就是当消息队列已经满的情况下,等待消息队列有空间时的最大等待时间。
? 返回值,返回 pdFAIL 表示此函数向消息队列发送消息失败,返回 pdPASS 表示此函数向消息队列发
送消息成功。 定时器任务实际执行消息队列发来的命令依赖于定时器任务的优先级,如果定时器任务
是高优先级会及时得到执行,如果是低优先级,就要等待其余高优先级任务释放 CPU 权才可以得到
执行。
使用这个函数要注意以下问题:
1. 使用前一定要保证定时器组已经通过函数 xTimerCreate 创建了。
2. 在 FreeRTOSConfig.h 文件中使能宏定义:
#define configUSE_TIMERS 1

3. 对于已经被激活的定时器,即调用过函数 xTimerStart 进行启动,再次调用此函数相当于调用了函数
xTimerReset 对定时器时间进行了复位。
4. 如果在启动 FreeRTOS 调度器前调用了此函数, 定时器是不会立即执行的,需要等到启动了 FreeRTOS
调度器才会得到执行,即从此刻开始计时,达到 xTimerCreate 中设置的单次或者周期性延迟时间才
会执行相应的回调函数。

函数 pvTimerGetTimerID
函数原型:
void *pvTimerGetTimerID( TimerHandle_t xTimer ); /* 定时器句柄 */
函数描述:
函数 pvTimerGetTimerID 用于返回使用函数 xTimerCreate 创建的软件定时器 ID。

? 第 1 个参数是定时器句柄。
? 返回值,返回定时器 ID。
使用这个函数要注意以下问题:
1. 使用前一定要保证定时器组已经通过函数 xTimerCreate 创建了。
2. 在 FreeRTOSConfig.h 文件中使能宏定义:
#define configUSE_TIMERS 1
3. 创建不同的定时器时,可以对定时器使用相同的回调函数,在回调函数中通过此函数获取是哪个定时
器的时间到了,这个功能就是此函数的主要作用。

代码操练场:

配置项:

实验验证:

使用软件定时器,100ms一次实现led反转,1000ms一次Beep翻转。

主要展示定时器任务和回调函数:

定时器任务:

static void AppObjCreate (void)
{
    uint32_t i;
    const TickType_t  xTimerPer[2] = {100, 1000};

    /*
      1. 创建定时器,如果在RTOS调度开始前初始化定时器,那么系统启动后才会执行。
      2. 统一初始化两个定时器,他们使用共同的回调函数,在回调函数中通过定时器ID来区分
         是那个定时器的时间到。当然,使用不同的回调函数也是没问题的。
    */
    for(i = 0; i < 2; i++)
    {
        xTimers[i] = xTimerCreate("Timer",          /* 定时器名字 */
                                   xTimerPer[i],    /* 定时器周期,单位时钟节拍 */
                                   pdTRUE,          /* 周期性 */
                                   (void *) i,      /* 定时器ID */
                                   vTimerCallback); /* 定时器回调函数 */

        if(xTimers[i] == NULL)
        {
            /* 没有创建成功,用户可以在这里加入创建失败的处理机制 */
        }
        else
        {
             /* 启动定时器,系统启动后才开始工作 */
             if(xTimerStart(xTimers[i], 100) != pdPASS)//等待延时100ms,其实设置成0在简单任务下也是可以的,这个数值根据项目需求更改
             {
                 /* 定时器还没有进入激活状态 */
             }
        }
    }
}

回调函数:

static void vTimerCallback(xTimerHandle pxTimer)
{
    uint32_t ulTimerID;

    configASSERT(pxTimer);

    /* 获取那个定时器时间到 */
    ulTimerID = (uint32_t)pvTimerGetTimerID(pxTimer);

    /* 处理定时器0任务 */
    if(ulTimerID == 0)
    {
        LED2_TOGGLE;
    }

    /* 处理定时器1任务 */
    if(ulTimerID == 1)
    {
        BEEP_TOGGLE;
    }
}

通过ID不同,判断是哪个定时器时间到,然后做相应的动作。

时间: 2024-08-07 08:22:51

FreeRTOS 定时器组的相关文章

RTX——第17章 定时器组

本章节为大家讲解 RTX 支持的定时器组,或者叫软件定时器,或者叫用户定时器均可.软件定时器的功能比较简单,也容易掌握. 被称为定时器组是因为用户可以创建多个定时器,创建的个数是可配置的. 定时器组介绍RTX 提供的定时器功能仅支持单次定时器,也就是用户创建了定时器并启动了定时器后,定时时间到将不再重新执行,此定时器会被删除掉并且就执行一次,下次使用要重新的创建,这个就是单次定时器的含义. 另外就是单次定时时间到后会调用定时器的回调函数,用户可以回调函数中加入需要执行的工程代码.使用此定时器组注

FreeRTOS 动态内存管理

本章节为大家讲解 FreeRTOS 动态内存管理,动态内存管理是 FreeRTOS 非常重要的一项功能,前面章节讲解的任务创建. 信号量. 消息队列. 事件标志组. 互斥信号量. 软件定时器组等需要的 RAM 空间都是通过动态内存管理从 FreeRTOSConfig.h 文件定义的 heap 空间中申请的. 动态内存管理介绍FreeRTOS 支持 5 种动态内存管理方案,分别通过文件 heap_1,heap_2,heap_3,heap_4 和 heap_5实现,这 5 个文件在 FreeRTOS

在Amazon FreeRTOS V10中使用运行时统计信息

在MCU on Eclipse网站上看到Erich Styger在8月2日发的博文,一篇关于在Amazon FreeRTOS V10中使用运行时统计信息的文章,本人觉得很有启发,特将其翻译过来以备参考.原文网址:https://mcuoneclipse.com/2018/08/02/tutorial-using-runtime-statistics-with-amazon-freertos-v10/ FreeRTOS包含一个很好的功能,可以向我提供有关每个任务在系统上运行的时间的信息: Free

RTX基础教程目录

6.第6章 RTX 操作系统源码方式移植 7.RTX--第7章 任务管理 8.RTX--第8章 任务优先级修改 9.RTX--第9章 任务运行在特权级或非特权级模式 10.RTX--第10章 任务调度-抢占式.时间片和合作式 11.RTX--第11章 临界段,任务锁和中断锁 12.RTX--第12章 系统时钟节拍和时间管理 13.RTX--第13章 事件标志组 14.RTX--第14章 信号量 15.RTX--第15章 互斥信号量 16.RTX--第16章 消息邮箱 17.RTX--第17章 定

FreeRTOS 事件标志组

为什么要使用事件标志事件标志组是实现多任务同步的有效机制之一.也许有不理解的初学者会问采用事件标志组多麻烦,搞个全局变量不是更简单?其实不然,在裸机编程时,使用全局变量的确比较方便,但是在加上 RTOS 后就是另一种情况了. 使用全局变量相比事件标志组主要有如下三个问题:? 使用事件标志组可以让 RTOS 内核有效地管理任务,而全局变量是无法做到的,任务的超时等机制需要用户自己去实现.? 使用了全局变量就要防止多任务的访问冲突,而使用事件标志组则处理好了这个问题,用户无需担心.? 使用事件标志组

FreeRTOS 任务计数信号量,任务二值信号量,任务事件标志组

本章节为大家讲解 FreeRTOS 计数信号量的另一种实现方式----基于任务通知(Task Notifications)的计数信号量,这里我们将这种方式实现的计数信号量称之为任务计数信号量. 任务计数信号量效率更高,需要的 RAM 空间更小.当然,缺点也是有的,它没有之前介绍的计数信号量实现的功能全面. 任务通知(Task Notifications)介绍FreeRTOS 每个已经创建的任务都有一个任务控制块(task control block),任务控制块就是一个结构体变量,用于记录任务的

黑马程序员——JAVA基础之Day24 多线程 ,死锁,线程间通信 ,线程组,线程池,定时器。

------- android培训.java培训.期待与您交流! ---------- Lock()实现提供了比使用synchronized方法和语句可获得更广泛的锁定操作. private Lock lock =new ReentrantLock(); 被锁的代码要用   lock.lock()                lock.unlock()    包括.其中用try   ...finally包围 同步:效率低,如果出现同步嵌套,会出现死锁.  但是安全. 死锁问题:两个或者两个以上

setInterval多组定时器函数封装传参

昨天的例子是针对只有单个函数运行的结果,但是实际中可能会有多组函数这样同时进行,那么这个时候就要对函数进行封装传参,来实现效果了.就还拿昨天的例子来说吧,昨天写的例子实现的效果是点击按钮,让div向前移动,那么今天就扩展一下,点击按钮让div向后移动,想要实现这个效果,就必须要传参了,来看代码: css代码 <style> #box{width:50px;height:50px;background:#ff6b0e;position: absolute;top:50px;left:50px;}

STM32CubeIDE+FreeRTOS软件定时器实验

软件定时器实验是在 FreeRTOS 中创建了两个软件定时器,其中一个软件定时器是单次模式, 5000 个 tick 调用一次回调函数,另一个软件定时器是周期模式, 1000 个 tick 调用一次回调函数,在回调函数中输出相关信息,. 创建工程RTOS_Timer, 配置HCLK,使用内部晶振,频率为180MHZ(根据板子设置) 将SYS中时基源(Timebase Source)改为除SysTick之外的任意定时器即可,如: 配置FreeRTOS,使用CMSIS_V1,使能USE_TIMERS