// Initialize that TX DMA is not pending dmaCfg.txDMAPending = FALSE; dmaCfg.txShdwValid = FALSE; } 波特率 2400 4800 9600 14400 19200 28800 38400 57600 76800 115200 230400 UxBAUD.BAUD_M[7:0] 59 59 59 216 59 216 59 216 59 216 216 UxGCR.BAUD_E 6 7 8 8 9 9 10 10 11 11 12 误差(%) 0.14 0.14 0.14 0.03 0.14 0.03 0.14 0.03 0.14 0.03 0.03 至此,串口的初始化已经全部完成了,我们在使用串口时只需要调用写串口函数HalUARTWrite(uint8 port, uint8 *buf, uint16 len)和串口读函数HalUARTRead(uint8 port, uint8 *buf, uint16 len)就行了,在这两个函数的内部同样是使用判断宏的方法来调用,DMA串口读写函数还是ISR串口读写函数,关于串口读写方式的具体实现方式在这里不再详细介绍,毕竟在使用时我们并不需要对它们进行改动。
上面结介绍了串口初始化的流程,接下来介绍如何在协议栈中配置使用串口。
二、 在协议栈中配置使用串口
首先确定你在options选项下定义了宏来使用串口功能,比如说宏ZTOOL_P1,如下图所示:
如果你的协议栈是默认的设置,就是装上以后没有修改过,那就按照下面的步骤来,如果是你修改使用过的,就只修改和你的不同的地方就行了。
首先新建两个C文件,GenericAppCoordinator.c和GenericAppEnddevice.c,然后将协议栈中原来的GenericApp中的内容分别复制进新建的两个文件中,这样做是为了能清除地区分不同设备类型的程序,然后将新建的两个文件添加到协议栈的APP层。
首先修改GenericAppCoordinator.c文件,修改GenericApp_Init函数如下(只写出并注释了添加的部分,省略部分和原代码相同): void GenericApp_Init( byte task_id ) {
halUARTCfg_t uartConfig; //定义一个串口配置有关的结构体类型 …………
uartConfig.configured = TRUE; //注册串口
uartConfig.baudRate = HAL_UART_BR_9600; //波特率设置为9600 uartConfig.flowControl = FALSE; //禁用硬件流控
uartConfig.flowControlThreshold = 1; // 2x30 don't care - see uart driver.
uartConfig.rx.maxBufSize = 255; // 2x30 don't care - see uart driver. uartConfig.tx.maxBufSize = 255; // 2x30 don't care - see uart driver.
uartConfig.idleTimeout = 1; // 2x30 don't care - see uart driver. uartConfig.intEnable = TRUE; // 2x30 don't care - see uart driver. uartConfig.callBackFunc =rxCB; //注册串口回调函数 HalUARTOpen (0,&uartConfig);//打开串口0 ………… }
将GenericApp_MessageMSGCB函数中的内容替换如下:
void GenericApp_MessageMSGCB( afIncomingMSGPacket_t *pkt ) {
uint8 sendBuff[11]; //定义一个发送的数据, switch ( pkt->clusterId ) {
case GENERICAPP_CLUSTERID:
osal_memcpy(&sendBuff,pkt->cmd.Data,sizeof(sendBuff));//将数据包中的数据拷贝到发送数组中
HalUARTWrite(0,(uint8*)&sendBuff,sizeof(sendBuff)); //将接收到的数据输出到串口 break; } }
在文件开头声明串口回调函数:
/********************************************************************* * LOCAL FUNCTIONS */
void GenericApp_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg ); void GenericApp_HandleKeys( byte shift, byte keys );
void GenericApp_MessageMSGCB( afIncomingMSGPacket_t *pckt );
void GenericApp_SendTheMessage( void );
void rxCB(uint8 port,uint8 event); …………
在文件的最后添加串口回调函数: void rxCB(uint8 port,uint8 event) {
/*添加自定义的功能,用来读出串口发来的数据*/ }
以上是协调器端的代码修改,具体的功能就是当协调器接收到无线数据后,就通过串口0将收到的无线数据通过串口发送,这样我们就可以在电脑上用串口调试软件来观察接收到的无线数据了。
要实现上述的功能还需要另外一个设备来向协调器发送无线数据,这里我们用一个终端节点设备来向协调器发送无线数据。
接着来修改GenericApp_Enddevice文件中的代码,GenericApp_Init函数的修改和GenericApp_Coordinator.c中的修改一样。
然后在GenericApp_ProcessEvent( byte task_id, UINT16 events )函数中,如果终端节点设备加入网络(网络状态改变),我们就触发发送数据的事件,代码修改如下(省略部分和原代码相同):
UINT16 GenericApp_ProcessEvent( byte task_id, UINT16 events ) {
…………
case AF_INCOMING_MSG_CMD:
GenericApp_MessageMSGCB( MSGpkt ); break;
case ZDO_STATE_CHANGE:
GenericApp_NwkState = (devStates_t)(MSGpkt->hdr.status); if ( (GenericApp_NwkState == DEV_ZB_COORD) || (GenericApp_NwkState == DEV_ROUTER)
|| (GenericApp_NwkState == DEV_END_DEVICE) ) {
// Start sending \osal_set_event(GenericApp_TaskID,GENERICAPP_SEND_MSG_EVT); }
break; …………
if ( events & GENERICAPP_SEND_MSG_EVT ) {
// Send \
GenericApp_SendTheMessage();
// Setup to send message again
osal_start_timerEx( GenericApp_TaskID,
GENERICAPP_SEND_MSG_EVT, GENERICAPP_SEND_MSG_TIMEOUT );
// return unprocessed events return (event ………… }
上述代码的意思时,如果终端节点的网络状态发生改变(加入网络),就设置事件GENERICAPP_SEND_MSG_EVT,如果事件GENERICAPP_SEND_MSG_EVT发生(被设置),就调用函数GenericApp_SendTheMessage()来发送无线数据,同时使用osal_start_timerEx函数来定时触发GENERICAPP_SEND_MSG_EVT事件,也就是每GENERICAPP_SEND_MSG_TIMEOUT时间发送一次数据。
既然要发送数据,我们还需要在GenericApp_SendTheMessage()函数中做些修改,将函数修改成如下内容:
void GenericApp_SendTheMessage( void ) {
char theMessageData[] = \要发送的数据
GenericApp_DstAddr.addrMode = (afAddrMode_t)Addr16Bit; //使用16位的短地址发送数据 GenericApp_DstAddr.endPoint = GENERICAPP_ENDPOINT; //数据源为终端节点
GenericApp_DstAddr.addr.shortAddr = 0x0000; //数据发送的目标地址为0X0000,即协调器
if ( AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc, GENERICAPP_CLUSTERID,
(byte)osal_strlen( theMessageData ) + 1, (byte *)&theMessageData, &GenericApp_TransID,
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ) {
// Successfully requested to be sent. } else {
// Error occurred in request to send. } }
剩下的就是串口回调函数了,处理方法和协调器那里的相同。 至此串口的配置就全部完成了,将程序非别烧到协调器和终端节点,然后用串口调试软件监测协调器的串口0,发现会以5秒每次的频率接收到Hello World数据。
前面讲过,协议栈默认地以DMA的方式来处理串口,而且默认的是使用串口0的,但是如果我们在项目中要用到两个串口时该怎么办呢?接下来就介绍如何在协议栈中配置以同时使用两个串口。
三、 在协议栈中配置以同时使用两个串口
要想同时使用两个串口,就需要以两种不同的方式来处理串口,既然串口0默认使用了DMA方式,那我们就看一下如何以ISR的方式来处理串口1。首先是初始化,在前面我们分析过,在串口的初始化函数HalUARTInit中是以判断宏的方式来决定以什么样的方式来对串口进行初始化的,同时我们也分析过在hal_board_cfg.h中宏HAL_UART_DMA被定义为了1,而宏HAL_UART_ISR被定义为了0,所以我们要在hal_board_cfg.hR文件中将宏HAL_UART_ISR定义为非0值,那么这个非0值是几呢?我们来看一下HalUARTOpen函数中的代码来确定: uint8 HalUARTOpen(uint8 port, halUARTCfg_t *config) {
(void)port; (void)config;
#if (HAL_UART_DMA == 1)//HAL_UART_DMA为1,所以会执行下面的语句 /*只有在串口号为0的时候才会以DMA的方式打开串口0*/ if (port == HAL_UART_PORT_0) HalUARTOpenDMA(config); #endif
#if (HAL_UART_DMA == 2) //下面的语句不会被执行
if (port == HAL_UART_PORT_1) HalUARTOpenDMA(config); #endif
#if (HAL_UART_ISR == 1) //如果将HAL_UART_ISR 设置为1,就会执行下面的语句 /*只有在串口号为0的时候才会以ISR的方式打开串口0*/ if (port == HAL_UART_PORT_0) HalUARTOpenISR(config); #endif
#if (HAL_UART_ISR == 2)// 如果将HAL_UART_ISR 设置为2,就会执行下面的语句 /*只有在串口号为1的时候才会以ISR的方式打开串口0*/ if (port == HAL_UART_PORT_1) HalUARTOpenISR(config); #endif
#if (HAL_UART_USB)
HalUARTOpenUSB(config); #endif
return HAL_UART_SUCCESS; }
通过上面所加的注释我们大致可以看出,串口0被固定地只能用DMA的方式来处理,也就是说如果想使用串口1的话就必须使用ISR的方式来处理串口1,也就是说给HAL_UART_ISR定义的非0值只能是2,明白了这一点以后,我们就在hal_board_cfg.h修改HAL_UART_ISR的宏定义为2,修改如下:
/* Set to TRUE enable UART usage, FALSE disable it */ …………
#define HAL_UART_DMA 1 #endif #endif
#define HAL_UART_ISR 2 #else …………
搜索“diyifanwen.net”或“第一范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,第一范文网,提供最新教学研究z-stack协议栈串口驱动详解与同时使用两个串口的配置方法 (2)全文阅读和word下载服务。
相关推荐: