zstack学习9--串口透传(SerialApp)的数据发送过程(转)

wateras1   2013-7-31 17:38 楼主

一、串口终端1的数据,如何被节点1所接收,并且发送出去的?

串口数据是由HAL层来负责的,让我们从主循环 (osal_start_system) 的Hal_ProcessPoll函数找下去 ,Hal_ProcessPoll ==> HalUARTPoll ==> HalUARTPollDMA。在这个 HalUARTPollDMA 函数里最后有这样一句话:dmaCfg.uartCB(HAL_UART_DMA-1,  evt); 对dmaCfg.uartCB 这个函数进行了调用,dmaCfg结构体类型如下:

typedef struct
{
  uint16 rxBuf[HAL_UART_DMA_RX_MAX];
  rxIdx_t rxHead;
  rxIdx_t rxTail;
  uint8 rxTick;
  uint8 rxShdw;

  uint8 txBuf[2][HAL_UART_DMA_TX_MAX];
  txIdx_t txIdx[2];
  volatile uint8 txSel;
  uint8 txMT;
  uint8 txTick;           // 1-character time in 32kHz ticks according to baud rate,
                          // to be used in calculating time lapse since DMA ISR
                          // to allow delay margin before start firing DMA, so that
                          // DMA does not overwrite UART DBUF of previous packet
  
  volatile uint8 txShdw;  // Sleep Timer LSB shadow.
  volatile uint8 txShdwValid; // TX shadow value is valid
  uint8 txDMAPending;     // UART TX DMA is pending

  halUARTCBack_t uartCB;
} uartDMACfg_t;

由上可知,uartCB是一个类型为halUARTCBack_t的函数指针(回调函数),那这个函数指针在那里赋值的呢?请看下面这条线路。void SerialApp_Init( uint8 task_id )里面有如下语句halUARTCfg_t uartConfig;在接下来的给uartConfig这个结构体变量赋值的语句中有如下语句:uartConfig.callBackFunc         = SerialApp_CallBack;即将uartConfig里的串口回调函数设置为SerialApp_CallBack,然后再通过HalUARTOpen (SERIAL_APP_PORT, &uartConfig);这个函数的调用(不再深入进去,有兴趣的同学可以进一步跟进)将uartConfig这个结构体变量的值转化为uartConfig这个结构体变量的值,注意两个结构体变量所属的类型不同,不能直接赋值,需要转化。

       这样就保证了,SerialApp_CallBack函数每次循环中被调用一次,SerialApp_CallBack( ) ==> SerialApp_Send( ),在SerialApp_Send()函数里会调用HalUARTRead()函数,将 DMA 数据读至数据 buffer 并通过 AF_DataRequest ()函数发送出去,注意:发送出去的信息的 CLUSTERID(信息簇ID)号为 SERIALAPP_CLUSTERID1。 

 

二、节点2 在收到空中的信号后,如何传递给与其相连的串口终端?
        节点 2 从空中捕获到信号后, 会应用层任务处理函数上进入AF_INCOMING_MSG_CMD分支,进入 SerialApp_ProcessMSGCmd()函数里进入处理。

void SerialApp_ProcessMSGCmd( afIncomingMSGPacket_t *pkt )  //对从空中捕获到的信号进行处理
{
  uint8 stat;
  uint8 seqnb;
  uint8 delay;
  switch ( pkt->clusterId )
  {
  // A message with a serial data block to be transmitted on the serial port.
  case SERIALAPP_CLUSTERID1:    //节点一发送过来的信息的 CLUSTERID(信息簇ID)号为 SERIALAPP_CLUSTERID1
    // Store the address for sending and retrying.
    osal_memcpy(&SerialApp_RxAddr, &(pkt->srcAddr), sizeof( afAddrType_t ));
    seqnb = pkt->cmd.Data[0];
    // Keep message if not a repeat packet
    if ( (seqnb > SerialApp_RxSeq) ||                    // Normal
        ((seqnb < 0x80 ) && ( SerialApp_RxSeq > 0x80)) ) // Wrap-around
    {
      // Transmit the data on the serial port.
      if ( HalUARTWrite( SERIAL_APP_PORT, pkt->cmd.Data+1, (pkt->cmd.DataLength-1) ) )  //通过串口发送数据到PC机
      {
        // Save for next incoming message
        SerialApp_RxSeq = seqnb;
        stat = OTA_SUCCESS;
      }
      else
      {
        stat = OTA_SER_BUSY;
      }
    }
    else
    {
      stat = OTA_DUP_MSG;
    }
    // Select approproiate OTA flow-control delay.
    delay = (stat == OTA_SER_BUSY) ? SERIALAPP_NAK_DELAY : SERIALAPP_ACK_DELAY;
    // Build & send OTA response message.
    SerialApp_RspBuf[0] = stat;
    SerialApp_RspBuf[1] = seqnb;
    SerialApp_RspBuf[2] = LO_UINT16( delay );
    SerialApp_RspBuf[3] = HI_UINT16( delay );
    osal_set_event( SerialApp_TaskID, SERIALAPP_RESP_EVT );  //受到数据后,向节点1发送一个响应事件,跳到SerialApp_ProcessEvent()
    osal_stop_timerEx(SerialApp_TaskID, SERIALAPP_RESP_EVT);  
    break;
......
  }
}


UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )  
{
......
   if ( events & SERIALAPP_RESP_EVT )    //串口响应事件,表示成功接受来自节点1的数据, 
  {
    SerialApp_Resp();      //向节点1发送  成功接受的response
    return ( events ^ SERIALAPP_RESP_EVT );
  }
......
}


static void SerialApp_Resp(void)
{
  if (afStatus_SUCCESS != AF_DataRequest(&SerialApp_RxAddr,         //通过AF_DataRequest函数,讲接收成功响应从空中发送出去
                                         (endPointDesc_t *)&SerialApp_epDesc,
                                          SERIALAPP_CLUSTERID2,
                                          SERIAL_APP_RSP_CNT, SerialApp_RspBuf,
                                         &SerialApp_MsgID, 0, AF_DEFAULT_RADIUS))
  {
    osal_set_event(SerialApp_TaskID, SERIALAPP_RESP_EVT);   //如果发送失败,重新发送
  }
}


节点1,接收到来自节点2的response。
UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events ) 
{
  ......
   while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SerialApp_TaskID )) )
    {
      switch ( MSGpkt->hdr.event )
      {
         ......
         case AF_INCOMING_MSG_CMD:      //在这个实验中,使用串口通讯时,触发的事件,从空中捕获到信号。
            SerialApp_ProcessMSGCmd( MSGpkt );  //处理这个消息      
            break;
       ......
      }
    }
}


SERIALAPP_CLUSTERID2代表接收到发送成功的response,取消自动重发,如果不,自动重发。
void SerialApp_ProcessMSGCmd( afIncomingMSGPacket_t *pkt ) 
{
  ......
   // A response to a received serial data block.
  case SERIALAPP_CLUSTERID2:        //SerialWsn_CLUSTERID2代表接收到发送成功的response
    if ((pkt->cmd.Data[1] == SerialApp_TxSeq) &&
       ((pkt->cmd.Data[0] == OTA_SUCCESS) || (pkt->cmd.Data[0] == OTA_DUP_MSG)))
    {
      SerialApp_TxLen = 0;
      osal_stop_timerEx(SerialApp_TaskID, SERIALAPP_SEND_EVT);  //当收到发送成功的response,停止自动从发
    }
    else
    {
      // Re-start timeout according to delay sent from other device.
      delay = BUILD_UINT16( pkt->cmd.Data[2], pkt->cmd.Data[3] );
      osal_start_timerEx( SerialApp_TaskID, SERIALAPP_SEND_EVT, delay ); //没有收到成功的response,自动重发
    }
    break;
    default:
      break;
}
 
三、下面部分来自网络上另一篇文章


粗略的看了下这个程序,发现这个程序有一个EP(终端),每个终端有两个簇(SERIALAPP_CLUSTERID1、SERIALAPP_CLUSTERID2),可以在以下代码中定义
// This list should be filled with Application specific Cluster IDs.
const cId_t SerialApp_ClusterList[SERIALAPP_MAX_CLUSTERS] =
{
  SERIALAPP_CLUSTERID1,
  SERIALAPP_CLUSTERID2
};
其中SERIALAPP_CLUSTERID1是负责数据的传输的,而SERIALAPP_CLUSTERID2则负责传输的反馈。在以下函数中可以看出
/*********************************************************************
* @fn      SerialApp_ProcessMSGCmd
*
* @brief   Data message processor callback. This function processes
*          any incoming data - probably from other devices. Based
*          on the cluster ID, perform the intended action.
*
* @param   pkt - pointer to the incoming message packet
*
* @return  TRUE if the 'pkt' parameter is being used and will be freed later,
*          FALSE otherwise.
*/
void SerialApp_ProcessMSGCmd( afIncomingMSGPacket_t *pkt )
{
  uint8 stat;
  uint8 seqnb;
  uint8 delay;
  switch ( pkt->clusterId )
  {
  // A message with a serial data block to be transmitted on the serial port.
  case SERIALAPP_CLUSTERID1:        // 处理数据
    // Store the address for sending and retrying.
    osal_memcpy(&SerialApp_RxAddr, &(pkt->srcAddr), sizeof( afAddrType_t ));  // 保存来信地址
    seqnb = pkt->cmd.Data[0];  // 保存计数(来自发送端,来用检验)
    // Keep message if not a repeat packet
    if ( (seqnb > SerialApp_RxSeq) ||                    // Normal, 是否是最新的数据
        ((seqnb < 0x80 ) && ( SerialApp_RxSeq > 0x80)) ) // Wrap-around,
    {
#ifndef   GEC_EP
      // Transmit the data on the serial port.
      if ( HalUARTWrite( SERIAL_APP_PORT, pkt->cmd.Data+1, (pkt->cmd.DataLength-1) ) )
      {
        // Save for next incoming message
        SerialApp_RxSeq = seqnb;   // 保存计数
        stat = OTA_SUCCESS;
      }
      else
      {
        stat = OTA_SER_BUSY;
      }
#else                   // 添加部分代码,实现数据回传显示到串口,没办法串口只有一个
    if (afStatus_SUCCESS != AF_DataRequest(&SerialApp_RxAddr,
                                           (endPointDesc_t *)&SerialApp_epDesc,
                                            SERIALAPP_CLUSTERID1,
                                            SerialApp_TxLen+1, SerialApp_TxBuf,
                                            &SerialApp_MsgID, 0, AF_DEFAULT_RADIUS))
    {
      osal_set_event(SerialApp_TaskID, SERIALAPP_SEND_EVT);
    }
#endif
    }
    else
    {
      stat = OTA_DUP_MSG;
    }
    // Select approproiate OTA flow-control delay.
    delay = (stat == OTA_SER_BUSY) ? SERIALAPP_NAK_DELAY : SERIALAPP_ACK_DELAY; // UART忙则延时16ms,否则延时1ms
    // Build & send OTA response message.
    SerialApp_RspBuf[0] = stat;   // 接收状态
    SerialApp_RspBuf[1] = seqnb;  // 计数
    SerialApp_RspBuf[2] = LO_UINT16( delay );
    SerialApp_RspBuf[3] = HI_UINT16( delay );
    osal_set_event( SerialApp_TaskID, SERIALAPP_RESP_EVT );  // 发送反馈信息
    osal_stop_timerEx(SerialApp_TaskID, SERIALAPP_RESP_EVT);  // 停止反馈定时器
    break;
  // A response to a received serial data block.
  case SERIALAPP_CLUSTERID2:    // 处理反馈
    if ((pkt->cmd.Data[1] == SerialApp_TxSeq) &&   // 反馈回来的是刚才发的
       ((pkt->cmd.Data[0] == OTA_SUCCESS) || (pkt->cmd.Data[0] == OTA_DUP_MSG)))  // 成功或者过期
    {
      SerialApp_TxLen = 0;    // 准备下一次发送
      osal_stop_timerEx(SerialApp_TaskID, SERIALAPP_SEND_EVT);  // 无需重发,关闭定时器
    }
    else   // 需要重发
    {
      // Re-start timeout according to delay sent from other device.
      delay = BUILD_UINT16( pkt->cmd.Data[2], pkt->cmd.Data[3] );    // 延时发送
      osal_start_timerEx( SerialApp_TaskID, SERIALAPP_SEND_EVT, delay );  //
    }
    break;
    default:
      break;
  }
}
总结一下以上的代码,当SERIALAPP_CLUSTERID1收到数据的时候,将会检验这个数据包是否是新的,由于逻辑有点复杂,下面用伪代码描述
if (新数据包)

   if (UART发送成功)
   {
        反馈成功信息;
     }
    else  否则反馈UART忙;

else 反馈此包过期;
同样,对于SERIALAPP_CLUSTERID2的信息处理也可以这样描述
if (刚才发送的包 && (成功 || 过期)) 
{
   关闭定时器,注备下一次的发送,也就是说没有收到确认信息,不会进行下一次的发送
}
else
{
  给我延时重发去
}

淘宝:https://viiot.taobao.com/Q群243090717 多年专业物联网行业经验,个人承接各类物联网外包项目

回复评论 (5)

顶一个。
点赞  2013-7-31 20:36
坐堂学习一下
点赞  2013-8-1 14:35
学习学习,分析得很好
点赞  2013-8-14 18:14
看帖,顶贴,不错的分享
点赞  2013-8-15 09:32
太激动了
点赞  2013-8-16 10:39
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复