通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UATR,是一种异步收发传输器。将数据由串行通信与并行通信间做传输转换,作为并行输入称为串行输出的芯片。UART是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。
异步串行通信数据格式
UART作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。其中各位的意义如下:
1、起始位:先发出一个逻辑”0”的信号,表示传输字符的开始。
2、数据位:起始位之后。资料位的个数可以是4、5、6、7、8等,构成一个字符。通常采用ASCII码。从最低位开始传送,靠时钟定位。
3、奇偶校验位:资料位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验资料传送的正确性。
4、停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
5、空闲位:处于逻辑“1”状态,表示当前线路上没有数据传输。
发送一个字节数据的时序图
波特率(Baud):是指从一设备发到另一设备的波特率,即每秒钟可以通信的数据比特个数。典型的波特率有300, 1200, 2400, 9600, 19200, 115200等。一般通信两端设备都要设为相同的波特率,但有些设备也可设置为自动检测波特率。
假设需要的波特率为9600bps,那么波特率时钟周期约为 104167ns,板载时钟频率为50MHz,周期为20ns,要计数到104167ns,需要5208个板载时钟周期。
发送模块的代码:
1 module UART_TX( 2 clk, 3 rst_n, 4 5 send_en, 6 tx_done, 7 8 data_in, 9 10 uart_tx 11 ); 12 13 input clk; 14 input rst_n; 15 input[7:0] data_in; // 输入数据 16 input send_en; // 发送使能 17 18 output reg tx_done; // 发送完成标志信号 19 output reg uart_tx; // 目标信号输出 20 21 reg[15:0] bps_cnt; // 波特率计数寄存器 22 reg[3:0] lsm_cnt; // 序列机计数 23 reg flag; // 发送标志信号 24 25 always@(posedge clk or negedge rst_n) // flag 信号设计 26 if(!rst_n) 27 flag <= 1'b0; 28 else if(send_en) // 检测到 发送使能信号,则flag信号拉高 29 flag <= 1'b1; 30 else if(lsm_cnt == 10) // 发送完一个字节后,flag信号拉低 31 flag <= 1'b0; 32 33 always@(posedge clk or negedge rst_n) // bps_cnt 信号设计 34 if(!rst_n) 35 bps_cnt <= 16'd0; 36 else if(flag) begin 37 if(bps_cnt == 5207) //从0开始计数,计数到5207后停止计数,则计数5208次 38 bps_cnt <= 16'd0; 39 else 40 bps_cnt <= bps_cnt + 1'b1; 41 end 42 else 43 bps_cnt <= 16'd0; 44 45 always@(posedge clk or negedge rst_n) // lsm_cnt 信号设计 46 if(!rst_n) 47 lsm_cnt <= 4'd0; 48 else if(bps_cnt == 5207) // 在 bps_cnt == 5207 时,则刚好计数满9600bps的一个周期 49 lsm_cnt <= lsm_cnt + 1'b1; 50 else if(lsm_cnt == 10) 51 lsm_cnt <= 4'd0; 52 53 always@(posedge clk or negedge rst_n) // 信号转换 54 if(!rst_n) 55 uart_tx <= 1'b1; 56 else if(flag) begin 57 case(lsm_cnt) 58 0 : uart_tx <= 1'b0; //起始位 59 1 : uart_tx <= data_in[0]; 60 2 : uart_tx <= data_in[1]; 61 3 : uart_tx <= data_in[2]; 62 4 : uart_tx <= data_in[3]; 63 5 : uart_tx <= data_in[4]; 64 6 : uart_tx <= data_in[5]; 65 7 : uart_tx <= data_in[6]; 66 8 : uart_tx <= data_in[7]; 67 9 : uart_tx <= 1'b1; //停止位 68 default: uart_tx <= 1'b1; 69 endcase 70 end 71 else 72 uart_tx <= 1'b1; 73 74 always@(posedge clk or negedge rst_n) // tx_done 信号设计 75 if(!rst_n) 76 tx_done <= 1'b0; 77 else if(flag && lsm_cnt == 10) // 发送完一个字节数据后拉高tx_done信号 78 tx_done <= 1'b1; 79 else 80 tx_done <= 1'b0; 81 82 endmodule
testbench代码:
1 `timescale 1ns/1ps 2 module UART_TX_tb; 3 reg clk; 4 reg rst_n; 5 reg send_en; 6 7 reg[7:0] data_in; 8 9 wire uart_tx; 10 wire tx_done; 11 12 UART_TX_TEST u0( 13 .clk(clk), 14 .rst_n(rst_n), 15 .send_en(send_en), 16 .tx_done(tx_done), 17 18 .data_in(data_in), 19 .uart_tx(uart_tx) 20 ); 21 22 initial 23 clk = 0; 24 always #10 clk = ~clk; 25 26 initial 27 begin 28 rst_n = 1'b0; 29 data_in = 8'd0; 30 send_en = 1'b0; 31 #21; 32 33 rst_n = 1'b1; 34 #1000; 35 data_in = 8'haa; 36 send_en = 1'b1; 37 #20; 38 send_en = 1'b0; 39 @(posedge tx_done) 40 41 #1000000; 42 data_in = 8'h55; 43 send_en = 1'b1; 44 #20; 45 send_en = 1'b0; 46 @(posedge tx_done) 47 48 #1000000; 49 $stop; 50 end 51 endmodule