1、zigbee串口第14 讲:Zstack 2006 串口机制学习我们将串口机制的学习分为如图X所示的四大部分:串口配置、串口初始化、发送数据和接收数据。图X 串口机制四大模块 串口配置:串口的配置主要完成配置使用UART0或者UART1,同时决定是否使用DMA,协议栈默认使用DMA,我们化繁为简不适用DMA。主要在文件hal_board_cfg.h中完成。 串口初始化:串口的初始化主要完成相关常量的初始化即打开串口串口的工作。主要涉及函数有SPIMgr_Init ()和HalUARTOpen()。 发送数据:发送数据主要完成将要发送的数据通过串口传递出去,主要涉及到的函数有HalUARTWri
2、te()和串口发送中断服务函数HAL_ISR_FUNCTION()。 接收数据:接收数据主要完成将串口传递的数据接收并传递给相应的层(通常为应用层),主要涉及的函数有HalUARTPoll()和串口接收中断服务函数HAL_ISR_FUNCTION()。串口配置串口的配置主要决定是否使用DMA,以及使用UART0还是UART1,主要在文件hal_board_cfg.h中完成,摘录如下:#ifndef HAL_UART/如果使用串口,必须至少编译以下四者之一#if (defined ZAPP_P1) | (defined ZAPP_P2) | (defined ZTOOL_P1) | (defin
3、ed ZTOOL_P2)#define HAL_UART TRUE#else#define HAL_UART FALSE #endif#endif#if HAL_UART /默认使用UART0 #define HAL_UART_0_ENABLE TRUE #define HAL_UART_1_ENABLE FALSE #if HAL_DMA #if !defined( HAL_UART_DMA ) /默认使用DMA #define HAL_UART_DMA 0 #endif #else #undef HAL_UART_DMA #define HAL_UART_DMA 0 #endif #if
4、!defined( HAL_UART_ISR ) /默认不适用普通UART #define HAL_UART_ISR 1 #endif #if !defined( HAL_UART_CLOSE ) #define HAL_UART_CLOSE FALSE #endif#else #define HAL_UART_0_ENABLE FALSE #define HAL_UART_1_ENABLE FALSE #define HAL_UART_DMA FALSE #define HAL_UART_ISR FALSE #define HAL_UART_CLOSE FALSE#endif串口初始化一、串
5、口初始化void SPIMgr_Init () halUARTCfg_t uartConfig; App_TaskID = 0; /UART配置 uartConfig.configured = TRUE; uartConfig.baudRate = SPI_MGR_DEFAULT_BAUDRATE; uartConfig.flowControl = SPI_MGR_DEFAULT_OVERFLOW; uartConfig.flowControlThreshold = SPI_MGR_DEFAULT_THRESHOLD; uartConfig.rx.maxBufSize = SPI_MGR_DE
6、FAULT_MAX_RX_BUFF; uartConfig.tx.maxBufSize = SPI_MGR_DEFAULT_MAX_TX_BUFF; uartConfig.idleTimeout = SPI_MGR_DEFAULT_IDLE_TIMEOUT; uartConfig.intEnable = TRUE; /根据编译选项配置回调函数#if defined (ZTOOL_P1) | defined (ZTOOL_P2) uartConfig.callBackFunc = SPIMgr_ProcessZToolData;#elif defined (ZAPP_P1) | defined
7、(ZAPP_P2) uartConfig.callBackFunc = SPIMgr_ProcessZAppData;#else uartConfig.callBackFunc = NULL;#endif/打开串口#if defined (SPI_MGR_DEFAULT_PORT) HalUARTOpen (SPI_MGR_DEFAULT_PORT, &uartConfig);#else (void)uartConfig;#endif/初始化全局变量SPIMgr_MaxZAppBufLen、SPIMgr_ZAppRxStatus#if defined (ZAPP_P1) | defined (
8、ZAPP_P2) SPIMgr_MaxZAppBufLen = 1; SPIMgr_ZAppRxStatus = SPI_MGR_ZAPP_RX_READY;#endif二、打开串口uint8 HalUARTOpen( uint8 port, halUARTCfg_t *config ) uartCfg_t *cfgPP = NULL; uartCfg_t *cfg;#if HAL_UART_0_ENABLE if ( port = HAL_UART_PORT_0 ) cfgPP = &cfg0; #endif/如果编译了HAL_UART_CLOSE则先关闭串口#if HAL_UART_CLO
9、SE HalUARTClose( port );#else HAL_UART_ASSERT( *cfgPP = NULL );#endif *cfgPP = (uartCfg_t *)osal_mem_alloc( sizeof( uartCfg_t ) ); cfg = *cfgPP; cfg-rxMax = config-rx.maxBufSize; cfg-txMax = config-tx.maxBufSize; cfg-txBuf = osal_mem_alloc( cfg-txMax+1 ); cfg-rxHead = cfg-rxTail = 0; cfg-txHead = cf
10、g-txTail = 0; cfg-rxHigh = config-rx.maxBufSize - config-flowControlThreshold; cfg-rxCB = config-callBackFunc;#if HAL_UART_0_ENABLE if ( port = HAL_UART_PORT_0 ) /设定波特率 U0BAUD = (config-baudRate = HAL_UART_BR_38400) ? 59 : 216; U0GCR = (config-baudRate = HAL_UART_BR_38400) ? 10 : 11; /UART0接收使能 U0CS
11、R |= CSR_RE;#if HAL_UART_DMA = 1#else cfg-flag = 0;/初始化标志位flag为0 cfg-rxBuf = osal_mem_alloc( cfg-rxMax+1 ); URX0IE = 1;/UART0接收中断使能 IEN2 |= UTX0IE; /UART0发送中断使能#endif /如果使用流控,进行相关设定 if ( config-flowControl ) cfg-flag |= UART_CFG_FLW; U0UCR = UCR_FLOW | UCR_STOP; P0SEL |= HAL_UART_0_P0_RTS; P0DIR |=
12、HAL_UART_0_P0_CTS; RX0_FLOW_ON; else U0UCR = UCR_STOP;/本身默認就是高位停止位 #endif return HAL_UART_SUCCESS; 首先注意该函数中指针的关系,如图X 所示。以*cfgPP为桥梁,函数中对指针*cfg的操作实际是相当于对全局指针*cfg0的操作。图X 指针关系函数主要功能是完成的对串口的配置,例如串口波特率的设定,串口接收中断使能等。同时该函数也初始化了串口接收缓存和串口发送缓存。具体分析如下:1、 设定串口相关内容,接收使能。、设定波特率U0GCR = (config-baudRate = HAL_UART_B
13、R_38400) ? 10 : 11;U0BAUD = (config-baudRate = HAL_UART_BR_38400) ? 59 : 216;、中断使能、允许接收URX0IE = 1IEN2 |= UTX0IEU0CSR |= CSR_RE;、设定结束电平U0UCR = UCR_STOP2、 设定了cfg0。1、 数据接收缓存根据初始化函数中的参数为接收缓存分配内存,并将头指针cfg-rxHead和尾指针cfg-rxTail归零。并根据初始化函数参数设定了准满的高度rxHigh。cfg-rxMax = config-rx.maxBufSize;fg-rxBuf = osal_mem
14、_alloc( cfg-rxMax+1 );cfg-rxHead = cfg-rxTail = 0;cfg-rxHigh = config-rx.maxBufSize - config-flowControlThreshold;图X 初始化接收缓存2、 数据发送缓存根据初始化函数中的参数为发送缓存分配内存,并将头指针txHead和尾指针cfg-txTail归零。 cfg-txMax = config-tx.maxBufSize; cfg-txBuf = osal_mem_alloc( cfg-txMax+1 ); cfg-txHead = cfg-txTail = 0;图X 初始化发送缓存3、
15、 cfg0其它配置cfg-flag = 0;/初始化标志位flag为0cfg-rxCB = config-callBackFunc;/保存回调函数接收数据该模块流程图如图X所示。图X 数据接收流程图一、产生数据HAL_ISR_FUNCTION( halUart0RxIsr, URX0_VECTOR ) cfg0-rxBufcfg0-rxHead = U0DBUF; if ( cfg0-rxHead = cfg0-rxMax ) cfg0-rxHead = 0; else cfg0-rxHead+; 当串口接收到数据时,会触发UART0接收中断,进入UART0的接收中断服务函数,协议栈将串口接收
16、到的数据存放到接收缓存rxBuf中,并将头指针cfg0-rxHead移动一个单位。处理流程图如图X所示。图X 串口接收中断服务函数流程图二、处理数据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 if ( cfg0 ) cfg = cfg0; #endif tick = ST0 - tickShdw; tickShdw = ST0; do if ( cfg-tx
17、Tick tick ) cfg-txTick -= tick; else cfg-txTick = 0; if ( cfg-rxTick tick ) cfg-rxTick -= tick; else cfg-rxTick = 0; #if HAL_UART_ISR pollISR( 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-rxH
18、ead = cfg-rxHigh) ) evt = HAL_UART_RX_ABOUT_FULL; else if ( cfg-rxTick = 0 ) evt = HAL_UART_RX_TIMEOUT; else evt = 0; if ( evt & cfg-rxCB ) cfg-rxCB( (cfg-flag & UART_CFG_U1F)!=0), evt ); 1、时间说明: Zstack协议栈串口接收到数据后可以触发三种事件:事件满(HAL_UART_RX_FULL)、准满(HAL_UART_RX_ABOUT_FULL)、时间溢出(HAL_UART_RX_TIMEOUT)。其中满
19、(HAL_UART_RX_FULL)和准满(HAL_UART_RX_ABOUT_FULL)对接收缓存而言,而时间溢出(HAL_UART_RX_TIMEOUT)是相对接收时间而言。1 、满(HAL_UART_RX_FULL)满(HAL_UART_RX_FULL)并非指接收缓存器完全被填满,而协议栈中将满(HAL_UART_RX_FULL)定义为cfg-rxHead = (cfg-rxMax - SAFE_RX_MIN),即留有一定的“空隙” SAFE_RX_MIN。因为留下这一点“空隙”可以在提取数据的同时还可以接收数据,接收缓存数据流程如图X所示。图X 接收缓存数据流程2 准满(HAL_UAR
20、T_RX_ABOUT_FULL)准满(HAL_UART_RX_ABOUT_FULL)意思是将要满了,与满类似,这里不在说明了。3 时间溢出(HAL_UART_RX_TIMEOUT)上述两种事件都是在接收缓存将要满或者已经“满”了才会被触发,但是如果有这样一种情况,我们整个实验过程只发送了一个字节的数据,没有达到接收缓存的准满和“满”。为了解决以上问题,协议栈还会触发一种事件,那就是我们现在所说的时间溢出(HAL_UART_RX_TIMEOUT),该事件主要是在一个设定的时间内如果没有接收到数据就将会被触发,去接收缓存寻找“漏网之鱼”。举例说明:假如溢出时间为1s。数据缓存接收了一个字节的数据后
21、定时1s,如果在1s内没有接收到数据,就触发了时间溢出(HAL_UART_RX_TIMEOUT)事件;如果在1s内接收到数据,溢出时间被归位重新倒计时,重新等待一个1s。 在Zstack中通过睡眠定时器控制溢出时间,为什么要使用睡眠定时器?因为在PM0、PM1、PM2三种功耗模式都可以使用睡眠模式,当设备进入低功耗模式PM1或者PM2仍然可以保证串口能正常工作。下面我们梳理溢出时间HAL_UART_RX_TIMEOUT。、函数HalUARTPoll第1次执行tick = ST0 - tickShdw;tickShdw = ST0;if ( cfg-rxTick tick ) cfg-rxTic
22、k -= tick;/更新时间else/时间溢出cfg-rxTick = 0; 代码分析: tick = ST0 tickShdw,初始tickShdw=0,所以执行完该语句后tick = ST0; tickShdw = ST0,将此时ST0的值保存到静态变量tickShdw中; if ( cfg-rxTick tick ),比较时间间隔tick与溢出时间cfg-rxTick;cfg-rxTick -= tick,若没有溢出,更新溢出时间cfg-rxTick;cfg-rxTick = 0,若溢出,则将溢出时间置为0。、函数HalUARTPoll第2次执行tick = ST0 - tickShd
23、w;tickShdw = ST0;if ( cfg-rxTick tick ) cfg-rxTick -= tick;/更新时间else/时间溢出cfg-rxTick = 0;代码分析: tick = ST0 tickShdw,其中tickShdw保存上次ST0,所以执行完该语句后tick =ST0,即两次执行该函数时间间隔; tickShdw = ST0,将此时ST0的值再次保存到静态变量tickShdw中; if ( cfg-rxTick tick ),比较时间间隔tick与溢出时间cfg-rxTick;cfg-rxTick -= tick,若没有溢出,更新溢出时间cfg-rxTick;c
24、fg-rxTick = 0,若溢出,则将溢出时间置为0。可以看出,若在设定的时间内如果没有接收到数据,每次进行串口轮询都要将溢出时间cfg-rxTick减掉该轮询间隔,直到将溢出时间cfg-rxTick减为0最终触发事件:时间溢出(HAL_UART_RX_TIMEOUT)。2、UART轮询函数static void pollISR( uartCfg_t *cfg ) uint8 cnt = UART_RX_AVAIL( cfg ); /获取数量 if ( !(cfg-flag & UART_CFG_RXF) )/空闲,不再接收 if ( cfg-rxCnt != cnt )/比较数量 cfg-
25、rxTick = HAL_UART_RX_IDLE;/时间置位 cfg-rxCnt = cnt;/重置数量 /是否要紧急关停 if ( cfg-rxCnt = (cfg-rxMax - SAFE_RX_MIN) ) RX_STOP_FLOW( cfg ); 代码分析:、通过UART_RX_AVAIL( cfg )获取缓存中剩余空间,具体代码如下:#define UART_RX_AVAIL( cfg ) ( (cfg-rxHead = cfg-rxTail) ? (cfg-rxHead - cfg-rxTail) : (cfg-rxMax - cfg-rxTail + cfg-rxHead +1
26、 ) )图解如图X所示:Cnt=rxHead-rxTail Cnt=rxMax-rxTail+rxHead+1图X UART_RX_AVAIL( cfg )图解、更新相关信息,代码如下:if ( cfg-rxCnt != cnt )/比较数量 cfg-rxTick = HAL_UART_RX_IDLE;/时间置位 cfg-rxCnt = cnt; 如果此时没有正在接收数据,即满足条件if ( !(cfg-flag & UART_CFG_RXF) ),那么根据接收缓存器数据的数量判断是否收到数据,如果收到数据将cfg-rxCnt更新为此时接收缓存器数据的数量,并且将溢出时间置为HAL_UART_
27、RX_IDLE,与我们上面梳理的一致:接收到数据后溢出时间归位。、是否要紧急关停,代码如下:if ( cfg-rxCnt = (cfg-rxMax - SAFE_RX_MIN) ) RX_STOP_FLOW( cfg ); 如果接收缓存器中数据数量已经“满”了,则通过宏定义RX_STOP_FLOW禁止接收。(注意:该段代码只有使用了硬件流量控制才起作用)3、触发事件if ( cfg-rxHead = (cfg-rxMax - SAFE_RX_MIN) ) evt = HAL_UART_RX_FULL;else if ( cfg-rxHigh & (cfg-rxHead = cfg-rxHigh
28、) ) evt = HAL_UART_RX_ABOUT_FULL;else if ( cfg-rxTick = 0 ) evt = HAL_UART_RX_TIMEOUT;else evt = 0;可以由上面的代码看出,串口接收数据的三种事件与其轮询函数并没有直接关系,而是与接收缓存器的指针及溢出时间有关,三种事件的说明可以参见前面时间说明部分。4、回调函数void SPIMgr_ProcessZAppData ( uint8 port, uint8 event ) osal_event_hdr_t *msg_ptr; uint16 length = 0; uint16 rxBufLen =
29、Hal_UART_RxBufLen(SPI_MGR_DEFAULT_PORT); if (SPIMgr_MaxZAppBufLen!=0) & (SPIMgr_MaxZAppBufLen = rxBufLen) length = SPIMgr_MaxZAppBufLen; else length = rxBufLen; if (event = HAL_UART_TX_FULL) return; if (event&(HAL_UART_RX_FULL|HAL_UART_RX_ABOUT_FULL| HAL_UART_RX_TIMEOUT) if ( App_TaskID ) if (SPIMgr_ZAppRxStatus=SPI_MGR_ZAPP_RX_READY) & (length != 0) SPIMgr_AppFlowControl ( SPI_MGR_ZAPP_RX_NOT_READY ); msg_ptr = (osal_event_hdr_t *)osal_msg_allocate( length + sizeof
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1