RTT(RT-Thread)时钟管理

目录

时钟管理

时钟节拍

RTT工程目录结构介绍

配置文件:rtconfig.h

获取系统节拍

获取系统节拍数函数

实例

定时器

RT_Thread定时器介绍

定时器源码分析(了解即可)

rt_system_timer_init (硬件定时器初始化)

rt_system_timer_thread_init(软件定时器初始化)

总结

定时器工作机制

定时器相关接口

启动和停止定时器

动态创建定时器

创建定时器

删除定时器

实例

静态创建定时器

初始化定时器

脱离定时器

实例

控制定时器

实例

高精度延时


时钟管理

操作系统需要通过时间来规范其任务,本章主要介绍时钟节拍和基于时钟节拍的定时器。

时钟节拍

任何操作系统都需要提供一个时钟节拍,以供系统处理所有和时间有关的事件,如线程的延时、线程的时间片轮转调度以及定时器超时等。

RT-Thread 中,时钟节拍的长度可以根据 RT_TICK_PER_SECOND 的定义来调整。 rtconfig.h配置文件中定义:

cpp 复制代码
/* 
 *频率是1000HZ 周期是1/1000 s
 *所以节拍是1ms
 */
#define RT_TICK_PER_SECOND 1000

RTT工程目录结构介绍

配置文件:rtconfig.h

其中包括了内核相关的配置、内部线程通信配置、内存管理、内核设备对象、RTT组件、C++特性、设备驱动和USB配置等等

时钟节拍配置属于内核相关配置,默认配置为1000,表示1000Hz,一次节拍为1ms

系统滴答定时器中断处理函数(每1ms触发一次systick定时器中断):每一次发生中断都会进入中断处理函数

我们可以通过启动文件的中断向量表中进入

cpp 复制代码
void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    HAL_IncTick();
    rt_tick_increase();// ++ rt_tick:全局变量自加,
                       //记录的是系统从启动到现在的时间节拍数

    /* leave interrupt */
    rt_interrupt_leave();
}

获取系统节拍

获取系统节拍数函数

cpp 复制代码
/**
 * This function will return current tick fromoperating system startup
 *
 * @return current tick
 */
rt_tick_t rt_tick_get(void)

实例

通过获取系统节拍数来验证时钟节拍1ms一次

cpp 复制代码
... ...
int main(void)
{

    int i=0;
    rt_tick_t tick=0;
    for(i=0;i<10;i++)
    {
        tick = rt_tick_get();
        rt_kprintf("tick:%u\n",tick);
        rt_thread_mdelay(500);
    }
    return RT_EOK;
}

运行结果:

通过结果可以验证时钟节拍确实为1ms一次

如果我们将频率改为10000Hz,即100ms一个节拍

修改会导致程序出现警告:division by zero 除数是0

但并不影响运行结果

定时器

定时器,是指从指定的时刻开始,经过一定的指定时间后触发一个事件,定时器有硬件定时器和软件定时器之分:

硬件定时器: 芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。如果用硬件定时器,触发中断以后进行的处理中断函数属于中断上下文。硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式。

软件定时器: 由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务。软件定时器触发的中断回调函数属于线程上下文。

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

RT_Thread定时器介绍

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

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

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

HARD_TIMER模式:中断上下文

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

SOFT_TIMER模式:线程上下文

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

定时器源码分析(了解即可)

(1) RT-Thread OS启动阶段,执行rtthread_startup函数,在该函数中调用了定时器初始化函数

cpp 复制代码
/* timer system initialization */
rt_system_timer_init();

/* timer thread initialization */
rt_system_timer_thread_init();

rt_system_timer_init (硬件定时器初始化)

cpp 复制代码
/**
 * @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);
    }
}

转到rt_list_init()函数定义处,可以发现在RTT中,内核是通过双向列表的方式来管理定时器的

再转到链表定义处,我们可知rt_system_timer_init初始化的是硬件定时器的列表

rt_system_timer_thread_init(软件定时器初始化)

cpp 复制代码
/**
 * @ingroup SystemInit
 *
 * This function will initialize system timer thread
 */
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
}

从开始的rt_list_init(rt_soft_timer_list + i);中我们可知软件定时器初始化还是先初始化了一个定时器列表,不过传参传的是软件定时器的列表

转到定义处

然后初静态创建定时器并启动

启动完成后会执行线程处理函数rt_thread_timer_entry();

在while(1)中,做了一个超时检测,如果超时则表示软件定时器不存在,则将软件定时器线程挂起,让CPU调度其它线程。否则执行定时器的正常功能。

cpp 复制代码
/* system timer thread entry */
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);
            }
        }

        /* check software timer */
        rt_soft_timer_check();
    }
}

总结

内核在管理定时器的时候,将定时器分为了两类,一类是硬件定时器,一类是软件定时器,分别挂在不同的列表上进行管理。

定时器工作机制

下面以一个例子来说明 RT-Thread 定时器的工作机制。在 RT-Thread 定时器模块中维护着两个重要的全局变量:

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

如下图所示,系统当前tick值为20,在当前系统中已经创建并启动了三个定时器,分别是定时时间为50个tick的Timer1、100个tick的Timer2和500个tick的Timer3,这三个定时器分别加上系统

当前时间 rt_tick=20,从小到大排序链接在 rt_timer_list 链表中,形成如图所示的定时器链表结构。

而 rt_tick 随着硬件定时器的触发一直在增长(每一次硬件定时器中断来临,rt_tick 变量会加 1) ,50个tick以后,rt_tick从20增长到70,与Timer1的timeout值相等,这时会触发与Timer1定时器相关联的超时函数,同时将Timer1从rt_timer_list链表上删除。同理,100个tick和500个tick过去后,与Timer2 和 Timer3 定时器相关联的超时函数会被触发,接着将 Time2 和 Timer3 定时器从 rt_timer_list链表中删除。

如果系统当前定时器状态在 10 个 tick 以后(rt_tick=30)有一个任务新创建了一个 tick 值为 300 的Timer4定时器,由于Timer4定时器的timeout=rt_tick+300=330,因此它将被插入到Timer2和Timer3定时器中间,形成如下图所示链表结构:

定时器相关接口

启动和停止定时器

cpp 复制代码
/**
 * 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)

若想使它停止,可以使用下面的函数接口:

cpp 复制代码
/**
 * 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)

动态创建定时器

动态创建一个定时器和删除定时器

创建定时器

其中参数2:指向定时超时的回调函数(定时器中断函数),来处理当前的超时事件

参数3:为传递给超时函数的参数

参数4:为定时器时间,单位为节拍数

cpp 复制代码
/**
 * This function will create a timer
 *
 * @param name the name of timer
 * @param timeout the timeout function
 * @param parameter the parameter of timeoutfunction
 * @param time the tick of timer
 * @param flag the flag of timer
 * #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 callbackfunction will be called in tick isr. */
* #define RT_TIMER_FLAG_SOFT_TIMER       0x4           /**< soft timer,the timer's callback function will be called in timerthread. */
* @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)

其中flag可以传入以下标志

RT_TIMER_FLAG_ONE_SHOT表示单次触发

RT_TIMER_FLAG_PERIODIC表示周期性的触发

返回值为一个结构体指针

结构体描述当前定时器的信息

删除定时器

传入参数为定时器的结构体指针,返回值为错误码:正确为RT_EOK,错误为负的RT_ERROR

cpp 复制代码
/**
 * This function will delete a timer andrelease 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)
实例

首先对动态创建定时器函数进行参数配置,其中标志使用了周期性触发和使用软件定时器。然后定义中断函数,创建一个定时器结构体指针来接收返回值,如果创建失败就返回-没有内存

运行效果

其中timer里面显示了四个定时器,包括了自己创建的tm_demo,处于deactivated未活动的状态,其它三个定时器为系统创建的tshell、tidle0和timer

完善超时处理函数的内容,3s打印一次数据;启动定时器

运行效果

可以发现定时器状态已经开启

静态创建定时器

初始化定时器
cpp 复制代码
/**
 * This function will initialize a timer,normally this function is used to
 * initialize a static timer object.
 *
 * @param timer the static timer object  (typedef struct rt_timer *rt_timer_t;)
 * @param name the name of timer
 * @param timeout the timeout function
 * @param parameter the parameter of timeoutfunction
 * @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)
脱离定时器

静态定时器不需要再使用时,可以使用下面的函数接口脱离定时器:

cpp 复制代码
/**
 * This function will detach a timer from timermanagement.
 *
 * @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_ttimer)
实例

运行结果

当前时间节拍数

控制定时器
cpp 复制代码
/**
 * This function will get or set some optionsof the timer
 *
 * @param timer the timer to be get or set
 * @param cmd the control command
 * @param arg the argument
 * #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 */
* #defineRT_TIMER_CTRL_SET_PERIODIC        0x3            /**< change timer to periodic*/
* @return RT_EOK
*/
rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)
实例

以重新设置定时器时间为例,15s中后修改定时节拍数为1000

高精度延时

注意:这个函数只支持低于1个OS Tick(系统节拍)的延时, 否则SysTick会出现溢出而不能够获得指定的延时时间

一般用于IIC、SPI等总线通信

cpp 复制代码
/**
 * This function will delay for some us.
 *
 * @param us the delay time of us
 */
void rt_hw_us_delay(rt_uint32_t us)
相关推荐
一路往蓝-Anbo21 小时前
第46期:低功耗设计:软硬件协奏曲
驱动开发·stm32·单片机·嵌入式硬件
不能跑的代码不是好代码1 天前
STM32 标准外设库中关于 GPIO(通用输入输出) 模块的函数声明
stm32·单片机·嵌入式硬件
Moonquakes5401 天前
嵌入式开发基础学习笔记(LED实验C语言实现、蜂鸣器实验、SDK裸机驱动、链接脚本、BSP工程管理)
stm32·单片机·嵌入式硬件
我是海飞1 天前
杰理 AC792N 使用 WebSocket 连接百度语音大模型,实现 AI 对话
c语言·单片机·嵌入式·ai对话·杰理·websockey
Zeku1 天前
Linux内核中SPI 子系统的整体架构
stm32·freertos·linux驱动开发·linux应用开发
不凉帅1 天前
NO.2计算机基础
网络·嵌入式·硬件·软件·计算机基础
炸膛坦客1 天前
FreeRTOS 学习:(二十五)任务时间统计相关 API 函数
stm32·操作系统·freertos
时光の尘1 天前
【STM32】两万字详解SD卡移植最新版本FatFs文件系统(ff16)
stm32·mcu·dma·sd·fatfs·sdio·ff16
bai5459362 天前
STM32 CubeIDE 使用串口中断模式
stm32·单片机·嵌入式硬件
fanged2 天前
STM32(4)--时钟树
stm32·单片机·嵌入式硬件