「ZigBee模块」协议栈-串口透传,打造无线串口模块
前面寫比較仔細(xì),后面一個(gè)么因?yàn)楹颓懊嬷貜?fù)了,不多說了,還有個(gè)原因...我懶...O(∩_∩)O哈哈~
串口透?jìng)鳎蛟鞜o線串口模塊
一、實(shí)驗(yàn)?zāi)康?/strong>
兩臺(tái)PC機(jī)各使用串口連接一個(gè)zigbee模塊,連接正確后打開串口調(diào)試助手發(fā)送信息。利用zigbee將從串口接收到的數(shù)據(jù)無線傳送給另一個(gè)zigbee模塊,另一個(gè)zigbee模塊通過串口將數(shù)據(jù)傳給PC端并在屏幕上顯示。
二、實(shí)驗(yàn)平臺(tái)
硬件:兩個(gè)zigbee模塊,兩臺(tái)PC機(jī)(其實(shí)一臺(tái)也許,連接不同串口即可),編譯器,方口轉(zhuǎn)USB數(shù)據(jù)線兩根
軟件:基于Z-stack協(xié)議棧的SampleApp工程文件
三、實(shí)驗(yàn)過程分析
打開工程文件,打開MT_UART.c文件,找到函數(shù)初始化函數(shù)MT_UartInit()。注意其中部分代碼
1 #if defined (ZTOOL_P1) || defined (ZTOOL_P2) //預(yù)編譯 2 3 uartConfig.callBackFunc = MT_UartProcessZToolData; //|選擇ZTOOL或者ZAPP 4 5 #elif defined (ZAPP_P1) || defined (ZAPP_P2) //|P1-串口0 或 P2-串口1 6 7 uartConfig.callBackFunc = MT_UartProcessZAppData; //|在option->c/c++->preprocessor中選擇 8 9 #else //| 10 11 uartConfig.callBackFunc = NULL; //| 12 13 #endif //|
MT_UART.c
這部分是對(duì)串口進(jìn)行預(yù)編譯,我們定義的是ZTOOL_P1,故協(xié)議棧處理的函數(shù)是MT_UartProcessZToolData。查看其定義。
正式看它的定義之前我們先來了解一下協(xié)議棧中發(fā)送數(shù)據(jù)的格式。函數(shù)定義的上面有一段注釋部分,對(duì)串口傳送數(shù)據(jù)的格式進(jìn)行了說明(見圖1)。
圖1
SOP:0xFE數(shù)據(jù)幀頭
DataLength:Data的數(shù)據(jù)長(zhǎng)度,以字節(jié)計(jì)
CMD0:命令低字節(jié)
CMD1:命令高字節(jié)
Data:數(shù)據(jù)幀具體的數(shù)據(jù),長(zhǎng)度可變,但必須和DataLength相等。
FCS:校驗(yàn)和
看了這個(gè)數(shù)據(jù)格式我們就會(huì)發(fā)現(xiàn)一個(gè)問題,這個(gè)數(shù)據(jù)格式非常適合硬件之間的通信,因?yàn)樗司唧w數(shù)據(jù)以外的很多數(shù)據(jù)信息,但是卻不適合我們手動(dòng)發(fā)送數(shù)據(jù)。也就是說如果我們使用串口助手直接發(fā)送數(shù)據(jù),我們需要在數(shù)據(jù)前面加上FE、數(shù)據(jù)長(zhǎng)度、命令字,然后數(shù)據(jù)末尾再計(jì)算校驗(yàn)和。這對(duì)于我們來說實(shí)在太麻煩了,所以我們必須對(duì)這個(gè)函數(shù)作出一些修改。在修改函數(shù)之前我們還是要先來了解一下它原本的代碼。
順便再提一個(gè)東西,串口數(shù)據(jù)包(我是這樣叫它的,它的英文名是mtOSALSerialData_t)。串口數(shù)據(jù)包是一個(gè)結(jié)構(gòu)體,成員變量是一個(gè)事件包(也是我自己叫的,英文名叫osal_event_hdr_t)和一個(gè)指針。時(shí)間包也是一個(gè)結(jié)構(gòu)體,成員變量是事件(事件號(hào))和狀態(tài)。也就是說一個(gè)串口數(shù)據(jù)包里面有一個(gè)事件號(hào),一個(gè)事件狀態(tài),一個(gè)指針。很明顯,這個(gè)指針等一下一定會(huì)指向一個(gè)動(dòng)態(tài)數(shù)組,然后依次往數(shù)值里面放數(shù)據(jù)嘛~
好啦,大家久等啦,來看一下MT_UartProcessZToolData()這個(gè)函數(shù)吧~
1 //port是串口號(hào),event是事件
2
3 void MT_UartProcessZToolData ( uint8 port, uint8 event )
4
5 {
6
7 uint8 ch;
8
9 uint8 bytesInRxBuffer;
10
11
12
13 (void)event; // Intentionally unreferenced parameter
14
15
16
17 while (Hal_UART_RxBufLen(port)) //只要緩沖區(qū)有數(shù)據(jù)
18
19 {
20
21 HalUARTRead (port, &ch, 1); //傳入串口號(hào),讀取1個(gè)字符到ch
22
23
24
25 switch (state) //state一開始默認(rèn)0x00
26
27 {
28
29 case SOP_STATE: //SOP_STATE = 0xFE;
30
31 if (ch == MT_UART_SOF)
32
33 state = LEN_STATE; //切換狀態(tài)
34
35 break;
36
37
38
39 case LEN_STATE: //讀取數(shù)據(jù)長(zhǎng)度
40
41 LEN_Token = ch;
42
43
44
45 //接下去要正式接受有用的數(shù)據(jù)啦
46
47 //開始之前要為接收數(shù)據(jù)做一系列初始化
48
49 tempDataLen = 0; //初始化數(shù)據(jù)指針
50
51
52
53 /* Allocate memory for the data */
54
55 //為數(shù)據(jù)分配內(nèi)存,其實(shí)新建的是串口數(shù)據(jù)包(我是這樣叫它的)
56
57 pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof ( mtOSALSerialData_t ) +
58
59 MT_RPC_FRAME_HDR_SZ + LEN_Token );
60
61
62
63 if (pMsg) //如果內(nèi)存分配成功
64
65 {
66
67 /* Fill up what we can */
68
69 //把我們已知的內(nèi)容填入數(shù)據(jù)包
70
71 pMsg->hdr.event = CMD_SERIAL_MSG; //事件號(hào)
72
73 pMsg->msg = (uint8*)(pMsg+1); //為存放的數(shù)據(jù)的數(shù)組開辟一個(gè)空間
74
75 pMsg->msg[MT_RPC_POS_LEN] = LEN_Token; //數(shù)組第一位依舊是長(zhǎng)度
76
77 state = CMD_STATE1; //初始化結(jié)束,切換狀態(tài)
78
79 }
80
81 else
82
83 {
84
85 state = SOP_STATE;
86
87 return;
88
89 }
90
91 break;
92
93
94
95 case CMD_STATE1: //寫入CMD0
96
97 pMsg->msg[MT_RPC_POS_CMD0] = ch;
98
99 state = CMD_STATE2; //切換狀態(tài)
100
101 break;
102
103
104
105 case CMD_STATE2: //寫入CMD1
106
107 pMsg->msg[MT_RPC_POS_CMD1] = ch;
108
109 /* If there is no data, skip to FCS state */
110
111 //切換狀態(tài),如果數(shù)據(jù)長(zhǎng)度為0,則跳過一個(gè)狀態(tài)
112
113 if (LEN_Token)
114
115 {
116
117 state = DATA_STATE;
118
119 }
120
121 else
122
123 {
124
125 state = FCS_STATE;
126
127 }
128
129 break;
130
131
132
133 case DATA_STATE: //依次寫入數(shù)據(jù)
134
135
136
137 /* Fill in the buffer the first byte of the data */
138
139 pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen++] = ch;
140
141
142
143 /* Check number of bytes left in the Rx buffer */
144
145 bytesInRxBuffer = Hal_UART_RxBufLen(port);
146
147
148
149 /* If the remain of the data is there, read them all, otherwise, just read enough */
150
151 if (bytesInRxBuffer <= LEN_Token - tempDataLen)
152
153 {
154
155 HalUARTRead (port, &pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen], bytesInRxBuffer);
156
157 tempDataLen += bytesInRxBuffer;
158
159 }
160
161 else
162
163 {
164
165 HalUARTRead (port, &pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen], LEN_Token - tempDataLen);
166
167 tempDataLen += (LEN_Token - tempDataLen);
168
169 }
170
171
172
173 /* If number of bytes read is equal to data length, time to move on to FCS */
174
175 if ( tempDataLen == LEN_Token ) //寫完切換狀態(tài)
176
177 state = FCS_STATE;
178
179
180
181 break;
182
183
184
185 case FCS_STATE: //幀校驗(yàn)位,確保正確
186
187
188
189 FSC_Token = ch;
190
191
192
193 /* Make sure it's correct */
194
195 if ((MT_UartCalcFCS ((uint8*)&pMsg->msg[0], MT_RPC_FRAME_HDR_SZ + LEN_Token) == FSC_Token))
196
197 {
198
199 osal_msg_send( App_TaskID, (byte *)pMsg ); //校驗(yàn)正確就把數(shù)據(jù)包發(fā)送給...App_TaskID
200
201 } //這是什么?就是我們之前登記的任務(wù)號(hào)!
202
203 //具體查看MT_UartRegisterTaskID()這個(gè)函數(shù)!
204
205 else
206
207 {
208
209 /* deallocate the msg */
210
211 //錯(cuò)誤就把包丟掉(釋放內(nèi)存)
212
213 osal_msg_deallocate ( (uint8 *)pMsg );
214
215 }
216
217
218
219 /* Reset the state, send or discard the buffers at this point */
220
221 state = SOP_STATE; //數(shù)據(jù)包接收完畢,切換回初始狀態(tài)
222
223
224
225 break;
226
227
228
229 default:
230
231 break;
232
233 }
234
235 }
236
237 }
MT_UartProcessZToolData
代碼很長(zhǎng),注釋基本寫在代碼里面了,總結(jié)一下流程。
①判斷起始碼是不是0xFE(不是就別想繼續(xù)下去啦)
②讀取數(shù)據(jù)長(zhǎng)度,初始化串口數(shù)據(jù)包pMsg(mtOSALSerialData_tpMsg)
③給pMsg裝數(shù)據(jù)
④校驗(yàn)和,正確則把pMsg向上發(fā)送,錯(cuò)誤則丟棄
⑤初始化狀態(tài)
我們要做的就是簡(jiǎn)化流程,因?yàn)槲覀儼l(fā)送的數(shù)據(jù)格式是只含有數(shù)據(jù)內(nèi)容的,因此要把起始碼、數(shù)據(jù)長(zhǎng)度之類的去掉。但是這樣會(huì)導(dǎo)致數(shù)據(jù)長(zhǎng)度變成未知的,無法聲明動(dòng)態(tài)數(shù)組。改變思路,定義一個(gè)定長(zhǎng)的數(shù)組!
修改后代碼如下:
1 void MT_UartProcessZToolData ( uint8 port, uint8 event )
2
3 {
4
5 uint8 ch, len = 0;
6
7 uint8 uartData[128];
8
9 uint8 i;
10
11
12
13 (void)event; // Intentionally unreferenced parameter
14
15
16
17 while (Hal_UART_RxBufLen(port))
18
19 {
20
21 HalUARTRead (port, &ch, 1);
22
23 uartData[len+1] = ch;
24
25 len ++;
26
27 }
28
29 if(len)
30
31 {
32
33 uartData[0] = len;
34
35 pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof ( mtOSALSerialData_t ) +
36
37 len + 1 );
38
39 if (pMsg)
40
41 {
42
43 /* Fill up what we can */
44
45 pMsg->hdr.event = CMD_SERIAL_MSG;
46
47 pMsg->msg = (uint8*)(pMsg+1);
48
49 for(i=0; i<=len; i++)
50
51 pMsg->msg[i] = uartData[i];
52
53 osal_msg_send( App_TaskID, (byte *)pMsg );
54
55 }
56
57 }
58
59 }
MT_UartProcessZToolData
修改完接收數(shù)據(jù)包的代碼之后,我們就應(yīng)該去考慮下要怎么處理接收的代碼啦。這個(gè)自然就是在SampleApp.c中進(jìn)行的啦。還有,在開始之前要先在SampleApp.c中加入串口初始化,這個(gè)過程見上一篇《Z-Stack協(xié)議棧基礎(chǔ)和數(shù)據(jù)傳輸實(shí)驗(yàn)》的5.1串口初始化部分。原諒我比較懶......
打開SampleApp.c,找到事件處理函數(shù)SampleApp_ProcessEvent()。看到兩個(gè)if語(yǔ)句里面分別有一個(gè)SYS_EVENT_MSG和SAMPLEAPP_SEND_PERIODIC_MSG_EVT。這兩個(gè)就是事件的編號(hào)。關(guān)于事件之前有說過,每個(gè)任務(wù)都可以有16個(gè)事件。這個(gè)時(shí)候會(huì)有這樣的疑惑,這個(gè)事件和我們之前在串口數(shù)據(jù)包里面放入的事件有什么區(qū)別呢?為了解開這個(gè)疑惑,找到之前串口數(shù)據(jù)包的定義(MT_UART.c->MT_UartProcessZToolData()->pMsg查看定義->mtOSALSerialData_t查看定義->osal_event_hdr_t查看定義)。這樣找到這個(gè)event后發(fā)現(xiàn)它是uint8型的,說明它只有8位,這個(gè)顯然和上面提到的事件不一樣嘛~
這個(gè)問題解決了,那么我們應(yīng)該寫在哪個(gè)事件下面呢?MT_UART.c->MT_UartProcessZToolData()->osal_msg_send()查看定義,看到函數(shù)最后一行
osal_set_event( destination_task, SYS_EVENT_MSG );
看到這里應(yīng)該就明白了,我們這個(gè)是屬于SYS_EVENT_MSG事件噠。至于具體怎么工作,就是把消息放入隊(duì)列,處理消息之類的,這里不再多說啦。
回到SampleApp.c下的事件處理函數(shù)SampleApp_ProcessEvent(),在SYS_EVENT_MSG事件下還有一個(gè)選擇“MSGpkt->hdr.event”,好啦,這個(gè)就是我們熟悉的“小事件”啦(因?yàn)橹挥?位,英文名又叫event,所以我直接這樣叫它啦)。現(xiàn)在明白了吧,我們要寫一個(gè)case語(yǔ)句把事件CMD_SERIAL_MSG放進(jìn)去(這個(gè)事件名字就是初始化串口數(shù)據(jù)包的時(shí)候?qū)戇M(jìn)去的那個(gè))。同時(shí)要在SampleApp.c文件中添加一個(gè)頭文件#include“MT.h”,CMD_SERIAL_MSG是在這個(gè)文件中定義的。
代碼如下:
//處理串口數(shù)據(jù)包 case CMD_SERIAL_MSG: SampleApp_SerialMSG((mtOSALSerialData_t *)MSGpkt); break;
代碼里面SampleApp_SerialMSG()是什么函數(shù)呢?找了一圈沒有找到,其實(shí)它是要自己寫噠~你可以大概瀏覽一下SampleApp.c里面的函數(shù),有沒有發(fā)現(xiàn)沒有一個(gè)符合我們的需求的?所以要自己寫咯。
代碼如下:
1 void SampleApp_SerialCMD(mtOSALSerialData_t *sd)
2
3 {
4
5 uint8 i, num = sd->msg[0];
6
7 uint8 *ch = sd->msg;
8
9
10
11 for(i=1; i<=num; i++)
12
13 HalUARTWrite(0, ch+i, 1);
14
15 HalUARTWrite(0, "
", 1);
16
17
18
19 //這個(gè)是發(fā)送數(shù)據(jù)包的函數(shù),復(fù)制后修改參數(shù)即可
20
21 void SampleApp_SerialMSG(mtOSALSerialData_t *sd)
22
23 {
24
25 uint8 len = sd->msg[0];
26
27 uint8 *ch = &sd->msg[1];
28
29
30
31 HalUARTWrite(0, "I:", 2);
32
33 HalUARTWrite(0, ch, len);
34
35 HalUARTWrite(0, "
", 1);
36
37
38
39 if ( AF_DataRequest( &SampleApp_Flash_DstAddr, &SampleApp_epDesc,
40
41 SAMPLEAPP_SERIAL_CLUSTERID,
42
43 len+1,
44
45 ch,
46
47 &SampleApp_TransID,
48
49 AF_DISCV_ROUTE,
50
51 AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
52
53 {
54
55 }
56
57 else
58
59 {
60
61 // Error occurred in request to send.
62
63 }
64
65 }
SampleApp_SerialCMD
代碼其余部分不解釋,需要注意的是發(fā)送數(shù)據(jù)函數(shù)里的一個(gè)參數(shù)SAMPLEAPP_SERIAL_CLUSTERID,你去查看定義會(huì)發(fā)現(xiàn)查不到......嘿嘿,這個(gè)是要自己加上去噠。如圖2所示。
圖2
這個(gè)參數(shù)的作用之前已經(jīng)說過啦,名字可以任意取。要注意的是SAMPLEAPP_MAX_CLUSTERS這個(gè)的值也要相應(yīng)變大,看它名字就知道啦,它表示所有這類數(shù)中的最大值。
實(shí)驗(yàn)進(jìn)行到這里,我們已經(jīng)可以把程序燒錄到一個(gè)zigbee進(jìn)行測(cè)試了。因?yàn)闆]有接收部分代碼,實(shí)驗(yàn)結(jié)果只是通過串口助手發(fā)送數(shù)據(jù)給zigbee然后zigbee再發(fā)回給PC端。實(shí)驗(yàn)結(jié)果見圖3。
圖3
進(jìn)行到這一步有沒有點(diǎn)小開心?不過還要再堅(jiān)持下,還有接收部分的呢~
接收部分代碼和昨天的實(shí)驗(yàn)非常類似,就不詳細(xì)說啦,看看代碼應(yīng)該就能看懂啦~
1 void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
2
3 {
4
5 // uint16 flashTime;
6
7 // uint8 len = pkt->cmd.Data[0];
8
9 uint8 *ch = &pkt->cmd.Data[0];
10
11
12
13 switch ( pkt->clusterId )
14
15 {
16
17 case SAMPLEAPP_SERIAL_CLUSTERID:
18
19
20
21 HalUARTWrite(0, "friend:", 7);
22
23 HalUARTWrite(0, ch, pkt->cmd.DataLength-1);
24
25 HalUARTWrite(0, "
", 1);
26
27 break;
28
29 /*
30
31 case SAMPLEAPP_PERIODIC_CLUSTERID:
32
33 break;
34
35
36
37 case SAMPLEAPP_FLASH_CLUSTERID:
38
39 flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] );
40
41 HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) );
42
43 break;*/
44
45 }
46
47 }
SampleApp_MessageMSGCB
最后還要注意一點(diǎn)!兩個(gè)zigbee一個(gè)做協(xié)調(diào)器,一個(gè)做路由器!不然無法通信!就因?yàn)檫@個(gè)原因我被坑了好幾個(gè)小時(shí)......
四、實(shí)驗(yàn)結(jié)果
圖4兩個(gè)zigbee實(shí)驗(yàn)結(jié)果
圖5三個(gè)zigbee實(shí)驗(yàn)結(jié)果(一個(gè)協(xié)調(diào)器,兩個(gè)路由器)
五、總結(jié)流程
圖6
總結(jié)
以上是生活随笔為你收集整理的「ZigBee模块」协议栈-串口透传,打造无线串口模块的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑怎么连接双频路由器的5g频段双频路由
- 下一篇: 路由器这样放如何将路由器放去房间