RTT(RT-Thread)时钟管理

news2024/11/15 12:49:08

目录

时钟管理

时钟节拍

RTT工程目录结构介绍 

配置文件:rtconfig.h

获取系统节拍

获取系统节拍数函数

实例

定时器

RT_Thread定时器介绍

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

 rt_system_timer_init (硬件定时器初始化)

rt_system_timer_thread_init(软件定时器初始化)

总结

定时器工作机制

定时器相关接口

启动和停止定时器

动态创建定时器

创建定时器

删除定时器

实例

静态创建定时器

初始化定时器

脱离定时器

实例

控制定时器

实例

高精度延时


时钟管理

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

时钟节拍

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

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

/* 
 *频率是1000HZ 周期是1/1000 s
 *所以节拍是1ms
 */
#define RT_TICK_PER_SECOND 1000

RTT工程目录结构介绍 

配置文件:rtconfig.h

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

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

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

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

void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

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

    /* leave interrupt */
    rt_interrupt_leave();
}

获取系统节拍

获取系统节拍数函数

/**
 * This function will return current tick fromoperating system startup
 *
 * @return current tick
 */
rt_tick_t rt_tick_get(void)

实例

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

... ...
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函数,在该函数中调用了定时器初始化函数

/* timer system initialization */
rt_system_timer_init();

/* timer thread initialization */
rt_system_timer_thread_init();

 

 

 rt_system_timer_init (硬件定时器初始化)

/**
 * @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(软件定时器初始化)

/**
 * @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调度其它线程。否则执行定时器的正常功能。

/* 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定时器中间,形成如下图所示链表结构:

定时器相关接口

启动和停止定时器

/**
 * 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)

动态创建定时器

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

创建定时器

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

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

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

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

/**
 * 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打印一次数据;启动定时器

运行效果

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

静态创建定时器

初始化定时器
/**
 * 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)
脱离定时器

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

/**
 * 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)
实例

运行结果

当前时间节拍数

控制定时器
/**
 * 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等总线通信

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/839554.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python文件与目录操作

目录 文件编码 文件的读取 打开文件 mode常用的三种基础访问模式 读取文件 关闭文件 with open语法 文件的写入操作 文件综合案例 a.txt内容 代码实现 b.txt文件 目录操作 前言 os模块 具体方法 os.path模块 具体方法 文件编码 前言&#xff1a;由于计算机…

【在英伟达nvidia的jetson-orin-nx上使用调试can基础收发-硬件连接-开机自启动can-初步调试】

【在英伟达nvidia的jetson-orin-nx上使用调试can基础收发-硬件连接-开机自启动can-初步调试】 1、概述2、实验环境3、自我学习4-1、实验过程1、硬件原理图焊接连接模块2、输入命令3、测试过程 4-2、目前遗留问题# 1-1、发送可以发送&#xff0c;但是PC发送数据收不到。# 1-2、接…

任务 13、MidJourney种子激发极致创作,绘制震撼连贯画作

13.1 任务概述 通过本次实验任务&#xff0c;学员将深入了解Midjourney种子的概念和重要性&#xff0c;以及种子对生成图像的影响。他们将学会在Midjourney平台中设置种子值并调整其参数&#xff0c;以达到所需的效果。此外&#xff0c;任务还详细介绍了Midjourney V4.0版本中…

周末作业 c高级

1.判断家目录下&#xff0c;普通文件的个数和目录文件的个数&#xff1a; #!/bin/bash arr1(ls -la ~/|cut -d r -f 1|grep -wi d) arr2(ls -la ~/|cut -d r -f 1|grep -wi -)echo "普通文件个数为&#xff1a;${#arr2[*]}" echo "目录文件个数为&#xff1a;$…

【TypeScript】TS接口interface类型(三)

【TypeScript】TS接口interface类型&#xff08;三&#xff09; 【TypeScript】TS接口interface类型&#xff08;三&#xff09;一、接口类型二、实践使用2.1 常规类型2.2 设置属性只读 readonly2.3 设置索引签名2.4 设置可选属性2.5 函数类型接口 一、接口类型 TypeScript中的…

没有配置redis但是报错连接redis失败

问题 没有配置redis但是报错连接redis失败 检查maven配置是否引入了redis依赖&#xff08;可能是传递依赖&#xff0c;最好检查引进来的公共工程 解决办法 只需要在该工程application.yml文件中配置一下 redis就好&#xff0c;或者移除redis依赖

高效处理矢量大数据的高可用解决方案

高效处理矢量大数据的高可用解决方案 解决方案目标 存储海量矢量数据实时分析海量矢量数据实现海量矢量数据的可视化提供高可用、高性能和高可拓展性解决方案概述 海量数据查询与可视化 系统技术流程 方案一 数据存储: PostgreSQL+PostGIS(矢量数据存储和空间分析)数据服务…

企业场景--技术场景

目录 单点登录如何实现 使用jwt解决单点登录 总结 ​编辑 权限认证如何实现 五张表实现权限认证 框架 具体案例 上传数据的安全性如何控制 对称加密 非对称加密 你的项目遇到了哪些棘手的问题&#xff1f; 项目中日志怎么采集的 ELK进行日志采集 logstash kibana操…

P25LED透明屏:在商业广告中,透明显示效果怎么样?

P25LED透明屏是一种新型的显示屏技术&#xff0c;它具有高透明度和高亮度的特点&#xff0c;可以实现透明显示效果。 P25LED透明屏广泛应用于商业广告、展览展示、户外广告等领域&#xff0c;具有很大的市场潜力。 P25LED透明屏采用了先进的LED显示技术&#xff0c;具有高亮度…

浅析RabbitMQ死信队列

原文首发于公众号【CSJerry】 在现代分布式系统中&#xff0c;消息队列扮演着至关重要的角色。它们可以实现应用程序之间的异步通信&#xff0c;并确保数据的可靠传输和处理。而在这个领域中&#xff0c;RabbitMQ作为一种强大而受欢迎的消息队列解决方案&#xff0c;具备了高…

c语言——求n之内的素数和

//求n之内的素数和 //列如&#xff1a;2、3、5等 #include<stdio.h> #include<math.h> int main() {int i,j,k,n0;scanf("%d",&n);for(i2;i<n;i){k(int)sqrt(i);for(j2;j<k;j)if(i%j0)break;if(j>k){printf("%d,",i);n;if(n%50)p…

23.8.5总结(Web博客项目)

用户搜索&#xff0c;标签搜索。 主页面加上了标签和用户推荐 管理员页面还需要修改 还有这些功能点没有实现&#xff1a; 右键删除评论 点赞次数已达上限 删除博客 消息页面&#xff0c;收消息&#xff08;点赞和收藏要给被点赞这个博主发消息&#xff09; 管理员页面&#…

day52-Redis

Redis 1.Redis 1.1 RESP连接Redis 1.2 定义&#xff1a;是一个高性能的key-value数据库&#xff08;非关系型数据库&#xff09; 1.3 数据类型&#xff1a; key键的类型是字符串类型&#xff1b; 值的类型有五种&#xff1a;字符串String&#xff0c;哈希hash&#xff0…

layui之layer弹出层的icon数字及效果展示

layer的icon样式 icon如果在信息提示弹出层值(type为0)可以传入0-6&#xff0c;icon与图标对应关系如下&#xff1a; 如果是加载层&#xff08;type为3&#xff09;可以传入0-2&#xff0c;icon与图标对应关系如下&#xff1a;

javaAPI(五):System、Math、BigInteger、BigDecimal

System类 System类代表系统&#xff0c;系统级的很多属性和控制方法都放置在该类的内部。该类位于java.lang包。 由于该类的构造器是private的&#xff0c;所以无法创建该类的对象&#xff0c;也就是无法实例化该类。其内部的成员&#xff0c;所以也可以很方便的进行调用。变量…

STM32入门学习之独立看门狗(IWDG)

1.STM32的独立看门狗是一个具有独立时钟的片上外设。通常&#xff0c;为了防止程序卡死&#xff0c;可以设置看门狗定时复位。当看看门狗被使能之后&#xff0c;会按初始化时设置的计数值进行计数。当根据计数值计数的倒数时间为0时&#xff0c;便会自动复位程序&#xff0c;即…

嵌入式开发学习(STC51-12-I2C/IIC)

内容 在数码管右3位显示数字&#xff0c;从0开始&#xff0c;按K1键将数据写入到EEPROM内保存&#xff0c;按K2键读取EEPROM内保存的数据&#xff0c;按K3键显示数据加1&#xff0c;按K4键显示数据清零&#xff0c;最大能写入的数据是255&#xff1b; I2C介绍 I2C简介 I2C&…

CNN成长路:从AlexNet到EfficientNet(01)

一、说明 在 10年的深度学习中&#xff0c;进步是多么迅速&#xff01;早在 2012 年&#xff0c;Alexnet 在 ImageNet 上的准确率就达到了 63.3% 的 Top-1。现在&#xff0c;我们超过90%的EfficientNet架构和师生训练&#xff08;teacher-student&#xff09;。 如果我们在 Ima…

echarts 饼图的label放置于labelLine引导线上方

一般的饼图基础配置后长这样。 想要实现将文本放置在引导线上方&#xff0c;效果长这样 const options {// ...series: [{label: {padding: [0, -40],},labelLine: {length: 10,length2: 50,},labelLayout: {verticalAlign: "bottom",dy: -10,},},], };label.padd…

2配置篇:基础功能配置

前言 在上一章节中,我们学习了 NestJS CLI 的用法,得到了一套基础的项目工程。最开始做项目对比的时候也提到过,NestJS 作为一款自定义程度较高的框架,CLI 直接提供的基础功能虽然并不完善,但同时也为开发者提供了非常多的内置或配套的功能例如高速缓存、日志拦截、过滤器…