1. 程式人生 > >嵌入式Linux應用開發完全手冊(四)UART

嵌入式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

傳輸結構

  • 位,最小單元
  • 幀,不可分割的若干位
    • 開始位
    • 資料位
    • 校驗位(可選)
    • 停止位

波特率
每一位所需時間的倒數,即每秒可以傳輸的位數。

波形
Markdown
上圖是一個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中讀取資料
    Markdown

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暫存器
Markdown
傳輸格式包括這幾個方面的設定

  • 紅外模式開關
  • 校驗位設定
  • 停止位設定
  • 資料位寬度

其他幾項的含義非常直觀,不贅述。

紅外模式的含義這裡按照晶片手冊複述一下,來龍去脈並不清楚。
下圖是正常的串列埠波形,高電平是1,低電平是0。
Markdown

下圖是紅外模式的波形:
Markdown
從圖中可以看出,在紅外模式中:

  • 傳送時,通過寬度是3/16位的脈衝,判斷當前位是0,如果沒有脈衝,當前位是1
  • 接收時,通過寬度是3/16位的脈衝,發出訊號0,如果是1,不發出脈衝

4. UART控制暫存器 UCONn
Markdown

從低位往高位分析:
Markdown
接收/接收模式

  • 00 關閉,禁止接收
  • 01 中斷或者查詢模式
  • 10 ,11 DMA模式

Markdown
break訊號
自環模式
接收錯誤狀態中斷

  • 0 正常模式
  • 1 開啟/啟用

Markdown
接收超時中斷

  • 0 關閉
  • 1 開啟

傳送/接收中斷型別

  • 0 脈衝
  • 1 電平

Markdown
時鐘選擇

  • 00 10 PCLK
  • 01 UEXTCLK
  • 11 FCLK/n

PCLK是用於串列埠等速度較慢的外設的時鐘(用於APB 匯流排裝置),FCLK是核心晶振的頻率(用於CPU),HCLK用於液晶,記憶體等高速裝置(用於AHB匯流排裝置),UEXTCLK是外接的時鐘,用於UART
在2440中,這幾個時鐘的頻率如下

  • FCLK <= 400MHz
  • HCLK <= 136MHz
  • PCLK <= 68MHz

Markdown
最後如果選擇了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

5. FIFO配置(UFCONn暫存器),FIFO狀態(UFSTATn)
見下圖,含義比較明顯,不贅述。可以使用FIFIO佇列,也可以不使用。本篇的例項就沒有使用FIFO
Markdown
Markdown

6. 流量控制(UMCONn),流量狀態(UMSTATn)
本篇不涉及。

7. 傳送/接收狀態(UTRSTATn)
Markdown
記錄這3個狀態資訊

  • 接收緩衝資料就緒,接收到資料時,自動被設定為1
    • 0 空
    • 1 接收到資料
  • 傳送緩衝為空, 傳送緩衝區內沒有資料時,自動設為1
    • 0 緩衝不為空
    • 1 空
  • 傳送器空, 傳送緩衝區中沒有資料,並且最後一個數據也傳送出去了,自動設為1
    • 0 非空
    • 1 空

8. 錯誤狀態(UERSTATn)
Markdown
4種錯誤,見表格。
讀取這個暫存器時,會自動清0。

9. 傳送緩衝暫存器(UTXHn)
Markdown
CPU將資料寫入這個暫存器,UART會立即將它儲存到緩衝區中,並自動傳送。

10. 接收緩衝暫存器(URXHn)
Markdown
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;
}