RT_thread(三)时钟管理

RT_thread内核学习系列
1.RT_thread(一)系统的启动rtthread_startup
2.RT_thread(二)线程的操作


重点

一、时钟节拍(clock.c)

1.时钟节拍tick是什么

RT为real time,实时,需要通过时间来规范其任务的执行,操作系统中最小的时间单位是时钟节拍 (OS Tick)。任何操作系统都需要提供一个时钟节拍,以供系统处理所有和时间有关的事件,如线程的延时、线程的时间片轮转调度以及定时器超时等。时钟节拍的长度可以根据 RT_TICK_PER_SECOND 的定义来调整,等于 1/RT_TICK_PER_SECOND 秒。

在内核的clock文件中定义了static rt_tick_t rt_tick = 0,一个静态全局变量

时钟节拍由配置为中断触发模式的硬件定时器产生

/**
 * This is the timer interrupt service routine.
 *这是定时器中断服务程序
 */
void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();
    rt_tick_increase();
    /* leave interrupt */
    rt_interrupt_leave();
}

这个SysTick_Handler函数在到达设定时间值后芯片中断控制器产生时钟中断

SysTick_Handler PROC
                EXPORT  SysTick_Handler            [WEAK]
                B       .
                ENDP

在这里插入图片描述

函数中调用 rt_tick_increase() 对全局变量 rt_tick 进行自加

void rt_tick_increase(void)
{
    struct rt_thread *thread;

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

    /* check time slice */           
    thread = rt_thread_self();//到当前线程,这里就是时间节拍对线程的管理

    -- thread->remaining_tick;    
     //remaining_tick是当前线程占据处理器的剩余时间
    //系统++tick自增1,说明线程占据时间--remaining_tick自减1,时间到了就要让出处理器
    if (thread->remaining_tick == 0)
    {
        /* change to initialized tick */
        thread->remaining_tick = thread->init_tick;
        //时间到了,剩余时间初始化,赋初值(建立线程的时间片值)

        /* yield */
        rt_thread_yield();//线程让出处理器,重新插入就绪链表
    }

    /* check timer */
    rt_timer_check();//这个操作对定时器链表核查
}

2.获得当前tick

取出全局变量rt_tick的值

**
 * This function will return current tick from operating system startup
 *
 * @return current tick
 */
rt_tick_t rt_tick_get(void)
{
    /* return the global tick */
    return rt_tick;
}

3.tick的重赋值

给全局变量tick赋值

/**
 * This function will set current tick
 */
void rt_tick_set(rt_tick_t tick)
{
    rt_base_t level;

    level = rt_hw_interrupt_disable();
    rt_tick = tick;
    rt_hw_interrupt_enable(level);
}
}

4.时间换算

在线程延时中讲过

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;
}
RTM_EXPORT(rt_tick_from_millisecond);

二.定时器的管理(timer.c文件)

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

1.定时器结构体

继承的父类结构体中含(name,type,flag)

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.定时器控制

在这里插入图片描述

1.定时器机制

1.RT-Thread 的定时器提供两类定时器机制

第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动停止
第二类是周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动的停止,否则将永远持续执行下去

2.RT-Thread 的定时器提供两种定时器模式

HARD_TIMER 模式

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

SOFT_TIMER 模式

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

2.定时器初始化和脱离

定时器初始化


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);
    //把参数赋给定时器结构体中成员
}
RTM_EXPORT(rt_timer_init);

定时器脱离

rt_err_t rt_timer_detach(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));//判断对象是否为系统对象

    /* disable interrupt */
    level = rt_hw_interrupt_disable();//关中断1

    _rt_timer_remove(timer);//在链表中移除定时器

    /* enable interrupt */
    rt_hw_interrupt_enable(level);//开中断1

    rt_object_detach((rt_object_t)timer);//定时器对象脱离对象容器(不需要中断保护)

    return RT_EOK;
}
RTM_EXPORT(rt_timer_detach);

3.定时器创建和删除

定时器创建

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;//返回的是个定时器结构体
}
RTM_EXPORT(rt_timer_create);

定时器删除

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();//关中断1

    _rt_timer_remove(timer);//在链表中移除定时器

    /* enable interrupt */
    rt_hw_interrupt_enable(level);//开中断1

    rt_object_delete((rt_object_t)timer);//删除对象并清除对象内存

    return RT_EOK;
}
RTM_EXPORT(rt_timer_delete);

4.初始化和创建、脱离和删除的区别

初始化和创建

初始化中

rt_object_init((rt_object_t)timer, RT_Object_Class_Timer, name);
//定时器对象初始化

创建中

timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);
//在对象容器中创建定时器对象

初始化是在创建好的定时器句柄中设置,创建是自己定义一个对象并分配内存

脱离和删除
脱离

void rt_object_detach(rt_object_t object)
{
    register rt_base_t temp;

    /* object check */
    RT_ASSERT(object != RT_NULL);

    RT_OBJECT_HOOK_CALL(rt_object_detach_hook, (object));

    /* reset object type */
    object->type = 0;

    /* lock interrupt */
    temp = rt_hw_interrupt_disable();

    /* remove from old list */
    rt_list_remove(&(object->list));

    /* unlock interrupt */
    rt_hw_interrupt_enable(temp);
}

删除

void rt_object_delete(rt_object_t object)
{
    register rt_base_t temp;

    /* object check */
    RT_ASSERT(object != RT_NULL);
    RT_ASSERT(!(object->type & RT_Object_Class_Static));

    RT_OBJECT_HOOK_CALL(rt_object_detach_hook, (object));

    /* reset object type */
    object->type = 0;

    /* lock interrupt */
    temp = rt_hw_interrupt_disable();

    /* remove from old list */
    rt_list_remove(&(object->list));

    /* unlock interrupt */
    rt_hw_interrupt_enable(temp);

    /* free the memory of object */
    RT_KERNEL_FREE(object);                  //区别
}

删除比脱离多个RT_KERNEL_FREE(object); ,释放对象内存

5.定时器启动

定时器工作机制

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

定时器跳表 (Skip List) 算法
来自官网

定时器启动函数(RT-Thread 定时器默认的方式是 HARD_TIMER 模式)

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();//关中断1
    /* remove timer from list */
    _rt_timer_remove(timer);//没检查状态,不用管是否在链表中,在就从定时器链表移除,不在就不用管
    /* change status of timer */
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;//改变定时器状态
    rt_hw_interrupt_enable(level);//开中断1

    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);//最大超时刻度不得大于RT_tick_max/2
    timer->timeout_tick = rt_tick_get() + timer->init_tick;//超时=当前时间+初始节拍值(线程中是线程的时间片)

    /* disable interrupt */
    level = rt_hw_interrupt_disable();//关中断1
    {
        /* insert timer to system timer list */
        timer_list = rt_timer_list;//定时器链表指针指向定时器链表
    }

    row_head[0]  = &timer_list[0];//定时器跳表指针指向定时器跳表
    
//这个for循环是为了找到按从小到大排列链表的刚好大于等于前一个定时器的位置
    for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; 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;//p指针指向row_head[row_lvl]的第一个定时器

            /* fix up the entry pointer */
            t = rt_list_entry(p, struct rt_timer, row[row_lvl]);//t指针指向定时器结构体

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

//random_nr是一个静态变量,用于记录启动了多少个定时器 
    random_nr++;
    tst_nr = random_nr;
//在上方的for循环找到了位置,定时器插入链表
    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++)
    {
        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;
        /* 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);
    return RT_EOK;
}
RTM_EXPORT(rt_timer_start);

6.定时器暂停

启动定时器以后,若想使它停止,可以使用下面的函数

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();//关中断1

    _rt_timer_remove(timer);//定时器从定时器链表中移除

    /* enable interrupt */
    rt_hw_interrupt_enable(level);//开中断1

    /* change stat */
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;//标志位改为非运行

    return RT_EOK;
}
RTM_EXPORT(rt_timer_stop);

7.控制定时器

设置定时器的信息,命令有4个:设置定时时间,查看定时时间,设置单次触发,设置周期触发

rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)
{
    /* timer check */
    RT_ASSERT(timer != RT_NULL);//判断定时器句柄是否为空
    RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);//判断定时器对象类型是否为定时器

    switch (cmd)
    {
    case RT_TIMER_CTRL_GET_TIME://设置定时时间
        *(rt_tick_t *)arg = timer->init_tick;
        break;

    case RT_TIMER_CTRL_SET_TIME://查看定时时间
        timer->init_tick = *(rt_tick_t *)arg;
        break;

    case RT_TIMER_CTRL_SET_ONESHOT://设置单次触发
        timer->parent.flag &= ~RT_TIMER_FLAG_PERIODIC;
        break;

    case RT_TIMER_CTRL_SET_PERIODIC://设置周期触发
        timer->parent.flag |= RT_TIMER_FLAG_PERIODIC;
        break;
    }

    return RT_EOK;
}
RTM_EXPORT(rt_timer_control);

8.扫描函数

如果发生超时事件将调用相应的超时函数

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 enter\n"));

    current_tick = rt_tick_get();//读取当前时间

    /* disable interrupt */
    level = rt_hw_interrupt_disable();//关中断1
      //在定时器链表中遍历
    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_timeout_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_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);//开中断1

    RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check leave\n"));
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值