zigbee串口.docx
- 文档编号:7653631
- 上传时间:2023-01-25
- 格式:DOCX
- 页数:35
- 大小:185.02KB
zigbee串口.docx
《zigbee串口.docx》由会员分享,可在线阅读,更多相关《zigbee串口.docx(35页珍藏版)》请在冰豆网上搜索。
zigbee串口
第14讲:
Zstack2006串口机制学习
我们将串口机制的学习分为如图X所示的四大部分:
串口配置、串口初始化、发送数据和接收数据。
图X串口机制四大模块
串口配置:
串口的配置主要完成配置使用UART0或者UART1,同时决定是否使用DMA,协议栈默认使用DMA,我们化繁为简不适用DMA。
主要在文件hal_board_cfg.h中完成。
串口初始化:
串口的初始化主要完成相关常量的初始化即打开串口串口的工作。
主要涉及函数有SPIMgr_Init()和HalUARTOpen()。
发送数据:
发送数据主要完成将要发送的数据通过串口传递出去,主要涉及到的函数有HalUARTWrite()和串口发送中断服务函数HAL_ISR_FUNCTION()。
接收数据:
接收数据主要完成将串口传递的数据接收并传递给相应的层(通常为应用层),主要涉及的函数有HalUARTPoll()和串口接收中断服务函数HAL_ISR_FUNCTION()。
串口配置
串口的配置主要决定是否使用DMA,以及使用UART0还是UART1,主要在文件hal_board_cfg.h中完成,摘录如下:
#ifndefHAL_UART
//如果使用串口,必须至少编译以下四者之一
#if(definedZAPP_P1)||(definedZAPP_P2)||(definedZTOOL_P1)||(definedZTOOL_P2)
#defineHAL_UARTTRUE
#else
#defineHAL_UARTFALSE
#endif
#endif
#ifHAL_UART
//默认使用UART0
#defineHAL_UART_0_ENABLETRUE
#defineHAL_UART_1_ENABLEFALSE
#ifHAL_DMA
#if!
defined(HAL_UART_DMA)
//默认使用DMA
#defineHAL_UART_DMA0
#endif
#else
#undefHAL_UART_DMA
#defineHAL_UART_DMA0
#endif
#if!
defined(HAL_UART_ISR)
//默认不适用普通UART
#defineHAL_UART_ISR1
#endif
#if!
defined(HAL_UART_CLOSE)
#defineHAL_UART_CLOSEFALSE
#endif
#else
#defineHAL_UART_0_ENABLEFALSE
#defineHAL_UART_1_ENABLEFALSE
#defineHAL_UART_DMAFALSE
#defineHAL_UART_ISRFALSE
#defineHAL_UART_CLOSEFALSE
#endif
串口初始化
一、串口初始化
voidSPIMgr_Init()
{
halUARTCfg_tuartConfig;
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_DEFAULT_MAX_RX_BUFF;
uartConfig.tx.maxBufSize=SPI_MGR_DEFAULT_MAX_TX_BUFF;
uartConfig.idleTimeout=SPI_MGR_DEFAULT_IDLE_TIMEOUT;
uartConfig.intEnable=TRUE;
//根据编译选项配置回调函数
#ifdefined(ZTOOL_P1)||defined(ZTOOL_P2)
uartConfig.callBackFunc=SPIMgr_ProcessZToolData;
#elifdefined(ZAPP_P1)||defined(ZAPP_P2)
uartConfig.callBackFunc=SPIMgr_ProcessZAppData;
#else
uartConfig.callBackFunc=NULL;
#endif
//打开串口
#ifdefined(SPI_MGR_DEFAULT_PORT)
HalUARTOpen(SPI_MGR_DEFAULT_PORT,&uartConfig);
#else
(void)uartConfig;
#endif
//初始化全局变量SPIMgr_MaxZAppBufLen、SPIMgr_ZAppRxStatus
#ifdefined(ZAPP_P1)||defined(ZAPP_P2)
SPIMgr_MaxZAppBufLen=1;
SPIMgr_ZAppRxStatus=SPI_MGR_ZAPP_RX_READY;
#endif
}
二、打开串口
uint8HalUARTOpen(uint8port,halUARTCfg_t*config)
{
uartCfg_t**cfgPP=NULL;
uartCfg_t*cfg;
#ifHAL_UART_0_ENABLE
if(port==HAL_UART_PORT_0)
{
cfgPP=&cfg0;
}
#endif
//如果编译了HAL_UART_CLOSE则先关闭串口
#ifHAL_UART_CLOSE
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=cfg->txTail=0;
cfg->rxHigh=config->rx.maxBufSize-config->flowControlThreshold;
cfg->rxCB=config->callBackFunc;
#ifHAL_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接收使能
U0CSR|=CSR_RE;
#ifHAL_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|=HAL_UART_0_P0_CTS;
RX0_FLOW_ON;
}
else
{
U0UCR=UCR_STOP;//本身默認就是高位停止位
}
}
#endif
returnHAL_UART_SUCCESS;
}
首先注意该函数中指针的关系,如图X所示。
以**cfgPP为桥梁,函数中对指针*cfg的操作实际是相当于对全局指针*cfg0的操作。
图X指针关系
函数主要功能是完成的对串口的配置,例如串口波特率的设定,串口接收中断使能等。
同时该函数也初始化了串口接收缓存和串口发送缓存。
具体分析如下:
1、设定串口相关内容,接收使能。
①、设定波特率
U0GCR=(config->baudRate==HAL_UART_BR_38400)?
10:
11;
U0BAUD=(config->baudRate==HAL_UART_BR_38400)?
59:
216;
②、中断使能、允许接收
URX0IE=1
IEN2|=UTX0IE
U0CSR|=CSR_RE;
③、设定结束电平
U0UCR=UCR_STOP
2、设定了cfg0。
1、数据接收缓存
根据初始化函数中的参数为接收缓存分配内存,并将头指针cfg->rxHead和尾指针cfg->rxTail归零。
并根据初始化函数参数设定了准满的高度rxHigh。
cfg->rxMax=config->rx.maxBufSize;
fg->rxBuf=osal_mem_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、cfg0其它配置
cfg->flag=0;//初始化标志位flag为0
cfg->rxCB=config->callBackFunc;//保存回调函数
接收数据
该模块流程图如图X所示。
图X数据接收流程图
一、产生数据
HAL_ISR_FUNCTION(halUart0RxIsr,URX0_VECTOR)
{
cfg0->rxBuf[cfg0->rxHead]=U0DBUF;
if(cfg0->rxHead==cfg0->rxMax)
{
cfg0->rxHead=0;
}
else
{
cfg0->rxHead++;
}
}
当串口接收到数据时,会触发UART0接收中断,进入UART0的接收中断服务函数,协议栈将串口接收到的数据存放到接收缓存rxBuf中,并将头指针cfg0->rxHead移动一个单位。
处理流程图如图X所示。
图X串口接收中断服务函数流程图
二、处理数据
voidHalUARTPoll(void)
{
#if(HAL_UART_0_ENABLE|HAL_UART_1_ENABLE)
staticuint8tickShdw;
uartCfg_t*cfg;
uint8tick;
#ifHAL_UART_0_ENABLE
if(cfg0)
{
cfg=cfg0;
}
#endif
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;
}
#ifHAL_UART_ISR
pollISR(cfg);
#endif
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))
{
evt=HAL_UART_RX_ABOUT_FULL;
}
elseif(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)。
其中满(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_UART_RX_ABOUT_FULL)
准满(HAL_UART_RX_ABOUT_FULL)意思是将要满了,与满类似,这里不在说明了。
3时间溢出(HAL_UART_RX_TIMEOUT)
上述两种事件都是在接收缓存将要满或者已经“满”了才会被触发,但是如果有这样一种情况,我们整个实验过程只发送了一个字节的数据,没有达到接收缓存的准满和“满”。
为了解决以上问题,协议栈还会触发一种事件,那就是我们现在所说的时间溢出(HAL_UART_RX_TIMEOUT),该事件主要是在一个设定的时间内如果没有接收到数据就将会被触发,去接收缓存寻找“漏网之鱼”。
举例说明:
假如溢出时间为1s。
数据缓存接收了一个字节的数据后定时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->rxTick-=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-tickShdw;
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;
cfg->rxTick=0,若溢出,则将溢出时间置为0。
可以看出,若在设定的时间内如果没有接收到数据,每次进行串口轮询都要将溢出时间cfg->rxTick减掉该轮询间隔,直到将溢出时间cfg->rxTick减为0最终触发事件:
时间溢出(HAL_UART_RX_TIMEOUT)。
2、UART轮询函数
staticvoidpollISR(uartCfg_t*cfg)
{
uint8cnt=UART_RX_AVAIL(cfg);//获取数量
if(!
(cfg->flag&UART_CFG_RXF))//空闲,不再接收
{
if(cfg->rxCnt!
=cnt)//比较数量
{
cfg->rxTick=HAL_UART_RX_IDLE;//时间置位
cfg->rxCnt=cnt;//重置数量
}
//是否要紧急关停
if(cfg->rxCnt>=(cfg->rxMax-SAFE_RX_MIN))
{
RX_STOP_FLOW(cfg);
}
}
}
代码分析:
Ⅰ、通过UART_RX_AVAIL(cfg)获取缓存中剩余空间,具体代码如下:
#defineUART_RX_AVAIL(cfg)\
((cfg->rxHead>=cfg->rxTail)?
(cfg->rxHead-cfg->rxTail):
\
(cfg->rxMax-cfg->rxTail+cfg->rxHead+1))
图解如图X所示:
Cnt=rxHead-rxTailCnt=rxMax-rxTail+rxHead+1
图XUART_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_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;
}
elseif(cfg->rxHigh&&(cfg->rxHead>=cfg->rxHigh))
{
evt=HAL_UART_RX_ABOUT_FULL;
}
elseif(cfg->rxTick==0)
{
evt=HAL_UART_RX_TIMEOUT;
}
else
{
evt=0;
}
可以由上面的代码看出,串口接收数据的三种事件与其轮询函数并没有直接关系,而是与接收缓存器的指针及溢出时间有关,三种事件的说明可以参见前面时间说明部分。
4、回调函数
voidSPIMgr_ProcessZAppData(uint8port,uint8event)
{
osal_event_hdr_t*msg_ptr;
uint16length=0;
uint16rxBufLen=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
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- zigbee 串口