01UART基礎知識
通用異步收發傳輸器(Universal Asynchronous Receiver/Transmitter),通常稱作UART。它將要傳輸的資料在串行通信與并行通信之間加以轉換。作為把并行輸入信號轉成串行輸出信號的芯片,UART通常被集成于其他通訊接口的連結上。
具體實物表現為獨立的模塊化芯片,或作為集成于微處理器中的周邊設備。一般是RS-232C規格的,與類似Maxim的MAX232之類的標準信號幅度變換芯片進行搭配,作為連接外部設備的接口。在UART上追加同步方式的序列信號變換電路的產品,被稱為USART(Universal Synchronous Asynchronous Receiver Transmitter)。
1.1 早期串行通訊設備
早期的電報機器使用長度可變的脈沖信號(摩斯電碼)進行數據傳輸,后來出現的電傳打印機(teleprinters )使用5、6、7或8個數據位來表示各種字符編碼。隨著電傳打印機的普及,最終發展成為計算機外圍設備。
?
由于歷史的發展原因,早期在Unix終端是一個名字為ASR33的電傳打字機,而電傳打字機的英文單詞為Teletype(或Teletypewritter),縮寫為TTY。因此,終端設備也被稱為tty設備。這就是TTY這個名稱的來源。
1.2 早期的芯片級UART
DEC(Digital Equipment Corporation)公司的Gordon Bell 為該公司的PDP系列計算機設計了第一個UART,不過體積龐大,UART的線路占據了整個電路板;后來DEC將串行線路單元的設計濃縮為早期的UART單芯片,以方便自己使用。西部數據(Western Digital)公司在1971年左右將其開發為第一個廣泛可用的UART單芯片 WD1402A。這是中型集成電路的早期產品。
DEC是美國一家計算機公司;Western Digital是美國計算機硬盤驅動器制造商和數據存儲公司。
1.3 現代串行通訊設備
2000年開始,大多數IBM或者相關的計算機都刪除了其外部RS232的COM端口,將其替換為帶寬性能更加出色的USB端口;對于仍然需要RS-232串行COM端口的用戶,現在通常使用外部USB轉UART轉換器,常見的有CH340,Silicon Labs 210x的驅動程序,現在很多處理器和芯片都內置了UART。
?
02UART傳輸協議
2.1 UART協議
在串口通信中,數據在1位寬的單條線路上進行傳輸,一個字節的數據要分為8次,由低位到高位按順序一位一位的進行傳送,這個過程稱為數據的"串行化(serialized)"過程。由于串口通信是一種異步通信協議,并沒有時鐘信號隨著數據一起傳輸,而且空閑狀態(沒有數據傳輸的狀態)的時候,串行傳輸線為高電平1,所以發送方發送一個字節數據之前會先發送一個低電平0,接收方收到這個低電平0以后就知道有數據要來了,準備開始接收數據從而實現一次通信。串口通信的時序如下圖所示:
串口通信的規范如下:
1. 空閑狀態(沒有數據傳輸的狀態)下,串行傳輸線上為高電平1;
2. 發送方發送低電平0表示數據傳輸開始,這個低電平表示傳輸的起始位;
3. 8-bit的數據位(1 Byte)是從最低位開始發送,最高位最后發送;
4. 數據位的最高位發送完畢以后的下一位是奇偶校驗位,這一位可以省略不要,同時,當不發送奇偶校驗位的時候接收方也相應的不接收校驗位;
5. 最后一位是停止位,用高電平1表示停止位。
下面以發送字節0x55為例來說明整個的發送過程:
先把0x55轉化成二進制為:01010101。顯然0x55的最低位bit 0是1,次低位bit 1是0,……..,最高位bit 7是0,由于串口是從最低位開始發送一個字節,所以0x55各個位的發送順序是1-0-1-0-1-0-1-0,波形如下圖所示:
下面在給出一個波形,根據上面的規則也可以很容易判斷這是發送字節0x13的波形:
2.1.1 起始位
UART數據傳輸時在不傳輸數據時保持在高電平,當開始傳輸數據時,先發出1bit位寬的低電平,表示數據開始傳輸,即為起始位。
2.1.2 數據位
數據位包含正在傳輸的實際數據,位寬可以為4bit到10bit,大多數情況下,數據首先從低有效位發送。
2.1.3 校驗位
串口通信中的一種交錯方式,通常有偶校驗、奇校驗、高校驗和低校驗四種檢錯方式,沒有校驗位也是可以的。
偶校驗:數據位加上校驗位后,“1”的位數應為偶數;
奇校驗:數據位加上校驗位后,“1”的位數應為奇數;
2.1.4 停止位
在數據發送結束后發送一位高電平用于停止標識。
由于數據是在傳輸線上定時的,并且每一個設備有其自己的時鐘,很可能在通信中兩臺設備間出現了小小的不同步。因此停止位不僅僅是表示傳輸的結束,并且提供計算機校正時鐘同步的機會。適用于停止位的位數越多,不同時鐘同步的容錯性越好,但是數據傳輸率同時也越慢。
3.1.5 波特率
串口數據的傳輸速度用波特率(bit/s)進行衡量,常見的波特率有:9600、19200、38400、57600、115200。假設UART配置為1bit起始位,8bit數據位,沒有校驗位,1bit停止位,那么9600bit/s的波特率可得出每一位數據的時間寬度為:T=1/9600*10=1.04ms,即每個字節(10bit數據)傳輸需要1.04ms。同理可得各個波特率下數據位傳輸時間寬度。
2.2 UART傳輸過程
1. 發送端數據總線將數據包并行傳輸給發送端UART;
2. 發送端UART將起始位、奇偶校驗位和停止位添加到數據包中;
3. 接收端UART解析數據包數據;
4. 接收端UART將所解析的數據傳輸給接收端數據總線。
03UART代碼實現
3.1 UART目標實現功能
設計一個UART發送模塊和接收模塊,具體要求如下:
1. 設計一個UART發送模塊,該模塊接收一個8位輸入數據并采用UART協議發送,UART 采用10位傳輸協議,即一位起始位,8位數據位,一位終止位。發送數據時先發低位數據,最后發高位數據。要求波特率為1K。模塊的定義如下:
module uart_tx (?
clk_40k,? ????//clock signal, 40kHz
rst_n, ????????//reset signal, active low
din,????????//the input data which will be sent by the UART module, 8 bit width
send_start,//the start enable signal, active high, the width is one clock period
bit_out????????//the serial output data?
);?
2. 設計一個UART接收模塊,模塊的定義如下:
module uart_rx (?
clk_40k,? ?//clock signal, 40kHz
rst_n,? ? ? ?//reset signal, active low
bit_in,????//the input serial bit,
dout_vld,//the output valid signal, active high,the dout is valid when this signal is high.
dout??????//received data, 8 bit width?
);?
3. 設計一個testbench,對發送模塊和接收模塊進行測試,測試過程如下,testbench產生一個隨機數,然后啟動uart_tx模塊發送至uart_rx模塊,當uart_rx模塊接收到有效數據后,自動判斷接收的數據是否正確。
3.2 Verilog代碼
1. 發送模塊 (uart_tx):
module uart_tx (?
clk_40k,//clock signal, 40kHz
rst_n,?//reset signal, active low
din,//the input data which will be sent by the UART module, 8 bit width
send_start,//the start enable signal, active high, the width is one clock period
bit_out//the serial output data?
);?
input [7:0] din;
input clk_40k;
input rst_n;
input send_start;
output bit_out;
reg flag;
reg tx_flag;
reg [6:0] cnt;
reg [5:0] tx_cnt;
reg [9:0] din_temp;
//flag: 發送過程flag始終拉高
always @ (posedge clk_40k)
begin
if(~rst_n)
????flag <= 1'b0;
else if(send_start == 1'b1)
????flag <= 1'b1;
else if(tx_flag == 1'b1)
????flag <= 1'b0;
end
//tx_flag: 發送結束tx_flag拉高
always @ (posedge clk_40k)
begin
if(~rst_n)
????tx_flag <= 1'b0;
else if(flag == 1'b1 && din_temp[0] == 1'b0)
????tx_flag <= 1'b1;
else if(tx_cnt == 7'd10)
????tx_flag <= 1'b0;
end
//cnt: 發送數據計數,clk_40k分頻至1k波特率對傳輸數據進行計數
always @ (posedge clk_40k)
begin
if(~rst_n)
????cnt <= 7'b0;
else if(tx_flag == 1'b1 && cnt != 7'd39)
????cnt <= cnt + 1'b1;
else?
????cnt <= 7'b0;
end
always @ (posedge clk_40k)
begin
if(~rst_n)
????tx_cnt <= 6'b0;
else if(tx_flag == 1'b1 && cnt == 7'd39)
????tx_cnt <= tx_cnt + 1'b1;
else if(tx_flag == 1'b0)
????tx_cnt <= 6'b0;
end
//din_temp:?8bit數據移位操作,串行輸出
always @ (posedge clk_40k)
begin
if(~rst_n)
????din_temp <= 10'b1111111111;
else if(flag == 1'b1 || send_start == 1'b1)
????din_temp <= {1'b1,din,1'b0};
else if(tx_flag == 1'b1 && cnt == 7'd39)
????din_temp <= {1'b1,din_temp[9:1]};
end
assign bit_out = din_temp[0];
endmodule
2. 接收模塊 (uart_rx):
module uart_rx (?
clk_40k,? ?//clock signal, 40kHz
rst_n,? ? ? ?//reset signal, active low
bit_in,????//the input serial bit,
dout_vld,//the output valid signal, active high,the dout is valid when this signal is high.
dout??????//received data, 8 bit width?
);?
input bit_in;
input clk_40k;
input rst_n;
output reg dout_vld;
output reg [7:0] dout;
reg rx_flag;
reg [6:0] cnt;
reg [5:0] rx_cnt;
reg [7:0] dout_temp;
//rx_flag:?接收過程rx_flag始終拉高
always @ (posedge clk_40k)
begin
if(~rst_n)
????rx_flag <= 1'b0;
else if(bit_in == 1'b0)
????rx_flag <= 1'b1;
else if(rx_cnt == 6'd9)
????rx_flag <= 1'b0;
end
//cnt: 接收數據計數,clk_40k分頻至1k波特率對接收數據進行計數
always @ (posedge clk_40k)
begin
if(~rst_n)
????cnt <= 7'b0;
else if(rx_flag == 1'b1 && cnt != 7'd39)
????cnt <= cnt + 1'b1;
else if(rx_flag == 1'b0 || cnt == 7'd39)
????cnt <= 7'b0;
end
always @ (posedge clk_40k)
begin
if(~rst_n)
????rx_cnt <= 6'b0;
else if(rx_flag == 1'b1 && cnt == 7'd39)
????rx_cnt <= rx_cnt + 1'b1;
else if(rx_flag == 1'b0)
????rx_cnt <= 6'b0;
end
//dout_temp:?將串行接收數據轉換還原為8bit數據
always @ (posedge clk_40k)
begin
if(~rst_n)
????dout_temp <= 8'b0;
else if(rx_flag == 1'b1 && cnt == 7'd39)
begin
????dout_temp[7] <= bit_in;
????dout_temp[6:0] <= dout_temp[7:1];
end
end
//dout_vld: 傳輸完成標識,8bit數據傳輸結束拉高
always @ (posedge clk_40k)
begin
if(~rst_n)
????dout_vld <= 1'b0;
else if(rx_cnt == 6'd9 && cnt == 7'b0)
begin
????dout <= dout_temp;
????dout_vld <= 1'b1;
end
else?
????dout_vld <= 1'b0;
end
endmodule
3. Testbench(tb):
`timescale 1us/1us
module tb();
reg clk_40k;
reg rst_n;
reg [7:0] din;
reg send_start;
wire bit_out;
wire bit_in;
wire dout_vld;
wire [7:0] dout;
assign bit_in = bit_out;
uart_tx i_uart_tx(
.clk_40k? ? ? ? ? (clk_40k? ?),
.rst_n? ? ? ? ? ? ? (rst_n? ? ?),
.din? ? ? ? ? ? ? ? ?(din? ? ? ?),
.send_start? ? ?(send_start),
.bit_out? ? ? ? ? ?(bit_out? ?)
);
uart_rx i_uart_rx(
.clk_40k? ? ? ? ? (clk_40k? ?),
.rst_n? ? ? ? ? ? ? (rst_n? ? ?),
.bit_in? ? ? ? ? ? ?(bit_in? ? ? ?),
.dout_vld? ? ? ? (dout_vld? ? ?),
.dout? ? ? ? ? ? ? ?(dout? ? ??)
);
initial?
begin
????rst_n = 1'b0;
????#10
????rst_n = 1'b1;
end
initial
begin
????clk_40k = 1'b0;
????forever
????#1
????clk_40k = ~clk_40k;
end
initial?
begin
????send_start = 1'b0;
????din = 8'd0;
????forever
????begin
????????#1000
????????din = $random()%256;
????????send_start = 1'b1;
????????#2
????????send_start = 1'b0;
????end
end
endmodule
4. 仿真結果 :
按照testbench對UART收發端進行仿真,仿真結果如圖:
1. 系統時鐘和傳輸速率通常不一致,clk_40k為高頻系統時鐘,利用計數器分頻實現1k波特率;
2. 復位信號rst_n低電平有效,正常傳輸時始終處于高電平。
3. 開始傳輸時send_start信號拉高,傳輸結束時dout_vld信號拉高;
4. 輸入數據din在傳輸結束后在輸出數據dout體現出來,傳輸時延為1個數據長度。
04UART的優缺點
4.1 UART協議優點
1. 通信只需要兩條數據線;
2. 無需時鐘信號;
3. 有奇偶校驗位,方便通信的差錯檢查;
4. 只需要接收端和發送端設置好數據包結構,即可穩定通信。
4.2 UART協議缺點
1. 數據幀最大支持9位數據;
2. 不支持多主機或多從機的主從系統。
? 審核編輯:湯梓紅
評論
查看更多