1、ZigBee串口收发数据本文转载自: 串口接收发送数据有两种方式,一种是中断的模式,另一种是DMA方式,这里主要以中断的方式,来看一下使用串口来发送,接收数据的整个流程。这里以SerialApp例程为例子。 在mian函数中的调用HalDriverInit();函数,在函数中初始化串口,主要是配置管脚和DMA通道void HalDriverInit (void)#if (defined HAL_UART) & (HAL_UART = TRUE) HalUARTInit();#endif 从程序中可以看出要想使用协议栈中串口,初始化串口必须定义HAL_UART和HAL_UART TRUE 在ha
2、l_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#endif 然后在osal_start_system()开始系统后,会调用Hal_ProcessPoll()来读取时间和串口。 在CC2430的数据手册中有这样一段话。Data reception on the UART is initiatedwhen a
3、 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 w
4、hen 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.并且,当这个接收操作
5、完成后接收中断会被产生。接收到的数据可以通过操作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寄存器,也就是接收到数
6、据的寄存器,把数据读取来放到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 #define SERIAL_APP_RX_MAX 64 #endif#endifSerialApp_Init()函数中有下面的赋值,uartConfig.rx.maxBufSize = SERIAL_A
7、PP_RX_MAX;HalUARTOpen()函数中有下面的赋值:所以其cfg-rxMax=128,cfg-rxMax = config-rx.maxBufSize; 其中rxHead这个参数始终指向像一个参数被存放到rxBuf的位置。因为硬件串口缓存器U0DBUF只能存放一个字节,如果不及时把这个接收到的转移出去,那么就会被下一个到来的字节覆盖掉,所以rxHead变量就指向了这个存放的地址,当然是基于定义的rxBuf存储空间。而if ( cfg0-rxHead = cfg0-rxMax )这一句判断也说明的很清楚,一旦这个计数达到了定义的最大接收数量,也就是说已经把rxBuf存储空间占满了,
8、那么就不能在继续存放了。 中断函数执行完后,就应该跳到发生中断时执行的地方,这时程序继续执行,然后在osal_start_system()开始系统后,会循环调用Hal_ProcessPoll()来读取时间和串口,void Hal_ProcessPoll () HalTimerTick();#if (defined HAL_UART) & (HAL_UART = TRUE) HalUARTPoll();#endif下面是HalUARTPoll();函数的源代码,在这里有对接收到的数据进行处理的程序。void HalUARTPoll( void )#if ( HAL_UART_0_ENABLE |
9、 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 anyw
10、ay)./系统上电后,睡眠定时器就会自动启动做自增计数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
11、 ( cfg-flag & UART_CFG_DMA ) pollDMA( cfg ); else/中断方式#endif pollISR( cfg ); #elif HAL_UART_DMA pollDMA( cfg );#endif 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
12、 = 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
13、 & 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(
14、 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
15、= HAL_UART_RX_IDLE; cfg-rxCnt = cnt; if ( cfg-rxCnt = (cfg-rxMax - SAFE_RX_MIN) ) RX_STOP_FLOW( cfg ); #endifpollISR()函数主要作用就是设置rxTick和rxCn,/关于安全界限,在程序中有下面一段:/如果声明了流控制,为保证数据的正确接收需要在RX缓存区中预留出足够的空间。CC2430可以使用的最大串口波特率为115.2k。这个安全界限的数字跟使用的波特率还有串口tick有关。具体参考Z-STACK问题之串口结构uartCfg_t乱说文章。 可以看到,在初始化时rxHead=r
16、xTail=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 S
17、erialApp_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函数。程序中定义了两个串口接收缓冲区:otaBu
18、f上otaBuf2.当otaBuf中无数据时,处于空闲状态时,由otaBuf接收串口数据;当otaBuf中保留有数据时,下等待接收节点发送接收数据响应或由于某些正在重新给接收节点发送数据时,可通过otaBuf2接收数据,当otaBuf和otaBuf2都没有处于空闲状态时,说明数据没有及时发送给接收节点,发生了数据累积,缓冲区被占用,需要进行流量控制,所以直接退出接收回调函数,暂不接收数据。static void rxCB( uint8 port, uint8 event ) uint8 *buf, len; if ( otaBuf2 ) /缓冲区被占用 return; if ( !(buf =
19、 osal_mem_alloc( SERIAL_APP_RX_CNT ) ) return; 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 ( otaBuf ) /otaBuf正在被占用 otaBuf2 = buf; /otaBuf2接收数据 otaLen2 = len; else otaBuf = buf; /otaBuf接收数据 otaLen = len;
20、 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_D
21、ataRequest(),通过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_DstAd
22、dr, (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 ); rtr
23、yCnt = 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: seqn
24、b = 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_Seq
25、Rx = 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 )
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1