CANOpen定时器

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 16:20   2707   0

在CANOpen中,有部分和时间相关的子协议,比如pdo和lifegrd等,这就要求移植的时候实现定时器的底层接口。

在timer.h中给出了接口声明

/* 设置定时器重载值 */
void setTimer(TIMEVAL value);

/* 获取当前定时器计数器值 */
TIMEVAL getElapsedTime(void);


timer.c通过一个定时器实现各种定时事务的管理,定时事件数的上限取决于config.h中的宏MAX_NB_TIMER,在timer.c中定义了定时事件入口数组s_timer_entry timers[MAX_NB_TIMER]。

/* 定时事件入口结构体 */
struct struct_s_timer_entry 
{
 UNS8 state;   /* 事件状态 */
 CO_Data *d;   /* 节点指针 */
 TimerCallback_t callback; /* 回调函数 */
 UNS32 id;    /* 回调函数参数,用来区分事件 */
 TIMEVAL val;   /* 定时时间 */
 TIMEVAL interval;   /* 是否周期触发,0表示单次触发,非0表示周期触发 */
};


所有定时事件入口有4中状态

#define TIMER_FREE 0  /* 空闲,即该入口没有被定时事件占用 */
#define TIMER_ARMED 1  /* 被占用,即该入口已经定时事件占用 */
#define TIMER_TRIG 2  /* 单次触发 */
#define TIMER_TRIG_PERIOD 3 /* 周期触发 */

向定时事件入口中添加一个定时事件

/* 功能:添加一个定时事件
 * 参数:d表示节点指针
 *  id作为回调函数的参数,用以区分事件
 *  callback是回调函数
 *  value表示定时时间(32位即最长71.6分钟)
 *  period为0表示单次触发,不为0表示循环触发
 */
TIMER_HANDLE SetAlarm(CO_Data *d, UNS32 id, TimerCallback_t callback, TIMEVAL value, TIMEVAL period)
{
 TIMER_HANDLE row_number;
 s_timer_entry *row;

 /* 遍历定时事件入口 */
 for(row_number = 0, row = timers; row_number <= last_timer_raw + 1 && row_number < MAX_NB_TIMER; row_number++, row++)
 {
  /* 当注册的回调函数不为空,并且该入口状态为空闲,则可以将事件添加到该入口 */
  if(callback && row->state == TIMER_FREE)
  {
   TIMEVAL real_timer_value;
   TIMEVAL elapsed_time;

   /* 更新定时事件在事件入口数组中的最大下标 */
   if(row_number == last_timer_raw + 1) 
    last_timer_raw++;

   /* 获取定时器当前已经流逝的时间 */
   elapsed_time = getElapsedTime();
   /* STM32自动重载寄存器为16位,所以这个触发时间不能大于65535 */
   real_timer_value = value;
   real_timer_value = min_val(real_timer_value, TIMEVAL_MAX);

   /* 比较原本预定下一次唤醒时间和本事件触发时间,如果本事件先触发,则更新定时器唤醒时间 */
   if(total_sleep_time > elapsed_time && total_sleep_time - elapsed_time > real_timer_value)
   {
    /* 更新唤醒时间 */
    total_sleep_time = elapsed_time + real_timer_value;
    /* 设置定时器重载值 */
    setTimer(real_timer_value);
   }

   /* 初始化回调函数 */
   row->callback = callback;
   /* 初始化节点指针 */
   row->d = d;
   /* 初始化id */
   row->id = id;
   /* 初始化触发时间 */
   row->val = value + elapsed_time;
   /* 初始化周期 */
   row->interval = period;
   /* 初始化状态为入口以被占用 */
   row->state = TIMER_ARMED;
   
   /* 返回定时器事件入口数组下标,作为句柄 */
   return row_number;
  }
 }

 /* 返回错误 */
 return TIMER_NONE;
}

从定时事件入口中删除一个定时事件

/* 删除定时事件 */
TIMER_HANDLE DelAlarm(TIMER_HANDLE handle)
{
 MSG_WAR(0x3320, "DelAlarm. handle = ", handle);
 
 /* 将该定时事件入口状态置为空闲 */
 if(handle != TIMER_NONE)
 {
  if(handle == last_timer_raw)
   last_timer_raw--;
  timers[handle].state = TIMER_FREE;
 }
 
 /* 返回错误 */
 return TIMER_NONE;
}

在移植CANOpen协议栈时,要在中断处理函数中调用TimeDispatch函数进行定时事件触发处理

/* 定时事件触发处理函数 */
void TimeDispatch(void)
{
 TIMER_HANDLE i;
 TIMEVAL next_wakeup = TIMEVAL_MAX;
 
 /* 获取定时器中断到现在已经流逝的时间 */
 UNS32 overrun = (UNS32)getElapsedTime();

 /* 计算出到目前为止真正流逝的时间 */
 TIMEVAL real_total_sleep_time = total_sleep_time + overrun;

 s_timer_entry *row;

 /* 遍历定时事件入口 */
 for(i = 0, row = timers; i <= last_timer_raw; i++, row++)
 {
  /* 如果该定时事件入口被定时事件占用,如判断是否超时 */
  if(row->state & TIMER_ARMED)
  {
   /* 如果已经超时,则需要触发 */
   if(row->val <= real_total_sleep_time)
   {
    /* 如果该事件为单次触发,则将该事件状态设置为已触发 */
    if(!row->interval)
    {
     row->state = TIMER_TRIG;
    }
    /* 如果该事件为周期触发 */
    else
    {
     /* 校正定时时间 */
     row->val = row->interval - (overrun % (UNS32)row->interval);
     /* 状态置为已周期触发 */
     row->state = TIMER_TRIG_PERIOD;
     
     /* 更新定时器唤醒时间 */
     if(row->val < next_wakeup)
      next_wakeup = row->val;
    }
   }
   /* 没有超时,不需要触发 */
   else
   {
    /* 更新该事件唤醒时间 */
    row->val -= real_total_sleep_time;

    /* 更新定时器唤醒时间 */
    if(row->val < next_wakeup)
     next_wakeup = row->val;
   }
  }
 }

 /* 将得出定时器唤醒时间赋值 */
 total_sleep_time = next_wakeup;

 /* 设置定时器重载值 */
 setTimer(next_wakeup);

 /* 遍历所欲定时事件接口 */
 for(i = 0, row = timers; i <= last_timer_raw; i++, row++)
 {
  /* 如果该事件已经被触发(单次触发/周期触发) */
  if(row->state & TIMER_TRIG)
  {
   /* 将单次触发更新为空闲,周期触发的更新为被占用 */
   row->state &= ~TIMER_TRIG;
   
   /* 需要触发的事件,调用一下回调函数 */
   if(row->callback)
    (*row->callback)(row->d, row->id);
  }
 }
}
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP