2011年7月17日 星期日

5.1) USART Transmitter

這篇將示範如何使用 USART 傳輸資料。

首先,先看使用 Arduino 的版本:

程式很簡單,就不解釋了。接著讓我們改用直接控制 USART 暫存器來傳輸資料。

非中斷版本

底下這支程式會在 Serial Port 印出 "Hello World" 訊息:

mySerial_begin() 函式用來開啟 Serial Port,主要進行下列通訊設定:

  1. 透過 UBBR0H 和 UBBR0L 暫存器設定通訊速率
  2. 透過 UCSR0B 啟用 transmitter 和 receiver,並且不使用中斷
  3. 透過 UCSR0C 設定 Frame format 為 N, 8, 1

serial_putchar() 會將一個字元放到 UDR0 等待傳送,我們必須等到 UDRE (USART Data Register Empty) 旗號豎起來時才能把資料放到 UDR0,其中 loop_until_bit_is_set 是一個巨集指令,它其實只是個簡單的迴圈:

下圖是程式的執行結果:

image_thumb[5]

中斷版本

底下則是使用中斷的版本:

mySerial_begin() 函式沒有變動。

當呼叫 interrupt_on() 時,會立即發生 USART Data Register Empty 中斷,CPU 就會跳到 ISR 執行。

程式加了 USART_UDRE_vect 的 ISR 用來處理中斷,它的邏輯是從 message 取出字元放到 UDR0 去傳送,直到遇到字串的 NULL byte 為止,當資料都送完時,就呼叫 interrupt_off() 關閉中斷。

3 意見:

Sven Wang 提到...

Cooper好~~~

請問
UBRR0H = (unsigned char)(baud_setting >> 8);
UBRR0L = (unsigned char)baud_setting;

這兩行的語法還有意思是什麼?
這部分我不太了解....請Cooper幫忙解答~~
謝謝~~~

Cooper Maa 提到...

以這行而言:

UBRR0H = (unsigned char)(baud_setting >> 8);

(baud_setting >> 8) 是右移 8 個位元。假如 baud_setting 是:

0000 0001 0000 1111

右移 8 個位元之後,結果將會是:

0000 0000 0000 0001

然後結果會轉成 unsigned char 變成只有一個 byte:

0000 00001

UBRR0H 跟 UBRR0L 這兩個暫存器是用來設定 baud rate 的
(見這裏的說明 http://coopermaa2nd.blogspot.tw/2011/07/5-usart.html)

公式是:

ubbr 設定值 = (F_CPU/16/baud rate) – 1

舉例:

假如要設成 9600,那麼根據公式:

(16000000/16/9600) – 1

得 103,所以:

UBRR0H = (unsigned char) (103 >> 8);
UBRR0L = (unsigned char) 103;

得 UBRR0L 是 103,而 UBRR0H 則是 0 (因為 103 右移 8 個位元後結果為 0)

一般情況下,UBRR0H 通常都是 0,但是在某些 baud rate 設定下,公式算出來的結果會超過一個位元組(超過 255 ),這時光靠 UBRR0L 就不夠表示,所以就會用到 UBRR0H。

比如 2400 的 baud rate,根據公式

(16000000/16/2400) – 1

得 416,所以:

UBRR0H = (unsigned char) (416 >> 8);
UBRR0L = (unsigned char) 416;

得 UBRR0H 為 0000 0001 (416 右移 8 個位元後結果為 1)

而 UBRR0L 則為 160 (1010 0000),因為 416 轉成 unsighed char 後,超過 256 的部份就被截掉了

希望可以解答你的疑惑

Sven Wang 提到...

超級清楚!!! 謝謝~~~~~