2011年7月17日 星期日

5.2) USART Receiver

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

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

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

非中斷版本

底下這支程式會從 Serial Port 接收資料,並且把收到的資料回傳回去 (Echo back):

Serial Port 是否有資料可讀,必須檢查 UCSR0A 的 RXC0 位元,當 RXC0 位元豎起來時代表有收到資料。程式接著從 UDR0 讀取資料並放到 ReceivedChar,然後再利用 serial_putchar() 把資料傳送回去。

下圖是程式執行結果:

image  
▲ 輸入 12345,按下 Send,Arduino 會回傳回來

中斷版本

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

現在用 USART_RX_vect 這個 ISR 來處理中斷,一收到資料,就馬上放到 UDR0 讓 USART 傳送回去。

要特別注意是,mySerial_begin() 這次打開了 Receive Complete Interrupt:

注意事項

在中斷的版本中,我們是自己撰寫 ISR 處理中斷,由於 Arduino 也有同樣的 ISR,因此你不能同時使用 Arduino 的 Serial API,如 Serial.begin(), Serial.read(), Serial.println() 等函式,不然的話程式在編譯時會失敗,編譯器會抱怨 ISR 有重覆定義的問題。

31 則留言:

  1. 請問Cooper前輩~

    1. 為什麼使用中斷的方法時直接使用下面的指令
    ISR(USART_RX_vect)
    {
    char ReceivedChar;
    ReceivedChar = UDR0;
    UDR0 = ReceivedChar;
    }
    而沒有使用檢查UDRE0的下列這指令呢?
    loop_until_bit_is_set(UCSR0A, UDRE0);

    2.請教下列指令的意思是什麼
    ReceivedChar = UDR0;
    UDR0 = ReceivedChar;
    為什麼UDR0的資料要存給ReceivedChar
    然後又要在把ReceivedChar存回UDR0呢?
    意義又是什麼呢?

    謝謝Cooper百忙之中抽空解答:)

    回覆刪除
  2. 我先回答你第 2 個問題:

    ReceivedChar = UDR0;
    UDR0 = ReceivedChar;
    為什麼UDR0的資料要存給ReceivedChar
    然後又要在把ReceivedChar存回UDR0呢?

    好問題!你可以做簡單的實驗,把 UDR0 = ReceivedChar; 這行拿掉,執行程式的時候,你會發現,在你輸入資料後,Serial Monitor 裏不會看到任何回應。

    我加了 UDR0 = ReceivedChar; 這一行
    目的是讓 Arduino 把收到的資料原封不動的傳回電腦端達到一個 Echo 的效果,所以在你輸入資料後,Serial Monitor 上就會看到剛剛輸入的資料

    回覆刪除
  3. loop_until_bit_is_set(UCSR0A, UDRE0);

    會用這一行,是因為 UART 傳輸資料速度有限,不能讓程式丟資料丟太快給 UART,不然會來不及消化。

    為什麼中斷程式的版本沒有用同一行指令檢查 UDRE0 呢? 嗯....是因為我偷懶沒有做..哈
    不過,其實也沒有必要,因為收資料速度跟送資料的速度是一樣的,所以這會放一個 byte 到 UDR0 準備傳送出去,下會兒收到一個 byte 時,前面那個 byte 就已經送完畢了,所以不加也沒關係

    回覆刪除
  4. 這邊又有些許問題請教拉~~~麻煩Cooper

    1.所以我解釋一下
    ReceivedChar = UDR0;
    上面的UDR0是RXD
    UDR0 = ReceivedChar;
    上面的UDR0是TXD
    這樣解釋對嗎?

    2.我們只要把資料放進UDR0馬上就會自動傳出去嗎?還需要做什麼設定嗎?

    3.請教TXC0跟UDRE0的差別
    我困惑的點是:
    當資料被傳送出去時,TXC0與UDRE0都會豎起
    當UDR0裡面有資料時,TXC0跟UDRE0都不會豎起
    請問這樣好似同步的效果, 實際上應用有什麼差別呢?

    4.請問您的TXC0教學文裡面 Shift Register是什麼東西呢?

    麻煩Cooper幫我解惑~~~非常感謝!!!!

    回覆刪除
  5. About Q1 & Q2:

    Q1: USART 的 transmitter 和 receiver 共用 UDR0 暫存器的 I/O bus:

    ReceivedChar = UDR0;

    當程式這樣對 UDR0 進行讀取動作,資料會從 RXB (Receive Data Buffer) 暫存器取出,而:

    UDR0 = ReceivedChar;

    當程式對 UDR0 進行寫入動作時,資料會寫入到 TXB (Transmit Data Buffer) 暫存器

    Q2: 是的,只要把資料放進 UDR0 馬上就會自動傳出去,條件是 transmitter 要 enable (要設定 UCSR0B 的 TXEN0 位元)

    回覆刪除
  6. 一併回答 3 跟 4:

    故事是這樣的,有位管理者和一位工程師,管理者一次只收一件工作,而當工程師收到工作時,會把工作拆成八個動作,一次執行一個動作,當八個動作全部執行完畢時便算完成一件工作。

    如果說一個動作等於一個位元,八個動作就等於一個位元組,也就是一件工作。這工程師相當於是 Shift Register,每一個 clock 推送出一位元。

    而管理者呢,其實他就是 UDR0,或者更精確一點是 TXB 暫存器

    也就是說:

    工程師 = Shift Register
    管理者 = UDR0 (TXB)

    當我寫入資料到 UDR0 時,便是交付一件工作給管理者 (UDR0),而管理者會轉給工程師 (Shift Register) 去執行。而工程師真是真正執行工作的角色,會一個位元一個位元的把資料傳出去。

    與之相關的兩個旗號: TXC0 和 UDRE0:

    當管理者手上沒有工作時,代表可以再接一件工作,這時 UDRE0 會豎起。
    當管理者手上沒有工作,而且工程師前一件工作也做完了的時候,這時 TXC0 就會豎起。

    差別在: UDRE0 會比較早豎起來,因為管理者把工作交給工程師執行時,它就會說「老闆,我有空了」雖然這時工程師還在辛苦工作中...

    而 TXC0 則是用來確認資料是不是已經全部傳出去的旗號,因為只要 TXC0 豎起,代表資料已經全部傳給接收端了。

    呼~~ 好像在繞口令,講得鬍子都打結了! haha~

    回覆刪除
  7. 這樣說明超級清楚!!!
    打通了!!
    :))

    回覆刪除
  8. 哈哈,有些時候打個比喻會比較容易理解。

    回覆刪除
  9. // Enable receiver and transmitter, enable RX interrupt
    UCSR0B |= _BV(RXEN0) | _BV(TXEN0);
    UCSR0B |= _BV(RXCIE0);
    UCSR0B &= ~_BV(TXCIE0);


    -----------------------------------
    // Set frame format: No parity check, 8 Data bits, 1 stop bit

    UCSR0C |= _BV(UCSZ01) | _BV(UCSZ00);

    -----------------------------------
    請問一下,為什麼UCSR0B與UCSR0C這樣設定,就有可以致能的功用,可以講解詳細點嗎?謝謝(第一次學)

    回覆刪除
  10. UCSR0B
    UDRIE0: USART Data Register Empty Interrupt Enable 0
    這個UDRIE0不用設定嗎?

    回覆刪除
  11. UCSR0B 暫存器比較重要的是 RXEN0 和 TXEN0 位元,這兩個元位用來啟用接收器 (receiver) 和發射器 (transmitter),一般來說,我們會啟用這兩個元。

    這行的目的就是啟用 receiver 和 transmitter,下完之後,UART 就可以接收與傳送資料了:

    // Enable receiver and transmitter
    UCSR0B |= _BV(RXEN0) | _BV(TXEN0);

    至於這行則是啟用接收中斷:

    // enable RX interrupt
    UCSR0B |= _BV(RXCIE0);

    所以一旦有資料進來,就會產生中斷跑去執行 ISR:

    ISR(USART_RX_vect)
    {
    char ReceivedChar;
    ReceivedChar = UDR0;
    UDR0 = ReceivedChar;
    }

    回覆刪除
  12. 如果只是要讓 receiver 和 transmitter 致能,只需設定 RXEN0 和 TXEN0 這兩個位元就好。

    UDRIE0 這個位元是 "USART Data Register Empty Interrupt Enable 0",如果致能這個位元,那麼當 UDRn (Data Register) 暫存器有空時就會觸發中斷。

    回覆刪除
  13. 假如我用Arduino 的 uart與wifi通訊(傳送與接收)
    不曉得有沒有範例可以參考
    或是去設定那些東西?

    回覆刪除
  14. 你用的是哪一塊 WiFi Shield?
    目前好像有三種 WiFi Shield:

    1. WiFly Shield (Rovering Network)
    https://www.sparkfun.com/products/9954

    2. WiFi Shield (ZeroG)
    http://www.cutedigi.com/wireless/wifi/wifi-shield-wishield-v2-0-for-arduino.html

    3. WizFi (WizNet)
    http://blog.wiznet.co.kr/sw-release-of-wizfi-shield-for-arduino/#.UCefvqFlT44

    看了一下,這三塊目前跟 Arduino 之間走的介面都是 SPI
    Rovering Network 其實本來是走 UART,不過 WiFly Shield 還是用了一個 SPI to UART chip 轉換跟 Arduino 銜接,這樣做的好處是不佔用 Arduino UART

    回覆刪除
  15. http://www.roundsolutions.com/techdocs/ds/MOD-WXX-CXX20131.pdf

    1.我用的是RN-131C的WIFI

    然後再接UART轉USB(PL2303)的轉接頭

    重點是Arduino 的 uart與wifi通訊(傳送與接收)的參考範例!?

    2.板子都只有一組RX.TX,可以外接多組控制器(聲納,WIFI...等等)

    感謝!

    回覆刪除
  16. 第2個是問題,可以外接多組控制器嗎?

    回覆刪除
  17. 這個可能是你要的:
    https://github.com/perezd/arduino-wifly-serial

    外接多組控制器? 你是說 Arduino 嗎? 外接多組控制是指什麼? 走什麼介面?

    回覆刪除
  18. 外接控制器的意思是指

    我用arduino的Wild Thumper 6WD Robot Chassis(馬達驅動電路板) 或 arduino(開發板)這塊,當中的腳位有RX,TX,而我要做的功能是有WIFI (需要用到RX,TX) 跟 maxsonar(聲納也需要用到RX,TX),但板子上面只有一組RX,TX,我可以接兩組 或 兩組以上嗎?還是只能接一組?

    回覆刪除
  19. 要看你用的 arduino 開發板版本的能力喔
    你需要接很多個 UART 裝置,可能要選 Arduino mega 之類的比較適合,因為它有 4 個 UART,如果是 UNO,因為只有一個 UART 可用,所以就只能接一個

    回覆刪除
  20. http://www.google.com/imgres?imgurl=http://robosavvy.com/store/images/DAGU/wild-thumper-connection.jpg&imgrefurl=http://robosavvy.com/forum/viewtopic.php?t%3D7082&h=482&w=918&sz=88&tbnid=RoRTbLxyCRqhgM:&tbnh=65&tbnw=124&zoom=1&usg=__CVMDN918zF-9aShiok5yf-SeHaY=&docid=VhPXB3gszYI1uM&hl=zh-TW&sa=X&ei=pyMrUN7CAeftmAX_5oD4Cg&ved=0CGcQ9QEwBA&dur=368
    馬達驅動板(ATmega168)

    http://coopermaa2nd.blogspot.tw/
    用來當開發板(ATmega328)UNO

    我所使用的板子是這兩個,最後是用馬達驅動板那塊板子!

    回覆刪除
  21. 這塊是用 atmega168,只有一個 UART 可用
    你要做車子嗎?

    回覆刪除
  22. http://www.pololu.com/catalog/product/1561

    是,要做車子!
    關於車子周邊的功能,全部都是第一次接觸,問題很多!
    而且你提供的WIFI範例,Verify過不了,不知道哪裡出問題,跟設定有關嗎?
    EX.NewSoftSerial wifi(2,3); -> error: 1.'wifi' was not declared in this
    or
    2.Client client("google.com", 80);->error: 'client' was not declared in this scope

    3.error: MemoryFree.h: No such file or directory
    以上幾乎類似問題一直出現,這是缺少什麼檔案,還是設定?



    回覆刪除
  23. 老實說,我還沒用過 Arduino Wifi shield
    所以也不知道是怎麼回事

    感覺好像是少了什麼 library.

    BTW, 你前面問的問題: "板子都只有一組RX.TX,可以外接多組控制器(聲納,WIFI...等等)?" 有答案了,你有注意到 WiFlySerial library 的 code 嗎? 如果你要接多組控制器,那麼 SoftwareSerial 應該是可能的解

    回覆刪除
  24. 我有嘗試使用SoftwareSerial指令去測試!
    印象有在其他網站看到,似乎沒辦法擴充!
    有沒有一些簡單的測試可以讓WIFI回傳值?

    回覆刪除
  25. 那個 WiFlySerial library 還是沒試成功嗎?
    看起來可能需要裝 Arduino Streaming library:
    http://arduiniana.org/libraries/streaming/

    回覆刪除
  26. 因為馬達驅動板(ATmega168)這塊,USB燒入功能損壞,所以現在改用atmel studio 6.0的生成檔再配合progisp,把ISP轉USB燒路進去,因此程式碼可能暫時沒辦法使用,我知道可以沿用原本arduino裡面的程式碼,但是"arduino.h"載入檔案,需要什麼檔案,我不太清楚,只好先搞懂Atmel stuio 6.0的語法,指令!
    很感謝你的幫忙!

    回覆刪除
  27. 不客氣! 其實我也沒幫上忙啊。 逃~~

    回覆刪除
  28. Coopermaa你好~

    我想請問一下
    就是以上arduino和非中斷版本和中斷版本的程式可以執行
    讓Arduino UNO接收Wifly RN-131從智慧型手機傳輸的資料嗎?

    我是剛接觸Arduino的學生
    所以對以上的程式還不太了解

    所以也想請問arduino和非中斷版本和中斷版本的最大差異是?

    不好意思麻煩您解答了
    謝謝

    回覆刪除
  29. 我沒用過 WiFly RN-131,不過應該是可以通的

    非中斷跟中斷兩種方法,最大的差異是一個是用中斷的方式處理啊
    (額~~ 有回答等於沒有回答...Orz)

    這問題其實用 polling v.s interrupt 來聯想比較簡單
    就像醫院掛號系統一樣,我們是讓服務小姐一個一個問病人「你掛號了沒?」用這種 polling
    的方式幫病人掛號比較好呢? 還是讓病人抽號碼牌,只要處理完成一個案件,服務小姐就按一下燈號(產生中斷)請下一個病人來掛號這種 interrupt 的方式掛號好呢?

    這就是非中斷與中斷的差別喔!

    BTW, Rovering Network (就是做 RN-131 的公司) 今年被 microchip 收購了
    如果你想對 RN-131 有進一步的了解,可以到 microchip 去上課,microchip 大部份課程都是免費的喔!

    Microchip 網站 (注意右手邊選單的教育訓練):

    http://www.microchip.com.tw/

    回覆刪除
  30. Coopermaa你好~

    我已經可以讓Arduino正確接收從Android手機傳輸到Arduino了

    現在想要讓Arduino感測到的數值回傳給手機端,我把RN131無線晶片的RX接到Arduino的TX,我手機傳輸資料給Arduino時,Arduino會把我手機傳輸的資料再原封不動地回傳至手機...

    還有
    讓Arduino回傳到手機是用digitalWrite沒錯吧?

    回覆刪除
  31. 線路接法沒錯,應該是 Arduino 的 Tx 接 RN131 的 Rx, 然後 Arduino 的 Rx 接 RN131 的 Tx。

    Arduino 要傳資料給 Android 是用 Serial.print() 或 Serial.write()
    你是怎麼讓 Android 傳資料給 Arduino 的? 在 Arduino 這邊是用 Serial.read() 讀到資料的嗎?

    回覆刪除

注意:只有此網誌的成員可以留言。