ZIGBEE通讯-11.协议栈串口透传

在协议栈中已经自带了串口的驱动与使用函数,所以只需要对串口进行初始化,并将串口绑定在APP层,重新编写串口回调函数就可以使用了。接下来将主要分析在协议栈中串口的初始化、串口数据的接收等。

从main函数进入系统初始化函数,再进入任务初始化函数,找到MT层任务初始化MT_TaskInit(),进入其中,将串口初始化函数与绑定函数复制到APP层任务初始化函数中。如下:

APP层想要调用MT层串口函数,必须包含头文件MT_UART.h,如图:

 进入函数MT_UartInit()中,对串口进行配置,函数中配置了串口的波特率、流控、回调函数等,如图:

将波特率改为常用的115200,由于我们的串口用的TX\RX两线式,所以必须关闭流控如下:

 下面来分析回调函数,当串口有数据接收时,就会调用串口回调函数。先来看下回调函数的注释与介绍:

/***************************************************************************************
 * @fn      MT_UartProcessZToolData
 *
 * @brief   | SOP | Data Length  |   CMD   |   Data   |  FCS  |
 *          |  1  |     1        |    2    |  0-Len   |   1   |
 *
 *          Parses the data and determine either is SPI or just simply serial data
 *          then send the data to correct place (MT or APP)
 *
 * @param   port     - UART port
 *          event    - Event that causes the callback
 *
 *
 * @return  None
**************************************************************************************/

根据注释所讲,如果PC想通过串口给板子发消息,如果要使用官方的回调函数,就必须按照上面的格式发送,否则板子是收不到任何数据的。现在我们要实现的是串口可以接收到任何消息,然后原封不动的将消息发送出去,所以我们可以对官方回掉函数进行分析,然后对其进行修改,让其符合我们的需求。接下来先来分析官方回调函数:

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); // 读一个字节,读一个缓冲区清一个

    switch (state) // 判断状态机
    {
      case SOP_STATE: // 起始位
        if (ch == MT_UART_SOF) // MT_UART_SOF的值为0xFE 为开头
          state = LEN_STATE;
        break;

      case LEN_STATE: // 长度位
        LEN_Token = ch;

        tempDataLen = 0;

        /* Allocate memory for the data 分配内存*/
        pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof ( mtOSALSerialData_t ) +
                                                        MT_RPC_FRAME_HDR_SZ + LEN_Token );

        if (pMsg) // 如果分配成功
        {
          /* Fill up what we can */
          pMsg->hdr.event = CMD_SERIAL_MSG; // 串口事件编号 非常重要
          pMsg->msg = (uint8*)(pMsg+1); // 定位数据位置
          pMsg->msg[MT_RPC_POS_LEN] = LEN_Token;
          state = CMD_STATE1;
        }
        else
        {
          state = SOP_STATE;
          return;
        }
        break;

      case CMD_STATE1: // 命令位1
        pMsg->msg[MT_RPC_POS_CMD0] = ch;
        state = CMD_STATE2;
        break;

      case CMD_STATE2: // 命令位2
        pMsg->msg[MT_RPC_POS_CMD1] = ch;
        /* If there is no data, skip to FCS state */
        if (LEN_Token)
        {
          state = DATA_STATE;
        }
        else
        {
          state = FCS_STATE;
        }
        break;

      case DATA_STATE: // 数据位

        /* Fill in the buffer the first byte of the data */
        pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen++] = ch;

        /* Check number of bytes left in the Rx buffer */
        bytesInRxBuffer = Hal_UART_RxBufLen(port);

        /* If the remain of the data is there, read them all, otherwise, just read enough */
        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);
        }

        /* If number of bytes read is equal to data length, time to move on to FCS */
        if ( tempDataLen == LEN_Token )
            state = FCS_STATE;

        break;

      case FCS_STATE: // 校验位

        FSC_Token = ch;

        /* Make sure it's correct */
        if ((MT_UartCalcFCS ((uint8*)&pMsg->msg[0], MT_RPC_FRAME_HDR_SZ + LEN_Token) == FSC_Token))
        {
          osal_msg_send( App_TaskID, (byte *)pMsg ); // 将数据打包发送至OSAL
        }
        else
        {
          /* deallocate the msg */
          osal_msg_deallocate ( (uint8 *)pMsg ); // 清内存
        }

        /* Reset the state, send or discard the buffers at this point */
        state = SOP_STATE; // 状态机一周完成

        break;

      default:
       break;
    }
  }
}

分析代码可知,串口在接收时,首先判断数据开头是否为0xFE,然后得到数据长度给pMsg分配内存,将数据装入pMsg,最后校验完成把数据打包发送至OSAL,释放内存。所以,我们如果要将串口接收改为我们自己的,需要做的就是首先接数据,然后判断长度并分配内存,打包发送给OSAL,最后释放内存,代码如下:

void MT_UartProcessZToolData ( uint8 port, uint8 event )
{
  uint8  flag = 0; // flag用于判断是否接收到数据
  uint8  i = 0,len = 0 ; // len用于记录数据长度
  uint8  buf[128]; // 串口缓冲区最大默认128
  
  (void)event;  // Intentionally unreferenced parameter
  
  while (Hal_UART_RxBufLen(port)) // 查询缓冲区信息,是否有数据
  {
    HalUARTRead (port, &buf[len], 1); // 读一个字节,读一个缓冲区清一个
    len ++;
    flag = 1;
  }
  
  if(flag == 1)
  {
    //分配内存空间,为结构体内容 + 数据内容 + 1个记录长度的数据
    pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof ( mtOSALSerialData_t ) +
                                                        len + 1);
    pMsg->hdr.event = CMD_SERIAL_MSG; // 串口事件编号 非常重要
    pMsg->msg = (uint8*)(pMsg+1); // 定位数据位置
    pMsg->msg[0] = len; // 传送至OSAL的第一个数据为长度
    for(i=0;i<len;i++) // 将缓冲区数据装入数据包
    {
      pMsg->msg[i+1] = buf[i];
    }
    osal_msg_send( App_TaskID, (byte *)pMsg ); // 将数据打包发送至OSAL
    osal_msg_deallocate ( (uint8 *)pMsg ); // 清内存
  }
  }

到这里,数据已经被接收完毕,并发送给上层了。由于串口在APP层进行初始化,并且注册在APP层,所以这个串口接收事件就被发送至APP层,我们现在需要在APP层中对该进行处理。进入APP层事件处理函数SampleApp_ProcessEvent()中,并未发现有串口事件的处理,如下:

显而易见,串口事件需要我们自己添加进去,并且需要自己编写被调用的串口相应函数,比如,按键、无线的处理函数。如下:

void SampleApp_SerialCB(mtOSALSerialData_t *SerialMsg)
{
  uint8 len,*buf; // len为数据长度 buf为发送数据首地址
  
  buf = SerialMsg -> msg; // 数据起始
  len = *buf; // 获取长度
  if ( AF_DataRequest( &SampleApp_Periodic_DstAddr, &SampleApp_epDesc, // 广播模式
                       SAMPLEAPP_SERIAL_CLUSTERID, // 数据类型ID 簇ID 自己动手定义
                       len+1, // 发送数据长度
                       buf, // 发送数据的地址 变量值为0
                       &SampleApp_TransID,
                       AF_DISCV_ROUTE,
                       AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
  {
  }
  else
  {
    // Error occurred in request to send.
  }
}

 函数在编写过程中,由于串口事件宏定义在MT.h中,需要包含这个头文件,以及我们在发送时,簇ID为SAMPLEAPP_SERIAL_CLUSTERID,这个也是我们自己定义的,函数编写完成后,还需要在初始化函数前进行声明,如下:

这些完成之后,就可以在SampleApp_ProcessEvent()中对串口接收事件进行判断处理,如下:

 到此,从PC发送消息,开发板通过串口接收消息,并通过无线发送出去已经完成,接下来需要做的就只剩下从无线拿到数据通过串口发送出去了。

在SampleApp_ProcessEvent()中找到无线接收处理函数SampleApp_MessageMSGCB(),进入其中,添加关于串口消息的处理,如下:

void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
  uint16 flashTime;
  uint8 i,len;

  switch ( pkt->clusterId )
  {
    case SAMPLEAPP_SERIAL_CLUSTERID: // 判断为串口数据
         len = pkt->cmd.Data[0];
         for(i=0;i<len;i++)
         {
           HalUARTWrite(0,&pkt->cmd.Data[i+1],1);
         }
      break;
      
    case SAMPLEAPP_PERIODIC_CLUSTERID: // 判断为广播数据
      if( *pkt->cmd.Data == 0) // 从数据包中找到用户数据
      {
        HalLedSet(1,HAL_LED_MODE_TOGGLE); // 切换LED的状态
      }
      break;

    case SAMPLEAPP_FLASH_CLUSTERID:
      flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] );
      HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) );
      break;
  }
}

其中,串口发送函数HalUARTWrite(),在hal_uart.h文件中声明,直接调用即可。

最后右键工程文件,进入options中,将一些关于调试的宏关闭,防止在串口打印时,打印很多无关的乱码调试信息,如下:

现象:分别给两个模块下载协调器、终端节点或者路由的代码,并将开发板分别与PC通过串口连接,打开两个串口助手,其中一个发送hello,另外一个将会显示hello,说明串口透传功能实现了。如图:

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值