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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 综合教程 >内容正文

综合教程

「ZigBee模块」协议栈-串口透传,打造无线串口模块

發(fā)布時(shí)間:2024/6/21 综合教程 33 生活家
生活随笔 收集整理的這篇文章主要介紹了 「ZigBee模块」协议栈-串口透传,打造无线串口模块 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前面寫比較仔細(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。