2012年3月27日 星期二

UART

UART (Universal Asynchronous Receiver / Transmitter) 是通用非同步收發器(非同步串列通信口)的英文縮寫,它包括了RS232、RS449、RS423、RS422和RS485等介面標準規範和匯流排標準規範。 
現在PC的COM 接口均為RS232。若配有多個非同步串列通信口,則分別稱為COM1、COM2,COMxxx。

UART的工作就是從 CPU 一次接收8 bits的資料(parallel),然後將這些資料 1次 1bit 的送往周邊設備(serially)。同時,UART還可以接收周邊設備傳送來的資料,當組成 8 bits時,再將資料送往 CPU。


UART Frame

下圖是UART Frame格式,其構成為
  • 1 個起始位元(Start bit),TXD或RXD由高電位轉為低電位代表起始位元,此位元會通知receiver  將有資料送出,並且強制同步 receiver 和 transmitter 的時脈。
  • 5~9個資料位元(Data bit),以 LSB 的方式排列 (一般都是8個,為了相容較舊的規格才保留這些彈性)
  • 0~1個同位位元(Parity bit),若存在同位位元,可以設定為奇同位或偶同位。
  • 1, 1.5 或  2個停止位元(stop bit)


資料訊框長度介於7位元到12位元之間,長度則依照資料長度(Data)、是否有同位位元(Parity)和停止位元(Stop bit)的數量決定,這三個參數可以由使用者手動進行設定。




訊框的傳送過程,會依序送出Start Bit, Data Bit, Parity Bit和Stop bit,摘錄原文說明如下:
When a word is given to the UART for Asynchronous transmissions, a bit called the "Start Bit" is added to the beginning of each word that is to be transmitted. The Start Bit is used to alert the receiver that a word of data is about to be sent, and to force the clock in the receiver into synchronization with the clock in the transmitter. These two clocks must be accurate enough to not have the frequency drift by more than 10% during the transmission of the remaining bits in the word. (This requirement was set in the days of mechanical teleprinters and is easily met by modern electronic equipment.) 
After the Start Bit, the individual bits of the word of data are sent, with the Least Significant Bit (LSB) being sent first. Each bit in the transmission is transmitted for exactly the same amount of time as all of the other bits, and the receiver “looks” at the wire at approximately halfway through the period assigned to each bit to determine if the bit is a 1 or a 0. For example, if it takes two seconds to send each bit, the receiver will examine the signal to determine if it is a 1 or a 0 after one second has passed, then it will wait two seconds and then examine the value of the next bit, and so on. 
The sender does not know when the receiver has “looked” at the value of the bit. The sender only knows when the clock says to begin transmitting the next bit of the word.
When the entire data word has been sent, the transmitter may add a Parity Bit that the transmitter generates. The Parity Bit may be used by the receiver to perform simple error checking. Then at least one Stop Bit is sent by the transmitter. 
When the receiver has received all of the bits in the data word, it may check for the Parity Bits (both sender and receiver must agree on whether a Parity Bit is to be used), and then the receiver looks for a Stop Bit. If the Stop Bit does not appear when it is supposed to, the UART considers the entire word to be garbled and will report a Framing Error to the host processor when the data word is read. The usual cause of a Framing Error is that the sender and receiver clocks were not running at the same speed, or that the signal was interrupted.
Regardless of whether the data was received correctly or not, the UART automatically discards the Start, Parity and Stop bits. If the sender and receiver are configured identically, these bits are not passed to the host.
UART auto baud

在 PC端,通常可以透過終端管理員或 putty 等軟體透過 UART 與設備溝通。使用時將會需要設定 Baud rate,Data bit,Priority bit,Stop Bit,Flow control 等資訊。

但對於較新的設備而言,可能會支援auto baud功能,可以藉由 UART 收到的資料,自動判斷發送端的 UART baudrate,如此發送端(例如PC)可以隨意地選擇 baud rate。

以下舉出兩種方式,可用以計算 UART的 baud rate
  1. 計算收到 start bit 的 falling edge 與 least significant bit 的 falling edge 之間的時間。
  2. 計算收到整個 start bit 的長度所占的時間。
UART control method

在PC端要控制UART,只需要開啟對應的 com port,接著直接呼叫 read(),write()程式即可。而嵌入式系統的UART控制,一般會有兩種方式。
  • Interrupt Driven: 使用中斷向量表中的 UART interrupt 實作 UART 功能。
  • Polled I/O:使用 polling 的方式實作 UART 功能。通常是啟動timer,當timer interrupt發生時查詢 UART 對應的 register。
接著,下面將會以 NXP LPC1114 為例,介紹 MCU 上設定控制 UART 的方法。


UART register

首先需要了解一些與 UART 有關的暫存器,我們可以透過控制這些暫存器,送出或接收資料,下表摘錄幾個常用的 UART 暫存器:


RBR
Read Buffer Register. The CPU reads data bytes from this register.
THR
Transmitter Holding Register. The CPU writes data bytes to this register.
IER
Interrupt Enable Register. The CPU writes a byte to this register to tell the UART when to generate interrupts. Individual bits within the byte indicate whether to generate an interrupt when the RBR is full, when the THR is empty, or when an error condition is detected.
IIR
Interrupt Identification Register. The CPU reads this register to find out why the UART generated an interrupt.
LCR
Line Configuration Register. The CPU writes bytes to this register to configure parameters like baud rate (the speed at which bits are sent and received) and parity (error checking) options.
LSR
Line Status Register. The CPU reads from this register to find out whether the RBR is full, whether the THR is empty, or whether an error condition has been detected.


UART Control Method

以 UART Interrupt 作法為例,其初始化 UART 的過程如下。

1. 先 Disable Interrupt
NVIC_DisableIRQ(UART_IRQn); // Disable UART interrupt,
2. 設定要使用的 UART 接腳。需要查詢 LPC1114的 user manual,摘錄PIO1_7的資料如下。
LPC_IOCON->PIO1_6 &= ~0x07; // 設定使用 UART0, 其對應的接腳是 PIO1_6
LPC_IOCON->PIO1_6 |= 0x01; // PIO1_6 共有三種功能,設定功能為 UART RXD
LPC_IOCON->PIO1_7 &= ~0x07;
LPC_IOCON->PIO1_7 |= 0x01; // 設定目前的功能為 UART TXD

3. 設定 UART Clock


// Enable UART clock
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<12);
LPC_SYSCON->UARTCLKDIV = 0x1;     // divided by 1 


4. 設定資料位元,同步位元,停止位元


LPC_UART->LCR = 0x83;             // 8 bits character length, 1 Stop bit, no Parity

5. 設定 baud rate,並且 reset TX/RX FIFO。

Fdiv = (((SystemCoreClock*LPC_SYSCON->SYSAHBCLKDIV)/regVal)/16)/baudrate ; /*baud rate */
LPC_UART->DLM = Fdiv / 256; LPC_UART->DLL = Fdiv % 256;
LPC_UART->LCR = 0x03; /* DLAB = 0 */
LPC_UART->FCR = 0x07; /* Enable and reset TX and RX FIFO. */


6. 把 TX內的資料全部送出,RX 內的資料全部讀出

// Ensure a clean start, no data in either TX or RX FIFO. */
// CodeRed - added parentheses around comparison in operand of &
  while (( LPC_UART->LSR & (LSR_THRE|LSR_TEMT)) != (LSR_THRE|LSR_TEMT) );
  while ( LPC_UART->LSR & LSR_RDR )
  {
regVal = LPC_UART->RBR; /* Dump data from RX FIFO */
  }

  /* Enable the UART Interrupt */
  NVIC_EnableIRQ(UART_IRQn);

7. 完成初始化過程之後,PC便可以利用 UART 送資料給 Device。當Device 從 UART 有資料進來,就會產生 interrupt,並且呼叫對應的 UART Interrupt Handler。收取資料的過程如下。
  1. 檢查 LSR 狀態,如果 RDR(Read Data Ready)=1,表示 RBR 內有資料待讀取。
  2. 若有錯誤發生,則 LSR 除了設定 RDR 之外,還會定義對應的錯誤,此時要執行對應的錯誤處理。 
  3. 若沒有任何錯誤發生,則直接讀取 RBR。
    舉例如下:
if ( ((LSRValue & (LSR_PE|LSR_RDR)) == (LSR_PE|LSR_RDR)) && (LPC_UART->RS485CTRL & RS485_NMMEN) ){ // ... }
else if (LSRValue & (LSR_OE | LSR_PE | LSR_FE | LSR_RXFE | LSR_BI))
{ // .... }
else if (LSRValue & LSR_RDR) /* Receive Data Ready */
{ // ... }

參考資料

  1. UART Design and Programming
  2. Serial and UART Tutorial with FreeBSD
  3. LPC1114 user manual
  4. LPC1114 datasheet
  5. http://en.wikipedia.org/wiki/Universal_asynchronous_receiver/transmitter
  6. tiny printfhttp://www.sparetimelabs.com/tinyprintf/index.html