嵌入式Linux應用開發完全手冊(四)UART
11. 通用非同步收發器 UART
11.1 UART原理,部件使用方法
11.1.1UART原理
UART是Universal Asynchronous Receiver Transmitter的縮寫,即通用非同步收發器
UART用來傳輸序列資料:
- 傳送時,CPU將並行資料寫入UART,UART按照一定格式在一根電線上序列發出
- 接收時,UART檢測另一根電線上的訊號,收集序列資料存放在緩衝區,供CPU讀取
連線圖:
圖中收發各有一條線,有的裝置比如SIM卡,只有一條線,收發共用。
- 2條線可以實現全雙工
- 1條線可以實現半雙工
電平邏輯
- TTL/CMOS
- 5V,3.3V,2.5V/1.8V 表示1
- 0V,表示0
- RS-232
- 3~12V 表示1
- -3~-12V表示0
傳輸結構
- 位,最小單元
- 幀,不可分割的若干位
- 開始位
- 資料位
- 校驗位(可選)
- 停止位
波特率
每一位所需時間的倒數,即每秒可以傳輸的位數。
波形
上圖是一個7位資料位的幀波形。
- 空閒狀態,高電平
- 拉低1位,表示幀開始
- 然後按照次序,按照約定的每幀資料位數,傳輸資料
- 資料位全部傳輸完成後,傳輸校驗位,校驗方式提前約定,可以是奇校驗,也可以是偶校驗
- 下一位是停止位,拉高電平
- 停止位長度可以設定,1,1.5,2,為了保護各個幀,防止錯亂
11.1.2 2440的UART
3個通道
- UART0
- UART1
- UART2
工作模式
- 中斷模式
- DMA模式
收發過程
發資料
- 2440的UART由深度64的FIFO控制
- CPU寫入資料到FIFO
- UART將FIFO的資料複製到“傳送移位暫存器”Transmit Shifter
- Shifter將資料傳送的TxDn資料線上
收資料
- 資料線RxDn上的資料進入接收移位器,Receive Shifter
- Shifter複製資料到FIFO中
- CPU從FIFO中讀取資料
11.1.3 2440的UART使用
使用前的設定
- 波特率
- 傳輸格式
- 資料位寬度
- 是否使用校驗位
- 奇校驗還是偶校驗
- 多少個停止位
- 是否使用流量控制
- 設定管腳
- 對應管腳設定位UART管腳
- UART通道工作模式
- 中斷模式
- DMA模式
設定好以後的使用
- 往某暫存器寫入資料即傳送
- 讀取某個暫存器即獲得接收到的資料
- 傳送完畢,接收到資料的資訊獲取
- 查詢狀態暫存器
- 設定中斷
1. UART管腳設定
- UART通道0
- TxD0 GPH2
- RxD0 GPH3
所以GPH2和GPH3需要設定成TxD0和RxD0管腳。
2. 波特率 UBRDIVn暫存器
根據晶片手冊,時鐘頻率,波特率和UBVRDIVn暫存器的數學關係如下
UBRDIVn = int(UART clock/(baud rate x 16)) - 1
那麼如果給定時鐘頻率是40MHz,波特率要求是115200,那麼可以計算出暫存器UBRDIVn的設定值應該是
UBRDIVn = int(40000000/(115200 x 16)) - 1
= int(21.7) - 1 /*取最近接的整數*/
= 22 - 1
= 21
3. 傳輸格式 ULCONn暫存器
傳輸格式包括這幾個方面的設定
- 紅外模式開關
- 校驗位設定
- 停止位設定
- 資料位寬度
其他幾項的含義非常直觀,不贅述。
紅外模式的含義這裡按照晶片手冊複述一下,來龍去脈並不清楚。
下圖是正常的串列埠波形,高電平是1,低電平是0。
下圖是紅外模式的波形:
從圖中可以看出,在紅外模式中:
- 傳送時,通過寬度是3/16位的脈衝,判斷當前位是0,如果沒有脈衝,當前位是1
- 接收時,通過寬度是3/16位的脈衝,發出訊號0,如果是1,不發出脈衝
4. UART控制暫存器 UCONn
從低位往高位分析:
接收/接收模式
- 00 關閉,禁止接收
- 01 中斷或者查詢模式
- 10 ,11 DMA模式
break訊號
自環模式
接收錯誤狀態中斷
- 0 正常模式
- 1 開啟/啟用
接收超時中斷
- 0 關閉
- 1 開啟
傳送/接收中斷型別
- 0 脈衝
- 1 電平
時鐘選擇
- 00 10 PCLK
- 01 UEXTCLK
- 11 FCLK/n
PCLK是用於串列埠等速度較慢的外設的時鐘(用於APB 匯流排裝置),FCLK是核心晶振的頻率(用於CPU),HCLK用於液晶,記憶體等高速裝置(用於AHB匯流排裝置),UEXTCLK是外接的時鐘,用於UART
在2440中,這幾個時鐘的頻率如下
- FCLK <= 400MHz
- HCLK <= 136MHz
- PCLK <= 68MHz
最後如果選擇了FCLK/n,那麼這個n由FCLK Divider確定。
規則比較複雜:
- UCON2[15] 是使能位,決定是否允許FCLK/n作為時鐘源
- n的確定
- n = 7 ~ 21
- n = divider + 6, divider = UCON0[15:12]
- n = 22 ~ 36
- n = divider + 21, divider = UCON1[15:12]
- n = 37 ~ 43
- n = divider + 36, divider = UCON2[14:12]
- n = 44
- UCON0[15:12], UCON1[15:12], UCON2[14:12]都是0
- n = 7 ~ 21
5. FIFO配置(UFCONn暫存器),FIFO狀態(UFSTATn)
見下圖,含義比較明顯,不贅述。可以使用FIFIO佇列,也可以不使用。本篇的例項就沒有使用FIFO
6. 流量控制(UMCONn),流量狀態(UMSTATn)
本篇不涉及。
7. 傳送/接收狀態(UTRSTATn)
記錄這3個狀態資訊
- 接收緩衝資料就緒,接收到資料時,自動被設定為1
- 0 空
- 1 接收到資料
- 傳送緩衝為空, 傳送緩衝區內沒有資料時,自動設為1
- 0 緩衝不為空
- 1 空
- 傳送器空, 傳送緩衝區中沒有資料,並且最後一個數據也傳送出去了,自動設為1
- 0 非空
- 1 空
8. 錯誤狀態(UERSTATn)
4種錯誤,見表格。
讀取這個暫存器時,會自動清0。
9. 傳送緩衝暫存器(UTXHn)
CPU將資料寫入這個暫存器,UART會立即將它儲存到緩衝區中,並自動傳送。
10. 接收緩衝暫存器(URXHn)
UART接收到資料時,CPU讀取這個暫存器,就可以獲得資料。
11.2 UART操作例項
在串列埠上接收一個字元,然後ASCII + 1,從串列埠輸出
1. UART初始化
#include "s3c24xx.h"
#include "serial.h"
#define TXD0READY (1<<2)
#define RXD0READY (1)
#define PCLK 50000000 // init.c中的clock_init函式設定PCLK為50MHz
#define UART_CLK PCLK // UART0的時鐘源設為PCLK
#define UART_BAUD_RATE 115200 // 波特率
#define UART_BRD ((UART_CLK / (UART_BAUD_RATE * 16)) - 1)
/*
* 初始化UART0
* 115200,8N1,無流控
*/
void uart0_init(void)
{
GPHCON |= 0xa0; // GPH2,GPH3用作TXD0,RXD0
GPHUP = 0x0c; // GPH2,GPH3內部上拉
ULCON0 = 0x03; // 8N1(8個數據位,無較驗,1個停止位)
UCON0 = 0x05; // 查詢方式,UART時鐘源為PCLK
UFCON0 = 0x00; // 不使用FIFO
UMCON0 = 0x00; // 不使用流控
UBRDIV0 = UART_BRD; // 波特率為115200
}
2. 傳送字元函式
/*
* 傳送一個字元
*/
void putc(unsigned char c)
{
/* 等待,直到傳送緩衝區中的資料已經全部發送出去 */
while (!(UTRSTAT0 & TXD0READY));
/* 向UTXH0暫存器中寫入資料,UART即自動將它傳送出去 */
UTXH0 = c;
}
3. 接收字元函式
/*
* 接收字元
*/
unsigned char getc(void)
{
/* 等待,直到接收緩衝區中的有資料 */
while (!(UTRSTAT0 & RXD0READY));
/* 直接讀取URXH0暫存器,即可獲得接收到的資料 */
return URXH0;
}
4. 主函式
#include "serial.h"
int main()
{
unsigned char c;
uart0_init(); // 波特率115200,8N1(8個數據位,無校驗位,1個停止位)
while(1)
{
// 從串列埠接收資料後,判斷其是否數字或子母,若是則加1後輸出
c = getc();
if (isDigit(c) || isLetter(c))
putc(c+1);
}
return 0;
}