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

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

     

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

  • 相关阅读:
    如何使用 @ OutputCache 指令的 VaryByCustom 属性来缓存不同版本的页面
    看不懂 ASP.NET 相册上传代码
    asp.net判断是1.1还是2.0主要由Code*属性来解决,判断规则如下:
    引号看不懂
    GridView的行删除事件 //取当前行的Id
    <Columns></Columns>中间的是列集合
    DropDownList1.SelectedIndex = 0 DropDownList1处于位选择任何选项的状态下
    是一个查询语句 查询ZhuanJia表里面id=输入id的数据
    验证码图片的解释
    get和post区别:
  • 原文地址:https://www.cnblogs.com/bandynewer/p/2523704.html
Copyright © 2011-2022 走看看