RT-Thread–timer(三)

微信扫一扫,分享到朋友圈

RT-Thread–timer(三)

上一篇看完了rt_thread类的成员以及相关的函数,可曾记得,在rt_thread类的成员中有一个rt_timer的实例,这篇就来看看与rt_timer的成员以及相关函数。

rt_timer实际就是RTT中实现的一个软定时器。MCU底层做的多的人一般对硬件定时器都很熟悉,软件定时器反而不太熟悉。这里稍微介绍下软件定时器。

回忆一下硬件定时器,设置一个通用的硬件定时器,步骤如下:

  • 配置定时器时钟。
  • 初始化定时寄存器,前两步就是为了确定超时时间。
  • 写定时器中断函数,确定时间到了之后执行的代码。

再之后就是开启或者关闭定时器了。

软件定时器其实也一样,只是这些全部由程序来执行,软件定时器的时钟节拍由软件周期性的产生,在RTOS中一般由心跳提供。通过软定时器初始化函数来设置超时时间以及超时回调函数(对应硬件定时器的中断函数)。

在RTT中,软件定时器又被分为纯软定时器以及普通定时器。纯软定时器中,定时器是由一个专门的线程来执行超时回调,该线程默认优先级为0(既最高)。普通定时器的时钟节拍由心跳中断来提供,超时回调也在硬件ISR中执行,心跳中断一般是一个硬件定时器的中断。

开启

#define RT_USING_TIMER_SOFT

这个宏可以开启纯软定时器功能。

为了知道RTT如何实现这两种模式的软件定时器,先来看看rt_timer类

struct rt_timer
{
    struct rt_object parent;                            /**< inherit from rt_object */

    rt_list_t        row[RT_TIMER_SKIP_LIST_LEVEL];

    void (*timeout_func)(void *parameter);              /**< timeout function */
    void            *parameter;                         /**< timeout function's parameter */

    rt_tick_t        init_tick;                         /**< timer timeout tick */
    rt_tick_t        timeout_tick;                      /**< timeout tick */
};
typedef struct rt_timer *rt_timer_t;

rt_timer的成员比较少,

1.parent,是object类的一个实例,说明rt_timer也继承自rt_object。

2.row[RT_TIMER_SKIP_LIST_LEVEL],一个双向链表结构,用来把rt_timer加入各种链表中。RT_TIMER_SKIP_LIST_LEVEL被定义为1,暂时不清楚RT_TIMER_SKIP_LIST_LEVEL这个变量的作用。可能是为日后可以将timer加入更多的链表。

3.timeout_func,超时回调函数指针,指向定时器超时后执行的回调函数。

4.parameter,回调函数的参数指针,指向回调函数执行时输入的参数。

5.init_tick,超时周期。超时时间=超时周期*每个周期的时间

6.timeout_tick,超时剩余周期。每个周期,该变量会被减1,直到到达0,就会触发timeout回调函数。

只有6个成员,看来成员是真的少啊。接下来,还是一样,继续看看与rt_timer相关的函数。可以看见,rt_timer相关函数有很多都与rt_thread相同,如init,creat,delete,detach,

control等函数。

void rt_system_tick_init(void);
rt_tick_t rt_tick_get(void);
void rt_tick_set(rt_tick_t tick);
void rt_tick_increase(void);
int  rt_tick_from_millisecond(rt_int32_t ms);

void rt_system_timer_init(void);
void rt_system_timer_thread_init(void);
rt_tick_t rt_timer_next_timeout_tick(void);
void rt_timer_check(void);

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_err_t rt_timer_detach(rt_timer_t timer);
rt_timer_t rt_timer_create(const char *name,
                           void (*timeout)(void *parameter),
                           void       *parameter,
                           rt_tick_t   time,
                           rt_uint8_t  flag);
rt_err_t rt_timer_delete(rt_timer_t timer);
rt_err_t rt_timer_start(rt_timer_t timer);
rt_err_t rt_timer_stop(rt_timer_t timer);
rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg);

一、rt_timer_init()和rt_timer_create()

还是一个规律,rt_timer_init是针对静态的rt_timer进行初始化,rt_timer_create是动态创建rt_timer,并进行初始化。

rt_timer_t rt_timer_create(const char *name,
                           void (*timeout)(void *parameter),
                           void       *parameter,
                           rt_tick_t   time,
                           rt_uint8_t  flag)
{
    struct rt_timer *timer;

    /* allocate a object */
    timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);
    if (timer == RT_NULL)
    {
        return RT_NULL;
    }

    _rt_timer_init(timer, timeout, parameter, time, flag);

    return timer;
}

两个函数最后都调用了_rt_timer_init()这个实际的初始化函数

这个函数做了以下一些操作:

1.设置flag,去掉RT_TIMER_FLAG_ACTIVATED的标志,rt_timer使用了基类object中的flag成员作为自己的标志位。主要有以下几种标志:

#define RT_TIMER_FLAG_DEACTIVATED       0x0             /**< timer is deactive */
#define RT_TIMER_FLAG_ACTIVATED         0x1             /**< timer is active */
#define RT_TIMER_FLAG_ONE_SHOT          0x0             /**< one shot timer */
#define RT_TIMER_FLAG_PERIODIC          0x2             /**< periodic timer */
#define RT_TIMER_FLAG_HARD_TIMER        0x0             /**< hard timer,the timer's callback function will be called in tick isr. */
#define RT_TIMER_FLAG_SOFT_TIMER        0x4             /**< soft timer,the timer's callback function will be called in timer thread. */

可以看到有3个标志都是0x0,这点有点怪。

2.配置time_out回调函数,以及parameter参数

3.设置超时周期init_tick,并把timeout_tick置为0

4.初始化双向链表row

static void _rt_timer_init(rt_timer_t timer,
                           void (*timeout)(void *parameter),
                           void      *parameter,
                           rt_tick_t  time,
                           rt_uint8_t flag)
{
    int i;

    /* set flag */
    timer->parent.flag  = flag;

    /* set deactivated */
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;

    timer->timeout_func = timeout;
    timer->parameter    = parameter;

    timer->timeout_tick = 0;
    timer->init_tick    = time;

    /* initialize timer list */
    for (i = 0; i row[i]));
    }
}

二、rt_timer_detach()和rt_timer_delete()

同样的,这两个函数都是对rt_timer进行删除,rt_timer_detach()针对的是静态的。

主要执行以下操作:

1.参数检查

2.执行_rt_timer_remove()将rt_timer移除相应的timer队列。(ISR或软timer线程将不再执行它)

3.删除rt_timer(释放分配的内存,移出对象容器)

rt_err_t rt_timer_delete(rt_timer_t timer)
{
    register rt_base_t level;

    /* timer check */
    RT_ASSERT(timer != RT_NULL);
    RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
    RT_ASSERT(rt_object_is_systemobject(&timer->parent) == RT_FALSE);

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    _rt_timer_remove(timer);

    /* enable interrupt */
    rt_hw_interrupt_enable(level);

    rt_object_delete((rt_object_t)timer);

    return RT_EOK;
}

三、rt_timer_start()和rt_timer_stop()

从名字就可以看出这两个函数一个是启动定时器一个是停止定时器。先来看启动的代码,代码挺长的主要完成以下一些步骤:

1.参数检查

2.重置rt_timer(将timer移出当前队列,RT_TIMER_FLAG_ACTIVATED标志移除。

3.调用rt_object_take_hook钩子函数

4.设置timeout_tick。

5.将rt_timer加入到对应的定时器列表中(纯软定时器加入纯软队列,普通定时器加入普通队列)

这里详细解析下下面这段代码。

rt_list_entry是一个神奇的宏定义,这个宏定义我也看了很久,写的很复杂。。关于宏定义提一下,RTT中具有很多很神奇的宏定义,写的十分精彩,大大减小了代码的复杂度,可以说是很优雅了。

#define rt_container_of(ptr, type, member) 
    ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))

总的来说rt_list_entry会通过成员地址返回包含该成员的实例的地址。一般使用方式是,ptr为类中rt_list_t成员的地址,type为类名,member为成员名

这段代码搜寻timer_list这个列表中的所有timer,当

else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)

时停止,后面在这个位置,把当前timer加入列表。这条语句看似是一个相减小与 RT_TICK_MAX / 2的条件,其实真实的含义是t->timeout_tick – timer->timeout_tick>0,这里是利用的32位整型的表示方式,RT_TICK_MAX 为0xffffffff,在MCU中对于有符号的整型,最高为都是符号位,当两个数相减的时候实际是把减数按位取反+1再相加,所以其实只有一种情况t->timeout_tick – timer->timeout_tick>RT_TICK_MAX /2会成立,那就是为负数的时候。

综上,这个for循环,其实是在rt_timer列表中搜寻到了刚好超时时间大于启动的rt_timer的那个索引,作为之后rt_timer插入列表的位置。所以整个rt_timer的列表(软timer一个列表,普通timer一个列表),都是按照timeout_tick 这个参数的值进行排序的,是一个有序列表。

row_head[0]  = &timer_list[0];
    for (row_lvl = 0; row_lvl next)
        {
            struct rt_timer *t;
            rt_list_t *p = row_head[row_lvl]->next;

            /* fix up the entry pointer */
            t = rt_list_entry(p, struct rt_timer, row[row_lvl]);

            /* If we have two timers that timeout at the same time, it's
             * preferred that the timer inserted early get called early.
             * So insert the new timer to the end the the some-timeout timer
             * list.
             */
            if ((t->timeout_tick - timer->timeout_tick) == 0)
            {
                continue;
            }
            else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
            {
                break;
            }
        }
        if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1)
            row_head[row_lvl + 1] = row_head[row_lvl] + 1;
    }

至于之后函数,功能其实是把一个完整的rt_timer列表拆分成几份,这样做的作用我也还没有看出来。。。

6.若启动的定时器为纯软的定时器,那么检测定时器线程是否启动,若未启动则重新启动线程。

rt_err_t rt_timer_start(rt_timer_t timer)
{
    unsigned int row_lvl;
    rt_list_t *timer_list;
    register rt_base_t level;
    rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL];
    unsigned int tst_nr;
    static unsigned int random_nr;

    /* timer check */
    RT_ASSERT(timer != RT_NULL);
    RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);

    /* stop timer firstly */
    level = rt_hw_interrupt_disable();
    /* remove timer from list */
    _rt_timer_remove(timer);
    /* change status of timer */
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
    rt_hw_interrupt_enable(level);

    RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent)));

    /*
     * get timeout tick,
     * the max timeout tick shall not great than RT_TICK_MAX/2
     */
    RT_ASSERT(timer->init_tick timeout_tick = rt_tick_get() + timer->init_tick;

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

#ifdef RT_USING_TIMER_SOFT
    if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
    {
        /* insert timer to soft timer list */
        timer_list = rt_soft_timer_list;
    }
    else
#endif
    {
        /* insert timer to system timer list */
        timer_list = rt_timer_list;
    }

    row_head[0]  = &timer_list[0];
    for (row_lvl = 0; row_lvl next)
        {
            struct rt_timer *t;
            rt_list_t *p = row_head[row_lvl]->next;

            /* fix up the entry pointer */
            t = rt_list_entry(p, struct rt_timer, row[row_lvl]);

            /* If we have two timers that timeout at the same time, it's
             * preferred that the timer inserted early get called early.
             * So insert the new timer to the end the the some-timeout timer
             * list.
             */
            if ((t->timeout_tick - timer->timeout_tick) == 0)
            {
                continue;
            }
            else if ((t->timeout_tick - timer->timeout_tick) timeout_tick very easily
     * (actually, the timeout_tick is not random and easy to be attacked). */
    random_nr++;
    tst_nr = random_nr;

    rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - 1],
                         &(timer->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
    for (row_lvl = 2; row_lvl row[RT_TIMER_SKIP_LIST_LEVEL - row_lvl]));
        else
            break;
        /* Shift over the bits we have tested. Works well with 1 bit and 2
         * bits. */
        tst_nr >>= (RT_TIMER_SKIP_LIST_MASK + 1) >> 1;
    }

    timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;

    /* enable interrupt */
    rt_hw_interrupt_enable(level);

#ifdef RT_USING_TIMER_SOFT
    if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
    {
        /* check whether timer thread is ready */
        if ((timer_thread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_READY)
        {
            /* resume timer thread to check soft timer */
            rt_thread_resume(&timer_thread);
            rt_schedule();
        }
    }
#endif

    return RT_EOK;
}

stop函数就相对来说简单了很多,主要就是:

1.参数检查

2.调用rt_object_put_hook钩子函数

3.将rt_timer移出列表。

rt_err_t rt_timer_stop(rt_timer_t timer)
{
    register rt_base_t level;

    /* timer check */
    RT_ASSERT(timer != RT_NULL);
    RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);

    if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED))
        return -RT_ERROR;

    RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer->parent)));

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    _rt_timer_remove(timer);

    /* enable interrupt */
    rt_hw_interrupt_enable(level);

    /* change stat */
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;

    return RT_EOK;
}

四、rt_timer_control()函数

改函数可以通过参数中的cmd来实现不同的功能,cmd总共有4中取值:

#define RT_TIMER_CTRL_SET_TIME          0x0             /**< set timer control command */
#define RT_TIMER_CTRL_GET_TIME          0x1             /**< get timer control command */
#define RT_TIMER_CTRL_SET_ONESHOT       0x2             /**< change timer to one shot */
#define RT_TIMER_CTRL_SET_PERIODIC      0x3             /**< change timer to periodic */

分别对应:

  • 设置定时器时间
  • 获取定时器时间
  • 设置定时器为单触发模式
  • 设置定时器为周期性触发模式

当rt_timer被标记为周期性定时器后,当time_out回调触发后会执行start重新启动该定时器。

五、其他函数。

剩下的函数还有

(1)void rt_system_tick_init(void);
(2)rt_tick_t rt_tick_get(void);
(3)void rt_tick_set(rt_tick_t tick);
(4)void rt_tick_increase(void);
(5)int  rt_tick_from_millisecond(rt_int32_t ms);
(6)void rt_system_timer_init(void);
(7)void rt_system_timer_thread_init(void);
(8)rt_tick_t rt_timer_next_timeout_tick(void);
(9)void rt_timer_check(void);

(1)是个空函数。

(2)返回系统tick数,这个数在系统启动后就随着心跳不断的增加。

(3)设置系统tick数

(4)

void rt_tick_increase(void)
{
    struct rt_thread *thread;

    /* increase the global tick */
    ++ rt_tick;

    /* check time slice */
    thread = rt_thread_self();

    -- thread->remaining_tick;
    if (thread->remaining_tick == 0)
    {
        /* change to initialized tick */
        thread->remaining_tick = thread->init_tick;

        /* yield */
        rt_thread_yield();
    }

    /* check timer */
    rt_timer_check();
}

函数会对rt_tick加1,并检查线程时间片时间是否过期,若过期,则调用rt_thread_yield()。这个函数在上一篇分析过。

之后会执行(9)rt_timer_check()函数

(5)是个工具函数,将ms数换算成tick数

(6)初始化普通定时器列表

(7)若开启了纯软定时器,启动定时器线程,纯软定时器的time_out回调将在这个线程中执行,该线程默认优先级为0

(8)返回下一个最近会到期的普通定时器的time_out值。

(9)

void rt_timer_check(void)
{
    struct rt_timer *t;
    rt_tick_t current_tick;
    register rt_base_t level;

    RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check entern"));

    current_tick = rt_tick_get();

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    while (!rt_list_isempty(&rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
    {
        t = rt_list_entry(rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
                          struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);

        /*
         * It supposes that the new tick shall less than the half duration of
         * tick max.
         */
        if ((current_tick - t->timeout_tick) timeout_func(t->parameter);

            /* re-get tick */
            current_tick = rt_tick_get();

            RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %dn", current_tick));

            if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
                (t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
            {
                /* start it */
                t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
                rt_timer_start(t);
            }
            else
            {
                /* stop timer */
                t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
            }
        }
        else
            break;
    }

    /* enable interrupt */
    rt_hw_interrupt_enable(level);

    RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check leaven"));
}

该函数在ISR中执行,检测是否有普通定时器到期,对于到期的定时器先执行time_out回调,并判断是否是周期定时器,若是周期定时器,重新启动这个定时器。

那么对于rt_timer的解读就这么多,篇幅感觉越来越长呀。。

微信扫一扫,分享到朋友圈

RT-Thread–timer(三)

助推中国制造2025,天创电子用机器人与AI助力工业智能运维

上一篇

迷你书: AI前线(2018年9月)

下一篇

你也可能喜欢

RT-Thread–timer(三)

长按储存图像,分享给朋友