ZStack中串口发送接收的流程.docx
《ZStack中串口发送接收的流程.docx》由会员分享,可在线阅读,更多相关《ZStack中串口发送接收的流程.docx(14页珍藏版)》请在冰点文库上搜索。
ZStack中串口发送接收的流程
Z-Stackxx串口发送接收的流程
串口接收发送数据有两种方式,一种是中断的模式,另一种是DMA方式,这里主要以中断的方式,来看一下使用串口来发送,接收数据的整个流程。
这里以SerialApp例程为例子。
在mian函数中的调用HalDriverInit();函数,在函数中初始化串口,主要是配置管脚和DMA通道
voidHalDriverInit(void){.................................../*UART*/
#if(definedHAL_UART)&&(HAL_UART==TRUE)
HalUARTInit();
#endif
....................................}
从程序中可以看出要想使用协议栈中串口,初始化串口必须定义HAL_UART和HAL_UARTTRUE在hal_board_cfg.h文件中。
#ifndefHAL_UART
#if(definedZAPP_P1)||(definedZAPP_P2)||(definedZTOOL_P1)||(definedZTOOL_P2)
#defineHAL_UARTTRUE
#else
#defineHAL_UARTFALSE
#endif/*ZAPP,ZTOOL*/
#endif/*HAL_UART*/
然后在osal_start_system()开始系统后,会调用Hal_ProcessPoll()来读取时间和串口。
在CC2430的数据手册中有这样一段话。
DatareceptionontheUARTisinitiatedwhena1iswrittentotheUxCSR.REbit
TheUARTwillthensearchforavalidstartbitontheRXDxinputpinandsetthe
hardware.
当有数据接收时,UxCSR.RE位将被置1,然后,UART将在RXDx的输入引脚上查找一个有效的开始位,当找到这个开始位时,将设置
UxCSR.ACTIVE位为高电平。
当一个有效的开始位被查找到,收到的字节将被移动到接收寄存器中。
然后,UxCSR.RX_BYTE位设为1.并且,当这个接收操作完成后接收中断会被产生。
接收到的数据可以通过操作UxBUF寄存器,当UxBUF寄存器的数据被读出后,UxCSR.RX_BYTE位被硬件清除。
串口发生中断首先调用中断的处理函数,这个是接收的中断函数。
#ifHAL_UART_0_ENABLE
HAL_ISR_FUNCTION(halUart0RxIsr,URX0_VECTOR){cfg0->rxBuf[cfg0->rxHead]=U0DBUF;
if(cfg0->rxHead==cfg0->rxMax){cfg0->rxHead=0;}else{cfg0->rxHead++;}}
#endif
该中断函数主要是把U0DBUF寄存器,也就是接收到数据的寄存器,把数据读取来放到UART的结构体中的,cfg0->rxBuf[],中,这个数组的内存分配是在HalUARTOpen()函数中。
SerialApp.c中有下面的定义
#if!
defined(SERIAL_APP_RX_MAX)
#if(defined(HAL_UART_DMA))&&HAL_UART_DMA
#defineSERIAL_APP_RX_MAX128
#else
/*ThegenericsafeRxminimumis48,butifyouknowyourPCAppwillnot
*continuetosendmorethanabyteafterreceivingthe~CTS,lowermax
*hereandsafeminin_hal_uart.ctojust8.
*/
#defineSERIAL_APP_RX_MAX64
#endif
#endif
SerialApp_Init()函数中有下面的赋值,
uartConfig.rx.maxBufSize=SERIAL_APP_RX_MAX;
HalUARTOpen()函数中有下面的赋值:
所以其cfg->rxMax=128,cfg->rxMax=config->rx.maxBufSize;
其中rxHead这个参数始终指向像一个参数被存放到rxBuf的位置。
因为硬件串口缓存器U0DBUF只能存放一个字节,如果不及时把这个接收到的转移出去,那么就会被下一个到来的字节覆盖掉,所以rxHead变量就指向了这个存放的地址,当然是基于定义的rxBuf存储空间。
而if(cfg0->rxHead==cfg0->rxMax)这一句判断也说明的很清楚,一旦这个计数达到了定义的最大接收数量,也就是说已经把rxBuf存储空间占满了,那么就不能在继续存放了。
中断函数执行完后,就应该跳到发生中断时执行的地方,这时程序继续执行,然后在osal_start_system()开始系统后,会循环调用Hal_ProcessPoll()来读取时间和串口,
voidHal_ProcessPoll(){/*TimerPoll*/
HalTimerTick();
/*UARTPoll*/
#if(definedHAL_UART)&&(HAL_UART==TRUE)
HalUARTPoll();
#endif}下面是HalUARTPoll();函数的源代码,在这里有对接收到的数据进行处理的程序。
voidHalUARTPoll(void){#if(HAL_UART_0_ENABLE|HAL_UART_1_ENABLE)
staticuint8tickShdw;
uartCfg_t*cfg;
uint8tick;
#ifHAL_UART_0_ENABLE
//当发生串口接收中断时cfg0就会改变,如果串口没有数据输入cfg0为空,当接收到数据时cfg0将在串口中断服务程序中被改变
if(cfg0){cfg=cfg0;}#endif
#ifHAL_UART_1_ENABLE
if(cfg1){cfg=cfg1;}#endif
//UsetheLSBofthesleeptimer(ST0mustbereadfirstanyway).//系统上电后,睡眠定时器就会自动启动做自增计数ST0即睡眠定时器启动到现在计算值的最低8位
tick=ST0-tickShdw;
tickShdw=ST0;
//下面是一个无限循环
do{//------------发送超时时间
if(cfg->txTick>tick){cfg->txTick-=tick;}else{cfg->txTick=0;}//---------------------接收超时时间
if(cfg->rxTick>tick){cfg->rxTick-=tick;}else{cfg->rxTick=0;}//是使用DMA方式还是使用中断方式
#ifHAL_UART_ISR
#ifHAL_UART_DMA
if(cfg->flag&UART_CFG_DMA){pollDMA(cfg);}else//中断方式
#endif{pollISR(cfg);}#elifHAL_UART_DMA
pollDMA(cfg);
#endif
/*Thefollowinglogicmakescontinuouscallbacksonanyeligibleflag
*untiltheconditioncorrespondingtotheflagisrectified.*Soevenifnewdataisnotreceived,continuouscallbacksaremade.
//如果接收缓存中有数据,当接收数据时rxHead会增计数,当读取数据时rxTail会增计数,两个标志的初始值都为0,所以这两个标志的差值就指示了缓存中有多少的数据*/
if(cfg->rxHead!
=cfg->rxTail)//不相等表示有数据{uint8evt;
if(cfg->rxHead>=(cfg->rxMax-SAFE_RX_MIN)){//已保存的数据已经超过了安全界限,发送接收满事件
evt=HAL_UART_RX_FULL;}elseif(cfg->rxHigh&&(cfg->rxHead>=cfg->rxHigh)){
//rxBuf[]接收到预设值(默认80字节),则触发事件,为什么是80,在上一篇转载的文章中有介绍,这里重点关注执行的流程。
evt=HAL_UART_RX_ABOUT_FULL;}elseif(cfg->rxTick==0){//超时事件
evt=HAL_UART_RX_TIMEOUT;}else{evt=0;}//如果发生事件,并且配置了回调函数则调用回调函数
if(evt&&cfg->rxCB){//(cfg->flag&UART_CFG_U1F)!
=0)判读是那个串口,如果是串口1则为1,否则为0
cfg->rxCB(((cfg->flag&UART_CFG_U1F)!
=0),evt);}}
#ifHAL_UART_0_ENABLE
if(cfg==cfg0){#ifHAL_UART_1_ENABLE
if(cfg1){cfg=cfg1;}else
#endif
break;}else
#endif
break;
}while(TRUE);
#else
return;
#endif}说明:
(1)下面我们看一下pollISR()函数
staticvoidpollISR(uartCfg_t*cfg){//计算rxBuf[]中还有多少数据没有读出(以字节为单位)
if(!
(cfg->flag&UART_CFG_RXF)){//这里是针对流控制的,如果又有新的数据接收到了那么就要重置超时时间(超时时间由睡眠定时器来控制),而且需要把已经读出的数据数目减去!
//Ifanythingreceived,resettheRxidletimer.
//当接收缓存超过安全界限的时候停止RX流*/
if(cfg->rxCnt>=(cfg->rxMax-SAFE_RX_MIN)){RX_STOP_FLOW(cfg);}}}#endif
pollISR()函数主要作用就是设置rxTick和rxCn,
//关于安全界限,在程序中有下面一段:
*afterassertingflowcontrol,butbeforethetransmitterhasobeyedit.
*Atthemaxexpectedbaudrateof115.2k,16byteswillonlytake~1.3msecs,
*butattheminexpectedbaudrateof38.4k,theycouldtake~4.2msecs.
*SAFE_RX_MINandDMA_RX_DLYmustbothbeconsistentaccordingto
*themin&maxexpectedbaudrate.
*/
//如果声明了流控制,为保证数据的正确接收需要在RX缓存区中预留出足够的空间。
CC2430可以使用的最大串口波特率为115.2k。
这个安全界限的数字跟使用的波特率还有串口tick有关。
具体参考《Z-STACK问题之串口结构uartCfg_t乱说》文章。
可以看到,在初始化时rxHead=rxTail=0,如果发生接收中断,在中断服务函数中把U0DBUF寄存器中的数据传送到rxbuf[]中,这时rxHead和rxTail的值不在相等,其中,rxHead是rxBuf[]接收到数据的个数,rxTail是rxBuf[]移出的数据个数,再根据两者的差值,判断具体的事件evt。
然后,根据evt和设置的回调函数,通过cfg->rxCB调用相应的回调函数。
代码也是体显在下面一句。
cfg->rxCB(((cfg->flag&UART_CFG_U1F)!
=0),evt);
第一个参数主要是判断,是UART1还是UART0.第二个参数是触发的事件类型,那个个回调函数,具体是指向函数呢?
首先,我们在voidSerialApp_Init(uint8task_id)初始化函数中,对串口进行了配置,其中下面两句中有关于回调函数的。
#else
uartConfig.callBackFunc=rxCB;
#endif
HalUARTOpen(SERIAL_APP_PORT,&uartConfig);
其中,在HalUARTOpen()函数中,有下面的一条语句,
uint8HalUARTOpen(uint8port,halUARTCfg_t*config){...................
cfg->rxCB=config->callBackFunc;
...................}也就是调用下面的rxCB函数。
程序中定义了两个串口接收缓冲区:
otaBuf上otaBuf2.当otaBuf中无数据时,处于空闲状态时,由otaBuf接收串口数据;当otaBuf中保留有数据时,下等待接收节点发送接收数据响应或由于某些正在重新给接收节点发送数据时,可通过otaBuf2接收数据,当otaBuf和otaBuf2都没有处于空闲状态时,说明数据没有及时发送给接收节点,发生了数据累积,缓冲区被占用,需要进行流量控制,所以直接退出接收回调函数,暂不接收数据。
staticvoidrxCB(uint8port,uint8event){uint8*buf,len;
/*Whileawaitingretries/response,onlybuffer1nextbuffer:
otaBuf2.
*IfallowtheDMARxtocontinuetorun,allocatingRxbuffers,theap
*WhentheRxdataavailableisnotread,theDMARxMachineautomatically
*setsflowcontroloff-itisautomaticallyre-enableduponRxdataread.
*Whentheback-loggedotaBuf2issentOTA,anRxdatareadisscheduled.
*/
if(otaBuf2)//缓冲区被占用{return;}if(!
(buf=osal_mem_alloc(SERIAL_APP_RX_CNT))){return;}/*HALUARTManagerwillturnflowcontrolbackonifitcanafterread.
*Reserve1byteforthe'sequencenumber'.这里的
SERIAL_APP_RX_CNT为80
这里为什么是80,上篇文章中也有分析*/
len=HalUARTRead(port,buf+1,SERIAL_APP_RX_CNT-1);if(!
len)//Lengthisnotexpectedtoeverbezero.{osal_mem_free(buf);
return;}/*IfthelocalglobalotaBufisinuse,theneithertheresponsehandshake
*isbeingawaitedorretriesarebeingattempted.Whenthewait/retries
*processhasbeenexhausted,thenextOTAmsgwillbeattemptedfrom
*otaBuf2,ifitisnotNULL.otaBuf正在使用,则就使用otaBuf2*/
if(otaBuf)//otaBuf正在被占用{otaBuf2=buf;//otaBuf2接收数据
otaLen2=len;}else{otaBuf=buf;//otaBuf接收数据
otaLen=len;
/*Don'tcallSerialApp_SendData()fromhereinthecallbackfunction.
*SettheeventsoSerialApp_SendData()runsduringthistask'stimeslot.
产生发送数据事件*/
osal_set_event(SerialApp_TaskID,
SERIALAPP_MSG_SEND_EVT);}}
#endif
在事件处理函数中,有下面的判断。
UINT16SerialApp_ProcessEvent(uint8task_id,UINT16events){
........................
if(events&SERIALAPP_MSG_SEND_EVT){SerialApp_SendData(otaBuf,otaLen);//
return(events^SERIALAPP_MSG_SEND_EVT);}.........................}下面是SerialApp_SendData()函数的源代码,调用
AF_DataRequest(),通过OTA发送数据。
由于在数据包之前增加了序列号SerialApp_SeqTx,多次重发的数据不会被接收节点重复发送到串口。
staticvoidSerialApp_SendData(uint8*buf,uint8len){afStatus_tstat;
//Pre-pendsequencenumbertothestartoftheRxbuffer.*buf=++SerialApp_SeqTx;
otaBuf=buf;
otaLen=len+1;
stat=AF_DataRequest(&SerialApp_DstAddr,
(endPointDesc_t*)&SerialApp_epDesc,
SERIALAPP_CLUSTERID1,
otaLen,otaBuf,
&SerialApp_MsgID,0,AF_DEFAULT_RADIUS);if((stat==afStatus_SUCCESS)||(stat==afStatus_MEM_FAIL)){
//在设定的时间内没有发送成功,则重新发送。
osal_start_timerEx(SerialApp_TaskID,
SERIALAPP_MSG_RTRY_EVT,
SERIALAPP_MSG_RTRY_TIMEOUT);
rtryCnt=SERIALAPP_MAX_RETRIES;}else{FREE_OTABUF();//重发的次数}}
uint8stat;
uint8seqnb;
uint8delay;
switch(pkt->clusterId){//Amessagewithaserialdatablocktobetransmittedontheserialport.
//接收节点收到的接收数据命令,
caseSERIALAPP_CLUSTERID1:
seqnb=pkt->cmd.Data[0];
//Keepmessageifnotarepeatpacket
if((seqnb>SerialApp_SeqRx)||//Normal((seqnb<0x80)&&(SerialApp_SeqRx>0x80)))//Wrap-around{//Transmitthedataontheserialport.接收到的发送到串口if(HalUARTWrite(SERIAL_APP_PORT,pkt->cmd.Data+1,
SerialApp_SeqRx=seqnb;
stat=OTA_SUCCESS;}else{stat=OTA_SER_BUSY;}}
else{stat=OTA_DUP_MSG;}//SelectapproiateOTAflow-controldelay.
delay=(stat==OTA_SER_BUSY)?
SERIALAPP_NAK_DELAY:
SERIALAPP_ACK_DELAY;
//Build&sendOTAresponsemessage.发送响应消息
rspBuf[0]=stat;
rspBuf[1]=seqnb;
rspBuf[2]=LO_UINT16(delay);
rspBuf[3]=HI_UINT16(delay);
//发送接收数据响应命令
stat=AF_DataRequest(&(pkt->srcAddr),
(endPointDesc_t*)&SerialApp_epDesc,
SERIALAPP_CLUSTERID2,
SERIAL_APP_RSP_CNT,
rspBuf,&SerialApp_MsgID,0,
AF_DEFAULT_RADIUS);
if(stat!
=afStatus_SUCCESS){osal_start_timerEx(SerialApp_TaskID,
SERIALAPP_RSP_RTRY_EVT,
SERIALAPP_RSP_RTRY_TIMEOUT);//Storetheaddressforthetimeoutretry.存储发送超时的,目的地址
osal_memcpy(&SerialApp_RspDstAddr,&(pkt->srcAddr),sizeof(afAddrType_t));}break;
//Aresponsetoareceivedserialdatablock.接收到接收数据响应命令caseSERIALAPP_CLUSTERID2:
if((pkt->cmd.Data[1]==SerialApp_SeqTx)&&
((pkt->cmd.Data[0]==