原創文章,歡迎轉載,轉載請注明出處。
上個周末其實通訊協議就已經擬定完成了,這一個星期主要成了通訊協議的解析,然后通過通訊協議的實現,加入遙控器的控制和飛控信息的傳遞,從飛控傳到遙控器,再從遙控器傳到電腦上,通過matlab現實姿態信息和電機輸出控制信息。這章會一步一步介紹實現的過程。
1:遙控和飛控之間的通訊。
2:通訊協議的擬定。
3:通訊協議的實現與解析。
4:通過遙控控制飛控並且飛控姿態通過nrf上傳數據。
先上通過遙控控制飛控並且飛控姿態通過nrf上傳數據的視頻哈,看看效果,解釋在后面;
視頻地址http://v.youku.com/v_show/id_XNzg3NjcwMDc2.html
1:遙控和飛控之間的通訊
有了上章的環形緩沖和通訊的基礎,現在就驗證下環形緩沖和通訊結合起來的工作效果。
有了環形緩沖,我們實現發送和接收就比較簡單了,需要發送數據的時候,我們只要把數據push到緩沖里面,接收數據的時候在接受中斷了里將數據push進緩沖。我們有一個專門通訊管理的task,用來判斷緩沖里面是否有數據,如果發送緩沖里面有數據,就讀取數據,發送,發送后然后將數據pop,如果接收緩沖里面有數據,就讀取數據,並且根據協議解析,然后pop。
根據上面的說明,我們在遙控上面發送數據,然后飛控接到數據后會返回數據給遙控,如此往復。。具體通訊效果如下圖。

上圖左邊是遙控器的串口輸出信息,右邊是飛控輸出的信息。圖中txds為收到ACK包,txed為發送完成,rxed為接受完成,trmax為重試了設定的次數還沒收到ACK包,意味着發送失敗。發送失敗的還是有的哈,不過暫時沒有做發送失敗的處理,也沒有判斷。。我們可以失敗了不pop,稍微等待個幾毫秒再試着發送一次。。
2:通訊協議的擬定
通訊協議需要簡單,盡量少的數據傳輸盡量多的信息。。。
首先我們將協議進行了分類,分為控制類,查詢類,和主動上報類,一共三類,所以需要占用2位,一個字節里面占2位后還有6位,有64種組合,目前看應該夠我們用了,然后后面就是根據各個命令來傳輸數據了。協議大致如下圖:

根據協議我們定義了如下結構體:
1 typedef union 2 { 3 u8 mode; //飛行模式的mode參數 4 Com_Att_t Att; //姿態信息 5 u32 Height; //高度參數 6 Com_PID_t Pid; //PID信息 7 u8 RetPara; //返回信息 8 Com_Sensor_t Sensor; //傳感器數據 9 u8 Battery; //電池電量 10 }Comm_Para_tu; 11 12 typedef struct 13 { 14 u8 Command:6; //0~5位為命令 15 u8 ComType:2; //6~7位為命令類型 16 }Order_t; 17 18 19 typedef struct 20 { 21 u8 Len; //數據長度 22 Order_t Order; //命令類型 23 Comm_Para_tu Para; //參數 24 }Comm_Data_ts;
Comm_Data_ts是我們通訊用到的結構體,根據協議,里面有數據長度,然后有命令類型和對應的命令,后面就跟着參數。發送數據只需要對這個結構體賦值,接收數據后解析只需要根據這個機構體的Order找到對應的命令,然后根據對應的參數讀取數據就好了,這樣在上層操作的時候,我們不需要管通訊協議的哪個字節存什么數據,只需要對結構體進行操作。
3:通訊協議的實現與解析
為了方便代碼的實現,我們對每個命令寫了一個解析函數。那我們是怎樣找到這個函數的呢?雖然我們可以按照命令的排序做一個指向函數的指針數組,調用的時候根據命令就可以直接調用,而且速度非常快,但是這樣並不利於我們后期的維護,不能根據后期協議的更改方便的添加和刪除命令,所以我們棄用那種方法。而是創建一個結構體,里面有命令和命令對應的處理函數的函數指針,我們只需要進行命令匹配,匹配好后,就調用對應的函數指針調用對應的函數,這樣就完成了解析。
結構體如下:
1 /*命令回調結構*/ 2 typedef struct 3 { 4 void (*Comm_CallBack)(Comm_Data_ts *msg); 5 u8 command; 6 } SubComm_Fun; 7 8 9 typedef struct 10 { 11 const SubComm_Fun *SubComm; //指向子命令 12 u8 ComType; //命令類型 13 u8 Len; //子命令個數,優化搜索時間 14 } Comm_Fun;
因為我們命令分兩層,所以先對命令類型解析,然后搜索下一層命令,然后再運行對應的函數指針,打個比方,上面結構體的部分初始化如下:
1 //查詢類命令於對應函數定義 2 //注意,一定要注意,一定要修改FLY_QUERY_FUN_LEN宏定義 3 const SubComm_Fun Fly_Query_Fun[]= 4 { 5 { 6 Fly_Query_Sensor, 7 FLY_QUERY_SENSOR 8 }, 9 10 { 11 Fly_Query_Attitude, 12 FLY_QUERY_ATTITUDE 13 }, 14 15 { 16 Fly_Query_System_Sta, 17 FLY_QUERY_SYSTEM_STA 18 }, 19 20 { 21 Fly_Query_Battery, 22 FLY_QUERY_BATTERY 23 }, 24 }; 25 #define FLY_QUERY_FUN_LEN 4 26 27 28 //命令類型對子命令數定義 29 //注意,一定要注意,一定要修改Comm_Fun_Len宏定義 30 const Comm_Fun Fly_Fun[]= 31 { 32 { 33 Fly_Ctrl_Fun, 34 FLY_CTRL, 35 FLY_CTRL_FUN_LEN 36 }, 37 38 { 39 Fly_Query_Fun, 40 FLY_QUERY, 41 FLY_QUERY_FUN_LEN 42 }, 43 44 { 45 Fly_Report_Fun, 46 FLY_REPORT, 47 FLY_REPORT_FUN_LEN 48 }, 49 };
初始化上面的機構體后,我們就根據命令來解析,解析代碼如下:
1 void Comm_Decode(Comm_Data_ts *msg) 2 { 3 u8 i,k; 4 5 for(i = 0 ; i < COMM_FUN_LEN; i++) 6 { 7 /*查詢消息命令*/ 8 if(msg->Order.ComType == Fly_Fun[i].ComType) 9 { 10 for(k = 0 ;k < Fly_Fun[i].Len; k++) 11 { 12 if(Fly_Fun[i].SubComm[k].command == msg->Order.Command) 13 { 14 Fly_Fun[i].SubComm[k].Comm_CallBack(msg); 15 break; 16 } 17 } 18 break; 19 } 20 21 } 22 23 }
這樣一是代碼重用率很高,而且增加和刪減命令很簡單。不過我們可以將命令類型改成枚舉類型,可以避免出現錯誤的命令可能。
解析的效果如下圖:

上圖左邊為遙控器的nrf的debug信息,右邊為遙控器接收到數據后的解析的debug信息。遙控器通過手柄的及格按鍵發送不同的命令進行測試,通過實際觀察,運行良好。
4:通過遙控控制飛控並且飛控姿態通過nrf上傳數據
將上面功能全部結合起來,同時實現無線姿態采集,都是通過協議實現的。
先說明下我們的運行方式:飛控定時給遙控器發送傳感器信息和姿態信息,遙控通過串口發給matlab,並且進行實時現實。這里我們上傳的信息添加了電機的控制信息,包括總油門和每個電機的單獨油門,通過這個我們可以觀察飛控對槳的操作邏輯是否正確。同時遙控可以發送遙控命令,命令飛控的目標姿態。效果圖圖下:

左邊的柱狀圖12345分別為moto1,moto2,moto3,moto4和油門。
電機位置示意如下:

字很丑哈,隨便畫的當時,看不太明白的可以到如下網址看,電機的方位和角度的定義有點不同哈,但是原理是一樣的。
網址:http://bbs.21ic.com/icview-605405-1-1.html
最后聯機測試的視頻見頂端,通過測試,電機輸出邏輯正常,放心了哈。。后面加電調控制進去,調下PID,應該可以飛了。。
