ZStack 串口应用笔记(二)
软件平台:ZStack-CC2530-2.5.1a\SerialApp
硬件平台:FB2530EB V1.0 2010-09-20
应用层使用串口步骤:
1.配置串口参数
【源码剖析】
halUARTCfg_t uartConfig;
……………………………………
RegisterForKeys( task_id );
//配置串口参数
uartConfig.configured = TRUE; // 2x30 don't care - see uart driver.
uartConfig.baudRate = SERIAL_APP_BAUD;
uartConfig.flowControl = FALSE;//TRUE;不使用串口流控
uartConfig.flowControlThreshold = SERIAL_APP_THRESH; // 2x30 don't care - see uart driver.
uartConfig.rx.maxBufSize = SERIAL_APP_RX_SZ; // 2x30 don't care - see uart driver.
uartConfig.tx.maxBufSize = SERIAL_APP_TX_SZ; // 2x30 don't care - see uart driver.
uartConfig.idleTimeout = SERIAL_APP_IDLE; // 2x30 don't care - see uart driver.
uartConfig.intEnable = TRUE; // 2x30 don't care - see uart driver.
uartConfig.callBackFunc = SerialApp_CallBack;//串口回调函数
HalUARTOpen (SERIAL_APP_PORT, &uartConfig);//打开串口
2.串口回调函数响应触发事件处理
【源码剖析】
static void SerialApp_CallBack(uint8 port, uint8 event)
{
(void)port;
if ((event & (HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT)) &&
#if SERIAL_APP_LOOPBACK //串口发送回自己收到的数据
(SerialApp_TxLen < SERIAL_APP_TX_MAX))
#else
!SerialApp_TxLen)
#endif
{
SerialApp_Send();//串口触发事件处理函数
}
}
3.串口数据转无线发送
【源码剖析】
static void SerialApp_Send(void)
{
#if SERIAL_APP_LOOPBACK
if (SerialApp_TxLen < SERIAL_APP_TX_MAX)
{
//从串口接收数据缓存区读出到应用层数据缓存区
SerialApp_TxLen += HalUARTRead(SERIAL_APP_PORT, SerialApp_TxBuf+SerialApp_TxLen+1,
SERIAL_APP_TX_MAX-SerialApp_TxLen);
}
if (SerialApp_TxLen)
{
(void)SerialApp_TxAddr;
if (HalUARTWrite(SERIAL_APP_PORT, SerialApp_TxBuf+1, SerialApp_TxLen))//回写收到的数据
{
SerialApp_TxLen = 0;
}
else
{
osal_set_event(SerialApp_TaskID, SERIALAPP_SEND_EVT);//触发无线发送事件
}
}
#else
if (!SerialApp_TxLen && //从串口接收数据缓存区读出到应用层数据缓存区
(SerialApp_TxLen = HalUARTRead(SERIAL_APP_PORT, SerialApp_TxBuf+1, SERIAL_APP_TX_MAX)))
{
// Pre-pend sequence number to the Tx message.
SerialApp_TxBuf[0] = ++SerialApp_TxSeq;//在首位置插入数据包序号
}
if (SerialApp_TxLen)
{
//无线发送数据包
if (afStatus_SUCCESS != AF_DataRequest(&SerialApp_TxAddr,
(endPointDesc_t *)&SerialApp_epDesc,
SERIALAPP_CLUSTERID1,
SerialApp_TxLen+1, SerialApp_TxBuf,
&SerialApp_MsgID, 0, AF_DEFAULT_RADIUS))
{
osal_set_event(SerialApp_TaskID, SERIALAPP_SEND_EVT);//发送失败,触发无线发送事件重发
}
}
#endif
}
4.其中调用的 HalUARTRead和HalUARTWrite函数说明(_hal_uart_dma.c)
需要说明的是应用层在调用HalUARTWrite函数时,仅仅是完成了把应用层需要发送的数据移到可用的串口发送数据缓存区,并且置位了数据待发送标识txDMAPending,而并没有启动DMA发送传传输,也就是说此时数据并没通过串口发送出去。这一部分工作将在HalUARTPollDMA函数中启动执行。所以不能在应用层连续调用HalUARTWrite函数往缓存区写入大量数据,导致缓存区溢出,丢失需要发送的数据。
【源码剖析】
static uint16 HalUARTReadDMA(uint8 *buf, uint16 len)
{
uint16 cnt;
for (cnt = 0; cnt < len; cnt++)
{
if (!HAL_UART_DMA_NEW_RX_BYTE(dmaCfg.rxHead))
{
break;//没有读到有效数据时跳出循环
}
*buf++ = HAL_UART_DMA_GET_RX_BYTE(dmaCfg.rxHead);//读出缓存区低字节赋值
HAL_UART_DMA_CLR_RX_BYTE(dmaCfg.rxHead);//清除接收缓存区高字节为(U0BAUD ^ 0xFF)
#if HAL_UART_DMA_RX_MAX == 256
(dmaCfg.rxHead)++; //最大接收字节为256时,数据溢出自动清零
#else
if (++(dmaCfg.rxHead) >= HAL_UART_DMA_RX_MAX)
{
dmaCfg.rxHead = 0;//读数据索引清零
}
#endif
}
PxOUT &= ~HAL_UART_Px_RTS; // Re-enable the flow on any read.
return cnt;
}
static uint16 HalUARTWriteDMA(uint8 *buf, uint16 len)
{
uint16 cnt;
halIntState_t his;
uint8 txSel;
txIdx_t txIdx;
// Enforce all or none.
if ((len + dmaCfg.txIdx[dmaCfg.txSel]) > HAL_UART_DMA_TX_MAX)//发送数据缓存区溢出
{
return 0;
}
HAL_ENTER_CRITICAL_SECTION(his);
txSel = dmaCfg.txSel;
txIdx = dmaCfg.txIdx[txSel];
HAL_EXIT_CRITICAL_SECTION(his);
for (cnt = 0; cnt < len; cnt++)
{
dmaCfg.txBuf[txSel][txIdx++] = buf[cnt];//往发送缓存区写数据
}
HAL_ENTER_CRITICAL_SECTION(his);
if (txSel != dmaCfg.txSel)
{
HAL_EXIT_CRITICAL_SECTION(his);
txSel = dmaCfg.txSel;
txIdx = dmaCfg.txIdx[txSel];
for (cnt = 0; cnt < len; cnt++)
{
dmaCfg.txBuf[txSel][txIdx++] = buf[cnt];//往发送缓存区写数据
}
HAL_ENTER_CRITICAL_SECTION(his);
}
dmaCfg.txIdx[txSel] = txIdx;
if (dmaCfg.txIdx[(txSel ^ 1)] == 0)
{
// TX DMA is expected to be fired
dmaCfg.txDMAPending = TRUE; //发送缓存区有数据待发送标识置位
}
HAL_EXIT_CRITICAL_SECTION(his);
return cnt;
}