到目前爲止,咱們的開發板只能處理很小量的數據:讀取幾個引腳電平,輸出幾個LED,頂多用數碼管顯示一個兩位數字。至於輸入一個指令、輸出一條調試信息,甚至用scanf
和printf
來輸入輸出,在已經接觸過的這些器件上是不可思議的。而本講「串口發送」與下一講「串口接收」,將打開這一扇大門。html
本講的主題是UART(Universal Asynchronous Receiver-Transmitter,通用異步收發器),俗稱串口。實際上串口是串行接口的統稱,在單片機領域一般指UART。「串行」的意思是每次傳輸一個bit,而一個字節的數據被拆成8個bit傳輸;相比之下並行總線能夠一次傳輸一個或多個字節(這並不意味着並行總線必定優於串行總線)。編程
AVR單片機提供的硬件組件不是UART,而是USART(S表明Synchronous,同步的),相比UART額外支持同步通訊。所謂「同步」是指收發雙方經過時鐘同步,「異步」是指沒有時鐘來同步,但實際上雙方仍是由一些特殊信號同步的。安全
數據在UART總線上以「幀(frame)」爲單位發送,以下圖所示,帶有方括號的位是可選的。異步
一幀包含一個起始位、5~9個數據位(經常使用8位;不少設備不支持9位)、可選的一個校驗位(偶校驗或奇校驗,即全部數據位與0或1的異或結果)與1或2個終止位。起始位與終止位統稱爲同步位,用於在異步總線上起到同步的做用,這樣接收方纔能知道一幀什麼時候開始。函數
波特率的定義是信息在通訊信道上傳輸的速率。假如信號線上的波形容許1秒有9600個方框(方框表示高電平或低電平,實際電平是其中一個),那麼波特率就是9600。經常使用的波特率有9600與115200(打開Serial Port Utility或相似軟件,可選的波特率都是經常使用的)。ui
在開始通訊以前,收發雙方必須約定好波特率與幀格式。uart_init
函數的配置是波特率38400,8數據位,偶校驗,1中止位。相應地在電腦的串口調試軟件中也要這樣配置。編碼
因爲printf
是變參函數,不是很安全(若是格式串和參數對應錯,程序可能直接跑飛),我傾向於使用類型安全的函數,即函數經過C的句法知道實參類型(寫錯就編譯錯誤,而不是經過編譯器沒法檢測的格式串)。不過,avr-gcc的<stdio.h>
中仍是提供了printf
等函數,你能夠瞭解一下。3d
庫中提供的發送函數都是同步阻塞的,即等待硬件組件把數據所有發送完,函數才返回。這裏的「同步」與剛纔的「異步總線」所指是不一樣的。關於「同步」與「異步」、「阻塞」與「非阻塞」的概念,能夠參考:怎樣理解阻塞非阻塞與同步異步的區別?調試
不難計算,總線發送一個字節的時間是幾千個CPU週期,CPU會浪費大量時間在無用的等待上。這個問題直到咱們講到中斷纔會解決(也許我會把它封裝起來放進庫)。code
咱們來寫一個用串口發送按鍵與撥動開關信息的程序。若是你會相關的C#編程,就可讓電腦響應按鍵事件。
#include <ee1/delay.h> #include <ee1/button.h> #include <ee1/switch.h> #include <ee1/uart.h> int main(void) { button_init(PIN_6, PIN_7); switch_init(PIN_4, PIN_5); uart_init(UART_TX); uart_print_string("start\n"); while (1) { for (uint8_t i = 0; i != BUTTON_COUNT; ++i) if (button_pressed(i)) { uart_print_string("button "); uart_print_int(i); uart_print_string("\n"); } for (uint8_t i = 0; i != SWITCH_COUNT; ++i) if (switch_changed(i)) { uart_print_string("switch "); uart_print_int(i); uart_print_string(switch_status(i) ? " on\n" : " off\n"); } delay(1); } }
程序首先將UART初始化爲發送模式(UART_TX
),而後打印"start"
。在間隔一毫秒的循環中(實際上串口發送的時間遠長於一毫秒,由於是阻塞的),程序檢測每個按鍵與開關的動做,若是有則發送相應數據。
用printf
一行就能解決的操做這裏須要三行才能完成,這就是權衡吧。
基於uart_print_char
,實現my_print_int
函數,在串口上打印一個int
類型整數(在avr-gcc中,int
類型默認是16位寬度;注意負號和0;你能夠了解一下itoa
,儘管它是非標準的)。
將旋轉編碼器的數據經過串口傳輸給電腦。將原始數據(pin_read
或rotary_status
)與處理後的數據(rotary_rotated
)一同打印,你能夠更直觀地感覺數據處理的過程。