配置串口参数,开启串口功能
在Z-Stack中我们可以直接调用API接口来开启设备的串口功能,此API接口函数为“uint8 HalUARTOpen(uint8 port, halUARTCfg_t *config)”。我们先来看看这个函数的形参,形参“port”表示要开启的串口号,CC2530有两个串口,串口0和串口1,这个看个人硬件所用的串口来选择开启哪个串口。形参“config”里包含了这个串口的使用参数,详情如下:
typedef struct
{
bool configured;
uint8 baudRate;
bool flowControl;
uint16 flowControlThreshold;
uint8 idleTimeout;
halUARTBufControl_t rx;
halUARTBufControl_t tx;
bool intEnable;
uint32 rxChRvdTime;
halUARTCBack_t callBackFunc;
}halUARTCfg_t;
要自己配置的参数如下:
- configured
调用“HalUARTOpen()”函数时会自动将“.configured”参数置为TRUE;若“.configured”=TURE,再调用“HalUARTOpen()”函数打开同一个串口时,则会自动清除串口的RX、TX缓存和串口配置“uartRecord”再重新配置。此处我们设置为TRUE。
baudRate
串口的波特率有“HALUART_BR_9600”、“HAL_UART_BR_19200”、“HAL_UART_BR_38400”、 “HAL_UART BR_115200”可选。此处我们设置为“HAL_UART_BR_9600”波特率。
flowControl
硬件流控制,TRUE为开启硬件流控,FALSE为关闭硬件流控。有硬件流控功能的可以开启硬件流控,此处我们设置为FALSE,关闭硬件流控功能。
- flowControlThreshold
此参数表示RX缓存达到“maxRxBufSize”之前还有多少字节空余。当RX缓存到达“maxRxBufSize – flowControlThreshold”时,会触发相应的应用事件:“HAL_UART_RX_ABOUT_FULL”。此处我们设置为64,一般设置为“maxRxBufSize”的一半。
- idleTimeout
如果设备串口收到数据之后在“idleTimout”时间内RX没有收到新的数据了,将会触发相应的事件“HAL_UART_RX_TIMEOUT”,这时应用程序可以选择读出所有RX的值或者一部分的值,单位:毫秒。此处我们设置为6ms,当串口收到数据后,6ms内没有再收到新的数据,则触发“HAL_UART_RX_ABOUT_FULL”事件。
- rx.maxBufSize
rx包含“halUARTBufControl_t”数据结构,用于操作RX 缓冲区。当接收的字节数到达“rx.maxBufSize”时,会产生“HAL_UART_RX_FULL”事件。此处我们设置为128个字节
- intEnable
串口中断使能,TRUE为使能串口中断功能,FALSE为失能串口中断功能。此处我们设置为TRUE,使能串口中断功能。
- callBackFunc
回调函数,应用程序可以根据RX、TX出发的不同事件进行处理。此处会指向我们自己的串口事件处理函数“Handle_UartEvent()”。
配置串口参数,开启串口0。代码如下:
————————————————————————— Gateway.c ———————————————————————
void Gateway_Init( uint8 task_id )
{
halUARTCfg_t uartconf;
……
// 配置串口
uartconf.baudRate = HAL_UART_BR_115200;
uartconf.callBackFunc = Handle_UartEvent;
uartconf.configured = TRUE;
uartconf.flowControl = FALSE;
uartconf.flowControlThreshold = 64;
uartconf.idleTimeout = 6; // 6ms后串口空闲则发送串口空闲事件,参数不可设为0
uartconf.rx.maxBufSize = 128;
uartconf.tx.maxBufSize = 128;
uartconf.intEnable = TRUE;
// 开启串口0
HalUARTOpen(HAL_UART_PORT_0, &uartconf);
……
}
串口发送和接收功能
串口发送数据就只需要调用“uint16 HalUARTWrite(uint8 port, uint8 *buf, uint16 len)”接口,形参“port”可选择串口0或串口1,开启了哪个串口就用哪个串口;形参“buf”为要发送的数据;形参“len”为要发送数据的长度,一次性最多只能发送“HAL_UART_DMA_TX_MAX”个字节的数据,协议栈中“HAL_UART_DMA_TX_MAX”的默认值为256。
串口接收数据需要通过上面我们注册的回调函数“Handle_UartEvent”来处理串口事件。关于接收数据的串口事件只有三个:HAL_UART_RX_FULL、HAL_UART_RX_ABOUT_FULL和HAL_UART_RX_TIMEOUT。
- HAL_UART_RX_FULL:当RX缓存数据的字节数到达“rx.maxBufSize”时,会产生“HAL_UART_RX_FULL”事件。
- HAL_UART_RX_ABOUT_FULL:当RX缓存数据的字节数到达“maxRxBufSize – flowControlThreshold”时,会产生“HAL_UART_RX_ABOUT_FULL”事件。
- HAL_UART_RX_TIMEOUT:如果设备串口收到数据之后在“idleTimout”时间内串口没有收到新的数据了,会产生“HAL_UART_RX_TIMEOUT”事件。
串口透传功能
串口透传功能具体为终端设备接收到PC端通过串口发送过来的数据,然后将数据发送至协调器端,协调器端接收到数据后再通过串口将数据发送至PC端。
终端节点编程
要使用自己的串口功能,则必须确保关闭了Z-Stack内部的串口功能,不然两者会产生冲突,串口会出现不可预期的数据。要关闭Z-Stack内部的串口功能,我们只需要屏蔽以下几个预编译即可。
屏蔽掉Z-Stack默认开启的MT_TASK、MT_SYS_FUNC、MT_ZDO_FUNC。
———————————————————- Gateway.c ——————————————————————————————-
去掉与本项目无关的初始化,在自己的任务初始化函数当中配置串口参数,开启串口0功能。
void Gateway_Init( uint8 task_id )
{
halUARTCfg_t uartconf;
g_gateway_taskid = task_id;
// 填充端口描述符
g_gateway_epdesc.endPoint = GATEWAY_ENDPOINT;
g_gateway_epdesc.task_id = &g_gateway_taskid;
g_gateway_epdesc.simpleDesc = (SimpleDescriptionFormat_t *)&g_gateway_simpledesc;
g_gateway_epdesc.latencyReq = noLatencyReqs;
// 注册该端口
afRegister(&g_gateway_epdesc);
/* 新添加代码 START */
// 配置串口参数
uartconf.baudRate = HAL_UART_BR_115200;
uartconf.callBackFunc = Handle_UartEvent;
uartconf.configured = TRUE;
uartconf.flowControl = FALSE;
uartconf.flowControlThreshold = 64;
uartconf.idleTimeout = 6; // 6ms后串口空闲则发送串口空闲事件,参数不可设为0
uartconf.rx.maxBufSize = 128;
uartconf.tx.maxBufSize = 128;
uartconf.intEnable = TRUE;
// 开启串口0
HalUARTOpen(HAL_UART_PORT_0, &uartconf);
/* 新添加代码 END */
// 初始化LED灯
// Init_IndicatorLight();
// 通知g_gateway_taskid任务有LED灯闪烁事件发生
// osal_set_event(g_gateway_taskid, EVENT_FLASH_LED); // 关掉上一篇文章的LED灯闪烁功能
// 开始定期发送数据给协调器
// osal_set_event(g_gateway_taskid, EVENT_PERIOD_SEND_DATA);
}
创建串口事件处理回调函数。
static void Handle_UartEvent(uint8 port, uint8 event)
{
// 处理串口0接收事件
if(port==HAL_UART_PORT_0)
{
switch(event)
{
// 当发生 RX 事件
case HAL_UART_RX_FULL:
case HAL_UART_RX_ABOUT_FULL:
case HAL_UART_RX_TIMEOUT:
{
// 读取接收数据的长度
uint16 rxbuflen = Hal_UART_RxBufLen(HAL_UART_PORT_0);
// 创建接收数据内存
uint8* rxbuf = osal_mem_alloc(rxbuflen);
// 若内存创建成功
if(rxbuf!=NULL)
{
// 读取 RX 数据
HalUARTRead(HAL_UART_PORT_0, rxbuf, rxbuflen);
// 直接透传给协调器
SendMessageToCoor(rxbuf, rxbuflen);
// 释放内存
osal_mem_free(rxbuf);
}
}
break;
default:
break;
}
}
}
创建数据无线发送函数,发送数据到协调器中。
static void SendMessageToCoor(uint8* data, uint16 datalen)
{
// 目标地址为协调器
afAddrType_t CoorAddr = {0};
CoorAddr.addrMode = (afAddrMode_t)Addr16Bit;
CoorAddr.addr.shortAddr = 0x00;
CoorAddr.endPoint = GATEWAY_ENDPOINT;
// 发送数据到协调器
AF_DataRequest(&CoorAddr, &g_gateway_epdesc,
TRANSMISSION_CLUSTERID, datalen, data,
&g_transid, AF_DISCV_ROUTE, AF_DEFAULT_RADIUS);
}
协调器编程
要使用自己的串口功能,则必须确保关闭了Z-Stack内部的串口功能,不然两者会产生冲突,串口会出现不可预期的数据。要关闭Z-Stack内部的串口功能,我们只需要屏蔽以下几个预编译即可。
屏蔽掉Z-Stack默认开启的MT_TASK、MT_SYS_FUNC、MT_ZDO_FUNC。
———————————- Gateway.c ——————————————————————————————-
在自己的任务初始化函数当中配置串口参数,开启串口0功能。
void Gateway_Init( uint8 task_id )
{
halUARTCfg_t uartconf;
……
// 配置串口参数
uartconf.baudRate = HAL_UART_BR_115200;
uartconf.callBackFunc = NULL;
uartconf.configured = TRUE;
uartconf.flowControl = FALSE;
uartconf.flowControlThreshold = 64;
uartconf.idleTimeout = 6; // 6ms后串口空闲则发送串口空闲事件,参数不可设为0
uartconf.rx.maxBufSize = 128;
uartconf.tx.maxBufSize = 128;
uartconf.intEnable = TRUE;
// 开启串口0
HalUARTOpen(HAL_UART_PORT_0, &uartconf);
……
}
发送接收数据到PC端。
static void Gateway_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
switch ( pkt->clusterId )
{
case TRANSMISSION_CLUSTERID:
{
// 置反LED灯开关状态
// osal_set_event(g_gateway_taskid, EVENT_FLASH_LED);
// 发送接收数据到PC端
HalUARTWrite(HAL_UART_PORT_0, pkt->cmd.Data, pkt->cmd.DataLength);
}
break;
}
}
分别按下终端设备和协调器设备工程的编译按钮,0错误0警告。然后我们将程序分别烧录到两个CC2530设备中,等待设备自动组网后,通过“串口调试助手”发送数据给终端节点,然后再通过“串口调试助手”观察协调器就可以看到终端设备发送过来的数据了。