給用手機瀏覽的讀者
很抱歉, 這一篇因為用了 MathJax 套件來顯示分數, 目前設定和痞客邦的 "手機版模式" 有點小衝突 (它把行高設定死了), 要等改天有空才能修正. 請先改用 "電腦版模式" 或者換用 PC/平板 來閱讀. 已經修正好了, 如果閱讀上還有問題請留言告知.
UART 接收同步機制
下圖所示是經由 UART 輸送一個 ASCII 字元 'r' (0x72) 的 TTL 輸出 (未經過 RS-232 Transceiver). 圖上的小箭號所指的是接收端取樣訊號的相對時序位置, 上半部是理想狀況下接收 UART 資料的時序圖. 我們設定了鮑率之後, UART 在第一個向下訊號邊緣取得同步 (開始計時) 是為啟始位元, 然後以設定的鮑率開始取樣接收資料. 不過事情總不會如此美好. 萬一接收者和發送者的計時精準度不同 (即使是同樣的廠牌、型號, 總還是有製造上的誤差, 及操作溫度...等等因素的影響), 致使二者不完全一致, 下圖的下半部刻意把誤差放大, 我們很容易就看出一共有 4 個位元的資料錯了.

RS-232 時脈誤差造成取樣錯誤
不過只要稍微修改一下: 把開始取樣接收資料的時間點偏移一些, 就可以順利避免這個問題了. 由於這個時間差可能是提早, 也可能是落後, 因此把偏移時間設定成取樣週期的一半. 也就是同步之後的 1.5 倍取樣週期時間後, 再開始取樣接收第一個位元資料.

RS-232 調整取樣時間點, 使取樣錯誤不再發生
另外大部份晶片為了加強對抗雜訊的能力, 通常是以鮑率的 16 倍頻工作, 並在中心點附近取樣三次, 也就是在原先鮑率週期的 `sf{7/16}`, `sf{8/16}`, `sf{9/16}` 等三個位置取樣, 三次結果一致才視為正確. 現在的 UART 晶片幾乎都是以這樣的機制在運作, 所以我們可以很穩定的接收到正確資料.
不過傳送端和接收端的計時誤差總有個限度: 從同步開始到取得同位檢查位元, 之間的累計誤差不能超過原先鮑率的 ±`sf{7/16}`. 假設傳輸設定是 8+同位檢查位元, 則:
- 負誤差: 由 Start bit 至 Parity bit 的第一個取樣時間點中間一共是 `sf{9+7/16}` 個鮑率週期, 其值應大於 Partiy 的啟始點.
- 正誤差: 由 Start bit 至 Parity bit 的第三個取樣時間點中間一共是 `sf{9+9/16}` 個鮑率週期, 其值應小於 Partiy 的結束點.
於是我們得到下列式子:
- 設: -∆T' < ∆T < ∆T"
- 負誤差 (下限): (9 + 7/16)x(T - ∆T') = 9T
--> (9+7/16)∆T' = (7/16)T
- 正誤差 (上限): (9 + 9/16)x(T + ∆T") = 10T
--> (9+9/16)∆T" = (7/16)T
所以每個鮑率週期的誤差應該介於 `sf{-7/(9*16+7)}` 至 `sf{+7/(9*16+9)}` 之間
--> `sf{-7/151}` ~ `sf{+7/153}` ~= -4.63% ~ +4.57% (尾數應無條件捨去).

RS-232 Clock 累計誤差
如果不傳同位檢查位元則誤差可以稍微寬鬆一些 (`sf{-7/135}` ~ `sf{+7/137}`) ~= -5.18% ~ +5.10%.
還有一點要注意的是, 這二個數值是和正確的鮑率週期比較的. 如果你拿一個負誤差最大值的設備要和一個正誤差最大值的設備連接, 這二個設備還是連接不起來的. 試想一下, 我們拿各自的頻率不準確的傳輸波形和正確頻率的傳輸波形比較: 同樣是比較 Parity bit, 我的取樣中心點在接近 Parity bit 的啟始位置 (結束點應該超出它的中心點一些), 但你的取樣中心點在卻是在接近 Parity bit 的結束位置 (啟始點應該在它的中心點附近), 我們二個波形重疊的部份只有 Parity bit 中心的一小部份, 那是怎樣也無法取樣到正確的資料.
如果是要求把一個負誤差最大值的設備要和一個正誤差最大值的設備連接, 必需再把上列的數值再各減半, 約在 -2.31% ~ +2.28% 之間. 同時也提醒大家, 連線時要了解一下連線二端設備的 RS-232 Clock 是完全正確的, 還是有誤差的? 誤差有多大?
或許你會說還好吧? 石英震盪晶體運作時應該不會有這麼大的誤差啊! 原來問題出在 UART 使用的石英震盪器的頻率和一般 CPU 或者是單晶片微控制器常用的石英震盪晶體並不相同. UART 使用的石英震盪器的頻率如下表: (詳細請參考 wiki)
- 1.8432 MHz, 3.6864 MHz, 4.9152 MHz, 5.5296 MHz, 6.1440 MHz, 7.3728 MHz, 9.8304 MHz, 11.0592 MHz
- 12.2880 MHz, 12.9024 MHz, 14.7456 MHz, 16.5888 MHz, 18.4320 MHz, 19.6608 MHz, 20.2752 MHz, 22.1184 MHz
- 23.9616 MHz, 25.8048 MHz, 27.6480 MHz, 29.4912 MHz, 31.3344 MHz, 33.1776 MHz, 35.0208 MHz, 36.8640 MHz
基本上這些都是 9600*64 的整數倍. 但是一般的 CPU 或者單晶片微控制器則常用 12MHz, 20MHz, 25MHz, 40MHz... 而且單晶片微控制器為了省成本通常會只用一顆石英震盪晶體. 例如: 早期的 8051 常用的是 12MHz, 但是為了能設定全部常用的鮑率必需犧牲一點效能改用 11.0592MHz 的石英震盪晶體. 所以, 你了解的.
8051 鮑率設定計算
接下來, 我們來看看如何計算 8051 UART 的鮑率 Timer 設定.
首先 8051 的 serial port 共有 4 個模式
模式 | SM0 | SM1 | 功能 | SMOD | 鮑率 |
---|---|---|---|---|---|
0 | 0 | 0 | 固定鮑率, 同步傳輸 | - | Fosc/12 |
1 | 0 | 1 | 自定鮑率, 10bit UART (Start+8bit+Stop) |
0 | Fosc/12 x 1/32 x 1/(256-TH1) |
1 | Fosc/12 x 1/16 x 1/(256-TH1) | ||||
2 | 1 | 0 | 固定鮑率, 11bit UART (Start+8bit+Parity+Stop) |
0 | 1/16 x Fosc/4 |
1 | 1/16 x Fosc/2 | ||||
3 | 1 | 1 | 自定鮑率, 11bit (Start+8bit+Parity+Stop) |
0 | Fosc/12 x 1/32 x 1/(256-TH1) |
1 | Fosc/12 x 1/16 x 1/(256-TH1) |
Mode 0 及 Mode 2 用的是固定的頻率, 一般 PC 的 UART 無法設定成這二種鮑率, 只適用於和一樣是使用 8051 晶片的設備相互連接. 採用 Mode 1 和 Mode 3, 才能和 PC 互連. 同時除非 8051 CPU 用的是 11.0592 MHz 的石英震盪晶體, 否則和 PC 連接的鮑率無法大於 4800.
因為設定 SMOD=1 可以取得較高的 Baud, 所以我們用 Baud = Fosc/12 x 1/16 x 1/(256-TH1) 來計算 TH1, 前式我們可以導出 (256-TH1) = Fosc/(12*16*Baud), 其中 Fosc 用 11.0592M 代入, Baud 用 19200 代入, 可以求得 (256-TH1) = 3, 所以 TH1 之值為 253 或者 0xFD. 如果鮑率再取高一級的 38400, 則算出 (256-TH1) = 1.5, 四捨五入取整數 2. 但 1.5 和 2 誤差 33% 已經大於可接受範圍, 因此在傳統 8051 上使用 11.0592MHz 的石英震盪晶體最大可設成 19200 的傳輸速率.(註一)
但是如果是使用 12MHz 的石英震盪晶體, 在鮑率為 9600 時, 算出 (256-TH1) ~= 6.51, 四捨五入取整數 7. 但 6.51 和 7 誤差 7.52% 已經大於可接受範圍. 在鮑率為 4800 時, 算出 (256-TH1) ~= 13.02, 四捨五入取整數 13. 誤差 0.16% 在可接受範圍. 因此在傳統 8051 上使用 12MHz 的石英震盪晶體最大只能設成 4800 的傳輸速率. 相差有4倍之多.(註二)
註一: 你也可以這樣計算:
- 1.5 x (1+4.57%) = 1.56855
- 1.5 x (1-4.63%) = 1.43055
1.43055 ~ 1.56855 之間沒有整數, 故無法將鮑率設定為 38400.
註二: 你也可以這樣計算:
- 6.51 x (1+4.57%) ~= 6.808
- 6.51 x (1-4.63%) ~= 6.209
6.209 ~ 6.808 之間沒有整數, 故無法將鮑率設定為 9600.
- 13.02 x (1+4.57%) ~= 13.615
- 13.02 x (1-4.63%) ~= 12.417
12.417 ~ 13.615 之間有整數 13, 代入下式
(256-TH1) = 13
故設定 TH1 為 243 (0xF3), 可以和鮑率 4800 的 PC 進行傳輸.
Atmel ATmega 2560 的鮑率設定計算
我們再來驗算看看 Arduino Mega 2560 的 USART 的鮑率設定. Arduino Mega 2560 的 CPU 晶片選用的是 Atmel ATmega 2560, 振盪晶體則是用 16 MHz.
Asynchronous 操作模式 |
鮑率公式 | UBRR 計算公式 |
---|---|---|
一般模式 (U2Xn = 0) |
BAUD = Fosc/(16 x (UBBRn+1)) | UBBRn = Fosc/(16 x BAUD) - 1 |
雙倍模式 (U2Xn = 1) |
BAUD = Fosc/(8 x (UBBRn+1)) | UBBRn = Fosc/(8 x BAUD) - 1 |
Arduino Mega 2560 的 Debug Console 可以設定的最高鮑率是 115.2 Kbps. 我們把數值 115.2K 代入雙倍模式的公式, 可得 UBBRn+1 ~= 17.3611, UBBRn 應設為 16, 而鮑率誤差為 -0.36111/17 ~= -2.12% 還合乎上面所列的範圍. 不過要再往上提高, 可就會超出範圍了其實用雙倍模式還可以再提一級把傳輸速率提高到 230.4 Kbps. UBBRn+1 = 16M/(8x230.4K) ~= 8.68. 8.68 x (1+4.57%) ~= 9.077, 故可設定 UBBRn 為 8.
這個網頁列出了不同 CPU clock 下, 如何設定 AVR CPU 的 U2Xn 及 UBBRn 以對應不同的 RS-232 傳輸速率. WormFood's AVR Baud Rate Calculator