使用硬件定时器软模拟多个定时器(8.15改进版)
在嵌入式平台中,界面有许多的数据需要定时刷新,而硬件资源不可能提供大量的定时器。
在没有上系统的情况下,使用该方法模拟定时器是不错的方法。如果非裸奔大可不必如此麻烦。
该方法可单独使用也可以结合GUI中的消息循环配套使用,也可以给应用层定时获取底层数据。
基本思路:用结构体表示定时器,用结构体数组存放多个定时器,初始化模拟定时器时设定定时的最大时间。每个定时器固定ID。
定时器的管理人工实现,定时中断扫描整个数组来完成定时过程。
通过SetTimer和KillTimer来设置和取消定时器。
在定时器数量较少的情况下,扫描速度对定时影响可满足一般应用需求。
整体与windows定时处理机制类似。
与消息循环配合
这种实现方法效果非常好,定时器精度较高。定时器中断中完成以下过程:扫描数组,比较时间,发送消息,退出。
指向函数使用
定时精确度与指向的函数复杂度成反比。一般用于定时扫描键盘,或者扫描其他硬件返回值。定时器中断需要完成以下过程:扫描
数组,比较时间,所指向函数完整完成,退出。
以下是相关源代码,在IAR5.5编译通过,芯片STM32F207,LIB版本3.5。
本函数代码是与消息队列配合使用。
如需单独使用,请修改中断函数。
1.定时器的初始化。这个只要看官方例程。
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; void Tim2_Init() { NVIC_InitTypeDef NVIC_InitStructure; /* TIM2 clock enable */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); /* Enable the TIM2 gloabal Interrupt 全局中断配置,使能*/ NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /* Time base configuration */ /*APB1 60Mhz 在System_stm32f2xx.c中配置*/ /*每次进入中断服务程序间隔时间为((1+TIM_Prescaler )/60M)*(1+TIM_Period ) TIM_Prescaler from 0x0000 to 0xffff*/ TIM_TimeBaseStructure.TIM_Period = 99; TIM_TimeBaseStructure.TIM_Prescaler = 59999; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_ClearFlag(TIM2, TIM_FLAG_Update); /* Prescaler configuration */ TIM_PrescalerConfig(TIM2, 59999, TIM_PSCReloadMode_Update); //定时器时间间隔为100MS /* TIM Interrupts enable */ TIM_ITConfig(TIM2, TIM_IT_Update , ENABLE); //开启中断 /* TIM2 enable counter */ TIM_Cmd(TIM2, ENABLE); //使能 }
定时器中断函数,能放简单的处理语句,复杂函数,复杂语句建议不要使用
MSG msg; //在中断上下文执行用户提交的函数
#define RUN_IN_INTERRUPT_CONTEXT 0
//在线程上下文执行用户提交的函数
#define RUN_IN_THREAD_CONTEXT 1
*/
void TIM2_IRQHandler(void) //定时器中断函数
{
//multiple;
TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update);
//定时器定时范围25ms - 10s 定时间隔25ms
for(int i = 0;i<TimeQue_Size; i++)
{
if((Timque[i].IsActive!=0)&&(Timque[i].Time_No == 2))
{
if(Timque[i].Times == Timque[i].Ctim)
{
if(Timque[i].RepeatTimes == INFINITE)
{
if(Timque[i].priority == RUN_IN_INTERRUPT_CONTEXT )
{
Timque[i].Tim_P();
}
else
{
Timque[i].Ctim = 0;
MSG msg;
msg.Msg_Type = Tim_Msg;
msg.Msg_val1 = 0;
msg.Msg_val2 = 0;
msg.Msg_val3 = 0;
msg.Msg_val4 = 0;
msg.Msg_val5 = 0;
msg.Msg_cHandle = Timque[i].Ctrl;
msg.Msg_Fun = Timque[i].Tim_P;
PostMsg(&msg);
}
}
else
{
Timque[i].RepeatTime++;
if(Timque[i].RepeatTime <= Timque[i].RepeatTimes)
{
if(Timque[i].priority == RUN_IN_INTERRUPT_CONTEXT )
{
Timque[i].Tim_P();
}
else
{
Timque[i].Ctim = 0;
MSG msg;
msg.Msg_Type = Tim_Msg;
msg.Msg_val1 = 0;
msg.Msg_val2 = 0;
msg.Msg_val3 = 0;
msg.Msg_val4 = 0;
msg.Msg_val5 = 0;
msg.Msg_cHandle = Timque[i].Ctrl;
msg.Msg_Fun = Timque[i].Tim_P;
PostMsg(&msg);
}
}
else
{
KillTimer(i);
}
}
}
else
{
Timque[i].Ctim += TimUnit ;
}
}
// else return;
}
}
在直接为函数设定定时器时 函数类型我定义为返回值空,参数空。你可以根据你自己的需要定义,如果需要不同函数,可以使用C模拟多态。
具体应用可以根据个人的需要进行更改。
#define NoEnoughTim 0xff
#define INFINITE 0xffffffff
//在中断上下文执行用户提交的函数
#define RUN_IN_INTERRUPT_CONTEXT 0
//在线程上下文执行用户提交的函数
#define RUN_IN_THREAD_CONTEXT 1
typedef void (*Void_Fun)(void);
typedef struct{
unsigned char IsActive;
unsigned int Times; //用户设定定时器时间值
unsigned int Ctim; //记录定时器走过的时间
unsigned char Time_No; //定时器ID号
Crtl_Handle Ctrl; //定时控件句柄
Void_Fun Tim_P; //定时程序指针
unsigned int RepeatTimes; //设定的重复值
unsigned int RepeatTime; //已过去的重复次数
unsigned char priority; //中断执行 OR 发送到消息队列
}Tim_Que; #define TimeQue_Size 20 //模拟定时器的初始化 void Tim_Init()
{
do
{
Timque = (Tim_Que *) malloc(sizeof(Tim_Que)*TimeQue_Size);
}
while(Timque == NULL);
for(int i =0;i <TimeQue_Size;i++)
{
Timque[i].IsActive = 0;
Timque[i].Ctrl = (Crtl_Handle)0;
Timque[i].Tim_P = (void *)0;
Timque[i].Times = 100000;
Timque[i].Ctim = 0;
Timque[i].Time_No = 2;
Timque[i].RepeatTime = 0;
Timque[i].priority = 1;
}
}
定时器的设定与清除函数,函数源于windows中的定时器,可以参考《windows程序设计(第五版)》。
/*******************************************************************************
* @brief Init a Dropdown ,set default value
* @param Ctrl 定时刷新的控件
* @param Fun 定时执行的程序
* @param MSecs 定时时间MS
* @param RepeatTimes 重复执行几次
* @param priority 在中断中执行发送消息
* @retval TimeID 返回ID号
*****************************************************************************/
U8 SetTimer(Crtl_Handle Ctrl,Void_Fun Fun, unsigned int MSecs ,unsigned int RepeatTimes, unsigned int priority)
{
U8 TimID = 0;
if(TimID<0 || TimID >TimeQue_Size - 1) return NoEnoughTim; //TimID 从 0到 TimeQue_Size - 1
if((MSecs%TimUnit)) return NoEnoughTim;
for(int i = 0; TimID < TimeQue_Size; i++ )
{
if(Timque[TimID].IsActive == 0)
{
if((Ctrl==0)&&(Fun==0)) return NoEnoughTim; //定时器只能针对一种类型,控件和函数二选一
Timque[TimID].Times = MSecs;
Timque[TimID].RepeatTimes = RepeatTimes;
Timque[TimID].priority = priority;
if(Ctrl!= 0)
{
Timque[TimID].Ctrl = Ctrl;
}
else
{
if(Fun != 0)
Timque[TimID].Tim_P = Fun;
}
Timque[TimID].IsActive = 1;
return TimID;
}
TimID++;
}
return NoEnoughTim;
}
void KillTimer(unsigned char TimID)
{
if(TimID < 0 || TimID >TimeQue_Size - 1) return; //TimID 从 0到 TimeQue_Size - 1
if(Timque[TimID].IsActive != 0)
{
Timque[i].IsActive = 0;
Timque[i].Ctrl = (Crtl_Handle)0;
Timque[i].Tim_P = (void *)0;
Timque[i].Times = 100000;
Timque[i].Ctim = 0;
Timque[i].Time_No = 2;
Timque[i].RepeatTime = 0;
Timque[i].priority = 1;
}
else
{
return;
}
}
SetTimer和KillTimer两个函数为公共函数,在相关头文件包含。
初始化函数在系统初始化时初始化。
一套简单的定时器完成了,结构简单,易于管理。刚开博客几天时间,写的文章代码难免有各种缺陷,请各位不吝赐教,谢谢。