使用硬件定时器软模拟多个定时器(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两个函数为公共函数,在相关头文件包含。

初始化函数在系统初始化时初始化。

 

一套简单的定时器完成了,结构简单,易于管理。刚开博客几天时间,写的文章代码难免有各种缺陷,请各位不吝赐教,谢谢。

 

 

posted @ 2012-05-29 11:27  bandynewer  阅读(2531)  评论(0编辑  收藏  举报