




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、單片機的非os的事件驅動思考很多單片機項目恐怕都是沒有操作系統的前后臺結構,就是main函數里用while無限循環各種任務,中斷處理緊急任務。這種結構最簡單,上手很容易,可是當項目比較大時,這種結構就不那么適合了,編寫代碼前你必須非常小心的設計各個模塊和全局變量,否則最終會使整個代碼結構雜亂無序,不利于維護,而且往往會因為修改了某部分代碼而莫名其妙的影響到其他功能,而使調試陷入困境。改變其中局面的最有效措施當然是引入嵌入式操作系統,但是大多數的操作系統都是付費的(特別是商業項目)。我們熟悉的uc-os/ii如果你應用于非商業項目它是免費的,而應用于商業項目的話則要付費,而且價格不菲。我們也可以
2、自己編寫一套嵌入式os,這當然最好了。可要編寫一套完整的os并非易事,而且當項目并不是非常復雜的話也不需要一個完整的os支持。我們只要用到os最基本的任務調度和上下文切換就夠了。正是基于這樣的想法,最近的一個項目中我就嘗試采用事件驅動的思想重新構建了代碼架構,實際使用的效果還不錯,在這里做個總結。本質上新架構仍然是前后臺結構,只不過原來的函數直接調用改成通過指向函數的指針來調用。實際上這也是嵌入式os任務調度的一個核心。c語言中可以定義指向函數的指針: void (*handle)(void);這里的handle就是一個指向函數的指針,我們只要將某函數的函數名賦給該指針,就能通過實現函數的調用
3、了: void func1(void) / code handle = func1; (*handle)(); / 實現func1的調用有了這個函數調用新方法,我們就可以想辦法將某個事件與某個函數關聯,實現所謂的事件驅動。例如,按鍵1按下就是一個事件,func1響應按鍵1按下事件。但是,如果是單純的調用方法替代又有什么意義呢?這又怎么會是事件驅動呢?關鍵就在于使用函數指針調用方法可以使模塊和模塊之間的耦合度將到最低。一個例子來說明這個問題,一個按鍵檢測模塊用于檢測按鍵,一個電源模塊處理按鍵1動作。傳統的前后臺處理方法:main.c void main() . while(1) . keysca
4、n(); if(flagkeypress) keyhandle(); / 檢測到按鍵就設置flagkeypress標志,進入處理函數 key.c void keyhandle(void) switch (_keyname) / 存放按鍵值的全局變量 . case key1: pwropen(); break; case key2: pwrclose(); break; power.c void pwropen(void) . void pwrclose(void) . 這樣的結構的缺點在哪里呢?1. key代碼中直接涉及到power代碼的函數,如果power代碼里的函數變更,將引起key代碼的
5、變更2. 一個按鍵值對應一個處理函數,如果要增加響應處理函數就要再次修改key代碼3. 當項目越來越大時,引入的全局變量會越來越多,占用過多的內存很顯然key模塊與其他模塊的耦合程度太高了,修改其他模塊的代碼都勢必去修改key代碼。理想的狀態是key模塊只負責檢測按鍵,并觸發一個按鍵事件,至于這個按鍵被哪個模塊處理,它壓根不需要知道,大大減少模塊之間的耦合度,也減少出錯的幾率。這不正好是事件驅動的思想嗎?接下來,該如何實現呢?事件驅動的實現。需要一個事件隊列: u16 _eventmax_event_queue;它是一個循環隊列,保存事件編號,我們可以用一個16位數為各種事件編號,可以定義65
6、535個事件足夠使用了。一個處理函數隊列: typedef struct u16 event; / 事件編號 void (*handle)(void); / 處理函數 handletype; handletype _handlemax_handle_queue;它實際是一個數組,每個元素保存事件編號和對應的處理函數指針。一個驅動函數: void eventproc(void) u16 event; u8 i; if(_eventhead!=_eventtail) | _eventfull) / 事件隊列里有事件時才處理 event = _eq_eventhead; _event _eventh
7、ead+ = 0; / 清除事件 if(_eventhead= max_event_queue) _eventhead= 0; / 循環隊列 / 依次比較,執行與事件編號相對應的函數 for(i=0; i_handletail; i+) if(_handlei.event = event) (*_handlei.handle)(); main函數可以精簡成這樣: void main(void) . while(1) eventproc(); 這樣代碼的缺陷是需要為處理函數隊列分配一個非常大的內存空間,項目越復雜分配的空間越大,顯然不可取。需要做個變通,減少內存空間的使用量。變通方法就是將各模塊
8、的散轉處理保存在模塊內部,而不再寫入到處理函數隊列中,具體實現如下:事件隊列不需要修改。修改處理函數隊列: typedef struct void (*handle)(u16 event); / 僅保存模塊總的散轉函數 handletype; handletype _handlemax_handle_queue;修改驅動函數: void eventproc(void) u16 event; u8 i; if(_eventhead!=_eventtail) | _eventfull) / 事件隊列里有事件時才處理 . for(i=0; i= max_handle_queue) _handlefu
9、ll= true; 每個模塊都定義各自的散轉處理,然后在初始化的時候將該函數存入處理事件隊列中,即能實現事件處理又不會占用很多的內存空間。加入到事件隊列需要封裝成統一的函數dispatcheven,由各模塊直接調用。例如,key模塊就可以dispatchevent(event_key1_press)來觸發一個事件 void dispatchevent(u16 event) u8 i; bool candispatch; candispatch = true; if(!_eventfull) / 為了避免同一事件被多次加入到事件隊列中 for(i=_eventhead; i!=_eventtai
10、l;) if(_eventi = event) candispatch = false; break; i+; if(i = max_event_queue) i = 0; if(candispatch) _event_eventtail+ = event; if(_eventtail= max_event_queue) _eventtail= 0; if(_eventtail= _eventhead) _eventfull = true; 對于與時間相關的事件(循環事件和延時處理事件)需要做進一步處理。首先要設定系統tick,可以用一個定時器來生成,例如配置一個定時器,每10ms中斷一次。定
11、義一個時間事件隊列: typedef struct u8 type; / 事件類別,循環事件還是延時事件 u16 event; / 觸發的事件編號 u16 timer; / 延時或周期時間計數器 u16 timerbackup; / 用于周期事件的時間計數備份 timereventtype; timereventtype _timereventmax_timer_event_q;在定時器tick中斷中將時間事件轉換成系統事件: void systickhandler(void) . for(i=0; i_timereventtail; i+) _timereventi.timer-; if(_
12、timereventi.timer = 0) dispatchevent(_timereventi.event); if(_timereventi.type = cycle_event) / 循環事件,重新計數 _timereventi.timer = _timereventi.timerbackup; else / 延時事件,觸發后刪除 deltimerevent(_timereventi.event); 將增加和刪除時間事件封裝成函數,便以調用: void addtimerevent(u8 type, u16 event, u16 timer) _timerevent_timereventtail.type = type; _timerevent_timereventtail.event = event; _timerevent_timereventtail.timer = timer; / 時間單位是系統tick間隔時間 _timerevent_timereventtail.timerbackup = timer; / 延時事件并不使用 _timereventtail+; void deltimerevent(u16 event) . for(i=0; i_timereventtail; i+) if(_timereventi.event =
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 短視頻平臺內容監管與網絡生態治理創新研究與實踐報告
- 數字孿生視角下2025年城市規劃與建設中的智慧城市能源管理系統優化與評估優化優化報告
- 2025年智慧交通系統交通流量預測技術智能交通數據挖掘與智能控制報告
- 工業互聯網平臺微服務架構性能測試報告2025:邊緣計算與實時性能優化
- 電競商業贊助策略報告:2025年品牌合作案例分析
- 功能性飲料在健身器材銷售中的市場推廣策略報告
- 培訓班門店財務管理制度
- 亞馬遜銷售組長管理制度
- 早餐健康宿舍管理制度
- 房產公司運營部管理制度
- 項目管理從立項到結項全解析
- 全國導游人員資格考試單科綜合測試卷(科目一:政策與法律法規)
- 2024年中國鐵路成都局集團有限公司招聘考試《鐵路基本常識》真題庫及答案
- 中醫診斷學考點總結
- 生態草場使用權轉讓協議
- 第18課清朝的邊疆治理教學設計-統編版七年級歷史下冊
- 物流實操試題及答案詳解
- 播出設備檢修管理制度
- 國家開放大學學習網電大證券投資分析形考任務12345答案
- 醫院醫保獎懲管理制度
- 大件貨物運輸合同范本
評論
0/150
提交評論