通用异步收发器 UART(Universal Asynchronous Receiver/Transmitter),常称为串口,是一种串行、异步、全双工的通信协议,将数据逐位传输。只需要3根线:TX,RX,GND。
UART通讯在使用前需要进行设置,常见设置包括数据位,波特率,奇偶校验类型等。
UART_TX发送模块:
module uart_send #(
parameter CLK_FREQ = 32'd50_000_000,
parameter UART_BPS = 115200
)(
input sys_clk ,
input sys_rst ,
output reg uart_tx ,
input tx_data_vld ,
input [7:0] tx_data ,
output tx_data_done
);
localparam BPS_CNT=CLK_FREQ/UART_BPS;
(*mark_debug = "true"*)reg tx_flag; //开始发送数据标志
(*mark_debug = "true"*)reg [31:0]clk_cnt; //系统时钟计数器
(*mark_debug = "true"*)reg [3:0]tx_cnt; //发送数据位数计数
(*mark_debug = "true"*)reg [7:0]tx_data_r;
reg tx_flag_dyl ;
always@(posedge sys_clk )
begin
tx_flag_dyl <= tx_flag ;
end
assign tx_data_done = tx_flag_dyl & !tx_flag ;
//数据有效信号到来时,开始进入发送模式
always@(posedge sys_clk or negedge sys_rst)begin
if(!sys_rst)begin
tx_flag <=1'b0;
tx_data_r <=8'd0;
end
else begin
if(tx_data_vld)begin
tx_flag <=1'b1; //进入发送模式
tx_data_r<=tx_data;
end
else if((tx_cnt==4'd9)&&(clk_cnt==BPS_CNT-1))begin//计数到停止位中间
tx_flag<=1'b0;
tx_data_r<=8'd0;
end
else begin
tx_flag<=tx_flag;
tx_data_r<=tx_data_r;
end
end
end
//进入发送过程,启动系统计数和发送数据计数
always@(posedge sys_clk or negedge sys_rst)begin
if(!sys_rst)begin
clk_cnt<=32'd0;
tx_cnt<=4'd0;
end
else if (tx_flag)begin
if(clk_cnt<BPS_CNT-1)begin //波特率时钟计数
clk_cnt<=clk_cnt+1'b1;
tx_cnt<=tx_cnt;
end
else begin
clk_cnt<=32'd0; //波特率时钟记满,发送了一位数据
tx_cnt<=tx_cnt+1'b1;
end
end
else begin //非发送数据过程,均为0,不启动计数器。
clk_cnt<=32'd0;
tx_cnt<=4'd0;
end
end
//发送数据
always@(posedge sys_clk or negedge sys_rst)begin
if(!sys_rst)
uart_tx<=1'b1;
else if(tx_flag)begin
case(tx_cnt)
4'd0:uart_tx<=1'b0;
4'd1:uart_tx<=tx_data_r[0]; //最低位最先发送
4'd2:uart_tx<=tx_data_r[1];
4'd3:uart_tx<=tx_data_r[2];
4'd4:uart_tx<=tx_data_r[3];
4'd5:uart_tx<=tx_data_r[4];
4'd6:uart_tx<=tx_data_r[5];
4'd7:uart_tx<=tx_data_r[6];
4'd8:uart_tx<=tx_data_r[7];
4'd9:uart_tx<=1'b1; //停止位
default: uart_tx <= 1'b1;
endcase
end
else
uart_tx<=1'd1; //空闲状态下发送高电平
end
endmodule
UART_RX接收模块:
module uart_recv#(
parameter CLK_FREQ = 32'd50_000_000,
parameter UART_BPS = 115200
)(
input sys_clk,
input sys_rst,
input uart_rx,
output wire rx_data_vld,
output reg [7:0] rx_data
);
//parameter define
localparam BPS_CNT=CLK_FREQ/UART_BPS;
//reg define
reg uart_rx_r0;
reg uart_rx_r1;
reg rx_flag; //开始接收数据标志
reg [31:0]clk_cnt; //系统时钟计数器
reg [3:0]rx_cnt; //接收数据位数计数
//reg [7:0]rx_data; //接收数据
//wire define
wire start_flag ;
reg rx_flag_dly ;
assign rx_data_vld = !rx_flag & rx_flag_dly ;
always@(posedge sys_clk)
begin
rx_flag_dly <= rx_flag ;
end
//捕获接收端口下降沿时钟的到来
assign start_flag=(uart_rx_r1)&(~uart_rx_r0); //下降沿触发
always@(posedge sys_clk or negedge sys_rst)begin
if(!sys_rst)begin
uart_rx_r0<=1'b0;
uart_rx_r1<=1'b0;
end
else begin
uart_rx_r0<=uart_rx; //分别延迟一个时钟,用于检测下降沿
uart_rx_r1<=uart_rx_r0;
end
end
//start_flag 脉冲信号到来时,设置开始接收数据标志
always@(posedge sys_clk or negedge sys_rst)begin
if(!sys_rst)
rx_flag<=1'b0;
else begin
if(start_flag)
rx_flag<=1'b1; //进入接收模式
else if((rx_cnt==4'd9)&&(clk_cnt==BPS_CNT/2))//计数到停止位中间
rx_flag<=1'b0; //停止位传到一半,数据位已经寄存完毕
else
rx_flag<=rx_flag;
end
end
//进入接收过程,启动系统计数和接收数据计数
always@(posedge sys_clk or negedge sys_rst)begin
if(!sys_rst)begin
clk_cnt<=32'd0;
rx_cnt<=4'd0;
end
else if (rx_flag)begin
if(clk_cnt<BPS_CNT-1)begin //波特率时钟计数
clk_cnt<=clk_cnt+1'b1;
rx_cnt<=rx_cnt;
end
else begin
clk_cnt<=32'd0; //波特率时钟记满,接收了一位数据
rx_cnt<=rx_cnt+1'b1;
end
end
else begin //非接收数据过程,均为0,不启动计数器。
clk_cnt<=32'd0;
rx_cnt<=4'd0;
end
end
//寄存器存放接收数据
always@(posedge sys_clk or negedge sys_rst)begin
if(!sys_rst)
rx_data<=8'b0;
else if(rx_flag)begin
if(clk_cnt==BPS_CNT/2)begin //中间时刻采样数据最稳定
case(rx_cnt)
4'd1:rx_data[0]<=uart_rx_r1; //最低位最先接收
4'd2:rx_data[1]<=uart_rx_r1;
4'd3:rx_data[2]<=uart_rx_r1;
4'd4:rx_data[3]<=uart_rx_r1;
4'd5:rx_data[4]<=uart_rx_r1;
4'd6:rx_data[5]<=uart_rx_r1;
4'd7:rx_data[6]<=uart_rx_r1;
4'd8:rx_data[7]<=uart_rx_r1;
default:;
endcase
end
else
rx_data<=rx_data; //不是中间时刻不接收采样数据
end
else
rx_data<=rx_data;
end
endmodule
顶层模块:
module uart_top(
input sys_clk ,
input sys_rst ,
input [7:0] tx_data ,
input tx_data_vld ,
output tx_data_done ,
output [7:0] rx_data ,
output rx_data_vld ,
input uart_rx ,
output uart_tx
);
//parameter define
parameter CLK_FREQ=32'd80_000_000;
parameter UART_BPS=115200;
//reg define
//wire define
wire uart_r_done;
wire [7:0]uart_data;
uart_recv #(
.CLK_FREQ(CLK_FREQ),
.UART_BPS(UART_BPS)
)
u_uart_recv(
.sys_clk (sys_clk ),
.sys_rst (sys_rst ),
.rx_data_vld (rx_data_vld),
.rx_data (rx_data ),
.uart_rx (uart_rx )
);
uart_send #(
.CLK_FREQ(CLK_FREQ),
.UART_BPS(UART_BPS)
)
u_uart_send(
.sys_clk (sys_clk ),
.sys_rst (sys_rst ),
.tx_data_vld (tx_data_vld),
.tx_data (tx_data ),
.tx_data_done (tx_data_done),
.uart_tx (uart_tx )
);
endmodule