最近在做一個(gè)智能家居的項(xiàng)目,用到了TI的CC2530芯片以及對(duì)應(yīng)的zstack協(xié)議棧,其中串口通信部分使用的最多,下面就分享一下Z-Stack對(duì)串口封裝的使用心得。
成都創(chuàng)新互聯(lián)公司的客戶來自各行各業(yè),為了共同目標(biāo),我們?cè)诠ぷ魃厦芮信浜?,從?chuàng)業(yè)型小企業(yè)到企事業(yè)單位,感謝他們對(duì)我們的要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來驚喜。專業(yè)領(lǐng)域包括網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、電商網(wǎng)站開發(fā)、微信營銷、系統(tǒng)平臺(tái)開發(fā)。Z-Stack中對(duì)串口操作的封裝主要在hal_uart.h,hal_uart.c中, 支持DMA和ISR兩種處理方式, 真正的實(shí)現(xiàn)則都封裝在_hal_uart_dma.c 和_hal_uart_isr.c中, 但系統(tǒng)只推薦使用DMA方式, 可以通過修改宏定義來改為ISR的方式,宏定義在hal_board_cfg.h中。
Z-Stack對(duì)串口操作的封裝使用了緩沖區(qū)的方式, 讀寫都是直接操作緩沖區(qū), 不管是DMA方式還是ISR方式都是如此,下面以DMA為例介紹:
typedef struct { uint16 rxBuf[HAL_UART_DMA_RX_MAX]; #if HAL_UART_DMA_RX_MAX < 256 uint8 rxHead; uint8 rxTail; #else uint16 rxHead; uint16 rxTail; #endif uint8 rxTick; uint8 rxShdw; uint8 txBuf[2][HAL_UART_DMA_TX_MAX]; #if HAL_UART_DMA_TX_MAX < 256 uint8 txIdx[2]; #else uint16 txIdx[2]; #endif volatile uint8 txSel; uint8 txMT; uint8 txTick; // 1-character time in 32kHz ticks according to baud rate, // to be used in calculating time lapse since DMA ISR // to allow delay margin before start firing DMA, so that // DMA does not overwrite UART DBUF of previous packet volatile uint8 txShdw; // Sleep Timer LSB shadow. volatile uint8 txShdwValid; // TX shadow value is valid uint8 txDMAPending; // UART TX DMA is pending halUARTCBack_t uartCB; } uartDMACfg_t;
uartDMACfg_t結(jié)構(gòu)體定力了相關(guān)的數(shù)據(jù)結(jié)構(gòu), 其中rxBuf和txBuf分別對(duì)應(yīng)讀寫緩沖區(qū)
1、寫操作
static uint16 HalUARTWriteDMA(uint8 *buf, uint16 len) { uint16 cnt; halIntState_t his; uint8 txIdx, txSel; // 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; }
從這段代碼可以明顯看出, Z-Stack對(duì)串口的寫如果緩沖區(qū)剩余空間少于用戶寫入長度, 會(huì)直接返回0
也就是注釋里的enforce all or none, 由于DMA方式使用的是雙緩沖區(qū),這個(gè)函數(shù)里也對(duì)緩沖區(qū)切換的情況做了保護(hù)。
2、讀操作
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); if (++(dmaCfg.rxHead) >= HAL_UART_DMA_RX_MAX) { dmaCfg.rxHead = 0; } } PxOUT &= ~HAL_UART_Px_RTS; // Re-enable the flow on any read. return cnt; }
這個(gè)函數(shù)很簡單,就是直接從rxBuf里讀取數(shù)據(jù)到用戶緩沖區(qū)中, 需要注意的是,如果讀到緩沖區(qū)的末尾,會(huì)自動(dòng)調(diào)整游標(biāo)到緩沖區(qū)頭, 可能造成讀到的數(shù)據(jù)并非真實(shí)接受到的數(shù)據(jù), 所以在調(diào)用這個(gè)函數(shù)的時(shí)候,最好讀取數(shù)據(jù)不要超過HAL_UART_DMA_RX_MAX
3、poll操作
static void HalUARTPollDMA(void) { uint16 cnt = 0; uint8 evt = 0; if (HAL_UART_DMA_NEW_RX_BYTE(dmaCfg.rxHead)) { uint16 tail = findTail(); // If the DMA has transferred in more Rx bytes, reset the Rx idle timer. if (dmaCfg.rxTail != tail) { dmaCfg.rxTail = tail; // Re-sync the shadow on any 1st byte(s) received. if (dmaCfg.rxTick == 0) { dmaCfg.rxShdw = ST0; } dmaCfg.rxTick = HAL_UART_DMA_IDLE; } else if (dmaCfg.rxTick) { // Use the LSB of the sleep timer (ST0 must be read first anyway). uint8 decr = ST0 - dmaCfg.rxShdw; if (dmaCfg.rxTick > decr) { dmaCfg.rxTick -= decr; dmaCfg.rxShdw = ST0; } else { dmaCfg.rxTick = 0; } } cnt = HalUARTRxAvailDMA(); } else { dmaCfg.rxTick = 0; } if (cnt >= HAL_UART_DMA_FULL) { evt = HAL_UART_RX_FULL; } else if (cnt >= HAL_UART_DMA_HIGH) { evt = HAL_UART_RX_ABOUT_FULL; PxOUT |= HAL_UART_Px_RTS; } else if (cnt && !dmaCfg.rxTick) { evt = HAL_UART_RX_TIMEOUT; } if (dmaCfg.txMT) { dmaCfg.txMT = FALSE; evt |= HAL_UART_TX_EMPTY; } if (dmaCfg.txShdwValid) { uint8 decr = ST0; decr -= dmaCfg.txShdw; if (decr > dmaCfg.txTick) { // No protection for txShdwValid is required // because while the shadow was valid, DMA ISR cannot be triggered // to cause concurrent access to this variable. dmaCfg.txShdwValid = FALSE; } } if (dmaCfg.txDMAPending && !dmaCfg.txShdwValid) { // UART TX DMA is expected to be fired and enough time has lapsed since last DMA ISR // to know that DBUF can be overwritten halDMADesc_t *ch = HAL_DMA_GET_DESC1234(HAL_DMA_CH_TX); halIntState_t intState; // Clear the DMA pending flag dmaCfg.txDMAPending = FALSE; HAL_DMA_SET_SOURCE(ch, dmaCfg.txBuf[dmaCfg.txSel]); HAL_DMA_SET_LEN(ch, dmaCfg.txIdx[dmaCfg.txSel]); dmaCfg.txSel ^= 1; HAL_ENTER_CRITICAL_SECTION(intState); HAL_DMA_ARM_CH(HAL_DMA_CH_TX); do { asm("NOP"); } while (!HAL_DMA_CH_ARMED(HAL_DMA_CH_TX)); HAL_DMA_CLEAR_IRQ(HAL_DMA_CH_TX); HAL_DMA_MAN_TRIGGER(HAL_DMA_CH_TX); HAL_EXIT_CRITICAL_SECTION(intState); } if (evt && (dmaCfg.uartCB != NULL)) { dmaCfg.uartCB(HAL_UART_DMA-1, evt); } }
HalUARTPollDMA函數(shù)是整個(gè)串口操作的核心, 該函數(shù)會(huì)被系統(tǒng)大循環(huán)定時(shí)的調(diào)用,在這個(gè)函數(shù)里
會(huì)判斷讀寫緩沖區(qū)的狀態(tài), 進(jìn)而觸發(fā)回調(diào)函數(shù)halUARTCfg_t.callBackFunc。在觸發(fā)會(huì)調(diào)函數(shù)的時(shí)候,會(huì)傳給回調(diào)函數(shù)幾個(gè)事件,而這些事件涉及到以下4個(gè)值:
#define HAL_UART_DMA_FULL (HAL_UART_DMA_RX_MAX - 16) #define HAL_UART_DMA_HIGH (HAL_UART_DMA_RX_MAX / 2 - 16) #define HAL_UART_DMA_IDLE (6 * HAL_UART_MSECS_TO_TICKS) dmaCfg.txMT
當(dāng)緩沖區(qū)數(shù)據(jù)長度大于等于HAL_UART_DMA_FULL 時(shí), 觸發(fā)HAL_UART_RX_FULL事件
當(dāng)緩沖區(qū)數(shù)據(jù)長度大于等于HAL_UART_DMA_HIGH 時(shí), 觸發(fā)HAL_UART_RX_ABOUT_FULL事件
當(dāng)緩沖區(qū)數(shù)據(jù)長度小于HAL_UART_DMA_FULL且等待時(shí)間達(dá)到HAL_UART_DMA_IDLE 時(shí), 觸發(fā)HAL_UART_TIMEOUT事件
當(dāng)dmaCfg.txMT為真時(shí),表明寫緩沖區(qū)數(shù)據(jù)已經(jīng)全部寫入串口,觸發(fā)HAL_UART_TX_EMPTY事件
所以用Z-Stack的hal_uart庫對(duì)串口進(jìn)行操作時(shí), 推薦的做法是在回調(diào)函數(shù)里根據(jù)事件來判斷是否需要讀取數(shù)據(jù),而寫操作可以放到程序的任何位置,包括回調(diào)函數(shù)里, 寫入數(shù)據(jù)的時(shí)候要判斷一下返回值, 看數(shù)據(jù)是否真正寫入到緩沖區(qū)中。
HalUARTPollDMA的調(diào)用頻率大概是間隔200ms, 參考
http://www.360doc.com/content/11/1022/09/7906690_158136472.shtml
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。
本文題目:Z-Stack串口通信使用心得-創(chuàng)新互聯(lián)
新聞來源:http://jinyejixie.com/article32/dpohpc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營銷推廣、動(dòng)態(tài)網(wǎng)站、網(wǎng)站導(dǎo)航、靜態(tài)網(wǎng)站、微信小程序、云服務(wù)器
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容