串口接收發送數據有兩種方式,一種是中斷的模式,另一種是DMA方式,這裏主要以中斷的方式,來看一下使用串口來發送,接收數據的整個流程。這裏以SerialApp例程爲例子。
在mian函數中的調用HalDriverInit();函數,在函數中初始化串口,主要是配置管腳和DMA通道
void HalDriverInit (void)
{
...................................
#if (defined HAL_UART) && (HAL_UART == TRUE)
HalUARTInit();
#endif
....................................
}
從程序中可以看出要想使用協議棧中串口,初始化串口必須定義HAL_UART和HAL_UART TRUE 在hal_board_cfg.h文件中。
#ifndef HAL_UART
#if (defined ZAPP_P1) || (defined ZAPP_P2) || (defined ZTOOL_P1) || (defined ZTOOL_P2)
#define HAL_UART TRUE
#else
#define HAL_UART FALSE
#endif
#endif
然後在osal_start_system()開始系統後,會調用Hal_ProcessPoll()來讀取時間和串口。
在CC2430的數據手冊中有這樣一段話。
Data reception on the UART is initiatedwhen a 1 is written to the UxCSR.RE bit
The UART will then search for a valid start bit on the RXDx input pin and set the
UxCSR.ACTIVE bit high. When a validstart bit has been detected the received byte is shifted into the receive register .The UxCSR.RX_BYTE bit is set and a receive interrupt is generated when the operation has completed. The received data byte
is available through the UxBUF register. When UxBUF is read, UxCSR.RX_BYTE is cleared by hardware.
當有數據接收時,UxCSR.RE位將被置1,然後,UART將在RXDx的輸入引腳上查找一個有效的開始位,當找到這個開始位時,將設置UxCSR.ACTIVE位爲高電平。當一個有效的開始位被查找到,收到的字節將被移動到接收寄存器中。然後,UxCSR.RX_BYTE位設爲1.並且,當這個接收操作完成後接收中斷會被產生。接收到的數據可以通過操作UxBUF寄存器,當UxBUF寄存器的數據被讀出後,UxCSR.RX_BYTE位被硬件清除。串口發生中斷首先調用中斷的處理函數,這個是接收的中斷函數。
#if HAL_UART_0_ENABLE
HAL_ISR_FUNCTION( halUart0RxIsr, URX0_VECTOR )
{
cfg0->rxBuf[cfg0->rxHead] = U0DBUF;
if ( cfg0->rxHead == cfg0->rxMax )
{
cfg0->rxHead = 0;
}
else
{
cfg0->rxHead++;
}
}
#endif
該中斷函數主要是把U0DBUF寄存器,也就是接收到數據的寄存器,把數據讀取來放到UART的結構體中的,cfg0->rxBuf[],中,這個數組的內存分配是在HalUARTOpen()函數中。
SerialApp.c中有下面的定義
#if !defined( SERIAL_APP_RX_MAX )
#if (defined( HAL_UART_DMA )) && HAL_UART_DMA
#define SERIAL_APP_RX_MAX 128
#else
#define SERIAL_APP_RX_MAX 64
#endif
#endif
SerialApp_Init()函數中有下面的賦值,
uartConfig.rx.maxBufSize = SERIAL_APP_RX_MAX;
HalUARTOpen()函數中有下面的賦值:所以其cfg->rxMax=128,
cfg->rxMax = config->rx.maxBufSize;
其中rxHead這個參數始終指向像一個參數被存放到rxBuf的位置。因爲硬件串口緩存器U0DBUF只能存放一個字節,如果不及時把這個接收到的轉移出去,那麼就會被下一個到來的字節覆蓋掉,所以rxHead變量就指向了這個存放的地址,當然是基於定義的rxBuf存儲空間。
而if ( cfg0->rxHead == cfg0->rxMax )這一句判斷也說明的很清楚,一旦這個計數達到了定義的最大接收數量,也就是說已經把rxBuf存儲空間佔滿了,那麼就不能在繼續存放了。
中斷函數執行完後,就應該跳到發生中斷時執行的地方,這時程序繼續執行,然後在osal_start_system()開始系統後,會循環調用Hal_ProcessPoll()來讀取時間和串口,
void Hal_ProcessPoll ()
{
HalTimerTick();
#if (defined HAL_UART) && (HAL_UART == TRUE)
HalUARTPoll();
#endif
}
下面是HalUARTPoll();函數的源代碼,在這裏有對接收到的數據進行處理的程序。
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
//當發生串口接收中斷時cfg0就會改變,如果串口沒有數據輸入cfg0爲空,當接收到數據時cfg0將在串口中斷服務程序中被改變
if ( cfg0 )
{
cfg = cfg0;
}
#endif
#if HAL_UART_1_ENABLE
if ( cfg1 )
{
cfg = cfg1;
}
#endif
// Use the LSB of the sleep timer (ST0 must be read first anyway).
//系統上電後,睡眠定時器就會自動啓動做自增計數ST0即睡眠定時器啓動到現在計算值的最低8位
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;
}
//是使用DMA方式還是使用中斷方式
#if HAL_UART_ISR
#if HAL_UART_DMA
if ( cfg->flag & UART_CFG_DMA )
{
pollDMA( cfg );
}
else//中斷方式
#endif
{
pollISR( cfg );
}
#elif HAL_UART_DMA
pollDMA( 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->rxHead >= cfg->rxHigh) )
{
//rxBuf[ ]接收到預設值(默認80字節),則觸發事件,爲什麼是80,在上一篇轉載的文章中有介紹,這裏重點關注執行的流程。
evt = HAL_UART_RX_ABOUT_FULL;
}
else if ( cfg->rxTick == 0 )
{
//超時事件
evt = HAL_UART_RX_TIMEOUT;
}
else
{
evt = 0;
}
//如果發生事件,並且配置了回調函數則調用回調函數
if ( evt && cfg->rxCB )
{
//(cfg->flag & UART_CFG_U1F)!=0)判讀是那個串口,如果是串口1則爲1,否則爲0
cfg->rxCB( ((cfg->flag & UART_CFG_U1F)!=0), evt );
}
}
#if HAL_UART_0_ENABLE
if ( cfg == cfg0 )
{
#if HAL_UART_1_ENABLE
if ( cfg1 )
{
cfg = cfg1;
}
else
#endif
break;
}
else
#endif
break;
} while ( TRUE );
#else
return;
#endif
}
說明:(1)下面我們看一下pollISR()函數
static void pollISR( uartCfg_t *cfg )
{
//計算rxBuf[]中還有多少數據沒有讀出(以字節爲單位)
uint8 cnt = UART_RX_AVAIL( cfg );
//如果串口沒有接收到數據,也就是說沒有發生過串口接收中斷,那麼cfg應爲是爲空的,則cnt=0如果發生了串口中斷,則cnt計算出串口緩存中還有多少數據沒有讀出,這個緩存並不是硬件寄存器的緩存,而是程序中開闢一段空間
if ( !(cfg->flag & UART_CFG_RXF) )
{
//這裏是針對流控制的,如果又有新的數據接收到了那麼就要重置超時時間(超時時間由睡眠定時器來控制),而且需要把已經讀出的數據數目減去!
// If anything received, reset the Rx idle timer.
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 );
}
}
}
#endif
pollISR()函數主要作用就是設置rxTick和rxCn,
//關於安全界限,在程序中有下面一段:
//如果聲明瞭流控制,爲保證數據的正確接收需要在RX緩存區中預留出足夠的空間。CC2430可以使用的最大串口波特率爲115.2k。這個安全界限的數字跟使用的波特率還有串口tick有關。具體參考《Z-STACK問題之串口結構uartCfg_t亂說》文章。
可以看到,在初始化時rxHead=rxTail=0,如果發生接收中斷,在中斷服務函數中把U0DBUF寄存器中的數據傳送到rxbuf[]中,這時rxHead和rxTail的值不在相等,其中,rxHead是rxBuf[]接收到數據的個數,rxTail是rxBuf[]移出的數據個數,再根據兩者的差值,判斷具體的事件evt。然後,根據evt和設置的回調函數,通過cfg->rxCB調用相應的回調函數。代碼也是體顯在下面一句。
cfg->rxCB( ((cfg->flag & UART_CFG_U1F)!=0), evt );
第一個參數主要是判斷,是UART1還是UART0.第二個參數是觸發的事件類型,那個個回調函數,具體是指向函數呢?
首先,我們在void SerialApp_Init( uint8 task_id )初始化函數中,對串口進行了配置,其中下面兩句中有關於回調函數的。
#else
uartConfig.callBackFunc = rxCB;
#endif
HalUARTOpen (SERIAL_APP_PORT, &uartConfig);
其中,在HalUARTOpen()函數中,有下面的一條語句,
uint8 HalUARTOpen( uint8 port, halUARTCfg_t *config )
{
...................
cfg->rxCB = config->callBackFunc;
...................
}
也就是調用下面的rxCB函數。程序中定義了兩個串口接收緩衝區:otaBuf上otaBuf2.當otaBuf中無數據時,處於空閒狀態時,由otaBuf接收串口數據;當otaBuf中保留有數據時,下等待接收節點發送接收數據響應或由於某些正在重新給接收節點發送數據時,可通過otaBuf2接收數據,當otaBuf和otaBuf2都沒有處於空閒狀態時,說明數據沒有及時發送給接收節點,發生了數據累積,緩衝區被佔用,需要進行流量控制,所以直接退出接收回調函數,暫不接收數據。
static void rxCB( uint8 port, uint8 event )
{
uint8 *buf, len;
if ( otaBuf2 ) //緩衝區被佔用
{
return;
}
if ( !(buf = osal_mem_alloc( SERIAL_APP_RX_CNT )) )
{
return;
}
len = HalUARTRead( port, buf+1, SERIAL_APP_RX_CNT-1 );
if ( !len ) // Length is not expected to ever be zero.
{
osal_mem_free( buf );
return;
}
if ( otaBuf ) //otaBuf正在被佔用
{
otaBuf2 = buf; //otaBuf2接收數據
otaLen2 = len;
}
else
{
otaBuf = buf; //otaBuf接收數據
otaLen = len;
osal_set_event( SerialApp_TaskID, SERIALAPP_MSG_SEND_EVT );
}
}
#endif
在事件處理函數中,有下面的判斷。
UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )
{
........................
if ( events & SERIALAPP_MSG_SEND_EVT )
{
SerialApp_SendData( otaBuf, otaLen );//
return ( events ^ SERIALAPP_MSG_SEND_EVT );
}
.........................
}
下面是SerialApp_SendData()函數的源代碼,調用AF_DataRequest(),通過OTA發送數據。由於在數據包之前增加了序列號SerialApp_SeqTx,多次重發的數據不會被接收節點重複發送到串口。
static void SerialApp_SendData( uint8 *buf, uint8 len )
{
afStatus_t stat;
// Pre-pend sequence number to the start of the Rx buffer.
*buf = ++SerialApp_SeqTx;
otaBuf = buf;
otaLen = len+1;
stat = AF_DataRequest( &SerialApp_DstAddr,
(endPointDesc_t *)&SerialApp_epDesc,
SERIALAPP_CLUSTERID1,
otaLen, otaBuf,
&SerialApp_MsgID, 0, AF_DEFAULT_RADIUS );
if ( (stat == afStatus_SUCCESS) || (stat == afStatus_MEM_FAIL) )
{
//在設定的時間內沒有發送成功,則重新發送。
osal_start_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT,
SERIALAPP_MSG_RTRY_TIMEOUT );
rtryCnt = SERIALAPP_MAX_RETRIES;
}
else
{
FREE_OTABUF();//重發的次數
}
}
void SerialApp_ProcessMSGCmd( afIncomingMSGPacket_t *pkt )
{
uint8 stat;
uint8 seqnb;
uint8 delay;
switch ( pkt->clusterId )
{
// A message with a serial data block to be transmitted on the serial port.
//接收節點收到的接收數據命令,
case SERIALAPP_CLUSTERID1:
seqnb = pkt->cmd.Data[0];
// Keep message if not a repeat packet
if ( (seqnb > SerialApp_SeqRx) || // Normal
((seqnb < 0x80 ) && ( SerialApp_SeqRx > 0x80)) ) // Wrap-around
{
// Transmit the data on the serial port.接收到的發送到串口
if ( HalUARTWrite( SERIAL_APP_PORT, pkt->cmd.Data+1,
(pkt->cmd.DataLength-1)
) )
{
// Save for next incoming message
SerialApp_SeqRx = seqnb;
stat = OTA_SUCCESS;
}
else
{
stat = OTA_SER_BUSY;
}
}
else
{
stat = OTA_DUP_MSG;
}
// Select approproiate OTA flow-control delay.
delay = (stat == OTA_SER_BUSY) ? SERIALAPP_NAK_DELAY : SERIALAPP_ACK_DELAY;
// Build & send OTA response message. 發送響應消息
rspBuf[0] = stat;
rspBuf[1] = seqnb;
rspBuf[2] = LO_UINT16( delay );
rspBuf[3] = HI_UINT16( delay );
//發送接收數據響應命令
stat = AF_DataRequest( &(pkt->srcAddr),
(endPointDesc_t*)&SerialApp_epDesc,
SERIALAPP_CLUSTERID2,
SERIAL_APP_RSP_CNT ,
rspBuf,&SerialApp_MsgID, 0,
AF_DEFAULT_RADIUS );
if ( stat != afStatus_SUCCESS )
{
osal_start_timerEx( SerialApp_TaskID, SERIALAPP_RSP_RTRY_EVT,
SERIALAPP_RSP_RTRY_TIMEOUT
);
// Store the address for the timeout retry. 存儲發送超時的,目的地址
osal_memcpy(&SerialApp_RspDstAddr, &(pkt->srcAddr), sizeof( afAddrType_t ));
}
break;
// A response to a received serial data block. 接收到接收數據響應命令
case SERIALAPP_CLUSTERID2:
if ( (pkt->cmd.Data[1] == SerialApp_SeqTx) &&
((pkt->cmd.Data[0] == OTA_SUCCESS) ||
(pkt->cmd.Data[0] == OTA_DUP_MSG)) ) //目的設備接收數據的狀態
{
// Remove timeout waiting for response from other device. 接收到返回的狀態後,關閉定時器
osal_stop_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT );
FREE_OTABUF(); //釋放緩存區
}
else
{
delay = BUILD_UINT16( pkt->cmd.Data[2], pkt->cmd.Data[3] );
// Re-start timeout according to delay sent from other device.
osal_start_timerEx( SerialApp_TaskID, SERIALAPP_MSG_RTRY_EVT, delay );
}
break;
default:
break;
}
}
UINT16 SerialApp_ProcessEvent( uint8 task_id, UINT16 events )
{
if ( events & SYS_EVENT_MSG ) //ZDO層接收到註冊過的消息
{
afIncomingMSGPacket_t *MSGpkt;
while ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive(
SerialApp_TaskID
)) )
{
switch ( MSGpkt->hdr.event )
{
case ZDO_CB_MSG:
SerialApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt );
break;
case KEY_CHANGE:
SerialApp_HandleKeys( ((keyChange_t *)MSGpkt)->state,
((keyChange_t *)MSGpkt)->keys );
break;
//接收到命令,然後執行,zigbee協議信息的傳遞有兩種方式:消息和命令,消息長度不限,命令的大小則嚴格規定
case AF_INCOMING_MSG_CMD:
//執行發送過來消息命令的回調函數
SerialApp_ProcessMSGCmd( MSGpkt );
break
default:
break;
}
osal_msg_deallocate( (uint8 *)MSGpkt ); // Release the memory.
}
// Return unprocessed events
return ( events ^ SYS_EVENT_MSG );
}
//發送數據的事件,這裏是串口通過CC2430發送數據到另一個設備
if ( events & SERIALAPP_MSG_SEND_EVT )
{
SerialApp_SendData( otaBuf, otaLen );
return ( events ^ SERIALAPP_MSG_SEND_EVT );
}
//重發數據的事件,如果發送數據沒有成功的話
if ( events & SERIALAPP_MSG_RTRY_EVT )
{
if ( --rtryCnt )
{
AF_DataRequest( &SerialApp_DstAddr,
(endPointDesc_t *)&SerialApp_epDesc,
SERIALAPP_CLUSTERID1, otaLen,
otaBuf,
&SerialApp_MsgID, 0,
AF_DEFAULT_RADIUS );
osal_start_timerEx( SerialApp_TaskID,
SERIALAPP_MSG_RTRY_EVT,
SERIALAPP_MSG_RTRY_TIMEOUT );
}
else
{
FREE_OTABUF();
}
return ( events ^ SERIALAPP_MSG_RTRY_EVT );
}
//發送接收數據響應的重發事件
if ( events & SERIALAPP_RSP_RTRY_EVT )
{
afStatus_t stat = AF_DataRequest(
&SerialApp_RspDstAddr,
(endPointDesc_t *)&SerialApp_epDesc,
SERIALAPP_CLUSTERID2,
SERIAL_APP_RSP_CNT, rspBuf,
&SerialApp_MsgID, 0,
AF_DEFAULT_RADIUS );
if ( stat != afStatus_SUCCESS )
{
osal_start_timerEx( SerialApp_TaskID,
SERIALAPP_RSP_RTRY_EVT,
SERIALAPP_RSP_RTRY_TIMEOUT );
}
return ( events ^ SERIALAPP_RSP_RTRY_EVT );
}
#if SERIAL_APP_LOOPBACK
if ( events & SERIALAPP_TX_RTRY_EVT )
{
if ( rxLen )
{
if ( !HalUARTWrite( SERIAL_APP_PORT, rxBuf, rxLen ) )
{
osal_start_timerEx( SerialApp_TaskID, SERIALAPP_TX_RTRY_EVT,
SERIALAPP_TX_RTRY_TIMEOUT
);
}
else
{
rxLen = 0;
}
}
return ( events ^ SERIALAPP_TX_RTRY_EVT );
}
#endif
return ( 0 ); // Discard unknown events.
}
在串口通信中設置了多個重發機制,增加數據通信的可靠性,首先是利用重發數據事件SERIALAPP_MSG_RTRY_EVT重發數據,重發的次數由rtyCnt設定。由於在數據包之前增加了序列號SerialApp_SeqTx,多次生髮的數據不會被接收節點重複發送到串口。別外,加入了數據接收響應機制,發送節點在發送完數據後,等待接收節點返回接收數據響應,收到返回接收數據響應的命令SERIALAPP_CLUSTERID2:後,判斷信息包中的接收狀態參數。若接收狀態爲OTA_DUP_MSG,表明接收節點串口繁忙,應啓動重發機制,延時後產生重發數據事件,若接收狀態爲OTA_SUCCESS,表明接收節點將數據成功發送到串口,就釋放緩存區,等待串口接收下一包數據。
下面是串口的接受和發送的流程圖:
對於串口的使用及工作流程:
首先系統調用初始化函數進行串口的初始化
void HalUARTInit( void )
{
#if HAL_UART_DMA //在默認情況下,這個東西是定義爲true
halDMADesc_t *ch;//如果定義了DMA傳輸方式就定義一個通道號
#endif
// Set P2 priority - USART0 over USART1 if both are defined.
P2DIR &= ~P2DIR_PRIPO;
P2DIR |= HAL_UART_PRIPO;
#if HAL_UART_0_ENABLE //默認情況下爲HAL_UART_0_ENABLE
// Set UART0 I/O location to P0.
PERCFG &= ~HAL_UART_0_PERCFG_BIT;
P0SEL |= HAL_UART_0_P0_RX_TX;
ADCCFG &= ~HAL_UART_0_P0_RX_TX;
U0CSR = CSR_MODE; 模式位UART模式
U0UCR = UCR_FLUSH;
#endif
#if HAL_UART_1_ENABLE
// Set UART1 I/O location to P1.
PERCFG |= HAL_UART_1_PERCFG_BIT;
P1SEL |= HAL_UART_1_P1_RX_TX;
ADCCFG &= ~HAL_UART_1_P1_RX_TX;
U1CSR = CSR_MODE;
U1UCR = UCR_FLUSH;
#endif
#if HAL_UART_DMA
// Setup Tx by DMA.
ch = HAL_DMA_GET_DESC1234( HAL_DMA_CH_TX ); //設置發送通道
// The start address of the destination.
HAL_DMA_SET_DEST( ch, DMA_UDBUF );//目標的開始抵制
// Using the length field to determine how many bytes to transfer.
HAL_DMA_SET_VLEN( ch, HAL_DMA_VLEN_USE_LEN );//傳輸長度
// One byte is transferred each time.
HAL_DMA_SET_WORD_SIZE( ch, HAL_DMA_WORDSIZE_BYTE );//每次傳輸一個字節
// The bytes are transferred 1-by-1 on Tx Complete trigger.
HAL_DMA_SET_TRIG_MODE( ch, HAL_DMA_TMODE_SINGLE );
HAL_DMA_SET_TRIG_SRC( ch, DMATRIG_TX );
// The source address is decremented by 1 byte after each transfer.
HAL_DMA_SET_SRC_INC( ch, HAL_DMA_SRCINC_1 );
// The destination address is constant - the Tx Data Buffer.
HAL_DMA_SET_DST_INC( ch, HAL_DMA_DSTINC_0 );
// The DMA is to be polled and shall not issue
an IRQ upon completion.
HAL_DMA_SET_IRQ( ch, HAL_DMA_IRQMASK_DISABLE );
// Xfer all 8 bits of a byte xfer.
HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS );
// DMA Tx has shared priority for memory access - every other one.
HAL_DMA_SET_PRIORITY( ch, HAL_DMA_PRI_HIGH );
// Setup Rx by DMA.通過DMA設置接收通道
ch = HAL_DMA_GET_DESC1234( HAL_DMA_CH_RX );
// The start address of the source.
HAL_DMA_SET_SOURCE( ch, DMA_UDBUF );
// Using the length field to determine how many bytes to transfer.
HAL_DMA_SET_VLEN( ch, HAL_DMA_VLEN_USE_LEN );
HAL_DMA_SET_WORD_SIZE( ch, HAL_DMA_WORDSIZE_WORD );
// The bytes are transferred 1-by-1 on Rx Complete trigger.
HAL_DMA_SET_TRIG_MODE( ch, HAL_DMA_TMODE_SINGLE );
HAL_DMA_SET_TRIG_SRC( ch, DMATRIG_RX );
// The source address is constant - the Rx Data Buffer.
HAL_DMA_SET_SRC_INC( ch, HAL_DMA_SRCINC_0 );
// The destination address is incremented by 1 word after each transfer.
HAL_DMA_SET_DST_INC( ch, HAL_DMA_DSTINC_1 );
// The DMA is to be polled and shall not issue an IRQ
upon completion.
HAL_DMA_SET_IRQ( ch, HAL_DMA_IRQMASK_DISABLE );
// Xfer all 8 bits of a byte xfer.
HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS );
// DMA has highest priority for memory access.
HAL_DMA_SET_PRIORITY( ch, HAL_DMA_PRI_HIGH );
#endif
}
雖然看不懂,但先別管,搞清楚基本構架再說,來繼續看輪詢函數
void HalUARTPoll( void )
{
#if ( HAL_UART_0_ENABLE | HAL_UART_1_ENABLE )
static uint8 tickShdw;//tickshdw這個變量時幹嘛的?
uartCfg_t *cfg;看看uartCfg_t的定義
****************************************************
注:
typedef struct
{
uint8 *rxBuf;
uint8 rxHead;
uint8 rxTail;
uint8 rxMax;
uint8 rxCnt;
uint8 rxTick;
uint8 rxHigh;
uint8 *txBuf;
#if HAL_UART_BIG_TX_BUF
uint16 txHead;
uint16 txTail;
uint16 txMax;
uint16 txCnt;
#else
uint8 txHead;
uint8 txTail;
uint8 txMax;
uint8 txCnt;
#endif
uint8 txTick;
uint8 flag;
halUARTCBack_t rxCB;
} uartCfg_t;
****************************************************
uint8 tick; //這個變量時什麼東東?
#if HAL_UART_0_ENABLE
if ( cfg0 )
{
cfg = cfg0;
}
****************
注:
cfg0的定義:
#if HAL_UART_0_ENABLE
static uartCfg_t *cfg0;
cfg0在哪進行的配置?
串口又是在哪個地方打開的呢??
****************
#endif
#if HAL_UART_1_ENABLE
if ( cfg1 )
{
cfg = cfg1;
}
#endif
// Use the LSB of the sleep timer (ST0 must be read first anyway).
tick = ST0 - tickShdw;
tickShdw = ST0;//這兩句話是什麼意思??
**************
注:STO在哪定義?
**************
do 這他媽的是個死循環 難道不用退出來嗎??
{
if ( cfg->txTick > tick )
{
cfg->txTick -= tick;
}
else
{
cfg->txTick = 0;
}
if ( cfg->rxTick > tick )
{
cfg->rxTick -= tick;
}
else
{
cfg->rxTick = 0;
}
#if HAL_UART_ISR //是中斷方式還是DMA方式
************
#if !defined( HAL_UART_ISR )
#define HAL_UART_ISR FALSE
#endif
************
#if HAL_UART_DMA
if ( cfg->flag & UART_CFG_DMA )
{
pollDMA( cfg );
******************************************************************************
* @fn pollDMA
*
* @brief Poll a USART module implemented by DMA.
*
* @param cfg - USART configuration structure.
*
* @return none
*****************************************************************************
}
else
#endif
{
pollISR( cfg );
******************************************************************************
* @fn pollISR
*
* @brief Poll a USART module implemented by ISR.
*
* @param cfg - USART configuration structure.
*
* @return none
*****************************************************************************
}
#elif HAL_UART_DMA
pollDMA( 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->rxHead >= 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 );
}
}
//(cfg->flag & UART_CFG_U1F)!=0)判讀是那個串口,如果是串口1則爲1,否則爲0?
#if HAL_UART_0_ENABLE
if ( cfg == cfg0 )
{
#if HAL_UART_1_ENABLE
if ( cfg1 )
{
cfg = cfg1;
}
else
#endif
break;
}
else
#endif
break;
} while ( TRUE );
#else
return;
#endif
}
看了一大截,還是越來越模糊。
我們現在主要看看中斷方式,DMA方式還是算了吧,以後有時間在看。
讓我們看看這個函數:
static void pollISR( uartCfg_t *cfg )
{
//計算rxBuf[]中還有多少數據沒有讀出(以字節爲單位)
uint8 cnt = UART_RX_AVAIL( cfg );
//如果串口沒有接收到數據,也就是說沒有發生過串口接收中斷,那麼cfg應爲是爲空的,則cnt=0如果發生了串口中斷,則cnt計算出串口緩存中還有多少數據沒有讀出,這個緩存並不是硬件寄存器的緩存,而是程序中開闢一段空間
if ( !(cfg->flag & UART_CFG_RXF) )
{
//這裏是針對流控制的,如果又有新的數據接收到了那麼就要重置超時時間(超時時間由睡眠定時器來控制),而且需要把已經讀出的數據數目減去!
// If anything received, reset the Rx idle timer.
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 );
}
}
}
#endif
雖然看不懂,但是繼續看吧,誰叫我笨呢?
接下來看
@fn HalUARTOpen
*
* @brief Open a port according tp the configuration specified by parameter.
*
* @param port - UART port
* config - contains configuration information
*
* @return Status of the function call
*****************************************************************************/
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
#if HAL_UART_1_ENABLE
if ( port == HAL_UART_PORT_1 )
{
cfgPP = &cfg1;
}
#endif
HAL_UART_ASSERT( cfgPP );//這個函數是幹嘛用的?
****************************
注:
****************************
#if HAL_UART_CLOSE
// Protect against user re-opening port before closing it.
HalUARTClose( port );
#else
HAL_UART_ASSERT( *cfgPP == NULL );
#endif
HAL_UART_ASSERT( (config->baudRate == HAL_UART_BR_38400) ||
(config->baudRate == HAL_UART_BR_115200) );
*cfgPP = (uartCfg_t *)osal_mem_alloc( sizeof( uartCfg_t ) );
cfg = *cfgPP;
HAL_UART_ASSERT( cfg );
cfg->rxMax = config->rx.maxBufSize;
#if !HAL_UART_BIG_TX_BUF
HAL_UART_ASSERT( (config->tx.maxBufSize < 256) );
#endif
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;
#if HAL_UART_0_ENABLE
if ( port == HAL_UART_PORT_0 )
{
// Only supporting 38400 or 115200 for code size - other is possible.
U0BAUD = (config->baudRate == HAL_UART_BR_38400) ? 59 : 216;
U0GCR = (config->baudRate == HAL_UART_BR_38400) ? 10 : 11;
U0CSR |= CSR_RE;
#if HAL_UART_DMA == 1
cfg->flag = UART_CFG_DMA;
HAL_UART_ASSERT( (config->rx.maxBufSize <= 128) );
HAL_UART_ASSERT( (config->rx.maxBufSize > SAFE_RX_MIN) );
cfg->rxBuf = osal_mem_alloc( cfg->rxMax*2 );
osal_memset( cfg->rxBuf, ~DMA_PAD, cfg->rxMax*2 );
DMA_RX( cfg );
#else
cfg->flag = 0;
HAL_UART_ASSERT( (config->rx.maxBufSize < 256) );
cfg->rxBuf = osal_mem_alloc( cfg->rxMax+1 );
URX0IE = 1;
IEN2 |= UTX0IE;
#endif
// 8 bits/char; no parity; 1 stop bit; stop bit hi.
if ( config->flowControl )
{
cfg->flag |= UART_CFG_FLW;
U0UCR = UCR_FLOW | UCR_STOP;
// Must rely on H/W for RTS (i.e. Tx stops when receiver negates CTS.)
P0SEL |= HAL_UART_0_P0_RTS;
// Cannot use H/W for CTS as DMA does not clear the Rx bytes properly.
P0DIR |= HAL_UART_0_P0_CTS;
RX0_FLOW_ON;
}
else
{
U0UCR = UCR_STOP;
}
}
#endif
#if HAL_UART_1_ENABLE
if ( port == HAL_UART_PORT_1 )
{
// Only supporting 38400 or 115200 for code size - other is possible.
U1BAUD = (config->baudRate == HAL_UART_BR_38400) ? 59 : 216;
U1GCR = (config->baudRate == HAL_UART_BR_38400) ? 10 : 11;
U1CSR |= CSR_RE;
#if HAL_UART_DMA == 2
cfg->flag = (UART_CFG_U1F | UART_CFG_DMA);
HAL_UART_ASSERT( (config->rx.maxBufSize <= 128) );
HAL_UART_ASSERT( (config->rx.maxBufSize > SAFE_RX_MIN) );
cfg->rxBuf = osal_mem_alloc( cfg->rxMax*2 );
osal_memset( cfg->rxBuf, ~DMA_PAD, cfg->rxMax*2 );
DMA_RX( cfg );
#else
cfg->flag = UART_CFG_U1F;
HAL_UART_ASSERT( (config->rx.maxBufSize < 256) );
cfg->rxBuf = osal_mem_alloc( cfg->rxMax+1 );
URX1IE = 1;
IEN2 |= UTX1IE;
#endif
// 8 bits/char; no parity; 1 stop bit; stop bit hi.
if ( config->flowControl )
{
cfg->flag |= UART_CFG_FLW;
U1UCR = UCR_FLOW | UCR_STOP;
// Must rely on H/W for RTS (i.e. Tx stops when receiver negates CTS.)
P1SEL |= HAL_UART_1_P1_RTS;
// Cannot use H/W for CTS as DMA does not clear the Rx bytes properly.
P1DIR |= HAL_UART_1_P1_CTS;
RX1_FLOW_ON;
}
else
{
U1UCR = UCR_STOP;
}
}
#endif
return HAL_UART_SUCCESS;
}
在zstack中,串口應用主要有三種方式:
(1)與zstack交互進行通信:需要考慮到系統通信的數據格式,這種應用一般更加側重於與zstack交互。
舉個例子,在協調器中我們需要利用PC給zstack發出命令需要其執行相應的命令時,這個時候我們只要在協調器的編譯項加上ZTOOL_P1 MT_TASK兩個選項即可,如果這個時候如果需要MT層和應用層進行交互處理用戶數據(具體用戶數據的格式在zstack中的串口文檔中有定義,大家可以自行查看),就必須在應用層中加入
case MT_SYS_APP_MSG: // Z-Architect Messages
(具體爲什麼加這個如果還不清楚把zstack OSAL運行機制搞清楚之後再開發吧。)
這個時候才能串口發送的數據經過MT層處理後送到我們的應用層,這是接收過程,發送過程直接調用HalUARTWrite()函數即可。
(2)自己的串口應用,與zstack命令格式無關,但不想自己配置串口,想偷懶:這種情況下也很簡單,與上述編譯項不同的是,此時需要加入的編譯項爲:MT_TASK ZAPP_P1,當中的串口號根據實際情況而定,這個時候只要加入編譯項其實就行了,在應用過程中如果想在應用層處理串口的數據,和上述情況類似,這時候需要在應用層中加入的東西有:
#include "SPIMgr.h"頭文件
....
#if defined (ZAPP_P1)
SPIMgr_RegisterTaskID(SampleApp_TaskID);//在MT層註冊
SPIMgr_ZAppBufferLengthRegister(7); // BUFFER大小
#endif
.....
case SPI_INCOMING_ZAPP_DATA:
Bindnode_SerialMSGCB(MSGpkt);//自己的串口數據處理函數
#if defined(ZAPP_P1)
SPIMgr_AppFlowControl ( SPI_MGR_ZAPP_RX_READY );//流控制
#endif
break;
......
這是最主要的三步,完成上述添加,大家就可以使用串口了。
(需要主要注意的是一個串口不能同時實現上面的兩個功能,一定要實現上述ZAPP_Px編譯和ZTOOL_Px同時存在的話,那麼x一定不能相同,即要使用兩個不同的串口,否則會出現衝突。)
(3)可能瞧不起zstack定義好的串口,自己單獨寫一個玩玩,或者想另外加一個串口實現雙串口功能,這個很簡單,大家看看serialapp例子就行,我不細說。
使用的協議棧版本信息:
ZigBee2006ZStack-1.4.3-1.2.1
因爲用現在這模塊SerialApp沒做成功,上電後按鍵沒反應……兩塊無線龍小板子已經買來N年了.
自己想在SampleApp例子基礎上修改實現串口透明傳輸:
串口調試助手1<————>模塊1 <-----OTA-----> 模塊2<————>串口調試助手2
程序修改主要如下:
****************************************************************************************
****************************************************************************************
1、
宏定義事件 #define
UART_RX_CB_EVT 0x0002 (SampleApp.h)
全局變量聲明: (SPIMgr.h)
extern uint8 rxlen; //接收數據長度
extern uint8* databuf; //接收數據指針
****************************************************************************************
****************************************************************************************
2、
串口回調函數rxCB: (SPIMgr.c)
#if defined (ZTOOL_P1) || defined (ZTOOL_P2)
uartConfig.callBackFunc = rxCB;
//uartConfig.callBackFunc = SPIMgr_ProcessZToolData; //回調函數
****************************************************************************************
****************************************************************************************
3、十六進制轉字符函數 (SampleApp.c) 這兩個函數由青竹提供.
uint8 hextoword1(uint8 t )
{
uint8 abc;
uint8 cba;
uint8 xx1;
abc=t;
cba=0xf0;
abc=(abc&cba)>>4;
if(abc<10)
{
xx1=abc+48;
}
else
{
xx1=abc+55;
}
return xx1;
}
uint8 hextoword2(uint8 t)
{
uint8 abc;
uint8 cba;
uint8 xx2;
abc=t;
cba=0x0f;
abc=abc&cba;
if(abc<10)
{
xx2=abc+48;
}
else
{
xx2=abc+55;
}
return xx2;
}
****************************************************************************************
****************************************************************************************
4、定義串口回調函數rxCB() (SPIMgr.c)
static void rxCB( uint8 port, uint8 event )
{
// uint8 rxlen; //接收數據長度
// uint8* dataybuf;//接收數據塊指針
extern uint8 SampleApp_TaskID;
uint16 short_ddr;
uint8 short_ddr_H;
uint8 short_ddr_L;
// uint8 *pointer1;
// uint8 word_buffer[8];
short_ddr=NLME_GetShortAddr();
short_ddr_H=(uint8)((short_ddr&0xff00)>>8);
short_ddr_L=(uint8)short_ddr;
rxlen=Hal_UART_RxBufLen(SPI_MGR_DEFAULT_PORT); //接收緩衝區數據長度,字節爲單位
databuf=osal_mem_alloc(rxlen+1+2); //多分配3字節,分配如下
databuf[0]=rxlen; //一字節存放數據長度
databuf[1]=short_ddr_H; //一字節存放源地址高8位
databuf[2]=short_ddr_L; //一字節存放源地址低8位
//databuf[rxlen+1]='n'; //一字節存放換行符
HalUARTRead ( SPI_MGR_DEFAULT_PORT, databuf+3, rxlen); //讀接收緩衝區數據到內存databuf+3
if(!rxlen)
osal_mem_free( databuf ); //釋放內存
osal_set_event(SampleApp_TaskID,UART_RX_CB_EVT);
// rxCB_to_SampleApp( databuf, rxlen );
}
****************************************************************************************
****************************************************************************************
5、添加:事件處理函數 (SampleApp.c)
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
afIncomingMSGPacket_t *MSGpkt;
// 顯示網絡地址變量
uint16 short_ddr;
uint8 yy1;
uint8 yy2;
uint8 str_1[ ]="my short address is:";
#if defined(ZDO_COORDINATOR)
uint8 str_2[ ]="build the network successfully";
#else
uint8 str_2[ ]="join the network successfully ";
#endif
uint8 str_3[ ]={'n'};
uint8 shortaddr[7];
uint8 *pointer1;
uint8 *pointer2;
uint8 *pointer3;
uint8 *pointer4;
…………(省略)
case AF_INCOMING_MSG_CMD:
SampleApp_MessageMSGCB( MSGpkt );
break;
case ZDO_STATE_CHANGE:
SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status);
if ( (SampleApp_NwkState == DEV_ZB_COORD)
|| (SampleApp_NwkState == DEV_ROUTER)
|| (SampleApp_NwkState == DEV_END_DEVICE) )
{
//顯示本地網絡地址
short_ddr=NLME_GetShortAddr();
yy1=(uint8)((short_ddr&0xff00)>>8);
yy2=(uint8)short_ddr;
shortaddr[0]=48;
shortaddr[1]=120;
shortaddr[2]=hextoword1(yy1);
shortaddr[3]=hextoword2(yy1);
shortaddr[4]=hextoword1(yy2);
shortaddr[5]=hextoword2(yy2);
shortaddr[6]='n';
pointer1=&shortaddr[0];
pointer2=&str_1[0];
pointer3=&str_2[0];
pointer4=&str_3[0];
HalUARTWrite(0,pointer4,1);