鄭志雄,胡愛蘭
(華北計算機系統工程研究所,北京100083)
在嵌入式系統開發過程中,用到UART串口的機率非常高,往往某些場合用到的個數還比較多,如筆者現在正在開發的項目,由于要具備多種上、下行通信方式,并且基本上都需要基于UART串口來實現,統計后發現至少需要6個串口,而市面上具備6個以上串口的價位適中的MCU又比較少見,這時串口的擴展往往就不可避免。還有一種情況,在某些低成本應用中,所選的MCU通常只有1~2個串口,此時如果MCU內部串口已作他用,但還想用串口實現某些功能,比較好的方法也只有通過串口擴展實現。
串口擴展有硬件擴展和軟件擴展兩種方式:硬件擴展主要是通過串口擴展芯片實現,常見的有16C550、SC16IS752/SC16IS762等,但會增加成本和占用板面空間;軟件擴展就是利用MCU的GPIO引腳通過軟件編程來模擬UART的發送和接收功能,該方式不僅靈活可靠,而且節約成本。本文主要是介紹UART串口的軟件擴展方式。
目前有很多軟件模擬串口的方案,其中大部分可以歸結為兩種實現方式:第一,采用軟件延時來逐位輸入、輸出數據幀bit位;第二,利用MCU內部定時器定時輸入、輸出數據幀bit位。軟件延時方式在發送或接收過程中會一直占用MCU,效率較低,這對于一些波特率要求不高、但實時性要求較高的工控場合并不適用;定時器方式在中斷中接收,效率較高,但發送時往往還是采用阻塞式(持續判斷定時器溢出標志位)的發送方式,總體上還是有些欠缺,而且兩者都只能進行半雙工通信。本文的實現方式也是采用定時器方式,但完全是在定時器中斷服務中實現數據的發送和接收,經過測試發現,MCU的實際占用率可以降低到10%以下[1],具有較高的效率,很適合筆者所要開發的項目,并且還可以同時進行發送和接收,實現了全雙工的異步串行通信方式。
串行通信接口簡稱串口,分為異步和同步兩種通信方式,這里用到的是異步串行通信接口,即UART。通常它的一幀數據幀是10位或11位,圖1是一個11位的幀,包括1位起始位(0)、8位數據位、1位奇偶校驗位和1位停止位(1)。通信雙方必須采用相同的波特率(波特率即每秒收發的bit位數),嚴格按照雙方約定的幀格式進行數據傳輸,且空閑狀態下串口輸入/輸出線上保持高電平。

圖1 UART串口11位幀結構
根據異步串行通信接口的工作方式,UART軟件模擬實現的基本思路如下:在輸入上,可以利用LPC1768定時器的捕獲輸入引腳(Capture Input)來捕捉起始位,在確定是起始位后,按照波特率設置好定時器匹配寄存器(Match Register),以一定的時間間隔來采樣輸入引腳上的電平,在確認校驗位與停止位無誤后,完成一幀數據的采樣;在輸出上,只要按照所需波特率設置好定時器,在每一個定時中斷到來后,按照從低到高的順序,逐位輸出待發送數據幀的位即可,需要注意的是,空閑狀態下輸出引腳必須保持高電平。
基本思路是比較直接的、初步的實現方法,具體實現時,需要在基本思路的基礎上做些改變。另外需要說明的是,在輸入采樣時,對每一數據位一般需要多次采樣并確認一致后才完成輸入,但經過實際測試與對其他方案[2-3]的研究發現,每一數據位僅采樣一次也幾乎沒有錯誤發生,所以本文的程序僅在每一數據位傳輸的中間時刻采樣一次。
選擇 LPC1768[4]內部的定時器 Timer 0、相應的捕獲輸入引腳P1.26和通用GPIO口P1.28作為軟件實現所需的硬件資源。讓Timer 0工作在定時器模式(Timer Mode)下;開啟P1.26引腳(CAP0.0)的下降沿捕獲功能,使能其中斷(在捕捉到起始位后,該引腳轉換為GPIO輸入模式);接收和發送共用匹配寄存器1(MR1),計數器(TC)值匹配(即溢出)后TC自動清零并中斷;P1.28引腳配置為GPIO輸出模式。
為了能在定時器中斷服務程序中同時處理數據幀的接收和發送,作了以下設置:① 定義了兩個全局變量Tx-Enable和RxEnable用于使能和停止發送、接收功能;②定時器溢出常數(寫入MR1的值)設置為數據位寬度(波特率的倒數)的一半,即每半個bit周期中斷一次;③ 定義兩個全局變量,一個發送計數器TxCount和一個接收計數器RxCount。
在定時器中斷服務程序中,主要處理兩種類型的定時器中斷:定時器捕獲中斷和定時器溢出中斷。對前者的處理主要是開啟數據接收功能,對后者的處理主要是進行數據的發送和接收。定時器中斷服務程序的流程如圖2所示。

圖2 中斷服務程序流程圖
每次發送數據時使能發送功能(TxEnable=1)、啟動定時器、TxCount賦值0,定時周期到后,從定時器中斷服務程序中進入發送流程,判斷TxCount的值,進行數據的發送,并相應地使發送計數器TxCount加1。發送程序流程如圖3所示。

圖3 發送程序流程圖
接收數據由捕獲中斷觸發開始,在定時器捕獲中斷服務程序中禁止捕獲功能、啟動定時器、使能接收、RxCount賦值0,定時周期到后,由定時器中斷服務程序中進入接收流程,判斷RxCount值,進行數據的采樣接收,并使接收計數器RxCount加1。接收程序流程如圖4所示。

圖4 接收程序流程圖
這里需要指出的是,在將方案用于實際系統時,一般需要將定時器中斷設為最高的優先級,以保證發送、接收過程不會因為其他中斷的長時間延時而受到影響從而導致數據丟失。
根據實際項目需要,在模擬串口中實現了偶校驗功能。發送時,直接將偶校驗輸出位填入數據幀中;接收時,當接收到第10位時,將其與程序對前8位數據的偶校驗輸出進行對比,若一致則繼續接收下一位,否則丟棄本次數據,重新開始下一幀的接收。偶校驗程序如下:

為了給應用程序提供一個友好方便的接口,設計了發送和接收緩沖區,這樣軟件模擬串口就和硬件UART在使用上很接近了。緩沖區是兩個先入先出(FIFO)的循環隊列,其本質是兩個無符號char型數組。為兩個緩沖區分別定義了3個全局變量,對發送有:發送位置索引TxRdIndex,用于指示模擬串口下一個要發送的數據的位置;發送緩沖區寫索引SWTxIndex,用于指示應用程序下一個要寫入發送緩沖區的數據的寫入位置;發送緩沖區數據個數TxByteCnt,用于指示發送緩沖區中待發送的數據個數,示例如圖5所示(方格左下角為數組下標)。接收緩沖區原理類似,不再贅述。

圖5 發送緩沖區示例圖
發送一個字節時,首先判斷發送緩沖區是否滿,若是則阻塞等待,直到有空的位置,否則寫入數據。接著判斷發送是否使能,否則開啟發送功能、啟動定時器、TxCount賦值0。然后即可退出發送字節函數,去執行其他程序,由定時器中斷服務程序去執行數據的發送。
接收是在定時中斷服務程序中完成的,接收完一字節就將它存入接收緩沖區,并置位相應的接收標志位。應用程序通過查詢接收標志位和接收字節個數變量來讀取接收到的數據。若存儲數據時發現緩沖區已滿,則放棄該字節的存儲并置位接收緩沖區溢出標志位。
需要指出的是,實際測試發現,緩沖區的長度對系統的整體運行效率影響很大[5],所以實際應用時應根據需要定義合適長度的緩沖區。
源程序見本刊網站 www.mesnet.com.cn——編者注。
編寫了以下程序來對軟件模擬串口代碼進行測試:


軟件UART測試如圖6所示。測試時,上位機打開兩個串口調試助手,一個通過串口1與UART0連接,另一個通過USB轉的串口8與軟件UART連接,準備一系列隨機的字節數據用于向軟件UART發送。下位機電路板運行時,可以通過連接串口8的調試助手看到不斷收到數據“88”,同時通過發送區將準備的一系列數據發送出去,馬上就會看到連接串口1的調試助手收到剛發送出去的數據。仔細比對后,沒有發現錯誤。然后將發送區的數據連續多次發送出去,對比發送、接收的數據個數,發現二者是一致的,再選取發送、接收的部分數據進行對比,也無錯誤發生。測試結果可以看出,該軟件串口在全雙工通信狀態下的運行是穩定和可靠的。另外,經測試,該軟件串口在“背靠背”傳輸模式(發送、接收引腳短接)下工作也是正常的。表1為在LPC1768幾個工作主頻下軟件UART正常穩定工作的波特率范圍(定時器的PCLK=CCLK)。

表1 LPC1768幾個工作主頻下的軟件UART正常穩定工作的波特率范圍
對比本文,參考文獻[5]也實現了一種全雙工UART的軟件模擬方案,但該方案實現方式比較復雜,代碼量較大,移植也不方便,且不允許“背靠背”模式傳輸(無法在較嚴格的全雙工模式下運行)。與之相比,本文的實現方案具有較大的突破,具有較強的使用價值。另外值得一提的是,若不要求在全雙工方式下通信,可以對方案稍加修改,可最終使得軟件UART達到很高的波特率要求。

圖6 軟件UART測試結果
[1]廣州致遠電子有限公司.I/O模擬 UART實現[EB/OL].[2013-01-21].http:www.zlgmcu.com.
[2]呂剛,李強.AVR單片機軟件模擬UART通信接口[J].單片機與嵌入式系統應用,2003(2):73-76.
[3]劉亞平,刑濟收,劉相權.AVR單片機串行口的軟件擴展技術[J].北京信息科技大學學報:自然科學版,2010,25(4):53-56.
[4]NXP Semiconductors.LPC17xx User manual[EB/OL].[2012-12-02].http:www.nxp.com.
[5]NXP Semiconductors.Application Note:Full-duplex software UART for LPC111xand LPC13xx[EB/OL].[2012-12-02].http:www.nxp.com/documents/application_note/AN10955.pdf.