工作原理,中斷工作原理,然後分析輸入和輸出Buffer的處理等內容,學習者跟著協議棧的
串口底層壹直到頂層轉圈、轉圈、再轉圈,蒙圈了。
實際上,從應用角度講,我們根本就沒有必要去深入的追究Zstack中串口的工作機制,
也沒有必要去搞清楚到底是怎麽DMA和Interrupt的,我們只要調用幾個簡單函數就可以正
常使用串口了。其實協議棧已經把使用串口的條件準備好了,我們何必再糾結硬件底層實現
呢?應用者應該把協議棧看作壹個平臺,平臺之上的應用才是我們的目標。下面我就講壹下
如何利用協議棧現有平臺來實現自己的串口應用。這裏我所提及的現有平臺即是Zstack自帶
的MT包,其實Zstack中的這個MT包功能相當強大,通過TI提供的ZTOOL工具可以用串
口的方式同整個協議棧進行交互,在我們編寫Zigbee應用程序的過程中,很多不知道該如何
調用的函數都能在MT中找到參考!這個不多說了,有興趣的同學可以去專門研究壹下MT
包。
二、使用方法
在MT包中,已經有了串口初始化即串口數據處理函數可用,關鍵的幾個函數出現在
MT_Uart.c文件中。我們拿出來幾個關鍵函數說明壹下(我撿重要語句註釋):
第壹個函數
void MT_UartInit ()
{ // 這個是MT中的壹個串口初始化函數,主要作用是初始化串口工作的壹些規矩
halUARTCfg_t uartConfig;
App_TaskID = 0; //處理串口數據的任務ID,可以先不管
uartConfig.configured = TRUE;
uartConfig.baudRate = MT_UART_DEFAULT_BAUDRATE;
//默認38400波特率;可以更改,但是可能有新問題,具體解釋內容比較多,我不說;
uartConfig.flowControl = FALSE;//MT_UART_DEFAULT_OVERFLOW;
//禁止硬件流控,如果妳的串口只有RXD,TXD和GND三條線,必須這麽做;
uartConfig.flowControlThreshold= MT_UART_DEFAULT_THRESHOLD;
uartConfig.rx.maxBufSize = MT_UART_DEFAULT_MAX_RX_BUFF;
uartConfig.tx.maxBufSize = MT_UART_DEFAULT_MAX_TX_BUFF;
uartConfig.idleTimeout = MT_UART_DEFAULT_IDLE_TIMEOUT;
uartConfig.intEnable = TRUE;
#if defined (ZTOOL_P1) || defined (ZTOOL_P2)
uartConfig.callBackFunc = MT_UartProcessZToolData;
//如果編譯的時候選擇使用ZTOOL,那麽MT_UartProcessZtoolData將會處理串口接到的數
//據串
#elif defined (ZAPP_P1) || defined (ZAPP_P2)
uartConfig.callBackFunc = MT_UartProcessZAppData;
//如果編譯的時候沒有選擇ZTOOL,而是選擇使用了ZAPP,則由MT_UartProcessZAppData
//函數來處理串口數據串
(*如果是用CC2530的P0口那兩根串口引腳,妳就要define ZTOOL_P1,如果是P1口的那
兩根串口引腳,妳就要define ZTOOL_P2,對於ZAPP_P1和ZAPP_P2也是壹個情況*)
#else
uartConfig.callBackFunc = NULL;
//這個地方,如果妳有興趣自己寫壹個串口處理函數,那麽妳實現壹個My_UartProcessData
//函數,然後填到這裏,替換NULL。
#endif
#if defined (MT_UART_DEFAULT_PORT)
HalUARTOpen (MT_UART_DEFAULT_PORT, &uartConfig);
//如果定義了默認串口,(0或者1),打開串口,這個HalUartOpen函數會做壹大堆工作,具
//體說來就是初始化唄。。。,我沒有必要展開。需要註意的是這個函數把前面哪壹堆初始化
//的uartConfig做為參數傳進去了噢!
#else
(void)uartConfig;
#endif
#if defined (ZAPP_P1) || defined (ZAPP_P2)
MT_UartMaxZAppBufLen = 1;
MT_UartZAppRxStatus = MT_UART_ZAPP_RX_READY;
//這兩句,如果是不想使用MT_UartProcessZToolData來處理串口數據,就。。。。
//再說就要深入串口機制了,網上講解文章太多了,自己看吧,我壹會兒使用
//MT_UartProcessZToolData。
#endif
}
第二個函數
void MT_UartRegisterTaskID( byte taskID )
{
App_TaskID = taskID;
}
第三個函數
void MT_UartProcessZToolData ( uint8 port, uint8 event )
osal_msg_deallocate ( (uint8 *)pMsg );
}
我們往上看看這個Message是什麽?MT_UartProcessZToolData函數開始不遠的地方有以下程
序段:
if (pMsg)
{
pMsg->hdr.event = CMD_SERIAL_MSG;
pMsg->msg = (uint8*)(pMsg+1);
pMsg->msg[MT_RPC_POS_LEN] = LEN_Token;
state = CMD_STATE1;
}
從這裏看到,這個函數建立了壹個消息頭,用CMD_SERIAL_MSG做為消息,那麽
osal_msg_send給任務的那個消息將會以CMD_SERIAL_MSG出現。。。。。。。
好了,以上三個函數看完,我們試著使用壹下:
以GenericApp例子為例:
void GenericApp_Init( byte task_id )
{
XXXXXXXXXXXXX
XXXXXXXXXXXXX
(這個函數的最後,其實放在這個函數的哪裏都行)
MT_UartInit(); //added by kennan
MT_UartRegisterTaskID(GenericApp_TaskID);
}
再看壹下MT_UartRegisterTaskID(GenericApp_TaskID):
void MT_UartRegisterTaskID( byte taskID )
{
App_TaskID = taskID;
}
好了,這樣,我們順利地把串口發來的數據用MT_UartProcessZToolData來處理,並且把處理
後的數據打包發給了任務GenericApp_TaskID。
接下來,我們看壹下在GenericApp_TaskID中如何處理吧。
在GenericApp的主處理函數中:
UINT16 GenericApp_ProcessEvent( byte task_id, UINT16 events )
{
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t*)osal_msg_receive( GenericApp_TaskID );
while ( MSGpkt )
{
switch ( MSGpkt->hdr.event )
{
case ZDO_CB_MSG:
GenericApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt );
break;
case KEY_CHANGE:
GenericApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t
*)MSGpkt)->keys );
break;
//增加
Case CMD_SERIAL_MSG:
ProcessUartData((mtOSALSerialData_t *)MSGpkt);
//這個函數妳自己實現吧,想做啥呢?想做啥就做啥。如果想把接
//到的數據發回串口,調用HalUARTWrite就行了。
}
如果妳不知道如何提取串口消息並處理,我就好人做到底,幫妳實現壹個ProcessUartData()
函數吧。這個函數的作用是把接收到的數據從CM0開始壹直到payload的最後壹個字節發送
給串口回顯,不包括校驗字節噢。
ProcessUartCommand((mtOSALSerialData_t *)MSGpkt)
{ //為了正確地進行下面工作,用mtOSALSerialData_t類型來指向整個zigbee數據包(不是串
//口數據包)
uint8 *pMsg;
pMsg = MSGpkt->msg;
//定義壹個指針,指向真正的串口接收數據存放位置,MSGptk裏面還有壹些別的Header噢。
switch ( MSGpkt->hdr.event )
{
case CMD_SERIAL_MSG://如果是串口消息。。。。
HalLedSet( HAL_LED_RED, HAL_LED_MODE_FLASH );
//用LED燈指示壹下收到數據啦
uint8 *pBuffer;
uint8 datalength;
uint8 i;
//定義幾個變量,為從接收到的串口包裏面提取數據以及寫回串口做準備
datalength = *pMsg++;
//串口包中第壹個字節是數據長度噢
pBuffer = osal_mem_alloc(datalength);
//分配壹塊內存準備把串口消息數據拿出來
if(pBuffer != NULL)
{
for(i = 0;i < datalength; i++)
*pBuffer++ = *pMsg++;
//把消息中的串口數據按照datalength數量挨個撈出來放血(放血啥意思?把池子
//裏面那東西撈出來放血,明白不?
HalUARTWrite(0,pBuffer,datalength);
//撈出來放血的串口數據再寫回串口,也就是送到串口助手顯示
Osal_memfree(pBuffer);
//動態申請的內存記得用完了free壹下噢
}
break;
default:
break;
}
}
說明:因為ZTOOL發過來的數據是有格式的,所以如果妳用串口助手來測試,那麽發的數
據要按照格式來,如果妳不想按那個格式,妳可以自己去修改MT_UartProcessZToolData裏面
的相關程序。這種方法對於想要通過PC來控制zigbee的應用場合非常實用,因為妳PC發過
來的壹般也會有命令和數據,如果不用MT的格式,妳自己也要規範壹個格式,既然MT已
經有了,我們就借用就好。
對於MT_UartProcessZAppData這個處理方法,也就是妳define了ZAPP_P1或ZAPP_P2
的情況,其機制也是類似的,只不過沒有規定格式,妳更自由,這裏我就不多說了。
再有,如果妳真的在測試的時候不知道那麽壹長串數據的xor 結果是多少,也可以去
MT_UartProcessZToolData函數中,找到:
//if ((MT_UartCalcFCS ((uint8*)&pMsg->msg[0], MT_RPC_FRAME_HDR_SZ +
LEN_Token) == FSC_Token))
{
osal_msg_send( App_TaskID, (byte *)pMsg );
}
else
//{
//osal_msg_deallocate ( (uint8 *)pMsg );
//}
把我標紅的幾個位置註釋掉,就不會校驗了,妳也不用算xor結果了,不過發數據的時候
這個位置還是要的,妳隨便填個0好了。