在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);
}
}
}
|