RT-Thread时钟部分API指南:使用与源代码分析


layout: post
title: “RT-Thread时钟管理”
date: 2024-1-26 15:39:08 +0800
tags: RT-Thread


时钟管理

操作系统需要一个时钟用来规范任务

时钟节拍

时钟节拍的长度可以根据 RT_TICK_PER_SECOND 的定义来调整

rtconfig.h配置文件中定义

#define RT_TICK_PER_SECOND 1000

时钟是每秒1000次

void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();
	//更新时钟
    HAL_IncTick();
    rt_tick_increase();

    /* leave interrupt */
    rt_interrupt_leave();
}
/**
 * This function will return current tick from operating system startup
 *
 * @return current tick
 */
rt_tick_t rt_tick_get(void)

获取时钟

定时器

从指定的时刻开始,经过一定的指定时间后触发一个事件

硬件定时器: 芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。

软件定时器: 由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务。

RT-Thread操作系统提供软件实现的定时器,以时钟节拍(OS Tick)的时间长度为单位,即定时数值必须是OS Tick的整数倍

可以设置单次触发以及周期触发

根据定时器超时函数执行时所处的上下文环境,RT-Thread的定时器可以分为HARD_TIMER模式和SOFT_TIMER模式

RT-Thread时钟部分API指南:使用与源代码分析_第1张图片

HARD_TIMER模式:中断上下文

定时器超时函数的要求:执行时间应该尽量短,执行时不应导致当前上下文挂起、等待。例如在中断上下文中执行的超时函数它不应该试图去申请动态内存、释放动态内存等

SOFT_TIMER模式:线程上下文

该模式被启用后,系统会在初始化时创建一个 timer 线程,然后 SOFT_TIMER 模式的定时器超时函数在都会在timer线程的上下文环境中执行

这个是RT-Thread使用的

初始化

int rtthread_startup(void)里面有两个函数

/**
 * @ingroup SystemInit
 *
 * This function will initialize system timer
 */
void rt_system_timer_init(void)
{
    int i;

    for (i = 0; i < sizeof(rt_timer_list) / sizeof(rt_timer_list[0]); i++)
    {
        rt_list_init(rt_timer_list + i);
    }
}

系统定时器的初始化, 注意就是一个列表, 这个数组的个数是一个, 这个是一个硬件定时器的列表

void rt_system_timer_thread_init(void)
{
#ifdef RT_USING_TIMER_SOFT
    int i;

    for (i = 0;i < sizeof(rt_soft_timer_list) / sizeof(rt_soft_timer_list[0]);i++)
    {
        //这个只是一个链表头, 之后会使用定时器的时间顺序进行插入
        rt_list_init(rt_soft_timer_list + i);
    }

    /* start software timer thread */
    rt_thread_init(&timer_thread,
                   "timer",
                   rt_thread_timer_entry,
                   RT_NULL,
                   &timer_thread_stack[0],
                   sizeof(timer_thread_stack),
                   RT_TIMER_THREAD_PRIO,
                   10);

    /* startup */
    rt_thread_startup(&timer_thread);
#endif
}

机制

主要有两个列表

  • 当前系统经过的 tick 时间 rt_tick(当硬件定时器中断来临时,它将加 1) ;
  • 定时器链表 rt_timer_list。系统新创建并激活的定时器都会按照以超时时间排序的方式插入到rt_timer_list 链表中。

这个插入使用的是定时器跳表 (Skip List) 算法

类似于二分查找法, 会记录一个列表里面的部分项的位置, 用于之后的查找, 可以记录多级索引, 在 RT-Thread 中通过宏定义 RT_TIMER_SKIP_LIST_LEVEL 来配置跳表的层数,默认为 1

实际使用

创建

//动态创建
/**
 * This function will create a timer
 *
 * @param name the name of timer
 * @param timeout the timeout function
 * @param parameter the parameter of timeout function
 * @param time the tick of timer
 * @param flag the flag of timer
 *
 * @return the created timer object
 */
rt_timer_t rt_timer_create(const char *name,
                           void (*timeout)(void *parameter),
                           void       *parameter,
                           rt_tick_t   time,
                           rt_uint8_t  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. 软件的, 这一个主要是插入不同的列表里面*/

调用该函数接口后,内核首先从动态内存堆中分配一个定时器控制块,然后对该控制块进行基本的初始化。

如果是硬件的会在SystemTick中断里面进行处理, 不然的话会在timer线程处理

//静态创建
/**
 * This function will initialize a timer, normally this function is used to
 * initialize a static timer object.
 *
 * @param timer the static timer object
 * @param name the name of timer
 * @param timeout the timeout function
 * @param parameter the parameter of timeout function
 * @param time the tick of timer
 * @param flag the flag of timer
 */
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)
{
    /* timer check */
    RT_ASSERT(timer != RT_NULL);

    /* timer object initialization */
    rt_object_init((rt_object_t)timer, RT_Object_Class_Timer, name);

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

删除

/**
 * This function will delete a timer and release timer memory
 *
 * @param timer the timer to be deleted
 *
 * @return the operation status, RT_EOK on OK; RT_ERROR on error
 */
rt_err_t rt_timer_delete(rt_timer_t timer)
/**
 * This function will detach a timer from timer management.
 *
 * @param timer the static timer object
 *
 * @return the operation status, RT_EOK on OK; RT_ERROR on error
 */
rt_err_t rt_timer_detach(rt_timer_t timer)

开启

/**
 * This function will start the timer
 *
 * @param timer the timer to be started
 *
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
 */
rt_err_t rt_timer_start(rt_timer_t timer)

停止

/**
 * This function will stop the timer
 *
 * @param timer the timer to be stopped
 *
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
 */
rt_err_t rt_timer_stop(rt_timer_t timer)

控制

/**
 * This function will get or set some options of the timer
 *
 * @param timer the timer to be get or set
 * @param cmd the control command
 * @param arg the argument
 *
 * @return RT_EOK
 */
rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)

主要的参数

#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 调整为循环模式*/
#define RT_TIMER_CTRL_GET_STATE         0x4             /**< get timer run state active or deactive 获取状态(这一个在手册里面没有)*/

高精度延时

这一个延时的时间需要低于一个系统时钟, 否则SysTick会溢出

/**
 * This function will delay for some us.
 *
 * @param us the delay time of us
 */
void rt_hw_us_delay(rt_uint32_t us)

这一个函数没有实现, 官方的示例给出的是一个死循环

实际代码分析

用户API函数

创建

//动态获取一个时钟对象
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;

    /* 这一部分可以看RT-Thread对象篇的讲解 */
    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;
}
//主要是进行一下记录信息
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;
	//这个溢出时间是在start的时候设置才计算的
    timer->timeout_tick = 0;
    timer->init_tick    = time;

    /* initialize timer list */
    for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
    {
        rt_list_init(&(timer->row[i]));
    }
}
删除
rt_err_t rt_timer_delete(rt_timer_t timer)
{
    register rt_base_t level;

    /* 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_inline void _rt_timer_remove(rt_timer_t timer)
{
    int i;
	//主要是从链表里面移除
    for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
    {
        rt_list_remove(&timer->row[i]);
    }
}
开启
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;

    /* 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 < RT_TICK_MAX / 2);
    //获取当前的时间计算一下溢出的时间
    timer->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
    {
        //这个会在Systick中断里面处理
        /* insert timer to system timer list */
        timer_list = rt_timer_list;
    }
	//使用这一个变量进行遍历
    row_head[0]  = &timer_list[0];
    //这个时钟的链表只有一层
    for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
    {
        //依次遍历这一个链表
        //直到第一个溢出时间比这一个时钟长的时钟的时候退出
        //实际使用row_head[0]记录这一个值的前一个
        //用于升序插入
        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;

            /* 获取这个链表所在的时钟结构体(后面有分析) */
            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;
    }

    /* Interestingly, this super simple timer insert counter works very very
     * well on distributing the list height uniformly. By means of "very very
     * well", I mean it beats the randomness of timer->timeout_tick very easily
     * (actually, the timeout_tick is not random and easy to be attacked). 
     这是使用跳表(Skip List)算法的时候使用的, 不需要关心 */
    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 <= RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
    {
        //这是使用跳表(Skip List)算法的时候使用的, 不需要关心
		...
    }

    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_SUSPEND)
        {
            /* resume timer thread to check soft timer */
            rt_thread_resume(&timer_thread);
            rt_schedule();
        }
    }
#endif

    return RT_EOK;
}
/**获取一个链表所在的结构体的起始位置
 * @brief get the struct for this entry
 * @param node the entry point
 * @param type the type of structure
 * @param member the name of list in structure
 */
#define rt_list_entry(node, type, member) \
    rt_container_of(node, type, member)
/**这一个实际上是用一个结构体里面的某一个变量的地址减去他的相对于这一个结构体的起始的偏移
 * rt_container_of - return the member address of ptr, if the type of ptr is the
 * struct type.
 */
#define rt_container_of(ptr, type, member) \
    ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
时钟的关闭
rt_err_t rt_timer_stop(rt_timer_t timer)
{
    register rt_base_t level;

	//这不是一个启动了的时钟
    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_inline void _rt_timer_remove(rt_timer_t timer)
{
    int i;

    for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
    {
        rt_list_remove(&timer->row[i]);
    }
}

系统后台

中断时钟处理
/**这个是在Systick系统时钟里面的时钟处理
 * This function will notify kernel there is one tick passed. Normally,
 * this function is invoked by clock ISR.
 */
void rt_tick_increase(void)
{
    struct rt_thread *thread;

    /* 全局变量rt_tick自加, 使用这一个变量作为系统时钟 */
    ++ rt_tick;

    /* 线程部分的处理, 线程篇分析过了 */
	...
    /* check timer */
    rt_timer_check();
}
void rt_timer_check(void)
{
    struct rt_timer *t;
    rt_tick_t current_tick;
    register rt_base_t level;
	//获取时间
    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) < RT_TICK_MAX / 2)
        {
            //这一个时钟已经到点了
            RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t));

            /* remove timer from timer list firstly 从链表里面移除 */
            _rt_timer_remove(t);

            /* call timeout function 调用一下处理函数 */
            t->timeout_func(t->parameter);

            /* re-get tick 更新时钟, 因为不知道用户的代码的长度 */
            current_tick = rt_tick_get();
			//回调函数
            RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t));
            RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", 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 leave\n"));
}
软件时钟的处理
//这是一个时钟处理线程
static void rt_thread_timer_entry(void *parameter)
{
    rt_tick_t next_timeout;

    while (1)
    {
        /* get the next timeout tick 记录一下下一个溢出的时间 */
        next_timeout = rt_timer_list_next_timeout(rt_soft_timer_list);
        if (next_timeout == RT_TICK_MAX)
        {
            /* no software timer exist, suspend self. 没有需要处理的时钟了 */
            rt_thread_suspend(rt_thread_self());
            rt_schedule();
        }
        else
        {
            rt_tick_t current_tick;

            /* get current tick */
            current_tick = rt_tick_get();

            if ((next_timeout - current_tick) < RT_TICK_MAX / 2)
            {
                /* get the delta timeout tick */
                next_timeout = next_timeout - current_tick;
                //任务休眠, 实际使用一个中断时钟(任务篇有讲)
                rt_thread_delay(next_timeout);
            }
        }
		//到这里的时候1.有新的时钟插入 2.有时钟到点了
        /* check software timer */
        rt_soft_timer_check();
    }
}
void rt_soft_timer_check(void)
{
    rt_tick_t current_tick;
    rt_list_t *n;
    struct rt_timer *t;

    RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check enter\n"));

    current_tick = rt_tick_get();

    /* lock scheduler */
    rt_enter_critical();
	//遍历一下所有的软件处理的时钟
    for (n = rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next;
         n != &(rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]);)
    {
        //获取一个时钟
        t = rt_list_entry(n, 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) < RT_TICK_MAX / 2)
        {
            
            //这一个时钟时间到了
            RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t));

            /* move node to the next 链表移动到下一个时钟 */
            n = n->next;

            /* remove timer from timer list firstly 先从链表里面移出来 */
            _rt_timer_remove(t);

            /* not lock scheduler when performing timeout function 
            在处理这一个时钟的处理函数的时候不能关闭中断 */
            rt_exit_critical();
            /* call timeout function 调用时钟的函数 */
            t->timeout_func(t->parameter);

            /* re-get tick 获取当前时间 */
            current_tick = rt_tick_get();

            RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t));
            RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick));

            /* lock scheduler */
            rt_enter_critical();

            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; /* not check anymore */
    }

    /* unlock scheduler */
    rt_exit_critical();

    RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check leave\n"));
}

你可能感兴趣的:(单片机,mcu,笔记,经验分享,stm32)