app网站公司名称,做企业推广的公司,更新服务 wordpress,襄阳市建设局网站1时钟节拍
任何操作系统都需要提供一个时钟节拍#xff0c;以供系统处理所有和时间有关的事件#xff0c;如延时、线程的时间片轮转调度以及定时器超时等。时钟节拍#xff08;OS Tick#xff09;是操作系统中最小的时间单位。
时钟节拍是特定的周期性中断#xff0c;这… 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 个节拍的 Timer12定时为 100 个节拍的 timer23定时为 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今天先到这加油~