美文网首页
RTT笔记-定时器

RTT笔记-定时器

作者: lissettecarlr | 来源:发表于2018-12-15 14:15 被阅读0次

修改!该文变更为快速查阅编程文档,RTT进程实现分析将另起一文

动态定时器

//定时器回调
void timer_cb(void *p)
{}
//定义
static rt_timer_t timer_hander;

//创建和启动定时器
{
    timer_hander = rt_timer_create("timer1",timer_cb,
                             RT_NULL, 10,
                             RT_TIMER_FLAG_PERIODIC);//周期//RT_TIMER_FLAG_ONE_SHOT单次
       rt_timer_start(timer_hander);
}
//修改超时时间
{
     uint32_t value=100; //100 tick
   rt_timer_control(timer_hander, RT_TIMER_CTRL_SET_TIME, (void*)&value);
}
//停止定时器
{
   rt_timer_stop(timer_hander);
}

静态定时器

//静态定时器

//定义
static struct rt_timer timer_hander;
//创建和启动
{
        rt_timer_init(&timer_hander, "timer1",  /* 定时器名字是 timer1 */
                    timer_cb, /* 超时时回调的处理函数 */
                    RT_NULL, /* 超时函数的入口参数 */
                    10, /* 定时长度,以 OS Tick 为单位,即 10 个 OS Tick */
                    RT_TIMER_FLAG_ONE_SHOT); /* 周期性定时器 */
         rt_timer_start(&timer_hander);
}

下列文本待删除


该笔记类别主要是在自己学习时做的一些记录,方便自己很久不用忘掉时进行快速回忆

1 简述

rtt的定时方式

  • 其一是单次定时,超时后自动停止
  • 其二是按照设定时间周期性超时,每次超时后自动重置,直到手动停止。

两种超时函数

  • 其一是超时函数在(系统时钟)中断上下文环境中执行,这类就如同编写中断函数一样,执行时间应该尽量短,执行时不应导致当前上下文挂起、等待。例如在中断上下文中执行的超时函数它不应该试图去申请动态内存、释放动态内存等。

  • 其二是超时函数在线程的上下文环境中执行,就没有过多的限制,如同使用线程一般,但是通常也要求超时函数执行时间应该足够短,而不应该影响到其他定时器或本次定时器的下一次周期性超时.

  • RT_TIMER_FLAG_HARD_TIMER代表的是定时器超时函数执行上下文是在中断上下文环境中执行

  • RT_TIMER_FLAG_SOFT_TIMER代表的是定时器函数执行的上下文是timer线程(在rtconfig.h头文件中应该定义宏RT_USING_TIMER_SOFT使timer线程能被使用)

定时器管理控制块

struct rt_timer
{
    struct rt_object parent;

    rt_list_t row[RT_TIMER_SKIP_LIST_LEVEL];  /* 定时器列表算法用到的队列 */

    void (*timeout_func)(void *parameter);    /* 定时器超时调用的函数     */
    void      *parameter;                     /* 超时函数用到的入口参数   */

    rt_tick_t init_tick;                      /* 定时器初始超时节拍数     */
    rt_tick_t timeout_tick;                   /* 定时器实际超时时的节拍数 */
};
typedef struct rt_timer *rt_timer_t;

2 函数说明

函数名 功能 描述
rt_system_timer_init 初始化定时器管理系统 没有参数和返回值
rt_system_timer_thread_init 初始化软件定时器 如果需要使用SOFT_TIMER,则系统初始化时,应该调用下面这个函数
rt_timer_create 创建定时器 调用该函数接口后,内核首先从动态内存堆中分配一个定时器控制块,然后对该控制块进行基 本的初始化
rt_timer_delete 删除定时器 调用这个函数接口后,系统会把这个定时器从rt_timer_list链表中删除,然后释放相应的定 时器控制块占有的内存。
rt_timer_init 初始化定时器 静态创建定时器,使用该函数接口时会初始化相应的定时器控制块,初始化相应的定时器名称,定时器超时函数等等。
rt_timer_detach 脱离定时器 脱离定时器时,系统会把定时器对象从系统容器的定时器链表中删除,但是定时器对象所占有的内存不会被释放
rt_timer_start 启动定时器 当定时器被创建或者初始化以后,并不会被立即启动,必须在调用启动定时器函数接口后,才开始工作
rt_timer_stop 停止定时器 调用定时器停止函数接口后,定时器状态将更改为停止状态,并从rt_timer_list链表中脱离出来不参与定时器超时检查。当一个(周期性)定时器超时时,也可以调用这个函数接口停止这个(周期性)定时器本身
rt_timer_control 控制定时器 控制定时器函数接口可根据命令类型参数,来查看或改变定时器的设置

3 函数使用

rt_timer_create创建定时器

rt_timer_t rt_timer_create(const char* name,//定时器的名称
                           void (*timeout)(void* parameter),//定时器超时函数指针(当定时器超时时,系统会调用这个函数)
                           void* parameter,//定时器超时函数的入口参数(当定时器超时时,调用超时回调函数 会把这个参数做为入口参数传递给超时函数
                           rt_tick_t time, //定时器的超时时间,单位是系统节拍
                           rt_uint8_t flag //定时器创建时的参数,支持的值包括(可以用“或”关系取多个值)
                           );

关于flag参数在include/rtdef.h中定义了一些定时器相关的宏

#define RT_TIMER_FLAG_DEACTIVATED   0x0     /* 定时器为非激活态 */
#define RT_TIMER_FLAG_ACTIVATED     0x1     /* 定时器为激活状态 */
#define RT_TIMER_FLAG_ONE_SHOT      0x0     /* 单次定时     */
#define RT_TIMER_FLAG_PERIODIC      0x2     /* 周期定时     */
#define RT_TIMER_FLAG_HARD_TIMER    0x0     /* 硬件定时器   */
#define RT_TIMER_FLAG_SOFT_TIMER    0x4     /* 软件定时器   */

当指定的flag为RT_IMER_FLAG_HARD_TIMER时,如果定时器超时,定时器的回调函数将在时钟中断的服务例程上下文中被调用;当指定的flag为RT_TIMER_FLAG_SOFT_TIMER时,如果定时器超时,定时器的回调函数将在系统时钟timer线程的上下文中被调用
如果定时器创建成功,则返回定时器的句柄;如果创建失败,会返回RT_NULL(通常会由于系 统内存不够用而返回RT_NULL)
例程如下,这部分没有自己编程,程序来至官方文档

/*
 * 程序清单:动态定时器例程
 *
 * 这个例程会创建两个动态定时器对象,一个是单次定时,一个是周期性的定时
 */
#include <rtthread.h>

/* 定时器的控制块 */
static rt_timer_t timer1;
static rt_timer_t timer2;

/* 定时器1超时函数 */
static void timeout1(void* parameter)
{
    rt_kprintf("periodic timer is timeout\n");
}

/* 定时器2超时函数 */
static void timeout2(void* parameter)
{
    rt_kprintf("one shot timer is timeout\n");
}

int rt_application_init(void)
{
    /* 创建定时器1 */
    timer1 = rt_timer_create("timer1",  /* 定时器名字是 timer1 */
                        timeout1, /* 超时时回调的处理函数 */
                        RT_NULL,  /* 超时函数的入口参数 */
                        10,       /* 定时长度,以OS Tick为单位,即10个OS Tick */
                        RT_TIMER_FLAG_PERIODIC); /* 周期性定时器 */
    /* 启动定时器 */
    if (timer1 != RT_NULL) rt_timer_start(timer1);

    /* 创建定时器2 */
    timer2 = rt_timer_create("timer2",   /* 定时器名字是 timer2 */
                        timeout2, /* 超时时回调的处理函数 */
                        RT_NULL,  /* 超时函数的入口参数 */
                        30,       /* 定时长度为30个OS Tick */
                        RT_TIMER_FLAG_ONE_SHOT); /* 单次定时器 */

    /* 启动定时器 */
    if (timer2 != RT_NULL) rt_timer_start(timer2);

    return 0;
}

rt_timer_delete删除定时器

//返回RT_EOK (如果参数timer句柄是一个RT_NULL,将会导致一个ASSERT断言)
rt_err_t rt_timer_delete(rt_timer_t timer);

rt_timer_init静态创建定时器

void rt_timer_init(rt_timer_t timer,
                   const char* name, void (*timeout)(void* parameter), void* parameter,
                   rt_tick_t time, rt_uint8_t flag);

rt_timer_start启动定时器

调用定时器启动函数接口后,定时器的状态将更改为激活状态(RT_TIMER_FLAG_ACTIVATED),并按照超时顺序插入到rt_timer_list队列链表中。

//如果timer已经处于激活状态,则返回-RT_ERROR;否则返回RT_EOK
rt_err_t rt_timer_start(rt_timer_t timer);

rt_timer_stop停止定时器

//如果timer已经处于停止状态,返回-RT_ERROR;否则返回RT_EOK
rt_err_t rt_timer_stop(rt_timer_t timer);

rt_timer_control控制定时器

//rt_timer_t timer  定时器句柄,指向要进行控制的定时器控制块
//rt_uint8_t cmd  用于控制定时器的命令,当前支持四个命令接口,分别是设置定时时间,查看定时时间,设置单次触发,设置周期触发;
//void* arg  与command相对应的控制命令参数
//函数返回RT_EOK
rt_err_t rt_timer_control(rt_timer_t timer, rt_uint8_t cmd, void* arg);
#define RT_TIMER_CTRL_SET_TIME      0x0     /* 设置定时器超时时间       */
#define RT_TIMER_CTRL_GET_TIME      0x1     /* 获得定时器超时时间       */
#define RT_TIMER_CTRL_SET_ONESHOT   0x2     /* 设置定时器为单一超时型   */
#define RT_TIMER_CTRL_SET_PERIODIC  0x3     /* 设置定时器为周期型定时器 */

定时器控制使用例程:

/*
 * 程序清单:定时器控制接口示例
 *
 * 这个例程会创建1个动态周期型定时器对象,然后控制它进行更改定时器的时间长度。
 */
#include <rtthread.h>

/* 定时器的控制块 */
static rt_timer_t timer1;
static rt_uint8_t count;

/* 定时器超时函数 */
static void timeout1(void* parameter)
{
    rt_kprintf("periodic timer is timeout\n");

    count ++;
    /* 当超过8次时,更改定时器的超时长度 */
    if (count >= 8)
    {
        int timeout_value = 50;
        /* 控制定时器更改定时器超时时间长度 */
        rt_timer_control(timer1, RT_TIMER_CTRL_SET_TIME, (void*)&timeout_value);
        count = 0;
    }
}

int rt_application_init(void)
{
    /* 创建定时器1 */
    timer1 = rt_timer_create("timer1",  /* 定时器名字是 timer1 */
                            timeout1, /* 超时时回调的处理函数 */
                            RT_NULL, /* 超时函数的入口参数 */
                            10, /* 定时长度,以OS Tick为单位,即10个OS Tick */
                            RT_TIMER_FLAG_PERIODIC); /* 周期性定时器 */
    /* 启动定时器 */
    if (timer1 != RT_NULL)
        rt_timer_start(timer1);

    return 0;
}

4 延时

系统中HARD_TIMER定时器的最小精度是由系统时钟节拍所决定的(1 OS tick = 1/RT_TICK _PER_SECOND秒,RT_TICK_PER_SECOND值在rtconfig.h文件中定义),定时器设定的时间必须是OS tick的整数倍。当需要实现更短时间长度的系统定时时,例如OS tick是10ms,而程序需要实现1ms的定时或延时,这种时候操作系统定时器将不能够满足要求,只能通过读取系统某个硬件定时器的计数器或直接使用硬件定时器的方式。
rt_thread_delay函数的参数是tick数RT_TICK_PER_SECOND定义值如若是100,则表示100个tick等于1s,可以通过rt_tick_from_millisecond函数来将ms转变为tick,然后再作为参数传输给rt_thread_delay

//1秒延时
rt_thread_delay(RT_TICK_PER_SECOND);
//ms延时
rt_thread_delay(rt_tick_from_millisecond(500));

由于改公式也是根据RT_TICK_PER_SECOND来计算,所以延时取值是有范围的

int rt_tick_from_millisecond(rt_int32_t ms)
{
    int tick;

    if (ms < 0)
        tick = RT_WAITING_FOREVER;
    else
        tick = (RT_TICK_PER_SECOND * ms + 999) / 1000;

    /* return the calculated tick */
    return tick;
}

在Cortex-M3中,SysTick已经被RT-Thread用于作为OS tick使用,它被配置成1/RT_TICK_ PER_SECOND秒后触发一次中断的方式,中断处理函数使用Cortex-M3默认的SysTick_Handler名字。在Cortex-M3的CMSIS(Cortex Microcontroller Software Interface Standard)规范中规定了SystemCoreClock代表芯片的主频,所以基于SysTick以及SystemCoreClock,我们能够使用SysTick获得一个精确的延时函数,如下例所示,Cortex-M3上的基于SysTick的精确延时(需要系统在使能SysTick后使用):

#include <board.h>

void rt_hw_us_delay(rt_uint32_t us)
{
    rt_uint32_t delta;

    /* 获得延时经过的tick数 */
    us = us * (SysTick->LOAD/(1000000/RT_TICK_PER_SECOND));

    /* 获得当前时间 */
    delta = SysTick->VAL;

    /* 循环获得当前时间,直到达到指定的时间后退出循环 */
    while (delta - SysTick->VAL< us);
}

其中入口参数us指示出需要延时的微秒数目,这个函数只能支持低于1 OS tick的延时,否则SysTick会出现溢出而不能够获得指定的延时时间。

注:
以上是取之官网文档,在最新版RTT工程中延时函数被编写在board.c143行

void rt_hw_us_delay(rt_uint32_t us)
{
    rt_uint32_t delta;
    us = us * (SysTick->LOAD / (1000000 / RT_TICK_PER_SECOND));
    delta = SysTick->VAL;
    if (delta < us)
    {
        /* wait current OSTick left time gone */
        while (SysTick->VAL < us);
        us -= delta;
        delta = SysTick->LOAD;
    }
    while (delta - SysTick->VAL < us);
}

但是定义却没有在board.h,而是在rthw.h,所以想要使用该函数就要引入后者.

补充

rt_tick_get();

该函数可以获取系统时间,返回的是TICK数,转换关系可以通过RT_TICK_PER_SECOND宏来知道,这里宏的定义是100则 1 TICK = 10ms。
测试

     while(1)
     {
          tick_now=rt_tick_get();
          rt_kprintf("tick:%d\n",tick_now);
          rt_thread_delay(100);//延时100个TICK
           
     }

定时器在close后再start将会重新定时,而不是继续上次定时,例如下列程序,在开启10秒的定时器后的第3秒关闭定时器,延时2秒再打开定时器,最终在第二次开启定时器10s后超时。

void time_test_thread_entry(void *parameter)
{
      
      //设定定时器1000个tick
        timer1 = rt_timer_create("timer",  /* 定时器名字是 timer1 */
                                        timeout1, /* 超时时回调的处理函数 */
                                        RT_NULL,  /* 超时函数的入口参数 */
                                        1000,       /* 定时长度,以OS Tick为单位,即10个OS Tick */
                                        RT_TIMER_FLAG_ONE_SHOT);
      //打印tick,记录为a
      rt_kprintf("start :%d\n",rt_tick_get());
      //开启定时器
      rt_timer_start(timer1);
      //延时300tick
      rt_thread_delay(300);
      //打印tick,关闭定时器
      rt_kprintf("close :%d\n",rt_tick_get());
      rt_timer_stop(timer1);
      //延时200tick
      rt_thread_delay(200);
      //打开定时器,等待超时
      rt_timer_start(timer1);
        
}

相关文章

  • RTT笔记-定时器

    修改!该文变更为快速查阅编程文档,RTT进程实现分析将另起一文 动态定时器 静态定时器 下列文本待删除 该笔记类别...

  • RTT笔记-定时器相关代码分析

    将从初始化、应用、相关代码与系统的关联三个方面分析代码。 初始化 初始化顺序执行,先进行了链表的清空,在compo...

  • 工欲善其事,必先利其器:通过Telnet访问SEGGER RTT

    Segger提供了RTT Viewer、RTT Client和RTT Logger3个工具用于RTT的访问,下面列...

  • RTT笔记-shell

    RTT笔记-shell 模式切换 finsh同时支持c-c-style模式和msh模式,默认进入msh模式,在该模...

  • RTT笔记-线程

    继续按照程序流程前进,前面提到的时钟初始化,堆初始化都还未跳出板级初始化rt_hw_board_init函数 然后...

  • RTT笔记-SPI

    该笔记类别主要是在自己学习时做的一些记录,方便自己很久不用忘掉时进行快速回忆 1 简述 和串口不同,SPI属于总线...

  • RTT笔记-package

    该笔记类别主要是在自己学习时做的一些记录,方便自己很久不用忘掉时进行快速回忆 1 简述 被封装用于特定功能的代码,...

  • RTT笔记-进程

    修改!该文变更为快速查阅编程文档,RTT进程实现分析将另起一文 动态创建线程 静态创建线程 事件 消息队列 定义 ...

  • RTT笔记-ulog

    该笔记类别主要是在自己学习时做的一些记录,方便自己很久不用忘掉时进行快速回忆 1 导入组件 代码位置:rt-thr...

  • RTT笔记-AT组件

    1 导入系统 AT 组件中 AT Client 功能占用资源体积为 4.6K ROM 和 2.0K RAM; AT...

网友评论

      本文标题:RTT笔记-定时器

      本文链接:https://www.haomeiwen.com/subject/skkahqtx.html