天天看點

S5PV210系列(裸機九)之 序列槽通信

通信涉及的幾個基礎概念

1、通信的發展曆史

(1)、最早通信:烽火台、狼煙;信件;電子通信(電報、電話、網絡信号)

(2)、通信中最重要的兩個方面:資訊表示、解析方法 + 資訊的傳輸方法

(3)、通信雙方事先需要約定好資訊的表示方法和解析方法,做到一緻,否則資訊不能有效傳遞

(4)、信号的傳輸方法是指經過編碼後的通信資訊如何在傳輸媒體上傳輸的過程。

總結:通信過程其實分為 30 個步驟:首先發送方先按照資訊編碼方式對有效資訊進行編碼(程式設計成可以在通信線路上傳輸的信号形态),然後編碼後的資訊在傳輸媒體上進行傳輸,輸送給接收方;最後接收方接收到編碼資訊後進行解碼,解碼後得到可以了解的有效資訊。

2、同步通信和異步通信

(1)、同步和異步的差別:首先很多地方都有同步和異步的概念,簡單來說就是發送方和接收方按照同一個時鐘節拍工作就叫同步,發送方和接收方沒有統一的時鐘節拍、而各自按照自己的節拍工作就叫異步。

(2)、同步通信中,通信雙方按照統一節拍工作,是以配合很好;一般需要發送方給接收方發送資訊同時發送時鐘信号,接收方根據發送方給它的時鐘信号來安排自己的節奏。同步通信用在通信雙方資訊交換頻率固定,或者經常通信時。

(3)、異步通信又叫異步通知。在雙方通信的頻率不固定時(有時 3ms 收發一次,有時 3 天才收發一次)不适合使用同步通信,而适合異步通信。異步通信時接收方不必一直在意發送方,發送方需要發送資訊時會首先給接收方一個資訊開始的起始信号,接收方接收到起始信号後就認為後面緊跟着的就是有效資訊,才會開始注意接收資訊,直到收到發送方發過來的結束标志。

3、電平信号和差分信号

(1)、電平信号和差分信号是用來描述通信線路傳輸方式的。也就是說如何在通信線路上表達 1 和 0.

(2)、電平信号的傳輸線中有一個參考電平線(一般是 GND ),然後信号線上的信号值是由信号線電平和參考電平線的電壓差決定。

(3)、差分信号的傳輸線中沒有參考電平,所有都是信号線。然後 1 和 0 的表達靠信号線之間的電壓差。

總結:電平信号的 2 根通信線之間的電平差異容易受到幹擾,傳輸容易失敗;差分信号不容易受到幹擾是以傳輸品質比較穩定,現代通信一般都使用差分信号,電平信号幾乎沒有了。

總結2:看起來似乎相同根數的通信線下,電平信号要比差分信号要快;但是實際還是差分信号快,因為差分信号抗幹擾能力強,是以1個發送周期更短。

4、并行接口和串行接口

(1)、串行、并行主要是考慮通信線的根數,就是發送方和接收方同時可以傳遞的資訊量的多少

(2)、譬如在電平信号下,1 根參考電平線+ 1 根信号線可以傳遞 1 位二進制;如果我們有 3 根線( 2 根信号線+ 1 根參考線)就可以同時發送 2 位二進制;如果想同時發送 8 位二進制就需要 9 根線。

(3)、在差分信号下,2 根線(彼此差分)可以同時發送1位二進制;如果需要同時發送 8 位二進制,需要 16 根線。

總結:聽起來似乎并行接口比串行接口要快(串行接口一次隻能發送 1 位二進制,而并行接口一次可以發送多位二進制)要更優秀;但是實際上串行接口才是王道,用的比較廣。因為更省信号線,而且對傳輸線的要求更低、成本更低;而且串行時可以通過提高通信速度來提高總體通信性能,不一定非得要并行。

總結:其實這麼多年發展,最終勝出的是:異步、串行、差分,譬如 USB 和網絡通信。

序列槽通信的基本概念

序列槽通信的基本概念

序列槽通信的特點:異步、電平信号、串行

(1)、異步:序列槽通信的發送方和接收方之間是沒有統一的時鐘信号的。

(2)、電平信号:序列槽通信出現的時間較早,速率較低,傳輸的距離較近,是以幹擾還不太明顯,是以當時使用了電平信号傳輸。後期出現的傳輸協定都改成差分信号傳輸了。

(3)、串行通信:序列槽通信每次同時隻能傳輸 1 個二進制位。

1RS232 電平和 TTL 電平

(1)電平信号是用信号線電平減去參考線電平得到電壓差,這個電壓差決定了傳輸值是 1 還是 0.

(2)在電平信号時多少 V 代表 1,多少 V 代表 0 不是固定的,取決于電平标準。譬如 RS232 電平中 -3V〜-15V表示 1;+3〜+15V 表示 0;TTL 電平則是 +5V 表示1,0V 表示 0.

(3)不管哪種電平都是為了在傳輸線上表示 1 和 0. 差別在于适用的環境和條件不同。RS232 的電平定義比較大,适合幹擾大、距離遠的情況;TTL 電平電壓範圍小,适合距離近且幹擾小的情況。

(4)我們台式電腦後面的序列槽插座就是 RS232 接口的,在工業上用序列槽時都用這個,傳輸距離小于 15 米;TTL 電平一般用在電路闆内部兩個晶片之間。

(5)對程式設計來說,RS232 電平傳輸還是 TTL 電平是沒有差異的。是以電平标準對硬體工程師更有意義,而軟體工程師隻要略懂即可。(把 TTL 電平和 RS232 電平混接是不可以的)

波特率

(1)波特率(bandrate),指的是序列槽通信的速率,也就是序列槽通信時每秒鐘可以傳輸多少個二進制位。譬如每秒種可以傳輸 9600 個二進制位(傳輸一個二進制位需要的時間是 1/9600 秒,也就是 104 us),波特率就是 9600.

(2)序列槽通信的波特率不能随意設定,而應該在一些值中去選擇。一般最常見的波特率是 9600 或者 115200(低端單片機如 51 常用 9600,高端單片機和嵌入式 SoC 一般用 115200).為什麼波特率不可以随便指定?主要是因為:第一,通信雙方必須事先設定相同的波特率這樣才能成功通信,如果發送方和接收方按照不同的波特率通信則根本收不到,是以波特率最好是大家熟知的而不是随意指定的。第二,常用的波特率經過長久發展,就形成了共識,大家常用就是 9600 或者 115200.

起始位、資料位、奇偶校驗位、停止位

(1)序列槽通信時,收發是一個周期一個周期進行的,沒周期傳輸 n 個二進制位。這一個周期就叫做一個通信單元,一個通信單元是由:起始位 + 資料位 + 奇偶校驗位 + 停止位組成的。

(2)起始位表示發送方要開始發送一個通信單元;資料位是一個通信單元中發送的有效資訊位;奇偶校驗位是用來校驗資料位,以防止資料位出錯的;停止位是發送方用來表示本通信單元結束标志的。

(3)起始位的定義是序列槽通信标準事先指定的,是由通信線上的電平變化來反映的。

(4)資料位是本次通信真正要發送的有效資料,序列槽通信一次發送多少位有效資料是可以設定的(一般可選的有6、7、8、9,99%情況下我們都是選擇8位資料位。因為我們一般通過序列槽發送的文字資訊都是ASCII碼編碼的,而ASCII碼中一個字元剛好編碼為8位。)

(5)奇偶校驗位是用來給資料位進行奇偶校驗(把待校驗的有效資料逐個位的加起來,總和為奇數奇偶校驗位就為1,總和為偶數奇偶校驗位就為 0 )的,可以在一定程度上防止位反轉。

(6)停止位的定義是序列槽通信标準事先指定的,是由通信線上的電平變化來反映的。常見的有1位停止位,1.5位停止位,2位停止位等。99%情況下都是用1位停止位。

總結:序列槽通信時因為是異步通信,是以通信雙方必須事先約定好通信參數,這些通信參數包括:波特率、資料位、奇偶校驗位、停止位(序列槽通信中起始位定義是唯一的,是以一般不用選擇)

序列槽通信的基本原理

序列槽通信的基本原理

補充通信概念:單工通信和雙工通信

(1)單工就是單方向,雙工就是雙方同時收發,同時隻能但方向但是方向可以改變叫半雙工

(2)如果隻能A發B收則單工,A發B收或者B發A收(兩個方向不能同時)叫半雙工,A發B收同時B發A收叫全雙工。

三根通信線:Rx Tx GND

(1)任何通信都要有資訊傳輸載體,或者是有線的或者是無線的。

(2)序列槽通信是有線通信,是通過序列槽線來通信的。

(3)序列槽通信線最少需要2根(GND和信号線),可以實作單工通信,也可以使用3根通信線(Tx、Rx、GND)來實作全雙工。

(4)一般開發闆都會引出SoC上序列槽引腳直接輸出的TTL電平的序列槽(X210開發闆沒有),插座用插針式插座,每個序列槽引出的都有3個線(Tx、Rx、GND),可以用這些插座直接連接配接外部的TTL電平的序列槽裝置。

收發雙方事先規定好通信參數(波特率、資料位、奇偶校驗位、停止位等)

(1)序列槽通信屬于基層基本性的通信規約,它自己本身不會去協商通信參數,需要通信前通信雙方事先約定好通信參數(一般4個最重要的)

(2)序列槽通信的任何一個關鍵參數設定錯誤,都會導緻通信失敗。譬如波特率調錯了,發送方發送沒問題,接收方也能接收,但是接收到全是亂碼···

資訊以二進制流的方式在信道上傳輸

(1)、序列槽通信的發送方每隔一定時間(時間固定為1/波特率,機關是秒)将有效資訊(1或者0)放到通信線上去,逐個二進制位的進行發送。

(2)接收方通過定時(起始時間由讀到起始位标志開始,間隔時間由波特率決定)讀取通信線上的電平高低來區分發送給我的是1還是0。依次讀取資料位、奇偶校驗位、停止位,停止位就表示這一個通信單元(幀)結束,然後中間是不定長短的非通信時間(發送方有可能緊接着就發送第二幀,也可能半天都不發第二幀,這就叫異步通信),下來就是第二幀·····

總結:第一,波特率非常重要,波特率錯了整個通信就亂套了;資料位、奇偶校驗位、停止位也很重要,否則可能認不清資料。第三,通過序列槽不管發數字、還是文本還是指令還是什麼,都要先對發送内容進行編碼,編碼成二進制再進行逐個位的發送。

(3)序列槽發送的一般都是字元,一般都是ASCII碼編碼後的字元,是以一般設定資料位都是8,友善剛好一幀發送1個字元。

DB9接口介紹

(1)DB9接口是序列槽通信早期比較常用的一種規範化接口。

(2)串行通信在早期是計算機與外界通信的主要手段,那時候的計算機都有标準配置的序列槽以實作和外部通信。那時候就定義了一套标準的序列槽規約,DB9接口就是标準接口。

(3)DB9接口中有9根通信線,其中3根很重要,為GND、Tx、Rx,必不可少;剩餘6根都是和流控有關的,現代我們使用序列槽都是用來做調試一般都禁用流控,是以這6根沒用。

(4)現在一般使用序列槽時要記得把流控禁止掉,不然可能發生意想不到的問題。

S5PV210串行通信接口詳解

序列槽的名稱

(1)S5PV210的資料手冊中序列槽控制器在section8.1

(2)序列槽的官方名稱叫:universal asynchronous reciver and transmitter,通用異步收發器

英文縮寫是uart,中文簡稱序列槽。

OVERVIEW OF UNIVERSAL ASYNCHRONOUS RECEIVER AND TRANSMITTER

The Universal Asynchronous Receiver and Transmitter (UART) in S5PV210 provide four independent asynchronous, and serial input/output (I/O) ports. All the ports operate in an interrupt-based or a DMA-based mode. The UART generates an interrupt or a DMA request to transfer data to and from the CPU and the UART. The UART supports bit rates up to Mbps. Each UART channel contains two FIFOs to receive and transmit data:  bytes in ch0,  bytes in ch1 and  bytes in ch2 and ch3.

S5PV210 的 UART 提供四個獨立異步串行 I/O 端口。所有對端口的操作都有中斷或 DMA 模式。UART 生成一個中斷或 DMA 請求以便從 CPU 轉移資料到 UART 或者從 CPU 轉移資料到 UART。UART 支援最大比特率  Mbps。每個UART 通道包含兩個 FIFO 寄存器來接收和傳輸資料: ch0 有  位元組大小,ch1 有  個位元組大小,ch2 和 ch3 有  個位元組大小。

UART includes programmable baud rates, infrared (IR) transmitter/receiver, one or two stop bit insertion, -bit, - bit, -bit, or -bit data width and parity checking.
Each UART contains a baud-rate generator, a transmitter, a receiver and a control unit, as shown in Figure - The baud-rate generator uses PCLK or SCLK_UART. The transmitter and the receiver contain FIFOs and data shifters. The data to be transmitted is written to Tx FIFO, and copied to the transmit shifter. The data is then shifted out by the transmit data pin (TxDn). The received data is shifted from the receive data pin (RxDn), and copied to Rx FIFO from the shifter.
UART 包括可程式設計的波特率産生器、紅外線(IR)發射器/接收器,一個或兩個停止位插入, 位, 位, 位或  位資料位和奇偶校驗位。每個 UART 包含波特率發生器、發射器、接收器和一個控制單元,如圖 - 所示。波特率發生器使用的時鐘頻率是 PCLK 或 SCLK_UART。發射器和接收器包含 FIFO寄存器 和 資料轉換器。要傳輸的資料被寫入Tx FIFO, 并複制到傳輸轉換器,然後 TxDn 負責發送資料。RxDn 負責接收資料, 并将資料從接受移位器複制到Rx FIFO寄存器。

 KEY FEATURES OF UNIVERSAL ASYNCHRONOUS RECEIVER AND TRANSMITTER

• RxD0, TxD0, RxD1, TxD1, RxD2, TxD2, RxD3 and TxD3 with DMA-based or interrupt-based operation

• UART Ch0, ,  and  with IrDA1

• UART Ch  with -byte FIFO, Ch  with -byte FIFO, Ch2 and  with -byte FIFO

• UART Ch ,  and  with nRTS0, nCTS0, nRTS1, nCTS1, nCTS2 and nRTS2 for Auto Flow Control

• Supports handshake transmit/receive.

           

S5PV210的序列槽控制器工作原理框圖

S5PV210系列(裸機九)之 序列槽通信

(1)整個序列槽控制器包含 transmitter 和 receiver 兩部分,兩部分功能彼此獨立,transmitter 負責 210 向外部發送資訊,receiver 負責從外部接收資訊到 210 内部。

(2)總線角度來講,序列槽控制器是接在APB總線上的。對我們程式設計有影響的是:将來計算序列槽控制器的源時鐘時是以APB總線來計算的。

(3)transmitter由發送緩沖區和發送移位器構成。我們要發送資訊時,首先将資訊進行編碼(一般用ASCII碼)成二進制流,然後将一幀資料(一般是8位)寫入發送緩沖區(從這裡以後程式就不用管了,剩下的發送部分是硬體自動的),發送移位器會自動從發送緩沖區中讀取一幀資料,然後自動移位(移位的目的是将一幀資料的各個位分别拿出來)将其發送到 Tx 通信線上。

(4) receiver 由接收緩沖區和接收移位器構成。當有人通過序列槽線向我發送資訊時,資訊通過 Rx 通信線進入我的接收移位器,然後接收移位器自動移位将該二進制位儲存入我的接收緩沖區,接收完一幀資料後 receiver會産生一個中斷給 CPU,CPU 收到中斷後即可知道 receiver 接收滿了一幀資料,就會來讀取這幀資料。

總結:發送緩沖區和接收緩沖區是關鍵。發送移位器和接收移位器的工作都是自動的,不用程式設計控制的,是以我們寫序列槽的代碼就是:首先初始化(初始化的實質是讀寫寄存器)好序列槽控制器(包括發送控制器和接收控制器),然後要發送資訊時直接寫入發送緩沖區,要接收資訊時直接去接收緩沖區讀取即可。可見,序列槽底層的工作(譬如怎麼移位的、譬如起始位怎麼定義的、譬如 TTL 電平還是 RS232 電平等)對程式員是隐藏的,程式員不用去管。軟體工程師對序列槽操作的接口就是發送/接收緩沖區(實質就是寄存器,操作方式就是讀寫記憶體)

(5)序列槽控制器中有一個波特率發生器,作用是産生序列槽發送/接收的節拍時鐘。波特率發生器其實就是個時鐘分頻器,它的工作需要源時鐘(APB總線來),然後内部将源時鐘進行分頻(軟體設定寄存器來配置)得到目标時鐘,然後再用這個目标時鐘産生波特率(硬體自動的)。

自動流控(AFC:Auto flow control)

(1)為什麼需要流控?流控的目的是讓序列槽通信非常可靠,在發送方速率比接收方快的時候流控可以保證發送和接收不會漏掉東西。

(2)現在為什麼不用流控?現在計算機之間有更好更進階(usb、internet)的通訊方式,序列槽已經基本被廢棄了。現在序列槽的用途更多是SoC用來輸出調試資訊的。由于調試資訊不是關鍵性資訊、而且由于硬體發展序列槽本身速度已經相對慢的要死了,是以硬體都能協調發送和接收速率,是以流控已經失去意義了,是以現在基本都廢棄了。

FIFO模式及其作用

(1)典型的序列槽設計,發送/接收緩沖區隻有 1 位元組,每次發送/接收隻能處理1幀資料。這樣在單片機中沒什麼問題,但是到複雜 SoC 中(一般有作業系統的)就會有問題,會導緻效率低下,因為 CPU 需要不斷切換上下文。

(2)解決方案就是想辦法擴充序列槽控制器的發送/接收緩沖區,譬如将發送/接收緩沖器設定為 64 位元組,CPU 一次過來直接給發送緩沖區 64 位元組的待發送資料,然後 transmitter 慢慢發,發完再找CPU再要 64 位元組。但是序列槽控制器本來的發送/接收緩沖區是固定的 1 位元組長度的,是以做了個變相的擴充,就是 FIFO。

(3) FIFO 就是 first in first out,先進先出。fifo 其實是一種資料結構,這裡這個大的緩沖區叫 FIFO 是因為這個緩沖區的工作方式類似于 FIFO 這種資料結構。

DMA模式及其作用

(1)DMA direct memory access,直接記憶體通路。DMA本來是DSP中的一種技術,DMA技術的核心就是在交換資料時不需要CPU參與,子產品可以自己完成。

(2)DMA模式要解決的問題和上面FIFO模式是同一個問題,就是序列槽發送/接收要頻繁的折騰CPU造成CPU反複切換上下文導緻系統效率低下。

(3)傳統的序列槽工作方式(無FIFO無DMA)效率是最低的,适合低端單片機;高端單片機上CPU事物繁忙是以都需要序列槽能夠自己完成大量資料發送/接收。這時候就需要FIFO或者DMA模式。FIFO模式是一種輕量級的解決方案,DMA模式适合大量資料迸發式的發送/接收時。

IrDA模式及其用法

(1)IrDA其實就是紅外,紅外就是紅外線通信(電視機、空調遙控器就是紅外通信的)。

(2)紅外通信的原理是發送方固定間隔時間向接收方發送紅外信号(表示1或0)或者不發送紅外信号(表示0或者1),接收方每隔固定時間去判斷有無紅外線信号來接收1和0.

(3)分析可知,紅外通信和序列槽通信非常像,都是每隔固定時間發送1或者0(判斷1或0的實體方式不同)給接收方來通信。是以210就利用序列槽通信來實作了紅外發送和接收。

(4)210的某個序列槽支援IrDA模式,開啟紅外模式後,我們隻需要向序列槽寫資料,這些資料就會以紅外光的方式向外發射出去(當然是需要一些外部硬體支援的),然後接收方接收這些紅外資料即可解碼得到我們的發送資訊。

串行通信與中斷的關系

(1)序列槽通信分為發送/接收2部分。發送方一般不需要(也可以使用)中斷即可完成發送,接收方必須(一般來說必須,也可以輪詢方式接收)使用中斷來接收。

(2)發送方可以選擇使用中斷,也可以選擇不使用中斷。使用中斷的工作情景是:發送方先設定好中斷并綁定一個中斷處理程式,然後發送方丢一幀資料給 transmitter,transmitter 發送耗費一段時間來發送這一幀資料,這段時間内發送方 CPU 可以去做别的事情,等 transmitter 發送完成後會産生一個 TXD 中斷,該中斷會導緻事先綁定的中斷處理程式執行,在中斷處理程式中 CPU 會切換回來繼續給 transmitter 放一幀資料,然後CPU 切換離開;不使用中斷的工作情景是:發送方事先禁止 TXD 中斷(當然也不需要給相應的中斷處理程式了),發送方 CPU 給一幀資料到transmitter,然後 transmitter 耗費一段時間來發送這幀資料,這段時間CPU在這等着(CPU沒有切換去做别的事情),待發送方發送完成後CPU 再給它一幀資料繼續發送直到所有資料發完。CPU 是怎麼知道transmitter 已經發送完了? 原來是有個狀态寄存器,狀态寄存器中有一個位叫發送緩沖區空标志,transmitter發送完成(發送緩沖區空了)就會給這個标志位置位,CPU就是通過不斷查詢這個标志位為1還是0來指導發送是否已經完成的。

(3)因為序列槽通信是異步的,異步的意思就是說發送方占主導權。也就是說發送方随時想發就能發,但是接收方隻有時刻等待才不會丢失資料。是以這個差異就導緻發送方可以不用中斷,而接收方不得不使用中斷模式。

210串行通信接口的時鐘設計

(1)序列槽通信為什麼需要時鐘?因為序列槽通信需要一個固定的波特率,是以transmitter和receiver都需要一個時鐘信号。

(2)時鐘信号從哪裡來?源時鐘信号是外部APB總線(PCLK_PSYS,66MHz)提供給序列槽子產品的(這就是為什麼我們說序列槽是挂在APB總線上的),然後進到序列槽控制器内部後給波特率發生器(實質上是一個分頻器),在波特率發生器中進行分頻,分頻後得到一個低頻時鐘,這個時鐘就是給transmitter和receiver使用的。

(3)序列槽通信中時鐘的設定主要看寄存器設定。重點的有:寄存器源設定(為序列槽控制器選擇源時鐘,一般選擇為PCLK_PSYS,也可以是SCLK_UART),還有波特率發生器的2個寄存器。

(4)波特率發生器有2個重要寄存器:UBRDIVn和UDIVSLOTn,其中UBRDIVn是主要的設定波特率的寄存器,UDIVSLOTn是用來輔助設定的,目的是為了校準波特率的。

S5PV210串行通信程式設計實戰

整個程式流程分析

(1)整個序列槽通信相關程式包含2部分:uart_init負責初始化序列槽,uart_putc負責發送一個位元組

序列槽控制器初始化關鍵步驟

(1)初始化序列槽的 Tx 和 Rx 引腳所對應的 GPIO(查原理圖可知Rx和Rx分别對應 GPA0_1 和 GPA0_0 )

(2) GPA0CON(0xE0200000)bit[3:0] = 0b0010 bit[7:4] = 0b0010

(3)初始化這幾個關鍵寄存器 UCON0 ULCON0 UMCON0 UFCON0 UBRDIV0 UDIVSLOT0

主要的幾個寄存器

(1) ULCON0 = 0x3 // 0 校驗位、8 資料位、1 停止位

(2) UCON = 0x5 // 發送和接收都是 polling mode

(3) UMCON0 = 0x0 // 禁止 modem、afc

(4) UFCON0 = 0x0 // 禁止 FIFO 模式

(5) UBRDIV0 和 UDIVSLOT0 和波特率有關,要根據公式去算的

序列槽 Tx、Rx 對應的 GPIO 的初始化

給 GPA0CON 的相應 bit 位指派為相應值,用 C 語言位操作來完成。

UCON、ULCON、UMCON、UFCON等主要控制寄存器

依據上節中分析的值進行依次設定即可。

波特率的計算和設定

(1)第一步,用 PCLK_PSYS 和目标波特率去計算 DIV_VAL: DIV_VAL = (PCLK / (bps x 16))

(2)第二步,UBRDIV0 寄存器中寫入 DIV_VAL 的整數部分

序列槽發送和接收函數的編寫

(1)寫發送函數,主要發送前要用 while 循環等待發送緩沖區為空才能發送。

#define GPA0CON     xE0200000
#define UCON0       xE2900004
#define ULCON0      xE2900000
#define UMCON0      xE290000C
#define UFCON0      xE2900008
#define UBRDIV0     xE2900028
#define UDIVSLOT0   xE290002C
#define UTRSTAT0    xE2900010
#define UTXH0       xE2900020  
#define URXH0       xE2900024  

#define rGPA0CON    (*(volatile unsigned int *)GPA0CON)
#define rUCON0      (*(volatile unsigned int *)UCON0)
#define rULCON0     (*(volatile unsigned int *)ULCON0)
#define rUMCON0     (*(volatile unsigned int *)UMCON0)
#define rUFCON0     (*(volatile unsigned int *)UFCON0)
#define rUBRDIV0    (*(volatile unsigned int *)UBRDIV0)
#define rUDIVSLOT0  (*(volatile unsigned int *)UDIVSLOT0)
#define rUTRSTAT0       (*(volatile unsigned int *)UTRSTAT0)
#define rUTXH0      (*(volatile unsigned int *)UTXH0)
#define rURXH0      (*(volatile unsigned int *)URXH0)

// 序列槽初始化程式
void uart_init(void)
{
    // 初始化Tx Rx對應的GPIO引腳
    rGPA0CON &= ~(0xff<<0);            // 把寄存器的bit0~7全部清零
    rGPA0CON |= 0x00000022;         // 0b0010, Rx Tx

    // 幾個關鍵寄存器的設定
    rULCON0 = 0x3;
    rUCON0 = 0x5;
    rUMCON0 = 0;
    rUFCON0 = 0;

    // 波特率設定    DIV_VAL = (PCLK / (bps x 16))-1
    // PCLK_PSYS用66MHz算     餘數0.8
    //rUBRDIV0 = 34;    
    //rUDIVSLOT0 = 0xdfdd;

    // PCLK_PSYS用66.7MHz算       餘數0.18
    // DIV_VAL = (66700000/(115200*16)-1) = 35.18
    rUBRDIV0 = 35;
    // (rUDIVSLOT中的1的個數)/16=上一步計算的餘數=0.18
    // (rUDIVSLOT中的1的個數 = 16*0.18= 2.88 = 3
    rUDIVSLOT0 = 0x0888;        // 3個1,查官方推薦表得到這個數字
}


// 序列槽發送程式,發送一個位元組
void uart_putc(char c)
{                   
    // 序列槽發送一個字元,其實就是把一個位元組丢到發送緩沖區中去
    // 因為序列槽控制器發送1個位元組的速度遠遠低于CPU的速度,是以CPU發送1個位元組前必須
    // 确認序列槽控制器目前緩沖區是空的(意思就是序列槽已經發完了上一個位元組)
    // 如果緩沖區非空則位為0,此時應該循環,直到位為1
    while (!(rUTRSTAT0 & (1<<1)));
    rUTXH0 = c;
}

// 序列槽接收程式,輪詢方式,接收一個位元組
char uart_getc(void)
{
    while (!(rUTRSTAT0 & (1<<0)));
    return (rURXH0 & 0x0f);
}
           

繼續閱讀