RT-Thread源码阅读(4)——定时器管理

RT-Thread的定时器有硬件定时器和软件定时器之分:

  • 硬件定时器:在systick中断服务函数中判断超时并执行定时函数
  • 软件定时器:单独起一个高优先级的线程,在线程中判断超时并执行定时函数

通过是否定义宏RT_USING_TIMER_SOFT来决定启用软件定时器,默认不启用



定时器的使用

static rt_timer_t timer1;
/**
* @ "timer1" 是定时器名称
* @ test_timer_callback 是定时器回调函数
* @ RT_NULL 是定时器回调函数参数
* @ 100 是定时器周期,单位是tick,这里可以为0,start后不会马上触发,会在下一个tick才触发
* @ RT_TIMER_FLAG_PERIODIC 是定时器类型,周期性定时器,RT_TIMER_FLAG_ONE_SHOT是单次定时器
* @ 如果调用软件定时器还需要与上T_TIMER_FLAG_SOFT_TIMER,否则创建的还是硬件定时器
*/
timer1 = rt_timer_create("timer1", test_timer_callback, RT_NULL, 100, RT_TIMER_FLAG_PERIODIC);
rt_timer_start(timer1);



定时器创建源码

关于rt_object_allocate函数可参考内核对象管理章节

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;
	
    // 值不能超过 0xffffffff的一半
    RT_ASSERT(time < RT_TICK_MAX / 2);
    
    // 参考内核对象管理 章节
    timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);

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

    return timer;
}

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

    // 设置周期性定时器还是单次定时器
    timer->parent.flag = flag;

    // 刚开始不激活定时器
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
	
    // 简而言之这里的flag bit0表示是否激活,bit1表示是否周期性
    
    // 设置回调
    timer->timeout_func = timeout;
    timer->parameter = parameter;

    timer->timeout_tick = 0;
    // 设置定时时长
    timer->init_tick = time;

    // 跳表算法后续讲解,这里RT_TIMER_SKIP_LIST_LEVEL默认为1
    for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
    {
        rt_list_init(&(timer->row[i]));
    }
}



定时器启动源码

定时器的创建比较简单,定时器的启动可能稍微复杂点

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

    level = rt_hw_interrupt_disable();

    // 将自己从定时器链表中删除
    _timer_remove(timer);

    // 停止定时器  已经关中断了,这里有必要吗?
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; 

    // 设置定时器的超时时间
    timer->timeout_tick = rt_tick_get() + timer->init_tick; 

    // 软硬件定时器有各自的链表
#ifdef RT_USING_TIMER_SOFT
    if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
    {
        // rt_timer_create的时候必须带上RT_TIMER_FLAG_SOFT_TIMER
        // 不然创建的就是硬件定时器
        timer_list = _soft_timer_list;
    }
    else
#endif /* RT_USING_TIMER_SOFT */
    {
        timer_list = _timer_list;
    }

    row_head[0] = &timer_list[0];
    // RT_TIMER_SKIP_LIST_LEVEL 是跳表的层数 这里默认为1
    for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
    {
        // 遍历row_head[row_lvl]链表
        for (; row_head[row_lvl] != timer_list[row_lvl].prev; row_head[row_lvl] = row_head[row_lvl]->next)
        {
            struct rt_timer *t;
            rt_list_t *p = row_head[row_lvl]->next;

            // 获取到rt_timer结构体的地址
            t = rt_list_entry(p, struct rt_timer, row[row_lvl]);

            // 定时器链表是以绝对超时时间为依据的顺序表
            // 如果当前定时器的超时时间和链表中一样,应该插入到后面
            if ((t->timeout_tick - timer->timeout_tick) == 0)
            {
                continue;
            }
            // 首先思考一个问题,任意两个timeout_tick最大相差多少?
            // 那当然是timeout_tick1是0,timeout_tick2是 (RT_TICK_MAX / 2 - 1)

            // 所以这里需要插入的timer在链表中t的前面(即timeout_tick更小),此判断才成立
            else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
            {
                // 此时需要需要把timer插入到t的前面
                // 因为row_head[row_lvl]->next指向的就是t,所以这里直接break
                // 后面将timer插入到row_head[row_lvl]之后就行,timer也顺利成章变到了t之前
                break;
            }
        }
        if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1)
            row_head[row_lvl + 1] = row_head[row_lvl] + 1;
    }

    // 跳表相关的,这个版本跳表就一层,可以不予理会
    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]));

    // 调表的层数大于等于2时这里才会执行,这个版本跳表就一层,可以不予理会
    for (row_lvl = 2; row_lvl <= RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
    {
        if (!(tst_nr & RT_TIMER_SKIP_LIST_MASK))
            rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - row_lvl],
                                 &(timer->row[RT_TIMER_SKIP_LIST_LEVEL - row_lvl]));
        else
            break;

        tst_nr >>= (RT_TIMER_SKIP_LIST_MASK + 1) >> 1;
    }

    // 将定时器激活
    timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;
    
    #ifdef RT_USING_TIMER_SOFT
    if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
    {
        if ((_soft_timer_status == RT_SOFT_TIMER_IDLE) &&
           ((_timer_thread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND))
        {
            // [插个眼1]:如果定时器线程休眠了,将其唤醒
            rt_thread_resume(&_timer_thread);
            need_schedule = RT_TRUE;
        }
    }
#endif /* RT_USING_TIMER_SOFT */

    rt_hw_interrupt_enable(level);

    return RT_EOK;
}



硬件定时器唤醒源码

硬件定时器的唤醒一般在systick中断SysTick_Handler

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

    rt_list_init(&list);

    current_tick = rt_tick_get();

    level = rt_hw_interrupt_disable();

    // 定时器链表不为空
    while (!rt_list_isempty(&_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
    {
        // 获取当前rt_timer结构体指针
        t = rt_list_entry(_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, struct rt_timer,
                          row[RT_TIMER_SKIP_LIST_LEVEL - 1]);

        // current_tick >= t->timeout_tick,即定时已到
        if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2)
        {
            // 准备调用,先从链表中删除
            _timer_remove(t);

            if (!(t->parent.flag & RT_TIMER_FLAG_PERIODIC))
            {
                // 如非周期性定时器,就关闭
                t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
            }
            
            // 加入到临时链表中
            rt_list_insert_after(&list, &(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
            
            // 调用回调函数
            t->timeout_func(t->parameter);

            current_tick = rt_tick_get();

            /* Check whether the timer object is detached or started again */
            // 刚加入list又对list判空,为什么呢?
            // 因为回调函数中可能会对定时器操作,比如重新start或者卸载定时器
            if (rt_list_isempty(&list))
            {
                continue;
            }

            // 又从list中移出来
            rt_list_remove(&(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));

            // 如是周期性定时器,则重新启动
            if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) && (t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
            {
                t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
                rt_timer_start(t);
            }
        }
        else
            break;
    }

    rt_hw_interrupt_enable(level);
}



软件定时器唤醒源码

软件定时器线程优先级默认最高

#define RT_TIMER_THREAD_PRIO           0
static void _timer_thread_entry(void *parameter)
{
    rt_tick_t next_timeout;

    while (1)
    {
        if (_timer_list_next_timeout(_soft_timer_list, &next_timeout) != RT_EOK)
        {
            // 如果链表为空则主动休眠,否则返回最近的超时时间
            rt_thread_suspend(rt_thread_self());
            rt_schedule();
        }
        else
        {
            rt_tick_t current_tick;

            current_tick = rt_tick_get();

            if ((next_timeout - current_tick) < RT_TICK_MAX / 2)
            {
                // 如果没超时,还需要多久直接delay
                next_timeout = next_timeout - current_tick;
                // 思考: 如果在delay过程中,创建了一个更早触发的定时器,能响应吗?
                // 答案: 请看[插个眼1],创建定时器时会唤醒线程
                rt_thread_delay(next_timeout);
                
            }
        }

        // 和硬件定时器的rt_timer_check差不多
        rt_soft_timer_check();
    }
}

你可能感兴趣的:(RTOS,嵌入式,RTT,RT-Thread,单片机,物联网,STM32)