在日常项目开发中,我们经常会遇到一个动作执行时,我们需要等多久再去执行下一个动作。如果使用定时器就要给他一个专门的变量放到定时器中断中不断地查询增减。假设这种动作延时只有一两个还好说,如果有很多很多,我们就需要在中断中放置很多变量。这样会造成执行效率的下降,而且中断看起来也乱糟糟的,如果我们直接使用阻塞延时,又会导致其他任务得不到及时处理。
为了综合考虑我们还是要利用定时器来进行计数,所以我们先定义一个变量AppTimer,这时候我们又想到一个问题。假如我们需要同时对多个动作计时,就需要多个计数器。所以我们把变量改成数组,再通过宏确定数组的长度。顺便使用volatile 对数组修饰一下,确保每次使用的都是最新的值。
#define TIMERNUM 6
volatile uint16_t AppTimer[TIMERNUM];
然后我们在定时器中断中写一个自减的函数,对数组进行定时自减以确保数值和时间对应起来。为了提高效率和防止溢出。我们仅在使用这个定时器时进行自检。
//========================================================================
// 函数: 写定时器
// 描述: 此函数要放到定时器中断处理函数中执行,建议定时器每1ms中断一次
// 参数:
// 参数:
// 返回:
//========================================================================
void WriteTimer(void)
{
uint8_t i;
for(i=0;i<TIMERNUM;i++)
{
if(AppTimer[i] > 1)
{
AppTimer[i] --;
}
}
}
这里只有AppTimer[i] > 1的时候才会自减,如果自减到1就不会再自减。定时也就完成了。为什么是>1而不是>0呢?因为我们要判断定时器是不是空闲的,假如>1说明正在定时中,等于1说明定时已经完成了。因此就有了下边得定时是否完成的读取函数。
//========================================================================
// 函数: 检查定时器是否完成
// 描述: 定时器计时是否完成的检查函数
// 参数: TIMx: 定时器几,可以通过TIMERNUM定义定时器数量
// 返回: 完成返回AppOK 否则返回Appdefault
//========================================================================
AppStaType ReadTimerStatus(uint8_t TIMx)
{
AppStaType status = Appdefault;
if(AppTimer[TIMx] == 1)
{
AppTimer[TIMx] = 0;
status = AppOK;
}
return status;
}
这里又引出一个变量类型AppStaType,这又是什么鬼?我们读完以后总得直到他到底是个什么状态吧,在我的项目里一般每个函数我都会加一个AppStaType判断函数的执行状态,具体定义如下。
/*函数执行状态反馈*/
typedef enum
{
AppOK=0, //正常执行
Appdefault, //无效的执行
AppParam, //参数异常
AppOverFlow, //指针溢出
AppReturn //返回值异常
}AppStaType;
/*返回值诊断*/
因此定时没完成之前返回的都是Appdefault,完成时返回的是AppOK。我们只需要判断返回值是不是AppOK就知道是不是已经定时完成了。还有一句AppTimer[TIMx] = 0;又是什么意思呢?这个是为了重复开启某一个定时器。我们必须在AppTimer[TIMx] == 0;时候才认为这个定时器是空闲的,可以开启的。于是有了下边的函数
//========================================================================
// 函数: 打开定时器
// 描述: 定时器每1ms中断一次
// 参数: TIMx: 定时器几,可可以通过TIMERNUM定义定时器数量
// 参数: Time:定时长度(1-65534),*1ms 比如1000其实是100*1ms = 1000ms = 1s
// 返回: 开启成功返回AppOK 重复开启返回Appdefault
//========================================================================
AppStaType OpenTimer(uint8_t TIMx,uint16_t Time)
{
AppStaType status=Appdefault;
if(AppTimer[TIMx] == 0) //定时器没有启动,启动定时器, 否则什么都不做
{
AppTimer[TIMx] = Time+1;
status=AppOK;
}
return status;
}
到这里我们就可以通过OpenTimer开启某个定时器,通过ReadTimerStatus确定定时是不是到了。如果时间没到我们不想用了怎么办?关掉就完事了
//========================================================================
// 函数: 关闭定时器
// 描述: 强制关闭定时器
// 参数: TIMx: 定时器几,可可以通过TIMERNUM定义定时器数量
// 参数:
// 返回:
//========================================================================
void StopTimer(uint8_t TIMx)
{
AppTimer[TIMx] = 0;
}
如果某个条件到了,我们想知道定时还有多久呢?
//========================================================================
// 函数: 读定时器当前值
// 描述: 倒计时的当前剩余值
// 参数: TIMx: 定时器几,可以通过TIMERNUM定义定时器数量
// 返回: 返回0空闲,返回1定时完成等待读状态,其他当前剩余值。
//========================================================================
uint16_t ReadTimerValue(uint8_t TIMx)
{
return AppTimer[TIMx];
}
至此,一个开启,查询式的软定时器就完事了。可以配合我们的裸机轮询框架使用,也可以配合状态机使用。