MT层的串口API文件是MT_UART.c和MT_UART.h。如下图:
首先在应用层初始化函数下添加以下代码:
MT_UartInit();//串口初始化
MT_UartRegisterTaskID(task_id);//注册该事件,注意,该函数必须在MT_UartInit()调用之后才调用
HalUARTWrite(0 , "hello FANG\n", sizeof("hello FANG\n"));//打印串口。
但是现在还没配置好,我们还需要修改一些初始化选项。右键MT_UartInit(),选择go to definition of ‘MT_UartInit’,如下图:
这样就跟踪到了这个函数下来,该函数前面主要部分如下图:
有点串口基础的同学大概了解该结构体的配置参数,首先配置方式为TRUE,第二个参数即bauRate是波特率,
MT_UART_DEFAULT_BAUDRATE,这个参数意思是默认波特率,go to definiton跟踪它,可以看它对应的波特率参数,跟踪结果如下
上图本人设置的是115200的速度,我们再跟踪HAL_UART_BR_115200,可以看到以下结果:
因此可以根据以上选择来更改 MT_UART_DEFAULT_BAUDRATE的指定参数。现在我们再回到前面的MT_UartInit函数下来,到第三个属性是flowControl,是流控的意思,问是否使用流控,该协议栈默认是打开流控的,我们要修改为false,跟踪对应的参数
MT_UART_DEFAULT_OVERFLOW,更改结果如下:
剩下的参数不做修改,不过我们也可以分析一下它们大概的意思,有读最大缓冲区大小,有写最大缓冲区大小,这些我们只需默认。接下来还需要修改一个地方,如果我们的串口需要读操作,那么我们就需要看MT_UARTInit函数下这个部分代码块:
这里我们就可以定义ZTOOL_P1,我们可以通过非程序形式定义这个宏,如图:
这里我们还需要把MT_TASK这个宏给关闭掉,不然测试串口数据可能会乱码,只要在MT_TASK前面加个x就行,或者直接删除掉MT_TASK.
上述代码块语义是如果定义ZTOOL_P1或ZTOOL_P2,则这个串口结构体有个属性callBackFunc就等于MT_UartProcessZToolData,callBackFunc的意思是回调函数的意思,也就是说MT_UartProcessZToolData是一个函数,在这个MT_UART.c中可以找到该函数,当然也可以直接跟踪MT_UartProcessZToolData,结果如下:
这个函数处理的就是一个串口不断读取的函数,在注册了这个MT_UART后,这个函数会被不断的被调用,相当于事件一样,当串口有数据时,就处理这个数据并发送到上层即我们的应用层,但是,经过本人测试,该函数处理的结果无法成功地把数据发送出去,故我们需要重写这块代码,我们把这块代码修改成以下结果:
void MT_UartProcessZToolData ( uint8 port, uint8 event )
{
uint8 flag=0,i,j=0; //flag 是判断有没有收到数据,j 记录数据长度
uint8 buf[128]; //串口buffer 最大缓冲默认是128,我们这里用128.
(void)event; // Intentionally unreferenced parameter
while (Hal_UART_RxBufLen(port)) //检测串口数据是否接收完成
{
HalUARTRead (port,&buf[j], 1); //把数据接收放到buf 中
j++; //记录字符数
flag=1; //已经从串口接收到信息
}
if(flag==1) //已经从串口接收到信息
{
/* Allocate memory for the data */
//分配内存空间,为机构体内容+数据内容+1 个记录长度的数据
pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof( mtOSALSerialData_t )+j+1);
//事件号用原来的CMD_SERIAL_MSG
pMsg->hdr.event = CMD_SERIAL_MSG;
pMsg->msg = (uint8*)(pMsg+1); // 把数据定位到结构体数据部分
pMsg->msg [0]= j; //给上层的数据第一个是长度
for(i=0;i<j;i++) //从第二个开始记录数据
pMsg->msg[i+1]= buf[i];
osal_msg_send( App_TaskID, (byte *)pMsg ); //登记任务,发往上层
/* deallocate the msg */
osal_msg_deallocate ( (uint8 *)pMsg ); //释放内存
}
}
这样就能成功把消息数据通过osal_msg_send(App_TaskID,(byte*)pMsg)发送给应用层,也就是我们刚刚注册该MT_UART的这层,换句话说就是刚刚调用了MT_UartRegisterTaskID(task_id)的这层。理论上pMsg这个变量可以是任何类型的变量,包括任何的结构体变量,但是,这样的变量上在协议栈中有一个规范,就是必须数据头要有一个属性是osal_event_hdr_t的类型,记住这个osal_event_hdr_t类型,它是消息传递的关键。以上代码首先我们先看pMsg是一个mtOSALSerialData_t类型。其原型如下:
typedef struct
{
osal_event_hdr_t hdr;
uint8 *msg;
} mtOSALSerialData_t;
这个类型是MT_UART.h已经定义好的,并非自定义。当然也可以自己定义,当理解了大致的消息机制的框架我们自己定义类似的结构体。分析这个结构体,mtOSALSerialData_t里面有个osal_event_hdr_t hdr,这个上文提过是消息传递的关键,我们跟踪它,可以看到函数原型如下:
typedef struct
{
uint8 event;
uint8 status;
} osal_event_hdr_t;
根据前面代码块的描述pMsg->hdr.event = CMD_SERIAL_MSG,就是该事件的ID,status我们并没有给它赋值,因为此时并没用到,这里我如何分配内存的给pMsg的呢,该实现方式是:
pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof( mtOSALSerialData_t )+j+1);
假如我们跑动这个程序,然后再串口调试助手发送“123456”,说明这里就有6个字符了,通过第一个while循环后,得到 j 的值为6,那为什么+j之后还要+1呢,原因是我们再需要一个空间来描述字符长度,而这个空间只定义了1个字符的空间即最大长度是255,if语句处理数据的图解如下:
此时我们把这样的数据通过osal_msg_send(App_TaskID,(byte*)pMsg)发送给应用层。
接下来应用层如何接收呢,其实该事件发送应用层会触发应用层的SYS_EVENT_MSG系统事件。在我们的应用层里接收到该任何事件都会先把数据包先强制转换成afIncomingMSGPacket_t的类型,然后判断该判断这个数据头的event事件是什么,因此前面提到,其实发送任何数据都没问题,但是前提是数据头必须要有个osal_event_hdr_t类型的变量,我们可以跟踪这个afIncomingMSGPacket_t:
typedef struct
{
osal_event_hdr_t hdr; /* OSAL Message header */
uint16 groupId; /* Message's group ID - 0 if not set */
uint16 clusterId; /* Message's cluster ID */
afAddrType_t srcAddr; /* Source Address, if endpoint is STUBAPS_INTER_PAN_EP,
it's an InterPAN message */
uint16 macDestAddr; /* MAC header destination short address */
uint8 endPoint; /* destination endpoint */
uint8 wasBroadcast; /* TRUE if network destination was a broadcast address */
uint8 LinkQuality; /* The link quality of the received data frame */
uint8 correlation; /* The raw correlation value of the received data frame */
int8 rssi; /* The received RF power in units dBm */
uint8 SecurityUse; /* deprecated */
uint32 timestamp; /* receipt timestamp from MAC */
uint8 nwkSeqNum; /* network header frame sequence number */
afMSGCommandFormat_t cmd; /* Application Data */
} afIncomingMSGPacket_t;
虽然后面的数据会跟我们发送的mtOSALSerialData_t类型不一样,只要我们不去随便触碰不应该存在的数据就行了。因为我们知道后面数据是不存在groupid,clusterld,srcAddr等等,我们只知道这个数据包的存储空间结构是我们发送的msg和6和“123456”.因此,我们在测试这个数据时要再次转换回来。首先在uint16 Fang_ProcessEvent( uint8 task_id, uint16 events )函数下找到:
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( Fang_TaskID );
while ( MSGpkt )
{
switch ( MSGpkt->hdr.event )
{
case AF_INCOMING_MSG_CMD:
Fang_MessageMSGCB( MSGpkt );
break;
// Received whenever the device changes state in the network
case ZDO_STATE_CHANGE:
。。。。。
然后再switch里面添加case CMD_SERIAL_MSG:,测试代码块我们可以以下实现:
case CMD_SERIAL_MSG:
UartMsg = (mtOSALSerialData_t *)MSGpkt;
HalUARTWrite(0,&UartMsg->msg[1],UartMsg->msg[0]);
break;
UartMsg在函数的前面定义如下:
mtOSALSerialData_t * UartMsg;
编译时,可能会说什么类型未定义,一般是头文件没加进来,自行检查一般头文件需要加上MT_UART.h和MT.h,编译成功后,烧录程序打开串口调试助手,试着发送数据:
实验成功。 如果数据是乱码,可能是宏的定义列下有个MT_TASK的影响,把这个宏删除掉即可。也可以在这个宏前面加点其他元素比如:
当然有些人直接封装成函数形式实现如下:
case CMD_SERIAL_MSG:
SerialCMD((mtOSALSerialData_t *)MSGpkt);
break;
SerialCMD自己定义该函数 void SerialCMD(mtOSALSerialData_t * UartMsg);