MT层(也有叫MT包的)是Z-Stack自带的,TI公司提供的ZTOOL工具可以通过串口的方式来和模块通信,这个工具发送的数据具有一定的协议,MT层的函数就具有解析这些协议的功能,因此在使用MT层的函数的时候,必须要遵守TI公司提供的串口协议来发送和接收数据。协议如下:
0xFE:数据帧头
DataLength:Datapayload的数据长度(不包括命令字节),以字节计,低字节在前;
CM0:命令低字节;
CM1:命令高字节;(ZTOOL软件就是通过发送一系列命令给MT实现和协议栈交互)
Data payload:数据帧具体的数据,这个长度是可变的,但是要和DataLength一致;
FCS:校验和,从DataLength字节开始到Data payload最后一个字节所有字节的异或按字节操作;
在MT_UART.h中可以被外部调用的有这些函数:
extern void MT_UartInit (void);
extern uint8 MT_UartCalcFCS( uint8 *msg_ptr, uint8 length );
extern void MT_UartRegisterTaskID( uint8 taskID );
void MT_UartProcessZToolData ( uint8 port, uint8 taskId );
void MT_UartProcessZAppData ( uint8 port, uint8 event );
1、MT_UartInit 串口初始化函数:
void MT_UartInit ()
{
halUARTCfg_t uartConfig;//定义串口配置参数的结构体,包括波特率、使能串口等等
App_TaskID = 0;
uartConfig.configured = TRUE;
uartConfig.baudRate = MT_UART_DEFAULT_BAUDRATE;
uartConfig.flowControl = MT_UART_DEFAULT_OVERFLOW;
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;
/*预定义,作用是使用MT_UartProcessZToolData还是MT_UartProcessZAppData
*MT_UartProcessZToolData为和官方的串口调试工具通信,
*MT_UartProcessZAppData为传输app数据,这个需要做一些修改才可以使用*/
#if defined (ZTOOL_P1) || defined (ZTOOL_P2)
uartConfig.callBackFunc = MT_UartProcessZToolData;
#elif defined (ZAPP_P1) || defined (ZAPP_P2)
uartConfig.callBackFunc = MT_UartProcessZAppData;
#else
uartConfig.callBackFunc = NULL;//如果都不想用,那么就可以自己填一个串口处理回调函数
#endif
#if defined (MT_UART_DEFAULT_PORT)
HalUARTOpen (MT_UART_DEFAULT_PORT, &uartConfig);//串口初始化,调用上面的uartConfig作为参数,函数内还根据是否使用DMA来做不同的初始化
#else
(void)uartConfig;
#endif
#if defined (ZAPP_P1) || defined (ZAPP_P2)//同样的预定义,先不管这两个参数作用
MT_UartMaxZAppBufLen = 1;
MT_UartZAppRxStatus = MT_UART_ZAPP_RX_READY;
#endif
}
这个函数主要就是完成了串口的初始化,以及串口DMA的配置,最后根据宏定义ZTOOL_P1、ZTOOL_P2来确定MT的回调函数,如果都没有定义的话那么就使用用户指定的函数。如果不想用官方提供的串口模块,可以参考MT的初始化来完成自己的串口初始化代码。下面为我写的串口初始化函数,写的不好,欢迎大佬指点。
static char Uart_RX[200];
static char Uart_TX[200];
static void FS_Uart_CallBack(uint8 port,uint8 event);
/*
*串口初始化函数
*UARTx:要操作的是串口几
*baud:串口波特率
*/
void usart_init(uint8 UARTx,uint8 baud)
{
halUARTCfg_t uartConfig;
assert(ASSERT_UART_BAUD(baud));
assert(ASSERT_UARTX(UARTx));
uartConfig.configured = TRUE; // 2x30 don't care - see uart driver.
uartConfig.baudRate = baud;
uartConfig.flowControl = FALSE;
uartConfig.flowControlThreshold = 32; // 2x30 don't care - see uart driver.
uartConfig.rx.maxBufSize = 32; // 2x30 don't care - see uart driver.
uartConfig.tx.maxBufSize = 32; // 2x30 don't care - see uart driver.
uartConfig.idleTimeout = 6; // 2x30 don't care - see uart driver.
uartConfig.intEnable = TRUE; // 2x30 don't care - see uart driver.
uartConfig.callBackFunc = FS_Uart_CallBack; //指定串口回调函数
HalUARTOpen (UARTx, &uartConfig);
}
/*串口回调函数*/
static void FS_Uart_CallBack(uint8 port,uint8 event)
{
if(( event & ( HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT ) ) )
{
if(port == HAL_UART_PORT_0)
{
uint16 len = Hal_UART_RxBufLen(HAL_UART_PORT_0); //取出本次接收到的字符长度
HalUARTRead(HAL_UART_PORT_0, Uart_RX, len);
if ( len > 0 )
{
//成功接收到数据,对数据进行处理
osal_printf("%s",Uart_RX);
}
}
else//port == HAL_UART_PORT_1
{
}
}
}
//串口printf 函数
//确保一次发送数据不超过Uart_TX_LEN字节
void osal_printf(char* fmt,...)
{
uint16 i,j;
va_list ap;
va_start(ap,fmt);
vsprintf((char*)Uart_TX,fmt,ap);
va_end(ap);
i=strlen((const char*)Uart_TX); //此次发送数据的长度
for(j=0;j<i;j++) //循环发送数据
{
HalUARTWrite(HAL_UART_PORT_0, &Uart_TX[j], 1);
}
}
2、void MT_UartRegisterTaskID( uint8 taskID );
void MT_UartRegisterTaskID( byte taskID )//注册串口任务,在应用层中的任务注册
{
App_TaskID = taskID;
}
3、void MT_UartProcessZToolData ( uint8 port, uint8 taskId );
void MT_UartProcessZToolData ( uint8 port, uint8 event )
{
uint8 ch;
uint8 bytesInRxBuffer;
(void)event; // Intentionally unreferenced parameter
while (Hal_UART_RxBufLen(port))
{
HalUARTRead (port, &ch, 1);//从RX缓冲区中读取一个字节
switch (state)
{
//state开始的值肯定是0x00(猜的),判断数据第一个字节是不是0xFE,如果是那么state = 0x03,以此类推,下面的也是这个意思
case SOP_STATE: //SOP_STATE = 0x00
if (ch == MT_UART_SOF) //MT_UART_SOF = 0xFE
state = LEN_STATE; //LEN_STATE = 0x03
break;
case LEN_STATE: //LEN_STATE = 0x03
LEN_Token = ch;
tempDataLen = 0;
//这里的C语言语法有点难懂(对我来说),给pMsg分配内存空间,大小等于sizeof ( mtOSALSerialData_t )结构体大小+1个表示数据长度的标识符占用的空间+两个CMD占用的空间+数据占用的空间,注意这里最后强制转换成mtOSALSerialData_t这个结构体的指针,也就是pMsg指向了该分配内存的首地址,而且pMsg是mtOSALSerialData_t类型的。pMsg+1 等于pMsg+sizeof ( mtOSALSerialData_t )的大小,此时指向了数据的长度标识符。而且此时的协议第二个字节表示的数据的长度,ch = dataLength
pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof ( mtOSALSerialData_t ) +
MT_RPC_FRAME_HDR_SZ + LEN_Token );
if (pMsg)
{
pMsg->hdr.event = CMD_SERIAL_MSG;
pMsg->msg = (uint8*)(pMsg+1);
pMsg->msg[MT_RPC_POS_LEN] = LEN_Token; //发送CMD_SERIAL_MSG
state = CMD_STATE1; //CMD_STATE1 = 0x01
}
else
{
state = SOP_STATE;
return;
}
break;
//和下面的两个case都表示了CMD
case CMD_STATE1: //CMD_STATE1 = 0x01
pMsg->msg[MT_RPC_POS_CMD0] = ch;
state = CMD_STATE2; //CMD_STATE2 = 0x02
break;
case CMD_STATE2:
pMsg->msg[MT_RPC_POS_CMD1] = ch;
if (LEN_Token)
{
state = DATA_STATE; //DATA_STATE = 0x04
}
else
{
state = FCS_STATE; //FCS_STATE = 0x05
}
break;
//下面的case里就是处理数据的部分
case DATA_STATE:
//对于刚刚接手的这个字节ch的处理
pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen++] = ch;
//然后通过Hal_UART_RxBufLen这个函数看看Rx缓冲区还有多少个字节
bytesInRxBuffer = Hal_UART_RxBufLen(port);
//如果Rx缓冲区的字节数<=(总长度 - 已读出的长度),那么一次性将缓冲区的数据读出来,否则就将(总长度 - 已读出的长度 )的数据读出来,这里很好理解,其实我认为这里数据处理在上面的 case CMD_STATE2:来完成就好了,就免去了读取第一个字节的数据这步了,可能为了更直观吧。
if (bytesInRxBuffer <= LEN_Token - tempDataLen)
{
HalUARTRead (port, &pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen], bytesInRxBuffer);
tempDataLen += bytesInRxBuffer;
}
else
{
HalUARTRead (port, &pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen], LEN_Token - tempDataLen);
tempDataLen += (LEN_Token - tempDataLen);
}
//读完后,不要忘了把读取的数量更新下,全部读完后, state = FCS_STATE来看看是不是都读正确了
if ( tempDataLen == LEN_Token )
state = FCS_STATE;
break;
case FCS_STATE: //FCS_STATE = 0x05
FSC_Token = ch;
//通过校验位,来判断数据是否有错误
if ((MT_UartCalcFCS ((uint8*)&pMsg->msg[0], MT_RPC_FRAME_HDR_SZ + LEN_Token) == FSC_Token))
{
//如果正确,那么发送消息到应用层,这个消息是上面case LEN_STATE:中的
pMsg->hdr.event = CMD_SERIAL_MSG;
osal_msg_send( App_TaskID, (byte *)pMsg );
}
else
{
osal_msg_deallocate ( (uint8 *)pMsg );
}
state = SOP_STATE;
break;
default:
break;
}
}
}
4、void MT_UartProcessZAppData ( uint8 port, uint8 event );
这个函数就比较简单了,没有去分析那么多协议的东西
下面两个参数都是标志位,感觉没有特别大的用处。。。。
// MT_UartMaxZAppBufLen = 1;
// MT_UartZAppRxStatus = MT_UART_ZAPP_RX_READY;
void MT_UartProcessZAppData ( uint8 port, uint8 event )
{
osal_event_hdr_t *msg_ptr;
uint16 length = 0;
//确定Rx缓冲区数据长度
uint16 rxBufLen = Hal_UART_RxBufLen(MT_UART_DEFAULT_PORT);
//MT_UartMaxZAppBufLen一次最大处理数据的长度,查看缓冲区长度是否大于这个长度
if ((MT_UartMaxZAppBufLen != 0) && (MT_UartMaxZAppBufLen <= rxBufLen))
{
length = MT_UartMaxZAppBufLen;
}
else
{
length = rxBufLen;
}
if (event == HAL_UART_TX_FULL)
{
// Do something when TX if full
return;
}
//一堆宏定义,没有深究到底有何作用,都是满足条件的
if (event & ( HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT))
{
//App_TaskID = taskID;初始化函数后,任务ID就已经不为0了
if ( App_TaskID )
{
//在初始化函数中串口状态标识符,且有数据
if ((MT_UartZAppRxStatus == MT_UART_ZAPP_RX_READY ) && (length != 0))
{
//关闭流控
MT_UartAppFlowControl (MT_UART_ZAPP_RX_NOT_READY);
//分配内存,大小=数据长度+消息头长度
msg_ptr = (osal_event_hdr_t *)osal_msg_allocate( length + sizeof(osal_event_hdr_t) );
if ( msg_ptr )
{
msg_ptr->event = SPI_INCOMING_ZAPP_DATA;
msg_ptr->status = length;
HalUARTRead( MT_UART_DEFAULT_PORT, (uint8 *)(msg_ptr + 1), length );
//发送消息到应用层
osal_msg_send( App_TaskID, (uint8 *)msg_ptr );
}
}
}
}
}