Zigbee使用MT层实现串口写和读操作,简要了解osal_msg_send消息机制

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_P1ZTOOL_P2,则这个串口结构体有个属性callBackFunc就等于MT_UartProcessZToolDatacallBackFunc的意思是回调函数的意思,也就是说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);

 

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值