Mizar32/UART
通用非同步收發器電路或簡稱為 UART(發音為“you art”)是用於序列通訊的更常見的介面之一。某些計算機,例如 IBM PC,使用稱為 UART 的積體電路將字元轉換為非同步序列形式,反之亦然。所謂“序列”,是指資料一次傳輸一位。
該電路是推薦標準 - 232(簡稱 RS-232)的基礎,該標準還定義了 IBM PC 中的物理外部 COM 埠。RS-232 的最新修訂版是 EIA RS-232,該版本於 1997 年完成。
近年來,PC 已經不再使用 RS-232 埠(可能只在外部),取而代之的是 USB,但 RS-232 仍在廣泛使用,包括許多變體,從工業中使用的 RS-485 到空間站中使用的 SpaceWire。在更簡單的形式中,它目前在嵌入式行業中被廣泛採用。
為了更好地瞭解 UART 電路和 RS-232,讓我們回顧一下過去。序列傳輸的歷史非常悠久,它起源於第一臺電傳打字機(TTY)。電傳打字機從 1914 年開始使用,直到最近才停止使用,最後一家制造 TTY 的公司於 1990 年倒閉,而它們最後一次使用據報道是在最近幾年用於航空公司公告,大多數術語都來自 TTY 世界。例如,Mark 和 Space 是描述電傳打字機電路中邏輯電平的術語。波特率或符號率是序列連線的速度,它基於機電電傳打字機的速率的倍數。
如今,儘管它在個人計算機中變得越來越少見,但它仍然是微控制器 (MCU) 上最常見的外設之一,用於與外部裝置和系統通訊,與板載序列裝置通訊或在板之間、盒之間或嵌入式板之間以及具有 RS-232 埠的 PC 之間建立連線。
RS-232 全球範圍內指定
- 佈線
- 訊號電壓
- 訊號功能
- 訊號時序
- 資訊交換協議
- UART 配置
從微控制器角度來看,我們將現在考察以上所有要點。
UART 是一個全雙工通訊通道,在非同步模式下,每條線路在主要功能方面都獨立於其他線路。RX 引腳可以接收資料,而不管 TX 引腳的活動如何,反之亦然。
通常,來自微控制器的 UART 線路在 3 線配置(未實現流控制)中標記為 TX、RX、GND(分別代表傳送、接收和接地),以及 5 線 TX、RX、GND、RTS、CTS,其中 RTS 代表請求傳送,CTS 代表清除傳送。
該訊號被描述為正電壓,用於傳達邏輯值 0,稱為“Mark”,負電壓用於傳達邏輯值 1,稱為“Space”。
這些訊號的電流和電壓通常太弱,無法從微控制器中輸出,標準規定應使用 ±5V 到 ±15V 的電壓,並且電線長度為 10 米,那麼如何與輸出 ±3V 且電流非常低的 UART 線路進行介面?
Mizar32 的序列 UART 附加板上的 MAX232 晶片是一個 TTL 到 RS-232 電平轉換器,它將電壓從 ±3V 提升到 ±15V。
訊號時序以波特率來衡量,在二進位制通訊中,每一位波特率對應於每秒一位,因此在 9600 波特率下,我們有 9600 位/秒,描述每一位的時間範圍為 104 μs 1/9600。通常,時鐘的頻率將是波特率的 16 倍,以便接收器可以進行中心取樣。
資料交換協議非常簡單。
資料包以起始位開頭,該起始位是邏輯 0,首先被髮送/接收。在軟體方面,該位很重要,因為我們可以輪詢 RX 引腳以查詢該位,以指示正在接收一個數據包。資料位或有效載荷可能包含奇偶校驗位,也可能不包含奇偶校驗位,然後資料包以邏輯 1 結尾,即一個或兩個停止位。
| 0 | start) |
| XXXXXXX | (7 或 8 位資料) |
| X | 1 位可選奇偶校驗位 |
| 1 | 一個或兩個停止位 |
請注意,位元組的最低有效位首先發送,而我們通常將 LSB 寫在右側,因此我們應該從右到左讀取。
關於配置引數,常見配置(通常儲存在暫存器中)是 9600/8n1,這意味著對於序列埠:9600 波特率,8 位資料,無奇偶校驗位,1 個停止位。顯然,兩個 UART 應以相同的方式配置才能進行通訊。
Mizar32 在匯流排聯結器上有兩個可用的序列埠,UART0 在右側總線上,UART1 在左側總線上。UART0 只有資料 (TXD, RXD) 和硬體流控制 (CTS, RTS) 訊號,而 UART1 還具有調變解調器控制訊號 (DSR, DTR, DCD, RI)。
在 Atmel 文件中,這些被稱為“USART”,因為它們也可以程式設計為同步模式以用作額外的 SPI 埠。
| 訊號 | GPIO | 匯流排引腳 | eLua 名稱 | PicoLisp |
|---|---|---|---|---|
| UART0_RX | PA0 | BUS4 引腳 3 | pio.PA_0 |
'PA_0
|
| UART0_TX | PA1 | BUS4 引腳 4 | pio.PA_1 |
'PA_1
|
| UART0_RTS | PA3 | BUS4 引腳 5 | pio.PA_3 |
'PA_3
|
| UART0_CTS | PA4 | BUS4 引腳 6 | pio.PA_4 |
'PA_4
|
| UART1_RX | PA5 | BUS3 引腳 3 | pio.PA_5 |
'PA_5
|
| UART1_TX | PA6 | BUS3 引腳 4 | pio.PA_6 |
'PA_6
|
| UART1_DCD | PB23 | BUS3 引腳 5 | pio.PB_23 |
'PB_23
|
| UART1_DSR | PB24 | BUS3 引腳 6 | pio.PB_24 |
'PB_24
|
| UART1_DTR | PB25 | BUS3 引腳 7 | pio.PB_25 |
'PB_25
|
| UART1_RI | PB26 | BUS3 引腳 8 | pio.PB_26 |
'PB_26
|
| UART1_CTS | PA9 | BUS3 引腳 9 | pio.PA_9 |
'PA_9
|
| UART1_RTS | PA8 | BUS3 引腳 10 | pio.PA_8 |
'PA_8
|
附加的串行板有兩個組開關,DIP1 和 DIP2,用於選擇 RS232 和 RS485 模式。
如果 DIP1 的所有開關都向上,DIP2 的所有開關都向下,它將匯流排訊號轉換為其母 DB9 聯結器 J7 上的 RS232 電平。該聯結器被配置為 DCE 裝置,這與 PC 序列埠相反,因此與 PC 通訊的電纜應在兩端連線相同的引腳;不需要空閒調變解調器電纜。將其他 DCE 裝置(如調變解調器或 GPS 接收器)連線到它,需要交換 TX 和 RX,例如使用空閒調變解調器電纜。
請注意,在序列埠的 1.1.1 版本中,CTS 和 RTS 引腳錯誤地交換了位置,因此要在這裡獲得正確的連線,您需要修改板或電纜。但是,eLua 中的硬體流控制尚不可用,因此它沒有區別;請參閱 問題 #29。
| 訊號 | 匯流排引腳 | UART 模組 rev. 1.0 DB-9F 引腳 |
UART 模組 rev. 1.1.1 DB-9F 引腳 |
|---|---|---|---|
| UART0_RX | P5 引腳 3 | 引腳 3 (輸入) | 引腳 3 (輸入) |
| UART0_TX | P5 引腳 4 | 引腳 2 (輸出) | 引腳 2 (輸出) |
| UART0_RTS | P5 引腳 5 | 引腳 8 (輸出) | 引腳 7 (輸出) |
| UART0_CTS | P5 引腳 6 | 引腳 7 (輸入) | 引腳 8 (輸入) |
| GND | 各種 | 引腳 5 | 引腳 5 |
如果 DIP1 的所有開關都向下,DIP2 的所有開關都向上,則板的 DB9 聯結器將被停用,並且 RS485 訊號將出現在四個螺絲端子上。
此介面允許最多 32 個 RS485 裝置連線到同一條電線上,電纜長度最長可達 1200 米,速率為 100 kbit/秒。
目前,Alcor6L 不支援 RS485 模式;請參閱 問題 #77。
取決於您使用的韌體,UART0 可能用於 Lua 控制檯(配置為 115200 波特率,8 個數據位,1 個停止位,無奇偶校驗),並且 Lua 的預設輸入和輸出檔案是控制檯,因此像“print()”和“io.write()”這樣的函式可用於在序列埠上輸出字元(分別帶和不帶尾隨 CR-LF 換行符)。
在 eLua 中
-- Greet the user io.write( "What's your name? " ) -- Issue a prompt (with no trailing newline) name = io.read() -- Read a line of input and store it in "name" print( "Hello, " .. name .. "!" ) -- Salute them
在 PicoLisp 中
# Greet the user (prinl "What's your name? ") (prinl "Hello, " (setq name (read)) "!")
UART0 也可以使用更低階的 uart Alcor6L 模組訪問(而 UART1 必須使用該模組訪問),該模組可以更深入地控制 UART 的行為。
以下示例在 UART0 上設定不同的波特率,並在收到回覆字元之前以每秒兩次的頻率吐出一個提示字元。為此,它使用了 setup 函式和 getchar 函式的可選超時引數。
在 eLua 中
-- Prompt a 9600 baud serial device until we receive a character in reply uartid = 0 -- Which UART should we be talking on? timeout = 500000 -- Prompt once every half second timerid = 0 -- Use timer 0 to measure the timeout prompt = "U" -- The prompt character (0x55 : binary 01010101) uart.setup( uartid, 9600, 8, 0, 1 ) -- Configure the UART repeat uart.write( uartid, prompt ) reply = uart.getchar( uartid, timeout, timerid ) until reply ~= ""
在 PicoLisp 中
# Prompt a 9600 baud serial device until we receive # a character in reply (setq uartid 0 # Which UART should we be talking on? timeout 500000 # Prompt once every half second timerid 0 # Use timer 0 to measure the timeout prompt "U" ) # The prompt character (0x55 : binary 01010101) (de get-char-uart () (uart-getchar uartid timeout timerid) ) # Get a character from UART (setq reply (get-char-uart)) (until (= "" reply) (uart-write uartid prompt) (setq reply (get-char-uart)) )
請注意:您也可以從我們在 github 上的示例儲存庫中下載上述程式碼 uart-io.l。
請注意,使用
| 語言 | 程式碼 |
|---|---|
| eLua | uart.set_flow_control(uartid, uart.FLOW_RTS + uart.FLOW_CTS)
|
| PicoLisp | (uart-set-flow-control uartid (+ *uart-flow-rts* *uart-flow-cts*) )
|
啟用硬體流控制目前還無法實現。請參閱 問題 #29。
當 UART 接收一個字元時,它會記住該字元,直到您使用 getchar 請求它的值。但是,如果在您讀取第一個字元之前第二個字元到達,第一個字元將被遺忘。
您可以透過啟用 UART 緩衝區來解決這個問題,例如
| 語言 | 程式碼 |
|---|---|
| eLua | uart.setup(1, 115200, 8, 0, 1); uart.set_buffer(1, 1024);
|
| PicoLisp | (uart-setup 1 115200 8 0 1) (uart-set-buffer 1 1024)
|
上述程式碼配置了 UART 1 併為其提供了一個輸入緩衝區。這將允許 UART 接收多達 1024 個字元並記住所有這些字元,即使您尚未讀取第一個字元(第 1025 個字元將引發錯誤訊息並被遺忘)。
UART 緩衝區大小必須是 2 的冪,即 1、2、4、8、16 等等,最大為 32768 個字元。
一些韌體將 UART0 用作 Alcor6L 控制檯。在這種情況下,該 UART 上始終啟用緩衝區。
從 2013 年釋出版開始,較新的韌體包含在 USB 介面上模擬另一個序列埠的軟體。您將 Mizar32 連線到您的 PC 上,PC 上就會出現一個新的序列埠。在 Linux 下,它被稱為 /dev/ttyACM0,而在 Windows 下,它顯示為一個新的“USB 序列埠”。
通常,這個虛擬序列埠用作 eLua 控制檯,傳送 eLua 輸出和錯誤訊息,並接收來自使用者的鍵盤輸入。但是,您可以透過在 http://builder.simplemachines.it 編譯自己的韌體來將控制檯輸出傳送到其他地方,並且您可以透過將序列埠號指定為 176 來與之對話。 eLua 的低階 uart.*() 函式。
它與物理序列埠略有不同,因為它
- 比最快的 RS232 序列埠快十倍以上;
- 它始終實現流控制,確保您永遠不會因溢位而丟失任何輸出或輸入,但是如果您的程式產生輸出而沒有 PC 連線到 USB 埠,您的程式將在輸出 1 到 2 KB 後凍結;
- 一些設定(如波特率和停止位)沒有區別,因為訊號不會透過 RS232 線路傳輸;
- 我不知道 Lua 中斷是否在 USB 序列埠上工作。
請注意:PicoLisp 目前不支援中斷處理。請參閱 問題 #12。但是,您可以在 eLua 中使用中斷。
當在 UART 上啟用輸入緩衝區時,每當接收一個字元時都會生成一箇中斷。該中斷會將字元儲存在您請求的緩衝區中,直到您的程式準備好讀取它。
如果您使用具有 Lua 中斷的韌體(包含在 20120123 elua 0.8 版本的 Mizar A 和 B 韌體中),您也可以安排自己的程式碼段在每次接收字元時被呼叫。
以下示例程式碼在每次接收字元時快速閃爍板載 LED
-- Test UART interrupts handled in Lua. -- Should flash the onboard LED each time a character is received. led = pio.PB_29 -- Which PIO pin is the LED connected to? function uart_handler( resnum ) -- flash the onboard LED pio.pin.setlow( led ) for i=1,10000 do end -- for about 1/100th of a second pio.pin.sethigh( led ) end pio.pin.sethigh( led ) -- off pio.pin.setdir( pio.OUTPUT, led ) uart.setup( 0, 115200, 8, uart.PAR_NONE, 1 ) uart.set_buffer( 0, 1024 ) -- buffer must be enabled for UART IRQs to happen -- tell eLua which function it should call every time the UART receives cpu.set_int_handler( cpu.INT_UART_RX, uart_handler ) -- and enable that Lua interrupt cpu.sei( cpu.INT_UART_RX, 0 ) -- Wait for about ten seconds while the test runs for i=1,10000000 do end -- disable the Lua interrupt cpu.cli( cpu.INT_UART_RX, 0 ) -- and remove our handler function cpu.set_int_handler( cpu.INT_UART_RX, nil )
字元在 Lua 中斷例程被呼叫之前從 UART 接收並放置到緩衝區中,因此您可以在 Lua 中斷例程中使用 uart.getchar( 0, 0 ) 讀取它,並立即對其進行操作。
下表列出了 eLua 為最常用的波特率設定的實際波特率
| 想要 | 獲取 | 錯誤 |
|---|---|---|
| 300 | 300 | 0% |
| 600 | 600 | 0% |
| 1200 | 1200 | 0% |
| 2400 | 2400 | 0% |
| 4800 | 4799 | -0.02% |
| 9600 | 9604 | +0.04% |
| 19200 | 19186 | -0.07% |
| 31250 | 31250 | 0% |
| 38400 | 38372 | -0.07% |
| 57600 | 57692 | +0.07% |
| 115200 | 114583 | -0.5% |
如果 UART0 未使用,則可以透過將 pio.PA_1 用作通用的 Mizar32/PIO 輸出,來切換串行板上的 LED:低輸出值會關閉該 LED,而高輸出值會開啟該 LED。
在 eLua 中
-- Turn the serial board's TX LED on (a low output lights the LED) txled = pio.PA_1 pio.pin.setlow( txled ) -- Prepare "off" as the output value pio.pin.setdir( pio.OUTPUT, txled ) -- Make the pin a GPIO output, disabling serial port 0
在 PicoLisp 中
# Turn the serial board's TX LED on (a low output lights the LED) (setq txled 'PA_1) (pio-pin-setlow txled) # Prepare "off" as the output value (pio-pin-setdir *pio-output* txled) # Make the pin a GPIO output, disabling serial port 0
- 有關 Atmel AT32UC3A 資料手冊 第 26 章:通用同步/非同步接收器/傳送器 (USART) 的資訊。
- 有關所有
uart.*()函式的詳細資訊,請參閱 eLua 手冊的 UART 部分。 - 序列程式設計