前言
博客上面,人人都说uart简单,刚上手时,笔者也是一脸懵逼。只能假装uart协议确实简单,只不过。。。。(手动笑脸)
00.串口发送原理图
01.串口发送模块uart_byte_tx.v
module uart_byte_tx(
Clk ,//50M时钟
Rst_n ,//复位信号
send_en ,//发送使能信号
baud_set ,//波特率选项
data_byte ,//要发送的单字节数据
rs232_tx ,//要发送出去的单比特数据
tx_done ,//发送接收标志
tx_state //发送的状态
);
input Clk ;
input Rst_n ;
input send_en ;
input [2:0] baud_set ;
input [7:0] data_byte ;
output reg rs232_tx ;
output reg tx_done ;
output reg tx_state ;
//两级寄存器消除亚稳态
reg [7:0] r_data_byte;
//根据波特率选择查找表,选择分频计数时的最大值
reg [15:0] baud_dr;
//根据上面分频计数时的最大值,生成波特率时钟
reg bps_clk;
reg [15:0] div_cnt;
//这是根据波特时钟产生的计数器,方便后面进一步控制数据串并转换
reg [3:0] bps_cnt;
//两级寄存器消除亚稳态
//reg [7:0] r1_data_byte,r2_data_byte;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
r_data_byte <= 'd0;
end
else if(send_en) begin
r_data_byte <= data_byte;
end
//根据波特率选择查找表,选择分频计数时的最大值
//reg [15:0] baud_dr;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
baud_dr <= 15'd5207;
else begin
case(baud_set)
0:baud_dr <= 16'd5207;
1:baud_dr <= 16'd2603;
2:baud_dr <= 16'd1301;
3:baud_dr <= 16'd867;
4:baud_dr <= 16'd433;
default:baud_dr <= 16'd5207;
endcase
end
//根据上面分频计数时的最大值,生成波特率时钟
//reg bps_clk;
//reg [15:0] div_cnt;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
div_cnt <= 'd0;
else if(tx_state)
if(div_cnt == baud_dr)//这里之前赋值为baud_set了,波形不对
div_cnt <= 'd0;
else
div_cnt <= div_cnt + 1'b1;
else div_cnt <= 'd0;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_clk <= 1'b0;
//else if(div_cnt == baud_dr)//这是每次计数完的时候产生的波特率时钟,如果在计数结束时生成时钟就统一往后延迟了下,可以选择在div_cnt=1时生成
else if(div_cnt == 1'b1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
//这是根据波特时钟产生的计数器,方便后面进一步控制数据串并转换
//reg [3:0] bps_cnt;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_cnt <= 'd0;
else if(bps_cnt == 'd11)
bps_cnt <= 'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
//根据波特率时钟产生的计数器,选择数据的传输
localparam START_BIT = 1'b0;
localparam STOP_BIT = 1'b1;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
rs232_tx <= 1'b1;
else
case(bps_cnt)
'd0:rs232_tx <= 1'b1;
'd1:rs232_tx <= START_BIT;
'd2:rs232_tx <= r_data_byte[0];
'd3:rs232_tx <= r_data_byte[1];
'd4:rs232_tx <= r_data_byte[2];
'd5:rs232_tx <= r_data_byte[3];
'd6:rs232_tx <= r_data_byte[4];
'd7:rs232_tx <= r_data_byte[5];
'd8:rs232_tx <= r_data_byte[6];
'd9:rs232_tx <= r_data_byte[7];
'd10:rs232_tx <= STOP_BIT;
default:rs232_tx <= 1'b1;
endcase
//tx_done:当波特率时钟计数器计数到11时,计数就清零,方便进行下一轮的计数
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
tx_done <= 1'b0;
else if(bps_cnt == 'd11)
tx_done <= 1'b1;
else
tx_done <= 1'b0;
//tx_state:通过send_en和波特率计数(bps_cnt == 11)描述出发送的状态
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
tx_state <= 1'b0;
else if(send_en)
tx_state <= 1'b1;
else if(bps_cnt == 'd11)
tx_state <= 1'b0;
else
tx_state <= tx_state;
endmodule
02.串口发送仿真uart_byte_tx_tb.v
`timescale 1ns/1ns
`define clock_period 20
module uart_byte_tx_tb;
reg Clk ;
reg Rst_n ;
reg send_en ;
reg [2:0] baud_set ;
reg [7:0] data_byte ;
wire rs232_tx ;
wire tx_done ;
wire tx_state ;
uart_byte_tx uart_byte_tx_inst(
.Clk (Clk ),
.Rst_n (Rst_n ),
.send_en (send_en ),
.baud_set (baud_set ),
.data_byte (data_byte ),
.rs232_tx (rs232_tx ),
.tx_done (tx_done ),
.tx_state (tx_state )
);
//产生时钟
initial Clk = 1'b1;
always #(`clock_period/2) Clk = ~Clk;
//产生激励信号
initial begin
Rst_n = 1'b0;
data_byte = 8'b0;
send_en = 1'b0;
baud_set = 3'd4;
#(`clock_period*20 + 1);
Rst_n = 1'b1;
#(`clock_period*50);
data_byte = 8'haa;
send_en = 1'd1;
#`clock_period;
send_en = 1'd0;
@(posedge tx_done);
#(`clock_period*5000);
data_byte = 8'h55;
send_en = 1'd1;
#`clock_period;
send_en = 1'd0;
@(posedge tx_done);
#(`clock_period*5000);
data_byte = 8'hdc;
send_en = 1'd1;
#`clock_period;
send_en = 1'd0;
@(posedge tx_done);
#(`clock_period*5000);
data_byte = 8'hcd;
send_en = 1'd1;
#`clock_period;
send_en = 1'd0;
@(posedge tx_done);
#(`clock_period*5000);
$stop;
end
endmodule
功能仿真如下:
后记
下面就开始,串口接收模块。