目录
1.什么是软件定时器
软件定时器是用程序模拟出来的定时器,可以由一个硬件定时器模拟出成千上万个软件定时器,这样程序在需要使用较多定时器的时候就不会受限于硬件资源的不足,这是软件定时器的一个优点,即数量不受限制。但由于软件定时器是通过程序实现的,其运行和维护都需要耗费一定的CPU资源,同时精度也相对硬件定时器要差一些。
2.软件定时器的实现原理
在Linux,uC/OS,FreeRTOS等操作系统中,都带有软件定时器,原理大同小异。典型的实现方法是:通过一个硬件定时器产生固定的时钟节拍,每次硬件定时器中断到,就对一个全局的时间标记加一,每个软件定时器都保存着到期时间,程序需要定期扫描所有运行中的软件定时器,将各个到期时间与全局时钟标记做比较,以判断对应软件定时器是否到期,到期则执行相应的回调函数,并关闭该定时器。
以上是单次定时器的实现,若要实现周期定时器,即到期后接着重新定时,只需要在执行完回调函数后,获取当前时间标记的值,加上延时时间作为下一次到期时间,继续运行软件定时器即可。
3.基于STM32的软件定时器
3.1 时钟节拍
软件定时器需要一个硬件时钟源作为基准,这个时钟源有一个固定的节拍(可以理解为秒针的每次滴答),用一个32位的全局变量tickCnt来记录这个节拍的变化:
static volatile uint32_t tickCnt = 0; //软件定时器时钟节拍
每来一个节拍就对tickCnt加一(记录滴答了多少下):
/* 需在定时器中断内执行 */
void tickCnt_Update(void)
{
tickCnt++;
}
一旦开始运行,tickCnt将不停地加一,而每个软件定时器都记录着一个到期时间,只要tickCnt大于该到期时间,就代表定时器到期了。
3.2 数据结构
软件定时器的数据结构决定了其执行的性能和功能,一般可分为两种:数组结构和链表结构。什么意思呢?这是(多个)软件定时器在内存中的存储方式,可以用数组来存,也可以用链表来存。
两者的优劣之分就是两种数据结构的特性之分:数组方式的定时器查找较快,但数量固定,无法动态变化,数组大了容易浪费内存,数组小了又可能不够用,适用于定时事件明确且固定的系统;链表方式的定时器数量可动态增减,易造成内存碎片(如果没有内存管理),查找的时间开销相对数组大,适用于通用性强的系统,Linux,uC/OS,FreeRTOS等操作系统用的都是链表式的软件定时器。
本文使用数组结构:
static softTimer timer[TIMER_NUM]; //软件定时器数组
数组和链表是软件定时器整体的数据结构,当具体到单个定时器时,就涉及软件定时器结构体的定义,软件定时器所具有的功能与其结构体定义密切相关,以下是本文中软件定时器的结构体定义:
typedef struct softTimer {
uint8_t state; //状态
uint8_t mode; //模式
uint32_t match; //到期时间
uint32_t period; //定时周期
callback *cb; //回调函数指针
void *argv; //参数指针
uint16_t argc; //参数个数
}softTimer;
定时器的状态共有三种,默认是停止,启动后为运行,到期后为超时。
typedef enum tmrState {
SOFT_TIMER_STOPPED = 0, //停止
SOFT_TIMER_RUNNING, //运行
SOFT_TIMER_TIMEOUT //超时
}tmrState;
模式有两种:到期后就停止的是单次模式,到期后重新定时的是周期模式。
typedef enum tmrMode {
MODE_ONE_SHOT = 0, //单次模式
MODE_PERIODIC, //周期模式
}tmrMode