Z-Stack中串口发送接收的流程.doc_第1页
Z-Stack中串口发送接收的流程.doc_第2页
Z-Stack中串口发送接收的流程.doc_第3页
Z-Stack中串口发送接收的流程.doc_第4页
Z-Stack中串口发送接收的流程.doc_第5页
已阅读5页,还剩12页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Z-Stack中串口发送接收的流程 串口接收发送数据有两种方式,一种是中断的模式,另一种是DMA方式,这里主要以中断的方式,来看一下使用串口来发送,接收数据的整个流程。这里以SerialApp例程为例子。 在mian函数中的调用HalDriverInit();函数,在函数中初始化串口,主要是配置管脚和DMA通道void HalDriverInit (void)/* UART */#if (defined HAL_UART) & (HAL_UART = TRUE) HalUARTInit();#endif 从程序中可以看出要想使用协议栈中串口,初始化串口必须定义HAL_UART和HAL_UART TRUE 在hal_board_cfg.h文件中。#ifndef HAL_UART#if (defined ZAPP_P1) | (defined ZAPP_P2) | (defined ZTOOL_P1) | (defined ZTOOL_P2)#define HAL_UART TRUE#else#define HAL_UART FALSE#endif /* ZAPP, ZTOOL */#endif /* HAL_UART */ 然后在osal_start_system()开始系统后,会调用Hal_ProcessPoll()来读取时间和串口。 在CC2430的数据手册中有这样一段话。Data reception on the UART is initiatedwhen a 1 is written to the UxCSR.RE bitThe UART will then search for a valid start bit on the RXDx input pin and set theUxCSR.ACTIVE bit high. When a validstart bit has been detected the received byte is shifted into the receive register .The UxCSR.RX_BYTE bit is set and a receive interrupt is generated when the operation has completed. The received data byte is available through the UxBUF register. When UxBUF is read, UxCSR.RX_BYTE is cleared by hardware. 当有数据接收时,UxCSR.RE位将被置1,然后,UART将在RXDx的输入引脚上查找一个有效的开始位,当找到这个开始位时,将设置UxCSR.ACTIVE位为高电平。当一个有效的开始位被查找到,收到的字节将被移动到接收寄存器中。然后,UxCSR.RX_BYTE位设为1.并且,当这个接收操作完成后接收中断会被产生。接收到的数据可以通过操作UxBUF寄存器,当UxBUF寄存器的数据被读出后,UxCSR.RX_BYTE位被硬件清除。串口发生中断首先调用中断的处理函数,这个是接收的中断函数。#if HAL_UART_0_ENABLEHAL_ISR_FUNCTION( halUart0RxIsr, URX0_VECTOR ) cfg0-rxBufcfg0-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 #define SERIAL_APP_RX_MAX 128 #else /* The generic safe Rx minimum is 48, but if you know your PC App will not * continue to send more than a byte after receiving the CTS, lower max * here and safe min in _hal_uart.c to just 8. */ #define SERIAL_APP_RX_MAX 64 #endif#endifSerialApp_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()来读取时间和串口,void Hal_ProcessPoll () /* Timer Poll */ HalTimerTick(); /* UART Poll */#if (defined HAL_UART) & (HAL_UART = TRUE) HalUARTPoll();#endif下面是HalUARTPoll();函数的源代码,在这里有对接收到的数据进行处理的程序。void HalUARTPoll( void )#if ( HAL_UART_0_ENABLE | HAL_UART_1_ENABLE ) static uint8 tickShdw; uartCfg_t *cfg; uint8 tick;#if HAL_UART_0_ENABLE /当发生串口接收中断时cfg0就会改变,如果串口没有数据输入cfg0为空,当接收到数据时cfg0将在串口中断服务程序中被改变 if ( cfg0 ) cfg = cfg0; #endif#if HAL_UART_1_ENABLE if ( cfg1 ) cfg = cfg1; #endif / Use the LSB of the sleep timer (ST0 must be read first anyway)./系统上电后,睡眠定时器就会自动启动做自增计数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方式还是使用中断方式#if HAL_UART_ISR#if HAL_UART_DMA if ( cfg-flag & UART_CFG_DMA ) pollDMA( cfg ); else/中断方式#endif pollISR( cfg ); #elif HAL_UART_DMA pollDMA( cfg );#endif /* The following logic makes continuous callbacks on any eligible flag * until the condition corresponding to the flag is rectified. * So even if new data is not received, continuous callbacks are made./如果接收缓存中有数据,当接收数据时rxHead会增计数,当读取数据时rxTail会增计数,两个标志的初始值都为0,所以这两个标志的差值就指示了缓存中有多少的数据*/ if ( cfg-rxHead != cfg-rxTail ) /不相等表示有数据 uint8 evt; if ( cfg-rxHead = (cfg-rxMax - SAFE_RX_MIN) ) /已保存的数据已经超过了安全界限,发送接收满事件 evt = HAL_UART_RX_FULL; else if ( cfg-rxHigh & (cfg-rxHead = cfg-rxHigh) ) /rxBuf 接收到预设值(默认80字节),则触发事件,为什么是80,在上一篇转载的文章中有介绍,这里重点关注执行的流程。 evt = HAL_UART_RX_ABOUT_FULL; else if ( 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 ); #if HAL_UART_0_ENABLE if ( cfg = cfg0 ) #if HAL_UART_1_ENABLE if ( cfg1 ) cfg = cfg1; else#endif break; else#endif break; while ( TRUE );#else return;#endif说明:(1)下面我们看一下pollISR()函数static void pollISR( uartCfg_t *cfg )/计算rxBuf中还有多少数据没有读出(以字节为单位) uint8 cnt = UART_RX_AVAIL( cfg );/如果串口没有接收到数据,也就是说没有发生过串口接收中断,那么cfg应为是为空的,则cnt=0如果发生了串口中断,则cnt计算出串口缓存中还有多少数据没有读出,这个缓存并不是硬件寄存器的缓存,而是程序中开辟一段空间 if ( !(cfg-flag & UART_CFG_RXF) ) /这里是针对流控制的,如果又有新的数据接收到了那么就要重置超时时间(超时时间由睡眠定时器来控制),而且需要把已经读出的数据数目减去! / If anything received, reset the Rx idle timer. if ( cfg-rxCnt != cnt ) cfg-rxTick = HAL_UART_RX_IDLE; cfg-rxCnt = cnt; /* It is necessary to stop Rx flow in advance of a full Rx buffer becausebytes can keep coming while sending H/W fifo flushes./当接收缓存超过安全界限的时候停止RX流*/ if ( cfg-rxCnt = (cfg-rxMax - SAFE_RX_MIN) ) RX_STOP_FLOW( cfg ); #endifpollISR()函数主要作用就是设置rxTick和rxCn,/关于安全界限,在程序中有下面一段:/* Need to leave enough of the Rx buffer free to handle the incoming bytes* after asserting flow control, but before the transmitter has obeyed it.* At the max expected baud rate of 115.2k, 16 bytes will only take 1.3 msecs,* but at the min expected baud rate of 38.4k, they could take 4.2 msecs.* SAFE_RX_MIN and DMA_RX_DLY must both be consistent according to* the min & max expected baud rate.*/如果声明了流控制,为保证数据的正确接收需要在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.第二个参数是触发的事件类型,那个个回调函数,具体是指向函数呢? 首先,我们在void SerialApp_Init( uint8 task_id )初始化函数中,对串口进行了配置,其中下面两句中有关于回调函数的。#else uartConfig.callBackFunc = rxCB;#endif HalUARTOpen (SERIAL_APP_PORT, &uartConfig);其中,在HalUARTOpen()函数中,有下面的一条语句,uint8 HalUARTOpen( uint8 port, halUARTCfg_t *config )cfg-rxCB = config-callBackFunc; 也就是调用下面的rxCB函数。程序中定义了两个串口接收缓冲区:otaBuf上otaBuf2.当otaBuf中无数据时,处于空闲状态时,由otaBuf接收串口数据;当otaBuf中保留有数据时,下等待接收节点发送接收数据响应或由于某些正在重新给接收节点发送数据时,可通过otaBuf2接收数据,当otaBuf和otaBuf2都没有处于空闲状态时,说明数据没有及时发送给接收节点,发生了数据累积,缓冲区被占用,需要进行流量控制,所以直接退出接收回调函数,暂不接收数据。static void rxCB( uint8 port, uint8 event ) uint8 *buf, len; /* While awaiting retries/response, only buffer 1 next buffer: otaBuf2. * If allow the DMA Rx to continue to run, allocating Rx buffers, the heap * will become so depleted that an incoming OTA response cannot be received. * When the Rx data available is not read, the DMA Rx Machine automatically * sets flow control off - it is automatically re-enabled upon Rx data read. * When the back-logged otaBuf2 is sent OTA, an Rx data read is scheduled. */ if ( otaBuf2 ) /缓冲区被占用 return; if ( !(buf = osal_mem_alloc( SERIAL_APP_RX_CNT ) ) return; /* HAL UART Manager will turn flow control back on if it can after read. * Reserve 1 byte for the sequence number.这里的SERIAL_APP_RX_CNT为80 这里为什么是80,上篇文章中也有分析*/ len = HalUARTRead( port, buf+1, SERIAL_APP_RX_CNT-1 ); if ( !len ) / Length is not expected to ever be zero. osal_mem_free( buf ); return; /* If the local global otaBuf is in use, then either the response handshake * is being awaited or retries are being attempted. When the wait/retries * process has been exhausted, the next OTA msg will be attempted from * otaBuf2, if it is not NULL. otaBuf正在使用,则就使用otaBuf2 */ if ( otaBuf ) /otaBuf正在被占用 otaBuf2 = buf; /otaBuf2接收数据 otaLen2 = len; else otaBuf = buf; /otaBuf接收数据 otaLen = len; /* Dont call SerialApp_SendData() from here in the callback function. * Set the event so SerialApp_SendData() runs during this tasks time slot. 产生发送数据事件*/ osal_set_event( SerialApp_TaskID, SERIALAPP_MSG_SEND_EVT ); #endif在事件处理函数中,有下面的判断。UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )if ( events & SERIALAPP_MSG_SEND_EVT ) SerialApp_SendData( otaBuf, otaLen );/ return ( events SERIALAPP_MSG_SEND_EVT ); 下面是SerialApp_SendData()函数的源代码,调用AF_DataRequest(),通过OTA发送数据。由于在数据包之前增加了序列号SerialApp_SeqTx,多次重发的数据不会被接收节点重复发送到串口。static void SerialApp_SendData( uint8 *buf, uint8 len ) afStatus_t stat; / Pre-pend sequence number to the start of the Rx buffer. *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();/重发的次数 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: seqnb = pkt-cmd.Data0; / Keep message if not a repeat packet if ( (seqnb SerialApp_SeqRx) | / Normal (seqnb 0x80) ) / Wrap-around / 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_SeqRx = 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. 发送响应消息 rspBuf0 = stat; rspBuf1 = seqnb; rspBuf2 = LO_UINT16( delay );rspBuf3 = 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 ); / Store the address for the timeout retry. 存储发送超时的,目的地址 osal_memcpy(&SerialApp_RspDstAddr, &(pkt-srcAddr), sizeof( afAddrType_t ); break; / A response to a received serial data block. 接收到接收数据响应命令 case SERIALAPP_CLUSTERID2: if ( (pkt-cmd.Data1 = SerialApp_SeqTx) & (pkt-cmd.Data0 = OTA_SUCCESS) | (pkt-cmd.Data0 = OTA_DUP_MSG) ) /目的设备接收数据的状态 / Remove timeout waiting for response from other device. 接收到返回的状态后,关闭定时器 osal_stop_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT ); FREE_OTABUF(); /释放缓存区 else delay = BUILD_UINT16( pkt-cmd.Data2, pkt-cmd.Data3 ); / Re-start timeout according to delay sent from other device. osal_start_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT, delay ); break; default: break; UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events ) if ( events & SYS_EVENT_MSG ) /ZDO层接收到注册过的消息 afIncomingMSGPacket_t *MSGpkt; while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SerialApp_TaskID ) ) switch ( MSGpkt-hdr.event ) case ZDO_CB_MSG: SerialApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt ); break; case KEY_CHANGE: SerialApp_HandleKeys( (keyChange_t *)MSGpkt)-state, (keyChange_t *)MSGpkt)-keys ); break; /接收到命令,然后执行,zigbee协议信息的传递有两种方式:消息和命令,消息长度不限,命令的大小则严格规定 case AF_INCOMING_MSG_CMD: /执行发送过来消息命令的回调函数SerialApp_ProcessMSGCmd( MSGpkt ); break default: break; osal_msg_deallocate( (uint8 *)MSGpkt ); / Release the memory. / Return unprocessed events return ( events SYS_EVENT_MSG ); /发送数据的事件,这里是串口通过CC2430发送数据到另一个设备 if ( events & SERIALAPP_MSG_SEND_EVT ) SerialApp_SendData( otaBuf, otaLen ); return ( events SERIALAPP_MSG_SEND_EVT ); /重发数据的事件,如果发送数据没有成功的话 if ( events & SERIALAPP_MSG_RTRY_EVT ) if ( -rtryCnt ) AF_DataRequest( &SerialApp_DstAddr, (endPointDesc_t *)&SerialApp_epDesc, SERI

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论