日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

CANOpen定时器

發布時間:2025/3/15 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CANOpen定时器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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

總結

以上是生活随笔為你收集整理的CANOpen定时器的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。