1.UART 概述?(通用異步收發傳輸器)
1. 基本定義
UART(Universal Asynchronous Receiver/Transmitter)是一種常見的串行通信協議,用于在設備間通過異步串行通信傳輸數據。它不依賴獨立的時鐘信號,而是通過預定義的波特率(Baud Rate)?同步數據的收發。?核心特點:
- 異步通信:無需共享時鐘信號,僅通過數據線傳輸。
- 全雙工模式:收發雙方可同時發送和接收數據(需獨立TX、RX線)。
- 靈活配置:支持自定義波特率、數據位長度、校驗位、停止位等。
2. 工作原理
(1) 數據傳輸格式
每一幀數據包含以下部分(以典型8N1格式為例):
起始位 | 數據位(8位) | 校驗位(可選) | 停止位(1位) |
---|---|---|---|
1位,低電平 | LSB先發 | 奇偶校驗(可選) | 1位,高電平 |
- 起始位:低電平(0)表示數據傳輸開始。
- 數據位:傳輸的有效數據(5~9位),通常低位(LSB)先發送(如用戶之前的案例)。
- 校驗位:用于簡單錯誤檢測(奇校驗/偶校驗/無校驗)。
- 停止位:高電平(1)表示一幀數據結束,并允許接收端校準時序。
(2) 異步同步機制
- 波特率校準:收發雙方必須使用相同的波特率(如115200、9600)。 例如:
復制
波特率115200 → 每位持續時間 ≈ 1/115200 ≈ 8.68μs
- 數據采樣:接收端在起始位下降沿觸發,并在數據位中間點采樣,抵消時鐘偏移影響。
3. 典型應用場景
- 微控制器與外圍模塊通信:如ESP8266(Wi-Fi)、GPS模塊、傳感器(溫濕度)。
- 調試接口:通過UART輸出調試信息(常見于嵌入式開發板)。
- 有線設備互聯:舊式打印機、工業設備(如Modbus RTU協議)。
- 信號轉換:結合電平轉換芯片(如MAX232)實現RS232、RS485等長距離通信。
4. 優缺點分析
優點 | 缺點 |
---|---|
硬件簡單(僅需兩根數據線) | 傳輸距離短(通常<1米) |
成本低(無需復雜協議棧) | 無硬件錯誤恢復機制(需軟件糾錯) |
廣泛兼容性(幾乎所有MCU支持) | 需嚴格匹配波特率(誤差<3%) |
5. 硬件實現關鍵點
- 發送端(TX):
- 將并行數據轉為串行,按波特率逐位發送。
- 使用分頻器生成波特率時鐘(如50MHz主頻 → 115200波特率需分頻系數:50e6 / 115200 ≈ 434)。
- 接收端(RX):
- 檢測起始位下降沿,啟動同步采樣。
- 通過移位寄存器重組串行數據為并行數據。
6. 常見問題與解決方案
- 波特率失配: 若收發波特率差超過3%,會導致采樣偏移,需重新校準。
- 電磁干擾: 長距離使用需加屏蔽線或轉換為差分信號(如RS485)。
- 數據沖突: 全雙工通信需避免同時發送,可通過流控信號(RTS/CTS)解決。
7. 主流擴展協議
- RS-232:電平標準(±3~15V),支持更長距離(<15米)。
- RS-485:差分信號,可多點通信(工業現場總線)。
- USB轉UART:通過芯片(如CH340、CP2102)實現USB與串口無縫銜接。
2.verilog編寫
? ?這里需要講解下,這里使用到50mhz的時鐘, 波特率為115200,這里的50mhz的時鐘是在1秒內有50_000_000個周期的數據,波特率115200是在1秒內有115200bit的傳輸。
? ?50_000_000/115200? 指的是傳輸1bit需要傳輸多少個時鐘周期
在寫測試代碼的時候,#8680 是因為 在?50_000_000/115200= 434 個時鐘周期傳輸1bit, 而434個時鐘周期每個時鐘周期為20ns 434*20= 8680.
1,波形圖
接收和發送都根據這個圖編寫就行
2.1接收模塊代碼
module uart_rx (
input clk,
input rst,
input rx_en,
input data_in,
output reg [7:0] data_out,
output uart_rx_done
);localparam CLK = 50_000_000,BOTE = 115200,CNT = CLK / BOTE ;reg rx_en_d1;
reg rx_en_d2;
reg rx_flag ;
reg rx_valid;
reg [3:0] rx_cnt ;
reg [15:0] clk_cnt ;
reg [7:0] data_out_r;
reg uart_rx_done_r;assign uart_rx_done = uart_rx_done_r;always @(posedge clk or negedge rst )beginif (rst == 1'b1)beginrx_en_d1 <= 1'b0;rx_en_d2 <= 1'b0;endelse beginrx_en_d1 <= rx_en;rx_en_d2 <= rx_en_d1;end
endalways @ (posedge clk or negedge rst )beginif (rst == 1'b1)rx_flag <= 1'b0;else if (rx_en_d1 == 1'b1 && rx_en_d2 == 1'b0)rx_flag <= 1'b1;else rx_flag <= 1'b0;
endalways @ (posedge clk or negedge rst )beginif (rst == 1'b1)rx_valid <= 1'b0;else if (rx_cnt == 4'd9 && clk_cnt == CNT /2 )rx_valid <= 1'b0;else if (rx_flag == 1'b1)rx_valid <= 1'b1;else;
endalways @ (posedge clk or negedge rst )beginif (rst == 1'b1)clk_cnt <= 16'd0;else if (clk_cnt == CNT )clk_cnt <= 16'd0;else if (rx_valid == 1'b1)clk_cnt <= clk_cnt +1'b1;else clk_cnt <= 16'd0;
endalways @ (posedge clk or negedge rst )beginif (rst == 1'b1)rx_cnt <= 4'd0;else if (rx_en_d1 == 1'b1 && rx_en_d2 == 1'b0)rx_cnt <= 4'd0;else if (clk_cnt == CNT) rx_cnt <= rx_cnt +1'b1;else;
endalways @ (posedge clk or negedge rst )beginif (rst == 1'b1)uart_rx_done_r <= 1'b0;else if (rx_valid == 1'b1) beginif (rx_cnt == 4'd9)uart_rx_done_r <= 1'b1;else uart_rx_done_r <= 4'd0;endelseuart_rx_done_r <= 1'b0;
endalways @ (posedge clk or negedge rst )beginif (rst == 1'b1)data_out_r <= 8'd0;else if (rx_valid == 1'b1 ) beginif (clk_cnt == CNT /4) case (rx_cnt )4'd1 : data_out_r[0] = data_in ; 4'd2 : data_out_r[1] = data_in ; 4'd3 : data_out_r[2] = data_in ; 4'd4 : data_out_r[3] = data_in ; 4'd5 : data_out_r[4] = data_in ; 4'd6 : data_out_r[5] = data_in ; 4'd7 : data_out_r[6] = data_in ; 4'd8 : data_out_r[7] = data_in ; default : ;endcaseelse data_out_r <= data_out_r;endelse data_out_r <= 8'd0; endalways @ (posedge clk or negedge rst )beginif (rst == 1'b1)data_out <= 8'd0;else if (rx_cnt == 4'd9)data_out <= data_out_r;else data_out <= 8'd0;
endendmodule
?2.2發送模塊代碼
module uart_tx(
input clk,
input rst,input tx_en,
input [7:0] data_din,
output data_out,
output uart_tx_done );
localparam CLK = 50_000_000, //時鐘 50_000_000 一秒 50000000個時鐘周期 數據 波特率 9600 一秒9600個數據bit 5000000/9600 一個bit需要多少時鐘周期 BOTE =115200, CNT = CLK /BOTE ;reg tx_en_d1;
reg tx_en_d2;
reg start_flag;
reg tx_valid;
reg [3:0] tx_cnt;
reg [15:0] clk_cnt;
reg uart_done_r;
reg uart_dout_r;
reg [7:0] data_din_r;assign data_out = uart_dout_r;
assign uart_tx_done = uart_done_r;always @ (posedge clk or negedge rst) beginif (rst == 1'b1) begintx_en_d1 <= 1'b0;tx_en_d2 <= 1'b0;endelse begintx_en_d1 <=tx_en;tx_en_d2 <= tx_en_d1;end
endalways @ (posedge clk or negedge rst) beginif (rst == 1'b1)data_din_r <= 8'd0;else if (tx_cnt ==4'd9 && clk_cnt == CNT /2)data_din_r <= 8'd0;else if ( tx_en_d1 == 1'b1 && tx_en_d2 == 1'b0 ) data_din_r <= data_din;else;
endalways @ (posedge clk or negedge rst) beginif (rst == 1'b1)start_flag <= 1'b0;else if ( tx_en_d1 == 1'b1 && tx_en_d2 == 1'b0 ) start_flag <= 1'b1;else start_flag <= 1'b0;
endalways @ (posedge clk or negedge rst) beginif (rst == 1'b1)tx_valid <= 1'b0;else if (start_flag == 1'b1)tx_valid <= 1'b1;else if (tx_cnt ==4'd9 && clk_cnt == CNT /2 )tx_valid <= 1'b0;else;
endalways @ (posedge clk or negedge rst) beginif (rst == 1'b1)clk_cnt <= 16'd0;else if (clk_cnt == CNT )clk_cnt <= 16'd0;else if (tx_valid == 1'b1)clk_cnt <= clk_cnt +1'b1;else clk_cnt <= 16'd0;
endalways @ (posedge clk or negedge rst) beginif (rst == 1'b1)tx_cnt <= 4'd0;else if (tx_en_d1 == 1'b1 && tx_en_d2 == 1'b0)tx_cnt <= 4'd0;else if (clk_cnt == CNT)tx_cnt <= tx_cnt +1'b1;else;
endalways @ (posedge clk or negedge rst) beginif (rst == 1'b1)uart_done_r <= 1'b0;else if (tx_valid == 1'b1) beginif (tx_cnt == 4'd9) uart_done_r <= 1'b1;elseuart_done_r <= 1'b0; endelseuart_done_r <= 1'b0;
endalways @ (posedge clk or negedge rst) beginif (rst == 1'b1)uart_dout_r <= 1'b0; else if (tx_valid == 1'b1) begincase (tx_cnt)4'd0 : uart_dout_r <= 1'b0;4'd1 : uart_dout_r <=data_din_r[0]; 4'd2 : uart_dout_r <=data_din_r[1];4'd3 : uart_dout_r <=data_din_r[2];4'd4 : uart_dout_r <=data_din_r[3];4'd5 : uart_dout_r <=data_din_r[4];4'd6 : uart_dout_r <=data_din_r[5];4'd7 : uart_dout_r <=data_din_r[6];4'd8 : uart_dout_r <=data_din_r[7]; 4'd9 : uart_dout_r <=1'b1;default : ;endcaseendelseuart_dout_r <= 1'b1;
endendmodule
2.3頂層模塊代碼
module uart_top (
input clk,
input rst,
input uart_data_rx,
input rx_en,
output uart_data_out
);wire uart_tx_done;
wire uart_rx_done;
wire [7:0] data_out;uart_rx uart_rx (
.clk (clk ), //input
.rst ( rst ), //input
.rx_en ( rx_en ), //input
.data_in ( uart_data_rx ), //input
.data_out ( data_out ), //output reg [7:0]
.uart_rx_done ( uart_rx_done ) //output
);uart_tx uart_tx(
.clk ( clk ), //input
.rst ( rst ), //input
.tx_en ( uart_rx_done ), //input
.data_din ( data_out ), //input [7:0]
.data_out ( uart_data_out ), //output
.uart_tx_done ( uart_tx_done ) //output );endmodule
2.4仿真模塊代碼
module uart_tb();reg clk;
reg rst;
reg rx_en;
reg uart_data_rx;
wire uart_data_out;initial beginrst = 1'b1;clk = 1'b1;uart_data_rx = 1'b1;rx_en = 1'b0;#20rst = 1'b0;#4340uart_data_rx = 1'b0;rx_en = 1'b1;#20rx_en = 1'b0;#8680 uart_data_rx = 1'b1;#8680 uart_data_rx = 1'b0;#8680 uart_data_rx = 1'b1;#8680 uart_data_rx = 1'b0;#8680 uart_data_rx = 1'b1;#8680 uart_data_rx = 1'b0;#8680 uart_data_rx = 1'b1;#8680 uart_data_rx = 1'b0;#8680 uart_data_rx = 1'b1;endalways #10 clk = !clk;uart_top uart_top_inst (.clk(clk),.rst(rst),.rx_en(rx_en),.uart_data_rx(uart_data_rx),.uart_data_out(uart_data_out)
);endmodule
3.仿真波形
3.1 接收模塊仿真波形
?數據輸入先放到低位依次傳入
?
3.2 發送模塊仿真波形
?發送的時候,把需要發送的數據低位先發
?