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中有数据时,发送模块就会将数据读出并发送,仿真现象正确。
下板现象:
我们随便写入几个数据,会发现我们的发送模块和接收模块的数据完全一致,即接收和发送正常。
审核编辑:刘清
评论
查看更多