RT-Thread快速入门-定时器管理

 

1时钟节拍

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

时钟节拍是特定的周期性中断,这个中断之间的时间间隔取决于具体的应用,一般是 1-100ms。时钟节拍率越快,系统的额外开销就越大。

RT-Thread 中,一个时钟节拍的时长根据 rtconfig.h 配置文件中, RT_TICK_PER_SECOND 的 定 义 来 调 整, 等 于 1/RT_TICK_PER_SECOND 秒 。

时钟节拍的实现

时钟节拍由配置为中断触发模式的硬件定时器产 生,在中断服务程序中调用如下函数,通知操作系统已经过去一个系统时钟:

void rt_tick_increase(void)
{struct rt_thread *thread;/* 全局 rt_tick 递增 */
#ifdef RT_USING_SMPrt_cpu_self()->tick ++;
#else++ rt_tick;
#endif/* 检查时间片 */thread = rt_thread_self();-- thread->remaining_tick;if (thread->remaining_tick == 0){/* 重新赋初值 */thread->remaining_tick = thread->init_tick;/* 线程挂起 */thread->stat |= RT_THREAD_STAT_YIELD;/* yield */rt_thread_yield();}/* 检查定时器 */rt_timer_check();
}

从源代码中可以看出,每经过一个时钟节拍,全局变量 rt_tick 的值就会加 1。然后检查当前线程的时间片是否用完,以及是否有定时器超时。如果当前线程的时间片用完,则进行同优先级线程之间的切换。

不同的硬件定时器中断实现都不同,以 STM32 定时器中断为例:

void SysTick_Handler(void)
{/* 进 入 中 断 */rt_interrupt_enter();……rt_tick_increase();/* 退 出 中 断 */rt_interrupt_leave();
}

在中断函数中,调用 rt_tick_increase() 对全局变量 rt_tcik 加 1。

rt_tick 的值表示了系统从启动到现在共经过的时钟节拍个数。

2定时器工作机制

RT-Thread 提供的定时器基于系统的节拍,提供了基于节拍整数倍的定时能力,即定时器定时以时钟节拍为单位。如此,定时器定时长短是 OS Tick 时长的整数倍。

如果一个时钟节拍是 10ms,那么系统软件定时器时长只能是 10ms、20ms、100等,而不能是 15ms。

定时器介绍

RT-Thread 提供了两种类型的定时器:

  • 单次触发定时器。这类定时器触发一次定时器事件后,会自动停止。

  • 周期触发定时器。这类定时器会周期性地触发定时器事件,直到用户手动停止。

另外,根据超时函数执行时所处的上下文环境,RT-Thread 的定时器有两种工作模式:

  • HARD_TIMER 模式,超时函数在中断上下文环境中执行。

  • SOFT_TIMER  模式,在系统创建的定时器线程上下文环境中执行。

HARD_TIMER 模式的定时器

这种模式是 RT-Thread 定时器默认的工作方式,定时器超时后,超时函数在系统时钟中断的上下文环境中执行。

这种情况下,对于超时函数的要求与中断服务例程的要求相同:执行时间应该尽量短、执行时不应该导致当前线程挂起等。否则会导致其他中断的响应时间加长,或抢占了其他线程执行的时间。

SOFT_TIMER 模式的定时器

这种工作模式,需要通过宏定义 RT_USING_TIMER_SOFT 来决定是否启用。启用这个模式后,RT-Thread 会在初始化时创建一个 timer 线程,SOFT_TIMER 模式的定时器超时函数都会在 timer 线中执行。

定时器如何工作

RT-Thread 维护着两个重要的全局变量:

  • rt_tick , 当前系统经过的时钟节拍个数。

  • rt_timer_list , 定时器链表。创建并激活的定时器都会按照超时时间从小到大进行排序,插入到这个链表中。

如下图所示,系统当前的 rt_tick 值为 20,且已经创建并启动了三个定时器:(1)定时为 50 个节拍的 Timer1(2)定时为 100 个节拍的 timer2(3)定时为 500 个节拍的 timer3。

这三个定时器分别加上系统当前时间 rt_tick, 从小到大排序链接在 rt_timer_list 中:

图片

 

rt_tick 随着硬件定时器的触发一直在增长,50 个节拍后,rt_tick 从 20 增长到 70,与 Timer1 的 timerout 值相同,这时会触发 Timer1 定时器关联的超时函数,同时将其从 rt_timer_list 链表上删除。

同理,100 个节拍和 500 个节拍过去后,Timer2 和 Timer3 定时器的超时函数会被触发执行,将定时器 Timer2 和 Timer3 从 rt_timer_list 中删除。

定时器控制块

定时器控制块是 RT-Thread 用于管理定时器的一个数据结构,由结构体 struct rt_timer 定义形成定时器内核对象,再链接到内核容器中进行管理。

定时器控制块会存储定时器的一些信息,例如初始时钟节拍数、超时到达的节拍数、定时器之间连接用的链表结构、超时回调函数等。具体定义如下:

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;

3定时器管理

前面介绍了定时器相关的理论知识,那么 RT-Thread 提供了怎样的定时器操作函数,以及如何使用它们呢?

RT-Thread 提供的定时器相关的操作包括:

  • 创建/初始化定时器

  • 启动定时器

  • 控制定时器

  • 删除/脱离定时器

所有定时器会在定时超时后从定时器链表中被删除,而周期性定时器会在它再次启动时被加入定时器链表中。

图片

 

1. 创建定时器

创建一个定时器有两种方式:动态创建和静态初始化。

动态创建一个定时器,使用如下函数接口:

rt_timer_t rt_timer_create(const char *name,void (*timeout)(void *parameter),void       *parameter,rt_tick_t   time,rt_uint8_t  flag)

调用此函数后,内核自动从内存堆中分配一个定时器控制块,然后初始化该定时器控制块。各个参数说明如下:

参数描述
name定时器名称
timeout定时器超时函数指针
parameter定时器超时函数的入口参数
time定时器超时时间,单位是时钟节拍
flag创建定时器的参数,其值包括单次定时、周期定时、硬件定时器、软件定时器等

创建失败,返回 RT_NULL。创建成功,则返回定时器控制块指针。

定时器标志用到的宏定义:

#define RT_TIMER_FLAG_ONE_SHOT 0x0    /* 单 次 定 时 */
#define RT_TIMER_FLAG_PERIODIC 0x2    /* 周 期 定 时 */#define RT_TIMER_FLAG_HARD_TIMER 0x0  /* 硬 件 定 时 器 */
#define RT_TIMER_FLAG_SOFT_TIMER 0x4  /* 软 件 定 时 器 */

上面两组可以以 "或"逻辑方式赋值给 flag

静态创建一个定时器,需要用户定义一个定时器控制块结构体 struct rt_timer 变量,然后 rt_timer_init() 函数对其初始化。该函数原型如下:

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);

该函数比 rt_timer_create() 多了一个参数 timer,其他参数都相同,不再赘述。参数 timer 实际上是定时器控制块指针。

2. 启动定时器

定时器创建之后,不会被立即启动,需要在调用启动定时器函数接口后,才开始工作。

RT-Thread 提供的启动定时器函数如下:

rt_err_t rt_timer_start(rt_timer_t timer);

函数的参数 timer 为定时器控制块指针(定时器句柄),指向要启动的定时器控制块。

调用启动函数后,定时器的状态更改为激活状态,并按照超时时间顺序插入到 rt_timer_list 队列链表中。

启动定时器后,如果想停止它,可以用下面的函数:

rt_err_t rt_timer_stop(rt_timer_t timer);

调用该函数后,定时器状态更改为停止,并从 rt_timer_list 链表中脱离出来,不参与定时器超时检查。

函数返回 RT_EOK,表示成功停止定时器。返回 -RT_ERROR,说明定时器已经处于停止状态了。

4定时器应用演示

理论+实践是学习新知识最有效的方法。

举例来演示如何创建定时器。这个例程动态创建两个定时器,一个单次定时器,一个周期定时器,并让定时器运行一段时间后停止。代码如下:

#include <rtthread.h>/* 定时器的控制块 */
static rt_timer_t timer1;
static rt_timer_t timer2;
static int cnt = 0;/* 定时器1超时函数 */
static void timeout1(void *parameter)
{rt_kprintf("periodic timer is timeout %d\n", cnt);/* 运行第 10 次,停止周期定时器 */if (cnt++>= 9){rt_timer_stop(timer1);rt_kprintf("periodic timer was stopped! \n");}
}
/* 定时器 2 超时函数 */
static void timeout2(void *parameter)
{rt_kprintf("one shot timer is timeout\n");
}int main()
{/* 创建定时器1周期定时器 */timer1 = rt_timer_create("timer1", timeout1,RT_NULL, 10,RT_TIMER_FLAG_PERIODIC);/* 启动定时器1 */if (timer1 != RT_NULL) {rt_timer_start(timer1);}/* 创建定时器2单次定时器 */timer2 = rt_timer_create("timer2", timeout2,RT_NULL, 30,RT_TIMER_FLAG_ONE_SHOT);/* 启动定时器2 */if (timer2 != RT_NULL) {rt_timer_start(timer2);}return 0;
}

编译运行结果如下:

图片

 

周期性定时器 1 的超时函数,每 10 节拍运行 1 次,共运行 10 次,之后停止(调用 rt_timer_stop())。

单次定时器 2 的超时函数在 30 个时钟节拍后运行一次。

下面举例说明静态创建定时器,需要定义定时器控制块结构变量,然后调用初始化函数对其初始化:

#include <rtthread.h>/* 定时器的控制块 */
static struct rt_timer timer1;
static struct rt_timer timer2;
static int cnt = 0;/* 定时器1超时函数 */
static void timeout1(void* parameter)
{rt_kprintf("periodic timer is timeout\n");/* 运行10次 */if (cnt++>= 9){rt_timer_stop(&timer1);}
}
/* 定 时 器 2 超 时 函 数 */
static void timeout2(void* parameter)
{rt_kprintf("one shot timer is timeout\n");
}int main(void)
{/* 初始化定时器1 */rt_timer_init(&timer1, "timer1", /* 定 时 器 名 字 是 timer1 */timeout1, RT_NULL, 10, RT_TIMER_FLAG_PERIODIC); /* 周期定时器 *//* 初始化定时器2 */rt_timer_init(&timer2, "timer2", /* 定 时 器 名 字 是 timer2 */timeout2, RT_NULL, 30,RT_TIMER_FLAG_ONE_SHOT); /* 单次定时器 *//* 启动定时器 */rt_timer_start(&timer1);rt_timer_start(&timer2);return 0;
}

其执行结果与动态创建示例相同。

5其他定时器管理函数

初学者掌握定时器创建使用即可,RT-Thread 还提供了其他的定时器管理函数,可以了解学习。

1. 删除定时器

动态创建的定时器,可以用下面的函数删除:

rt_err_t rt_timer_delete(rt_timer_t timer);

调用这个函数接口后,系统会把这个定时器从 rt_timer_list 链表中删除,然后释放相应的定时器控制块占有的内存。

静态创建的定时器,可以用下边的函数脱离定时器:

rt_err_t rt_timer_detach(rt_timer_t timer);  

脱离定时器时,系统会把定时器对象从内核对象容器中脱离,但是定时器对象所占有的内存不会被释放。

2. 控制定时器

RT-Thread 也额外提供了定时器控制函数接口,以获取或设置更多定时器的信息。控制定时器函数接口如下:

rt_err_t rt_timer_control(rt_timer_t timer, rt_uint8_t cmd, void* arg);

控制定时器函数接口可根据命令类型参数,来查看或改变定时器的设置。

参数 cmd 为用于控制定时器的命令,当前支持四个命令:设置定时时间、查看定时时间、设置单次触发、设置周期触发。

#define RT_TIMER_CTRL_SET_TIME      0x0  /* 设置定时器超时时间 */
#define RT_TIMER_CTRL_GET_TIME      0x1  /* 获得定时器超时时间 */
#define RT_TIMER_CTRL_SET_ONESHOT   0x2  /* 设置定时器为单次定时器 */
#define RT_TIMER_CTRL_SET_PERIODIC  0x3  /* 设置定时器为周期型定时器 */

arg 为控制命令的参数。

OK,今天先到这,加油~

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

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

相关文章

vue预览和下载txt、PDF、execl等在线文件

因为浏览器默认能直接打开TXT、PDF等文件索引默认就是点击链接打开文件。但是浏览器却又不能在线打开execl、world等文件。 现在我们可以统一的实现文件的预览以及下载。 下载文件 downloadfile方法 downloadfile(url,fileName){const newUrl url;const x new XMLHttpRequ…

【vue3】vue3的一般项目结构、成功显示自己的vue3页面

一、vue3的一般项目结构 Vue 3并没有规定特定的项目结构&#xff0c;因此您可以根据项目的需求和个人偏好来组织您的Vue 3项目。以下是一个常见的Vue 3项目结构示例&#xff0c;供参考&#xff1a; your-project/|- public/| |- index.html # 应用程序的入口HTML文件…

智慧井盖监测管理系统解决方案

一、方案概述 近年来&#xff0c;随着城市化的不断发展&#xff0c;城市地下管道设施的一步步完善&#xff0c;井盖作为城市基础设施中必不可少的一部分&#xff0c;其重要性也逐渐凸显。然而&#xff0c;在实际应用中&#xff0c;井盖监测和管理并不容易。如井盖地理位置分散&…

第一百一十四天学习记录:C++提高:类模板案例(黑马教学视频)

类模板案例 main.cpp代码&#xff1a; #include "myarray.hpp"void printIntArray(MyArray <int>& arr) {for (int i 0; i < arr.getSize(); i){cout << arr[i] << " ";}cout << endl; }void test01() {MyArray <int&…

史上最全,接口测试-Fiddler抓包常用功能总结(超详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Fiddler中常用的功…

macOS Monterey 12.6.8 (21G725) 正式版发布,ISO、IPSW、PKG 下载

macOS Monterey 12.6.8 (21G725) 正式版发布&#xff0c;ISO、IPSW、PKG 下载 本站下载的 macOS 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U 盘安装&#xff0c;或者在虚拟机中启动安装。另外也支持…

C++运算符:优先级

#include <iostream> using namespace std;//#define INT int //宏命令 // typedef int BOO; //移动 // INT a10; // BOO b 12;void fun(string& str) {int pos str.find(a);cout << "位置" << pos << endl;str.replace(pos,…

Windows Active Directory密码同步

大多数 IT 环境中&#xff0c;员工需要记住其默认 Windows Active Directory &#xff08;AD&#xff09; 帐户以外的帐户的单独凭据&#xff0c;最重要的是&#xff0c;每个密码还受不同的密码策略和到期日期的约束&#xff0c;为不同的帐户使用单独的密码会增加用户忘记密码和…

Spring MVC -- 获取参数(普通对象+JSON对象+URL地址参数+文件+Cookie/Session/Header)

目录 1.获取参数 1.1获取单个参数 1.2获取多个参数 传参注意事项&#xff1a; 2.获取对象 3.后端参数重命名RequestParam 4.获取JSON对象RequestBody 5.从 URL 地址中获取参数 PathVariable 6.上传文件 RequestPart 7.获取Cookie/Session/Header 7.1 获取 Request 和…

【MySQL】MySQL索引、事务、用户管理

20岁的男生穷困潦倒&#xff0c;20岁的女生风华正茂&#xff0c;没有人会一直风华正茂&#xff0c;也没有人会一直穷困潦倒… 文章目录 一、MySQL索引特性&#xff08;重点&#xff09;1.磁盘、OS、MySQL&#xff0c;在进行数据IO时三者的关系2.索引的理解3.聚簇索引&#xff0…

Two Days wpf 分享 分页组件

迟来的wpf分享。 目录 一、序言 二、前期准备 三、前端界面 四、后台代码部分 1、先定义些变量后面使用 2、先是按钮事件代码。 首页按钮 上一页按钮 下一页按钮 末尾按钮 画每页显示等数据 每页显示多少条 判断是否为数字的事件 分页数字的点击触发事件 跳转到…

HCIP——OSPF基础

OSPF基础 一、OSPF基础二、OSPF的区域划分三、OSPF的数据包hello包数据库描述包DBD包链路状态请求包LSR包链路状态更新包LSU包链路状态确认包LSAck包 四、OSPF的状态机五、OSPF的工作过程六、链路状态型的路由生成过程七、条件匹配五、OSPF数据包头部八、OSPF的接口网络类型 一…

链动2+1系统开发模式有没有风险?

现在的分销模式层出不穷&#xff0c;很多人都不知道分销与传销的区别&#xff0c;甚至认为最近很火的一个分销模式链动21也是个传销盘。那么&#xff0c;链动21模式究竟涉不涉传&#xff1f;有没有风险呢&#xff1f;下面就来具体说一下。 首先分销不是传销&#xff0c;分销是商…

(css)列表点击前后样式

(css)列表点击前后样式 效果&#xff1a; html <ul v-show"rightOne" class"one-content"><liv-for"(item,index) in exampleList":key"index"click"searchHandle(item,index)"class"liClass":class&qu…

MySQL函数讲解(谓词、CASE)

目录 MySQL常见函数 字符串函数 进行字符串操作的函数 算术函数 进行数值计算的函数 日期函数 进行日期操作的函数 转换函数 进行数据类型和值转换的函数 流程函数 进行条件删选 聚合函数 进行数据聚合的函数 谓词 一般用于条件判断 什么是函数 函数指一段可以直接被…

【C++】-多态的语法细节详解

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

GPT-3.5:ChatGPT的奇妙之处和革命性进步

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

苹果开发“Apple GPT”AI科技迎来新格局

根据彭博社的马克・古尔曼&#xff08;Mark Gurman&#xff09;报道&#xff0c;苹果内部正在开发“Apple GPT”人工智能项目&#xff0c;足以媲美 OpenAI 的 ChatGPT &#xff0c;预计明年推出。就在彭博社消息发出之后&#xff0c;苹果股价上涨了2.3%&#xff0c;市值顶峰时增…

题目4 命令执行(保姆级教程)

url&#xff1a;http://192.168.154.253:84/ #打开http://XXX:81/&#xff0c;XXX为靶机的ip地址 审题 1、打开题目看到有一个提示&#xff0c;此题目需要通过利用命令执行漏洞执行Linux命令获取webshell&#xff0c;最后从根目录下key.php文件中获得flag 2、开始答题 第一步&…

css设置八等分圆

现需要上图样式的布局&#xff0c;我通过两张向右方的图片&#xff0c;通过定位和旋转完成了布局。 问题&#xff1a; 由于是通过旋转获取到的样式&#xff0c;实际的盒子是一个长方形&#xff0c;当鼠标移入对应的箭头时选中的可能是其他盒子&#xff0c;如第一张设计稿可以看…