[嵌入式][分享][交流]发布一个消息地图的模块
所謂消息地圖就是根據(jù)不同的狀態(tài)來執(zhí)行對應(yīng)的處理程序,這一技術(shù)成為消息地圖。例如我們平時使用的if、else語句switch、case
語句都是消息地圖的一種實現(xiàn)方式,而這個模塊采用的是函數(shù)指針的方式來實現(xiàn)消息地圖。采用全狀態(tài)機開發(fā)消息可以進行動態(tài)、靜態(tài)的
配置。消息地圖的技術(shù)來源于傻孩子老師的<深入淺出AVR單片機>,具體的細節(jié)請參考此書。
廢話少說,下面直接進入正題,講解一下怎么移植這個模塊。
1、準備工作
??????? 首先我們要有一個硬件平臺,具有一個串口的最新系統(tǒng)即可,為了體現(xiàn)代碼的平臺無關(guān)性請 參考文檔《平臺搭建》來搭建平臺。
這樣我們有了一個共同討論的基礎(chǔ)。(我的示例工程采用的是STM32神州III的開發(fā)板)。
2、解壓縮
??????? 將下載下來的“MsgMapService”解壓縮,模塊的目錄結(jié)構(gòu)如下所示,文件夾msgmap是服務(wù)實現(xiàn)的具體的代碼,而utilities文件夾
的內(nèi)容是改模塊依賴的一些宏以及隊列的模板,msgmap.h是調(diào)用該模塊的接口頭文件,app_cfg.h是該模塊的配置頭文件,使用該模
塊的時候?qū)δK的依賴進行配置。
??????? 解壓后將“MsgMapService”文件夾整個拷貝到你的工程中,將msgmap.c 和 checkstring.c添加到工程中參與編譯。
目錄樹結(jié)構(gòu)
[MsgMapService]
??????? | ---- msgmap.h
??????? | ---- app_cfg.h
??????? | ---- [utilities]????? ?
??????? |??????????????? | ---- ooc.h
??????? |??????????????? | --- app_type.h
??????? |??????????????? | ---- [template]
??????? |??????????????????????????????? | ---- t_queue.h
??????? |??????????????????????????????? | ---- template.h????????????????????????????????????????????????????????????????????????????? ?
??????? | ---- [msgmap]
??????????????????????? | ----??????? msgmap.c????? ?
??????????????????????? | ---- msgmap.h
??????????????????????? | ---- app_cfg.g
??????????????????????? | ---- [checkstring]????????????????????? ?
??????????????????????????????????????? | ---- checkstring.c
??????????????????????????????????????? | ---- checkstring.h
??????????????????????????????????????? | ---- app_cfg.h????? ?
3、配置模塊
??????? 該模塊是通過讀取隊列的字節(jié)流,而消息地圖是有用戶進行的配置,這里可以采用動態(tài)的配置和靜態(tài)的配置兩種方式。
首先該模塊依賴隊列,我在配置文件中插入一條宏:EXTERN_QUEUE(MsgMapQueue,uint8_t,uint8_t);MsgMapQueue是定義
的隊列的名稱,隊列的使用方法見t_queue.h.我們將數(shù)據(jù)接收隊列用tFIFOin命名,用宏進行插入。
#define CHECK_BYTE_QUEUE???? g_tFIFOin
??????? 然后我們需要配置消息系統(tǒng),這里我們采用靜態(tài)配置--所謂靜態(tài)配置是在編譯的階段對模塊的配置,
?
這兩條宏就實現(xiàn)了消息地圖的靜態(tài)配置,msg_apple_handler、msg_orange_handler、msg_hello_handler是消息處理函數(shù),
而字符串就是消息了。
??????? 消息地圖還有一個依賴,就是我們的字符輸出函數(shù)。即為平臺里的serial_out函數(shù),這里我們用宏來進行插入
#define SERIAL_OUT_HANDLE? serial_out。
現(xiàn)在我們的模塊的基本的使用配置就完成了,接下來我們看看如何調(diào)用。
4、模塊的使用
??????? 現(xiàn)在我們消息地圖來完成一個任務(wù),通過這個任務(wù)來介紹這個模塊的具體的調(diào)用的方法。我們要完成的這個任務(wù)的功能
是“芝麻開門”,就是我通過超級終端進行字符輸入,然后該任務(wù)對輸入的字符進行相應(yīng),不同的字符串對應(yīng)不同的相應(yīng),例如
我們輸入hello的時候向我們輸出world,就好比我們操作系統(tǒng)的命令行一樣,你輸入一個命令,操作系統(tǒng)給出一個響應(yīng),下面
看看這個任務(wù)怎么實現(xiàn)。
??????? 在模塊配置的環(huán)節(jié)我們介紹了消息地圖的靜態(tài)配置,現(xiàn)在我們繼續(xù)介紹消息地圖的另一種配置------動態(tài)配置,所謂動態(tài)
配置就是消息地圖在運行的工程中可以通過cmd_register進行注冊,通過cmd_unregister進行刪除。
??????? 首先定義消息地圖以及消息處理函數(shù):
?
現(xiàn)在動態(tài)消息地圖已經(jīng)配置好了,再使用前通過cmd_register(s_tUserMSGMap,UBOUND(s_tUserMSGMap));進行注冊。
??????? 在對msg_map_search的使用時將他進行了二次封裝,當(dāng)msg_map_search執(zhí)行到fsm_rt_cpl狀態(tài)時調(diào)用他的消息
處理函數(shù)。
?
現(xiàn)在消息地圖部分已經(jīng)OK,使用的時候調(diào)用CheckSringUseMsgMap就可以了。現(xiàn)在我們來實現(xiàn)task_a、task_b、task_c
這三個進程是輸出進程,他們的功能是等待事件觸發(fā),事件觸發(fā)后執(zhí)行事件的相應(yīng)-----輸出字符串。現(xiàn)在我們定義事件
?
然后進行初始化,初始化完成后就可以使用了:
1 INIT_EVENT(&s_tEventApple,false,MANUAL); 2 INIT_EVENT(&s_tEventOrange,false,MANUAL); 3 INIT_EVENT(&s_tEventWorld,false,MANUAL);?
然后編寫task_a的進程函數(shù)
?
1 #define TASK_A_FSM_RESET() do {s_tState = TASK_A_START;} while(0) 2 static fsm_rt_t task_a(void) 3 { 4 static enum { 5 TASK_A_START = 0, 6 TASK_A_WAIT_EVENT, 7 TASK_A_PRINT 8 }s_tState = TASK_A_START; 9 10 switch(s_tState) { 11 case TASK_A_START: 12 s_tState = TASK_A_WAIT_EVENT; 13 //break; 14 15 case TASK_A_WAIT_EVENT: 16 if(WAIT_EVENT(&s_tEventApple)){ 17 s_tState = TASK_A_PRINT; 18 } 19 break; 20 21 case TASK_A_PRINT: 22 if(fsm_rt_cpl == print_apple()){ 23 RESET_EVENT(&s_tEventApple); 24 TASK_A_FSM_RESET(); 25 return fsm_rt_cpl; 26 } 27 break; 28 } 29 30 return fsm_rt_on_going; 31 }?
?
?
1 #define PRINT_APPLE_RESET_FSM() do { s_tState = PRINT_APPLE_START; } while(0) 2 static fsm_rt_t print_apple(void) 3 { 4 static enum { 5 PRINT_APPLE_START = 0, 6 PRINT_APPLE_INIT, 7 PRINT_APPLE_SEND 8 }s_tState = PRINT_APPLE_START; 9 10 static uint8_t *s_pchString = (uint8_t *)"apple\r\n"; 11 static print_str_t s_tPrintStruct; 12 13 switch(s_tState) { 14 case PRINT_APPLE_START: 15 s_tState = PRINT_APPLE_INIT; 16 //break; 17 18 case PRINT_APPLE_INIT: 19 if(INIT_SRT_OUTPUT(&s_tPrintStruct,s_pchString)){ 20 s_tState = PRINT_APPLE_SEND; 21 }else { 22 return fsm_rt_err; 23 } 24 break; 25 26 case PRINT_APPLE_SEND: 27 if(fsm_rt_cpl == print_string(&s_tPrintStruct)){ 28 PRINT_APPLE_RESET_FSM(); 29 return fsm_rt_cpl; 30 } 31 break; 32 } 33 34 return fsm_rt_on_going; 35 }這里很清楚的可以看到該進程的處理過程,等待事件s_tEventApple觸發(fā),然后調(diào)用輸出 print_apple,而 子狀態(tài)機 print_apple
就是調(diào)用 print_string將輸出的內(nèi)容放到輸出隊列。其他的兩個進程以此編寫,這里不在贅述。
??????? 下面我們隊輸入輸出字節(jié)流的進程進行說明(stream_in_out),在這個進程中我們用到了隊列,而隊列的功能代碼通過
宏進行插入:DEF_QUEUE(MsgMapQueue,uint8_t,uint8_t,ATOM_ACESS);這樣我們就可以使用隊列了,首先定義兩個輸入、輸
出的隊列:
?
字節(jié)流的接口和發(fā)送很簡單參考如下代碼。
?
1 #define SERIAL_IN_TASK_FSM_RESET() do {s_tState = SERIAL_IN_TASK_START;} while(0) 2 static fsm_rt_t serial_in_task(void) 3 { 4 static uint8_t s_chByte = 0; 5 static enum { 6 SERIAL_IN_TASK_START = 0, 7 SERIAL_IN_TASK_READ 8 }s_tState = SERIAL_IN_TASK_START; 9 10 switch(s_tState) { 11 case SERIAL_IN_TASK_START: 12 s_tState = SERIAL_IN_TASK_READ; 13 //breka; 14 case SERIAL_IN_TASK_READ: 15 if(serial_in(&s_chByte)){ 16 ENQUEUE(MsgMapQueue,&g_tFIFOin,s_chByte); 17 SERIAL_IN_TASK_FSM_RESET(); 18 return fsm_rt_cpl; 19 } 20 break; 21 } 22 23 return fsm_rt_on_going; 24 } 25 26 #define SERIAL_OUT_TASK_FSM_RESET() do {s_tState = SERIAL_OUT_TASK_START;} while(0) 27 static fsm_rt_t serial_out_task(void) 28 { 29 static uint8_t s_chByte = 0; 30 static enum { 31 SERIAL_OUT_TASK_START = 0, 32 SERIAL_OUT_TASK_READ_QUE, 33 SERIAL_OUT_TASK_OUTPUT 34 }s_tState = SERIAL_OUT_TASK_START; 35 36 switch(s_tState) { 37 case SERIAL_OUT_TASK_START: 38 s_tState = SERIAL_OUT_TASK_READ_QUE; 39 //breka; 40 case SERIAL_OUT_TASK_READ_QUE: 41 if(DEQUEUE(MsgMapQueue,&g_tFIFOout,&s_chByte)){ 42 s_tState = SERIAL_OUT_TASK_OUTPUT; 43 } 44 break; 45 46 case SERIAL_OUT_TASK_OUTPUT: 47 if(serial_out(s_chByte)) { 48 SERIAL_OUT_TASK_FSM_RESET(); 49 return fsm_rt_cpl; 50 } 51 break; 52 } 53 54 return fsm_rt_on_going; 55 }?
?
?
現(xiàn)在各個進程都已經(jīng)準備完畢,就剩下我們進行調(diào)用了,main函數(shù)如下
?
1 int main(void) 2 { 3 system_init(); 4 5 INIT_EVENT(&s_tEventApple,false,MANUAL); 6 INIT_EVENT(&s_tEventOrange,false,MANUAL); 7 INIT_EVENT(&s_tEventWorld,false,MANUAL); 8 9 QUEUE_INIT(MsgMapQueue,&g_tFIFOin,s_tBuf, UBOUND(s_tBuf)); 10 QUEUE_INIT(MsgMapQueue,&g_tFIFOout,s_tPiPeBuf, UBOUND(s_tPiPeBuf)); 11 12 cmd_register(s_tUserMSGMap,UBOUND(s_tUserMSGMap)); 13 14 while(1) { 15 task_a(); 16 task_b(); 17 task_c(); 18 CheckSringUseMsgMap(); 19 stream_in_out(); 20 } 21 }?
?
?
個人水平有限,歡迎大家拍磚,蓋房娶媳婦。
?
轉(zhuǎn)載于:https://www.cnblogs.com/zhaoli/p/4742744.html
總結(jié)
以上是生活随笔為你收集整理的[嵌入式][分享][交流]发布一个消息地图的模块的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小米8电池耐用吗(小米官方售后服务)
- 下一篇: 自己做网站,论坛类型的