如何在Zstack中使用串口
发布网友
发布时间:2022-05-09 22:53
我来回答
共1个回答
热心网友
时间:2023-05-19 00:12
网上和论坛里面很多帖子都把精力集中到分析协议栈的串口工作机制上,比如分析DMA
工作原理,中断工作原理,然后分析输入和输出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好了。