串口透传(SerialApp)的数据发送过程

一、串口终端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。



static void SerialApp_Send(void)
{
#if SERIAL_APP_LOOPBACK // 本条件编译为FALSE
if (SerialApp_TxLen < SERIAL_APP_TX_MAX)
{
SerialApp_TxLen += HalUARTRead(SERIAL_APP_PORT, SerialApp_TxBuf+SerialApp_TxLen+1,
SERIAL_APP_TX_MAX-SerialApp_TxLen);
}

if (SerialApp_TxLen)
{
(void)SerialApp_TxAddr;
if (HalUARTWrite(SERIAL_APP_PORT, SerialApp_TxBuf+1, SerialApp_TxLen))
{
SerialApp_TxLen = 0;
}
else
{
osal_set_event(SerialApp_TaskID, SERIALAPP_SEND_EVT);
}
}
#else
// SerialApp_TxLen不为0时 代表有数据要发送或者正在发送
// SerialApp_TxLen为0时 代表没有数据发送或者已经发送完了。发送端接收到接收端的确认信息后,确定本次数据已经被接收到 会将SerialApp_TxLen = 0 为接收下次数据作准备。

if (!SerialApp_TxLen &&
(SerialApp_TxLen = HalUARTRead(SERIAL_APP_PORT, SerialApp_TxBuf+1, SERIAL_APP_TX_MAX)))
{
// 从串口接收内容 上面那个函数以后可以模仿着来写
// Pre-pend sequence number to the Tx message.
// SerialApp_TxSeq 为静态变量 默认为0
SerialApp_TxBuf[0] = ++SerialApp_TxSeq; // 发送数据最开始保存的是本次数据的序列号
}

if (SerialApp_TxLen)// 如果接收到内容
{
if (afStatus_SUCCESS != AF_DataRequest(&SerialApp_TxAddr,
(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);
//如果发送不成功 则再执行发送事件
// 第二次发送时还是进入本函数 不过不会再从串口缓存里读数据了 因为这时SerialApp_TxLen不为0了。
}
}
#endif
}

对于发送端来说,如果发送不成功,则数据重发,如果得知接收端数据没有接收成功,同样进行数据重发。上次数据没有发完的话,即使串口有了新数据也不重新读取串口里的新数据,而是继续重发上次数据。



二、节点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代表 发送节点接收到反馈信息。在这模块里,如果发送成功则取消自动重发。否则,继续重发。
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;
}


==============================================================

如果只有一个串口,接收端可以将数据再发回给发送端,让发送端输出到串口上,需要修改如下代码。当然代码要用条件编译区分发送端与接收端。这时两个端点的代码不再一样了。

==============================================================

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:
HalLedSet ( HAL_LED_2, HAL_LED_MODE_TOGGLE );
#if SEND_POINT
// 如果是发送端点 只需要把收到数据发给串口 这时要区分两种状态 如果
// 串口写成功 则修改状态 停止继续发送
// 如果串口忙即写不成功 则继续发送
if ( HalUARTWrite( SERIAL_APP_PORT, pkt->cmd.Data+1, (pkt->cmd.DataLength-1) ))
{
SerialApp_TxLen = 0;
osal_stop_timerEx(SerialApp_TaskID, SERIALAPP_SEND_EVT);
}
else
{
osal_start_timerEx( SerialApp_TaskID, SERIALAPP_SEND_EVT, 1000 );
}
#else
osal_memcpy(&SerialApp_RxAddr, &(pkt->srcAddr), sizeof( afAddrType_t ));
seqnb = pkt->cmd.Data[0];
if ( (seqnb > SerialApp_RxSeq) || // Normal
((seqnb < 0x80 ) && ( SerialApp_RxSeq > 0x80)) ) // Wrap-around
{
// 接收节点没有串口 故将数据再重新发回发送节点
if (afStatus_SUCCESS != AF_DataRequest(&SerialApp_RxAddr,
(endPointDesc_t *)&SerialApp_epDesc,
SERIALAPP_CLUSTERID1,
pkt->cmd.DataLength, pkt->cmd.Data,
&SerialApp_MsgID, 0, AF_DEFAULT_RADIUS))
{
osal_set_event(SerialApp_TaskID, SERIALAPP_RESP_EVT);
}
}
else
{
stat = OTA_DUP_MSG;
// Select approproiate OTA flow-control delay.
delay = SERIALAPP_ACK_DELAY;
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);
}
#endif
break;

// A response to a received serial data block.
case SERIALAPP_CLUSTERID2:
HalLedSet ( HAL_LED_3, HAL_LED_MODE_TOGGLE );
if ((pkt->cmd.Data[1] == SerialApp_TxSeq) &&
(pkt->cmd.Data[0] == OTA_DUP_MSG))
{
SerialApp_TxLen = 0;
osal_stop_timerEx(SerialApp_TaskID, SERIALAPP_SEND_EVT);
}

break;

default:
break;
}
}
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值