傳輸是從串口發(fā)送字節(jié)數(shù)據(jù)到串口服務(wù)器進行數(shù)據(jù)傳輸,發(fā)送的原理與接受相似。
傳輸原理:當計算機要從串行端口(到外部電纜)發(fā)送一個字節(jié)時,CPU 將計算機內(nèi)部總線上的字節(jié)發(fā)送到串行端口的 I/O 地址。串行端口獲取字節(jié),并在串行電纜連接器的傳輸引腳上一次一位(串行位流)發(fā)送出去。
UART芯片在串口完成了大部分工作,可以說UART就是串口引擎。為了傳輸一個數(shù)據(jù)字節(jié),串口設(shè)備驅(qū)動程序向串口I/O地址發(fā)送一個數(shù)據(jù)。該數(shù)據(jù)進入串行端口中的 1 字節(jié)“傳輸移位寄存器”。從這個移位寄存器中,一個比特一個字節(jié)地從數(shù)據(jù)中取出,并在串行線上被一個比特地發(fā)送出去。然后,當最后一位發(fā)送完畢并且移位寄存器需要發(fā)送另一個字節(jié)數(shù)據(jù)時,它可以只要求確認 CPU 向其發(fā)送另一個字節(jié)數(shù)據(jù)。這樣很簡單,但可能會引入延遲,因為 CPU 可能無法立即獲取字節(jié)。畢竟,CPU 通常除了處理串口之外,還會做其他事情。
消除這種延遲的一種方法是安排一些事情,以便 CPU 在移位寄存器需要之前獲取字節(jié)數(shù)據(jù)并將其存儲在串行端口硬件緩沖區(qū)中。然后當移位寄存器發(fā)送完字節(jié)數(shù)據(jù)并立即需要一個新字節(jié)時,串口硬件只是將下一個字節(jié)從自己的緩沖區(qū)傳輸?shù)揭莆患拇嫫?。無需調(diào)用 CPU 來獲取新字節(jié)。
這個串口緩沖區(qū)的大小原本只有一個字節(jié);今天它通常是 16 個字節(jié)?,F(xiàn)在仍然存在一個問題,即保持這個緩沖區(qū)足夠的字節(jié)數(shù),以便當移位寄存器需要一個字節(jié)來傳輸時,它總是會在那里找到一個字節(jié)(除非沒有更多的字節(jié)要發(fā)送)。CPU 使用中斷來做到這一點。
當移位寄存器從緩沖區(qū)中取出字節(jié)并且緩沖區(qū)需要另一個字節(jié)時,它會通過在計算機總線上的專用線上施加電壓來向 CPU 發(fā)送中斷。除非 CPU 正在做一些非常重要的事情,否則中斷會強制它停止正在做的事情并開始運行一個程序,該程序?qū)槎丝诘木彌_區(qū)提供另一個字節(jié)。此緩沖區(qū)的目的是在硬件中保持一個額外的字節(jié)(等待發(fā)送)排隊,以便在從串行端口電纜傳輸字節(jié)時不會出現(xiàn)間隙。
一旦 CPU 得到中斷,它就會知道是誰發(fā)送了中斷,因為每個串口都有一條專用的中斷線(除非中斷是共享的)。
然后 CPU 將開始運行串行設(shè)備驅(qū)動程序,它檢查 I/0 地址上的寄存器以找出發(fā)生了什么。它發(fā)現(xiàn)串行的發(fā)送緩沖區(qū)為空并等待另一個字節(jié)。因此,如果要發(fā)送更多字節(jié),它將下一個字節(jié)發(fā)送到串行端口的 I/0 地址。當前一個字節(jié)仍在傳輸移位寄存器中并且仍在逐位傳輸時,下一個字節(jié)應(yīng)該到達。
回顧一下,當一個字節(jié)從串行端口的傳輸線完全傳輸出去并且移位寄存器現(xiàn)在是空的時,以下 3 件事幾乎同時發(fā)生:
● 下一個字節(jié)從發(fā)送緩沖器移入發(fā)送移位寄存器。
● 這個新字節(jié)的傳輸(逐位)開始。
● 發(fā)出另一個中斷以告訴設(shè)備驅(qū)動程序?qū)⒘硪粋€字節(jié)發(fā)送到現(xiàn)在為空的傳輸緩沖區(qū)。
因此我們說串口是中斷驅(qū)動的。每次串口發(fā)出中斷;CPU 再發(fā)送一個字節(jié)。一旦一個字節(jié)被 CPU 發(fā)送到發(fā)送緩沖區(qū),CPU 就可以自由地進行一些其他活動,直到它得到下一個中??斷。串口以固定速率傳輸比特,由用戶(或應(yīng)用程序)選擇。它有時被稱為波特率。串行端口還會為每個字節(jié)(開始、停止和奇偶校驗位)添加額外的位,因此每個字節(jié)通常會發(fā)送 10 位。以每秒 19,200 位 (bps) 的速率(也稱為速度),因此有 1,920 字節(jié)/秒(以及 1,920 個中斷/秒)。
完成所有這些工作對 CPU 來說是大量的工作。這是事實,原因有很多。首先,僅通過 32 位數(shù)據(jù)總線(甚至 64 位)一次發(fā)送一個 8 位字節(jié)并不是對總線寬度的非常有效的使用。此外,處理每個中斷有很多開銷。當接收到中斷時,設(shè)備驅(qū)動程序只知道有什么東西在串口上引起了中斷,但不知道這是因為發(fā)送了一個字符。設(shè)備驅(qū)動程序必須進行各種檢查以找出發(fā)生了什么。相同的中斷可能意味著接收到一個字符、其中一條控制線改變了狀態(tài)等。
一個主要的改進是將串行端口的緩沖區(qū)大小從 1 字節(jié)擴大到 16 字節(jié)。這意味著當 CPU 收到中斷時,它會給串行端口最多 16 個新字節(jié)進行傳輸。這是服務(wù)中斷,但數(shù)據(jù)仍必須在寬總線上一次一個字節(jié)地傳輸。16 字節(jié)的緩沖區(qū)實際上是一個 FIFO(先進先出)隊列,通常稱為 FIFO。
通過串行端口接收字節(jié)類似于發(fā)送原理,只是方向相反。它也是中斷驅(qū)動的。
對于帶有 1 字節(jié)緩沖區(qū)的過時類型的串行端口,當從外部電纜完全接收到一個字節(jié)時,它會進入 1 字節(jié)接收緩沖區(qū)。然后端口給 CPU 一個中斷,告訴它選擇那個字節(jié),這樣串行端口就有空間來存儲當前正在接收的下一個字節(jié)。對于具有 16 字節(jié)緩沖區(qū)的較新串行端口,可以在接收緩沖區(qū)中有 14 個字節(jié)后發(fā)送此中斷(以獲取字節(jié))。然后 CPU 停止正在執(zhí)行的操作,運行中斷服務(wù)程序,并從端口獲取 14 到 16 個字節(jié)。對于在接收到第 14 個字節(jié)時發(fā)送的中斷,如果自中斷以來還有 2 個字節(jié)到達,則可能有 16 個字節(jié)要獲取。但是如果還有 3 個字節(jié)到達(而不是 2 個),那么 16 字節(jié)的緩沖區(qū)就會溢出。通過以這種方式設(shè)置或由于超時,它也可能拾取少于 14 個字節(jié)。
我們已經(jīng)討論了小型 16 字節(jié)串行端口硬件緩沖區(qū),但主內(nèi)存中還有更大的緩沖區(qū)。當 CPU 從硬件的接收緩沖區(qū)中取出一些字節(jié)時,它會將它們放入主內(nèi)存中一個更大(比如 8k 字節(jié))的接收緩沖區(qū)中。然后,從串行端口獲取字節(jié)的程序從大緩沖區(qū)中獲取它接收的字節(jié)(在程序中使用“讀取”語句)。對于要傳輸?shù)淖止?jié)也存在類似的情況。當 CPU 需要獲取一些要發(fā)送的字節(jié)時,它會將它們從主內(nèi)存中的大(8k 字節(jié))發(fā)送緩沖區(qū)中取出,并將它們放入硬件中的 16 字節(jié)小發(fā)送緩沖區(qū)中。