UART即通用異步收發器,是一種通用串行數據總線,用于異步通信。該總線為雙向通信,可以實現數據的接收與發送。
在數據傳輸過程中,我們需要解釋一下串行通信。假設現在我們傳輸數據的雙方為A和B,每次傳輸8bit數據,這8bit的數據在傳輸時按照A與B之間的連線分為串行通信和并行通信。串行通信即A與B之間僅有一根數據線,在傳輸數據時需要一次發送1bit,總共發送8次。并行通信即A與B之間有8根線,傳輸數據時,將8bit數據通過8根線一起傳輸,這樣一次就可以全部傳輸完成。
數據傳輸時,接收方和發送方使用的時鐘不是同一個時鐘域,這也就是異步傳輸。
在通信雙方傳輸數據之前,需要通過串口線進行連接,然后再傳輸數據,常用的串口線為DB9接口,但是由于這種接口體積大,不易攜帶等缺點而慢慢淘汰。我們在B04的開發板上使用到的是一個USB轉串口的芯片,這樣我們的MINI USB接口不僅可以給開發板供電,還可以進行串口數據傳輸。芯片為CP2102(USB <-->UART(LVCMOS/LVTTL)),對于開發者來說,就不需要關注電平標準了。
芯片電路圖如圖所示:
在電路圖中我們可以發現,串口接口只有兩根數據線,分別為RXD和TXD。那么在進行通信之前,我們需要先了解一下串口的傳輸規則。
在發送者沒有發送數據時,接收方如果一直接收數據,那就會導致數據出錯,所以,接收方在接收數據時需要有標志信號,然后啟動接收。在我們的串口協議中是這樣規定的:
1、空閑態數據線上為高電平。
2、發送數據時,先發送起始位,邏輯電平為低。
3、起始位結束之后,發送8bit數據,從低位開始傳輸。
4、數據傳輸完畢,是1bit的校驗位,采用奇偶校驗法。(可不使用)
5、停止位,為高電平,可以是1bit、1.5bit或者2bit。
那么我們清楚了數據傳輸規則之后,我們還需要明白一個內容,那就是1bit數據的時間長度。在算這個時間之前,我們需要了解一下波特率。波特率的單位是bit/s,也就是1秒時間內,傳輸的bit數。我們串口常用的波特率有9600、14400、19200等等。這個波特率是傳輸數據的雙方,提前規定好的。那么在同一速度下傳輸數據,就會簡單很多。那么根據波特率我們可以計算出來1bit數據的時長為104166ns。在清楚這些之后,接下來我們做一個回環測試。
首先我們先新建一個工程:
選好代碼存放位置,修改工程名字為uart。
選擇我們的芯片型號:XC7A35TFGG484-2。
新建好工程后,開始新建文件寫代碼。
點擊OK,頂層文件新建完成,后續各個模塊新建方式相同。接收代碼如下:
?
1 module uart_rx( 2 3 input wire clk, 4 input wire rst_n, 5 input wire RXD, 6 output reg [7:0] data, 7 output reg wr_en 8 ); 9 10 parameter t = 5208; 11 12 reg [14:0] cnt; 13 reg flag; 14 reg rxd_r, rxd_rr; 15 wire rx_en; 16 reg [3:0] num; 17 reg [7:0] data_r; 18 19 always @ (posedge clk) rxd_r <= RXD; 20 always @ (posedge clk) rxd_rr <= rxd_r; 21 22 assign rx_en = (~rxd_r) & rxd_rr; 23 24 always @ (posedge clk, negedge rst_n) 25 begin 26 if(rst_n == 1'b0) 27 cnt <= 15'd0; 28 else if(flag) 29 begin 30 if(cnt == t - 1) 31 cnt <= 15'd0; 32 else 33 cnt <= cnt + 1'b1; 34 end 35 else 36 cnt <= 15'd0; 37 end 38 39 always @ (posedge clk, negedge rst_n) 40 begin 41 if(rst_n == 1'b0) 42 flag <= 1'b0; 43 else if(rx_en) 44 flag <= 1'b1; 45 else if(num == 4'd10) 46 flag <= 1'b0; 47 else 48 flag <= flag; 49 end 50 51 always @ (posedge clk, negedge rst_n) 52 begin 53 if(rst_n == 1'b0) 54 num <= 4'd0; 55 else if(cnt == t / 2 - 1) 56 num <= num + 1'b1; 57 else if(num == 4'd10) 58 num <= 4'd0; 59 else 60 num <= num; 61 end 62 63 always @ (posedge clk, negedge rst_n) 64 begin 65 if(rst_n == 1'b0) 66 begin 67 data_r <= 8'd0; 68 data <= 8'd0; 69 end 70 else if(cnt == t / 2 - 1) 71 case(num) 72 4'd0 : ; 73 4'd1 : data_r[0] <= rxd_rr; 74 4'd2 : data_r[1] <= rxd_rr; 75 4'd3 : data_r[2] <= rxd_rr; 76 4'd4 : data_r[3] <= rxd_rr; 77 4'd5 : data_r[4] <= rxd_rr; 78 4'd6 : data_r[5] <= rxd_rr; 79 4'd7 : data_r[6] <= rxd_rr; 80 4'd8 : data_r[7] <= rxd_rr; 81 4'd9 : data <= data_r; 82 default : data <= data; 83 endcase 84 end 85 86 always @ (posedge clk, negedge rst_n) 87 begin 88 if(rst_n == 1'b0) 89 wr_en <= 1'b0; 90 else if(num == 4'd10) 91 wr_en <= 1'b1; 92 else 93 wr_en <= 1'b0; 94 end 95 96 endmodule
?
發送數據時,跟接收基本類似,按照數據格式發送數據,代碼如下:
?
1 module uart_tx( 2 3 input wire clk, 4 input wire rst_n, 5 input wire empty, 6 input wire [7:0] data, 7 output wire rd_en, 8 output reg TXD 9 ); 10 11 parameter t = 5208; 12 13 reg [14:0] cnt; 14 reg flag; 15 reg [3:0] num; 16 17 18 always @ (posedge clk, negedge rst_n) 19 begin 20 if(rst_n == 1'b0) 21 cnt <= 15'd0; 22 else if(flag) 23 begin 24 if(cnt == t - 1) 25 cnt <= 15'd0; 26 else 27 cnt <= cnt + 1'b1; 28 end 29 else 30 cnt <= 15'd0; 31 end 32 33 always @ (posedge clk, negedge rst_n) 34 begin 35 if(rst_n == 1'b0) 36 flag <= 1'b0; 37 else if(empty == 1'b0) 38 flag <= 1'b1; 39 else if(num == 4'd10) 40 flag <= 1'b0; 41 else 42 flag <= flag; 43 end 44 45 always @ (posedge clk, negedge rst_n) 46 begin 47 if(rst_n == 1'b0) 48 num <= 4'd0; 49 else if(cnt == t / 2 - 1) 50 num <= num + 1'b1; 51 else if(num == 4'd10) 52 num <= 4'd0; 53 else 54 num <= num; 55 end 56 57 assign rd_en = (num == 4'd0 && cnt == 15'd1) ? 1'b1 : 1'b0; 58 59 always @ (posedge clk, negedge rst_n) 60 begin 61 if(rst_n == 1'b0) 62 TXD <= 1'b1; 63 else if(cnt == t / 2 - 1) 64 case(num) 65 4'd0 : TXD <= 1'b0; 66 4'd1 : TXD <= data[0]; 67 4'd2 : TXD <= data[1]; 68 4'd3 : TXD <= data[2]; 69 4'd4 : TXD <= data[3]; 70 4'd5 : TXD <= data[4]; 71 4'd6 : TXD <= data[5]; 72 4'd7 : TXD <= data[6]; 73 4'd8 : TXD <= data[7]; 74 4'd9 : TXD <= 1'b1; 75 default : TXD <= 1'b1; 76 endcase 77 end 78 79 endmodule
?
其中讀使能我們只需在數據發送前將數據讀出即可。
在做完兩個模塊之后,我們還需要使用一個FIFO來做數據緩存,FIFO配置參數如下:
我們使用異步FIFO,深度選擇2048,位寬為8,復位信號暫時不使用。
生成FIFO后,將各個模塊例化到頂層當中,代碼如下:
?
1 module uart( 2 3 input wire clk, 4 input wire rst_n, 5 input wire RXD, 6 output wire TXD 7 ); 8 9 wire [7:0] rx_data; 10 wire wr_en; 11 wire rd_en; 12 wire [7:0] tx_data; 13 wire empty; 14 15 uart_rx uart_rx_inst( 16 17 .clk (clk ), 18 .rst_n (rst_n), 19 .RXD (RXD ), 20 .data (rx_data), 21 .wr_en (wr_en) 22 ); 23 24 fifo fifo_inst ( 25 .wr_clk(clk), // input wire wr_clk 26 .rd_clk(clk), // input wire rd_clk 27 .din(rx_data), // input wire [7 : 0] din 28 .wr_en(wr_en), // input wire wr_en 29 .rd_en(rd_en), // input wire rd_en 30 .dout(tx_data), // output wire [7 : 0] dout 31 .full(), // output wire full 32 .empty(empty) // output wire empty 33 ); 34 35 uart_tx uart_tx_inst( 36 37 .clk (clk ), 38 .rst_n (rst_n ), 39 .empty (empty ), 40 .data (tx_data), 41 .rd_en (rd_en ), 42 .TXD (TXD ) 43 ); 44 45 endmodule
?
功能部分寫完之后,我們寫一個仿真進行邏輯驗證,寫仿真時,我們按照數據順序模擬給值,每1bit持續104166ns的時間。代碼如下:
?
1 `timescale 1ns / 1ps 2 3 module uart_tb; 4 5 reg clk; 6 reg rst_n; 7 reg RXD; 8 wire TXD; 9 10 initial begin 11 clk = 0; 12 rst_n = 0; 13 RXD = 1; 14 #105; 15 rst_n = 1; 16 17 #1000; 18 RXD = 0; 19 #104166; 20 21 RXD = 1; 22 #104166; 23 RXD = 0; 24 #104166; 25 RXD = 1; 26 #104166; 27 RXD = 0; 28 #104166; 29 RXD = 1; 30 #104166; 31 RXD = 0; 32 #104166; 33 RXD = 0; 34 #104166; 35 RXD = 1; 36 #104166; 37 38 RXD = 1; 39 #104166; 40 41 #5000; 42 $stop; 43 end 44 45 always #10 clk = ~clk; 46 47 uart uart_inst( 48 49 .clk (clk ), 50 .rst_n (rst_n ), 51 .RXD (RXD ), 52 .TXD (TXD ) 53 ); 54 55 endmodule
?
打開仿真波形:
如圖,我們可以看到,當我們的接收模塊接收到數據時,會將數據寫入FIFO,FIFO中有數據時,發送模塊就會將數據讀出并發送,仿真現象正確。
下板現象:
我們隨便寫入幾個數據,會發現我們的發送模塊和接收模塊的數據完全一致,即接收和發送正常。
審核編輯:劉清
評論
查看更多