微信公众号上线,搜索公众号小灰灰的FPGA,关注可获取相关源码,定期更新有关FPGA的项目以及开源项目源码,包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等
UART数据发送和接收(Verilog)
UART 通信的原理,FPGA 来实现 UART 通信中的数据发送和接收
一、UART通信原理
UART即异步串行通信。一种异步收发传输器,数据发送将并行数据转换成串行数据发送,数据接收将串行数据转换成并行数据接收,可实现全双工传输和接收。
1、RS232的引脚定义
DCD——调制解调器通知PC有载波被侦测到
RXD——接收数据
TXD——发送数据
DTR——PC通知调制解调器进行传输
GND——地线
DSR——调制解调器通知PC准备就绪
RTS——PC要求调制解调器提交数据
CTS——调制解调器通知PC传输数据
RI——调制解调器通知PC有数据输入
2、UART常用参数及时序
UART常用参数设置包括:数据位、波特率、奇偶校验、停止位
数据位:单个UART数据开始到停止期间发送的数据。
波特率:每秒通信的数据比特个数,通常为115200/19200/9600/2400/1200。
奇偶校验:验证数据的正确性。一般不使用,若使用,在偶校验中,数据改变会使得传送数据(所有位数)中“1”个数为偶数,在奇校验中,数据改变会使得传送数据中“1”个数为奇数。
停止位:每个字节的数据位发送完成,发送停止位,标志一次数据传输完成。
RS-232标准,常用配置8个数据位,无奇偶校验,1个停止位,时序如下:
二、UART串口发送(Verilog)
波特率计算,采用定时计数器产生波特率的时钟,计数值的计算为系统时钟频率除以波特率数值。
module uart_tx(
input wire [00:00] i_clk,
input wire [00:00] i_rst_n,
input wire [02:00] i_baud, //波特率设置
input wire [07:00] i_data, //发送数据
input wire [00:00] i_Tx_start, //发送数据使能
output reg [00:00] o_data, //输出串行数据
output reg [00:00] o_Tx_stop, //发送数据结束
output reg [00:00] o_state //数据发送状态
);
/************************波特率时钟***************************/
reg [15:00] baud_cnt; //波特率时钟计数器
reg [00:00] baud_clk; //波特率时钟
reg [15:00] baud_tmp; //波特率数值设定值
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
baud_tmp <= #1 16'd5208;
else begin
case(i_baud)
0:baud_tmp <= #1 16'd5208; //波特率9600
1:baud_tmp <= #1 16'd2604; //波特率19200
2:baud_tmp <= #1 16'd1302; //波特率38400
3:baud_tmp <= #1 16'd868; //波特率57600
4:baud_tmp <= #1 16'd434; //波特率115200
default:baud_tmp <= #1 16'd5208;
endcase
end
end
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
baud_cnt <= #1 1'b0;
else if(o_state)
begin
if(baud_cnt == baud_tmp)
baud_cnt <= #1 1'b0;
else
baud_cnt <= #1 baud_cnt + 1'b1;
end
end
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
baud_clk <= #1 1'b0;
else if(baud_cnt == baud_tmp)
baud_clk <= #1 1'b1;
else
baud_clk <= #1 1'b0;
end
/************************波特率时钟***************************/
/***************数据发送状态和结束标志**************************/
reg [03:00] data_cnt; //数据计数器
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
data_cnt <= #1 1'b0;
else if(data_cnt == 4'd11)
data_cnt <= #1 1'b0;
else if(baud_clk)
data_cnt <= #1 data_cnt + 1'b1;
else
data_cnt <= #1 data_cnt;
end
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
o_state <= #1 1'b0;
else if(i_Tx_start)
o_state <= #1 1'b1;
else if(data_cnt == 4'd11)
o_state <= #1 1'b0;
else
o_state <= #1 o_state;
end
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
o_Tx_stop <= #1 1'b0;
else if(data_cnt == 4'd11)
o_Tx_stop <= #1 1'b1;
else
o_Tx_stop <= #1 1'b0;
end
/***************数据发送状态和结束标志**************************/
/***************数据发送*************************************/
reg [07:00] i_data_n; //数据存储器
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
i_data_n <= #1 1'b0;
else if(i_Tx_start)
i_data_n <= #1 i_data;
else
i_data_n <= #1 i_data_n;
end
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
o_data <= #1 1'b1;
else begin
case(data_cnt)
0:o_data <= #1 1'b1;
1:o_data <= #1 1'b0; //start
2:o_data <= #1 i_data_n[0];
3:o_data <= #1 i_data_n[1];
4:o_data <= #1 i_data_n[2];
5:o_data <= #1 i_data_n[3];
6:o_data <= #1 i_data_n[4];
7:o_data <= #1 i_data_n[5];
8:o_data <= #1 i_data_n[6];
9:o_data <= #1 i_data_n[7];
10:o_data <= #1 1'b1; //stop
default:o_data <= #1 1'b1;
endcase
end
end
/***************数据发送*************************************/
endmodule
三、UART串口接收(Verilog)
module uart_rx(
input wire [00:00] i_clk,
input wire [00:00] i_rst_n,
input wire [02:00] i_baud, //波特率设置
input wire [00:00] i_data, //串行发送数据
output reg [07:00] o_data, //输出并行数据
output reg [00:00] o_Rx_stop //接收数据结束
);
/**************************数据锁存***************************/
reg [00:00] state; //数据接收状态
reg [00:00] i_data_n; //数据接收状态
reg [00:00] i_data_r; //数据接收状态
reg [00:00] data_n;
reg [00:00] data_r;
wire [00:00] i_data_neg;
reg [15:00] data_cnt; //数据计数器
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
begin
i_data_n <= #1 1'b0;
i_data_r <= #1 1'b0;
end
else begin
i_data_n <= #1 i_data;
i_data_r <= #1 i_data_n;
end
end
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
begin
data_n <= #1 1'b0;
data_r <= #1 1'b0;
end
else begin
data_n <= #1 i_data_r;
data_r <= #1 data_n;
end
end
assign i_data_neg = (!i_data_r) & data_r;
/**************************数据锁存***************************/
/**************************数据发送状态************************/
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
state <= #1 1'b0;
else if(i_data_neg)
state <= #1 1'b1;
else if(data_cnt == 16'd159)
state <= #1 1'b0;
else
state <= #1 state;
end
/**************************数据发送状态************************/
/******************波特率时钟(16倍的波特率)********************/
reg [15:00] baud_cnt; //波特率时钟计数器
reg [00:00] baud_clk; //波特率时钟
reg [15:00] baud_tmp; //波特率数值设定值
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
baud_tmp <= #1 16'd325;
else begin
case(i_baud)
0:baud_tmp <= #1 16'd325; //波特率9600
1:baud_tmp <= #1 16'd162; //波特率19200
2:baud_tmp <= #1 16'd80; //波特率38400
3:baud_tmp <= #1 16'd53; //波特率57600
4:baud_tmp <= #1 16'd26; //波特率115200
default:baud_tmp <= #1 16'd325;
endcase
end
end
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
baud_cnt <= #1 16'd0;
else if(state)
begin
if(baud_cnt == baud_tmp)
baud_cnt <= #1 16'd0;
else
baud_cnt <= #1 baud_cnt + 1'b1;
end
end
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
baud_clk <= #1 1'b0;
else if(baud_cnt == baud_tmp)
baud_clk <= #1 1'b1;
else
baud_clk <= #1 1'b0;
end
/******************波特率时钟(16倍的波特率)********************/
/********************数据接收结束标志***************************/
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
data_cnt <= #1 16'd0;
else if((data_cnt == 16'd159)||((data_cnt == 16'd12)&&(start_buffer > 3'd2)))
data_cnt <= #1 16'd0;
else if(baud_clk)
data_cnt <= #1 data_cnt + 1'b1;
else
data_cnt <= #1 data_cnt;
end
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
o_Rx_stop <= #1 1'b0;
else if(data_cnt == 16'd159)
o_Rx_stop <= #1 1'b1;
else
o_Rx_stop <= #1 1'b0;
end
/********************数据接收结束标志***************************/
/***********************数据发送**************************/
reg [02:00] data_buffer [07:00]; //中间数据缓存器
reg [02:00] start_buffer ; //开始缓存器
reg [02:00] stop_buffer ; //结束缓存器
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
begin
start_buffer <= #1 3'd0;
data_buffer[0] <= #1 3'd0;
data_buffer[1] <= #1 3'd0;
data_buffer[2] <= #1 3'd0;
data_buffer[3] <= #1 3'd0;
data_buffer[4] <= #1 3'd0;
data_buffer[5] <= #1 3'd0;
data_buffer[6] <= #1 3'd0;
data_buffer[7] <= #1 3'd0;
stop_buffer <= #1 3'd0;
end
else begin
case(data_cnt)
0:begin
start_buffer <= #1 3'd0;
data_buffer[0] <= #1 3'd0;
data_buffer[1] <= #1 3'd0;
data_buffer[2] <= #1 3'd0;
data_buffer[3] <= #1 3'd0;
data_buffer[4] <= #1 3'd0;
data_buffer[5] <= #1 3'd0;
data_buffer[6] <= #1 3'd0;
data_buffer[7] <= #1 3'd0;
stop_buffer <= #1 3'd0;
end
6,7,8,9,10,11:
start_buffer <= #1 start_buffer + i_data_r;
22,23,24,25,26,27:
data_buffer[0] <= #1 data_buffer[0] + i_data_r;
38,39,40,41,42,43:
data_buffer[1] <= #1 data_buffer[1] + i_data_r;
54,55,56,57,58,59:
data_buffer[2] <= #1 data_buffer[2] + i_data_r;
70,71,72,73,74,75:
data_buffer[3] <= #1 data_buffer[3] + i_data_r;
86,87,88,89,90,91:
data_buffer[4] <= #1 data_buffer[4] + i_data_r;
102,103,104,105,106,107:
data_buffer[5] <= #1 data_buffer[5] + i_data_r;
118,119,120,121,122,123:
data_buffer[6] <= #1 data_buffer[6] + i_data_r;
134,135,136,137,138,139:
data_buffer[7] <= #1 data_buffer[7] + i_data_r;
150,151,152,153,154,155:
stop_buffer <= #1 stop_buffer + i_data_r;
default : begin
start_buffer <= #1 start_buffer ;
data_buffer[0] <= #1 data_buffer[0] ;
data_buffer[1] <= #1 data_buffer[1] ;
data_buffer[2] <= #1 data_buffer[2] ;
data_buffer[3] <= #1 data_buffer[3] ;
data_buffer[4] <= #1 data_buffer[4] ;
data_buffer[5] <= #1 data_buffer[5] ;
data_buffer[6] <= #1 data_buffer[6] ;
data_buffer[7] <= #1 data_buffer[7] ;
stop_buffer <= #1 stop_buffer ;
end
endcase
end
end
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
o_data <= #1 8'd0;
else if(o_Rx_stop)begin
o_data[0] <= #1 data_buffer[0][2] ;
o_data[1] <= #1 data_buffer[1][2] ;
o_data[2] <= #1 data_buffer[2][2] ;
o_data[3] <= #1 data_buffer[3][2] ;
o_data[4] <= #1 data_buffer[4][2] ;
o_data[5] <= #1 data_buffer[5][2] ;
o_data[6] <= #1 data_buffer[6][2] ;
o_data[7] <= #1 data_buffer[7][2];
end
end
/***********************数据发送**************************/
endmodule
三、UART串口接收和发送顶层Top(Verilog)
module uart_top(
input wire [00:00] i_clk,
input wire [00:00] i_rst_n,
input wire [02:00] i_baud, //波特率设置
input wire [07:00] i_data, //发送数据
input wire [00:00] i_Tx_start, //发送数据使能
output wire [00:00] o_Tx_stop, //发送数据结束
output wire [00:00] o_state, //数据发送状态
output wire [07:00] o_data_rx, //输出并行数据
output wire [00:00] o_Rx_stop //接收数据结束
);
wire [00:00] o_data;
uart_rx u_uart_rx(
.i_clk (i_clk ),
.i_rst_n (i_rst_n ),
.i_baud (i_baud ),
.i_data (o_data ),
.o_data (o_data_rx ),
.o_Rx_stop (o_Rx_stop )
);
uart_tx u_uart_tx(
.i_clk (i_clk ),
.i_rst_n (i_rst_n ),
.i_baud (i_baud ),
.i_data (i_data ),
.i_Tx_start (i_Tx_start ),
.o_data (o_data ),
.o_Tx_stop (o_Tx_stop ),
.o_state (o_state )
);
endmodule
四、UART串口接收和发送测试Testbench(Verilog)
`timescale 1ns/1ns
`define clk_period 7
module tb_uart_top();
reg [00:00] i_clk;
reg [00:00] i_rst_n;
wire [07:00] o_data_rx;
wire [00:00] o_Rx_stop;
reg [02:00] i_baud;
reg [07:00] i_data;
reg [00:00] i_Tx_start;
wire [00:00] o_Tx_stop;
wire [00:00] o_state;
uart_top u_uart_top(
.i_clk (i_clk),
.i_rst_n (i_rst_n),
.i_baud (i_baud),
.i_data (i_data),
.i_Tx_start(i_Tx_start),
.o_Tx_stop(o_Tx_stop),
.o_state(o_state),
.o_data_rx (o_data_rx),
.o_Rx_stop (o_Rx_stop)
);
initial i_clk = 0;
always#(`clk_period/2) i_clk = ~i_clk;
initial begin
#(`clk_period*20);
i_rst_n = 0;
#(`clk_period*20);
i_rst_n = 1;
#(`clk_period*20);
i_baud = 3'd0;
i_data = 8'h15;
i_Tx_start = 1;
#(`clk_period);
i_Tx_start = 0;
@(posedge o_Tx_stop)
#(`clk_period*20);
i_data = 8'h12;
i_Tx_start = 1;
#(`clk_period*20);
i_Tx_start = 0;
@(posedge o_Tx_stop)
#(`clk_period*20);
i_data = 8'h14;
i_Tx_start = 1;
#(`clk_period*20);
i_Tx_start = 0;
@(posedge o_Tx_stop)
#(`clk_period*20);
i_data = 8'h31;
i_Tx_start = 1;
#(`clk_period*20);
i_Tx_start = 0;
@(posedge o_Tx_stop)
#(`clk_period*200);
$stop;
end
endmodule