首先回顾一下UART
本质就是并行数据转串行数据
关键参数:起始位(0)、数据位(非任意位)、停止位(1)
假设发送8位数据,那实际上要发送10位数据,上升沿有效的话,需经历11个上升沿。
波特率计数计算:假设时钟周期为20ns,波特率为115200: 1000000000/115200/20=bps
接口规范:RS232、RS449、RS423、RS422、RS485**
工程描述:构建串口发送模块,并进行仿真测试, 串口仿真通过后,上板测试。
具体工程如下:
串口测试 : 8bit数据流发送:两个设计模块(serial: 底层,serial_test:顶层)一个tb模块:serial_test_tb
serial模块:
module serial(
clk,
reset_n,
data, send_go,
//串行信号带
uart_tx,
//发送结束标志
tx_done,
//可设置多种波特率
baud_set
);
input clk,reset_n;
input[7:0] data;
reg send_en;
//典型波特率有300,1200,2400,9600,19200,115200
//设置5种模式 0:9600,1:19200,2:38400,3:57600,4:115200
input[2:0] baud_set;
input send_go;
output reg uart_tx;
output reg tx_done;
wire bps_clk;
assign bps_clk=(div_cnt==bps_dr-1);
//计数器 之五种模式设置的值
reg[17:0] bps_dr;
always@(*)begin
case(baud_set)
0:bps_dr=1000000000/9600/20;
1:bps_dr=1000000000/19200/20;
2:bps_dr=1000000000/38400/20;
3:bps_dr=1000000000/57600/20;
4:bps_dr=1000000000/115200/20;
default:bps_dr=1000000000/9600/20;
endcase
end
always@(posedge clk,negedge reset_n)begin
if(!reset_n)
send_en<=0;
else if(send_go)
send_en<=1;
else if(tx_done)
send_en<=0;
end
reg [7:0] datacache;
always@(posedge clk)begin
if(send_go)
datacache<=data;
else
datacache<=datacache;
end
//计数器
reg[17:0] div_cnt;
always@(posedge clk,negedge reset_n)begin
if(!reset_n)
div_cnt<=0;
else if(send_en)begin
if(bps_clk)
div_cnt<=0;
else
div_cnt<=div_cnt+1;
end
else
div_cnt<=0;
end
reg[3:0] bps_cnt;
always@(posedge clk,negedge reset_n)begin
if(!reset_n)
bps_cnt<=0;
else if(send_en)begin
if(div_cnt==1)begin
if(bps_cnt==12)
bps_cnt<=0;
else
bps_cnt<=bps_cnt+1'b1;
end
end
else
bps_cnt<=0;
end
//发送数据
always@(posedge clk,negedge reset_n)begin
if(!reset_n)
begin
uart_tx<=1;
tx_done<=0;
end
else begin
case(bps_cnt)
1:uart_tx<=0;
2:uart_tx<=datacache[0];
3:uart_tx<=datacache[1];
4:uart_tx<=datacache[2];
5:uart_tx<=datacache[3];
6:uart_tx<=datacache[4];
7:uart_tx<=datacache[5];
8:uart_tx<=datacache[6];
9:uart_tx<=datacache[7];
10:uart_tx<=1;
11:begin
uart_tx<=1;
end
default:uart_tx<=1;
endcase
end
end
always@(posedge clk,negedge reset_n)begin
if(!reset_n)
tx_done<=0;
else if((bps_clk)&&(bps_cnt==10))
tx_done<=1;
else
tx_done<=0;
end
endmodule
serial_test模块:
`timescale 1ns/1ns
module serial_test(clk,reset_n,uart_tx);
input clk;
input reset_n;
output uart_tx;
reg send_go;
reg [7:0]data;
wire tx_done;
serial s1(
.clk(clk),
.reset_n(reset_n),
.data(data),
.send_go(send_go),
.uart_tx(uart_tx),
.tx_done(tx_done),
.baud_set(4)
);
//设置计数器每满10ms send_en拉高
reg[18:0]cnt;
always@(posedge clk,negedge reset_n)begin
if(!reset_n)
cnt<=0;
else if(cnt==499999)
cnt<=0;
else
cnt=cnt+1;
end
always@(posedge clk,negedge reset_n)begin
if(!reset_n)
send_go<=0;
else if(cnt==1)
send_go<=1;
else
send_go<=0;
end
always@(posedge clk,negedge reset_n)begin
if(!reset_n)
data<=0;
else if(tx_done)
data<=data+1;
end
endmodule
serial_test_tb:
`timescale 1ns / 1ns
module serial_test_tb( );
reg clk;
reg reset_n;
wire uart_tx;
serial_test s2(
.clk(clk),
.reset_n(reset_n),
.uart_tx(uart_tx));
initial clk=1;
always#10 clk=~clk;
initial begin
reset_n=0;
#201;
reset_n=1;
#50000000;
end
endmodule
仿真结果图:
图1:
图2:
图3:
如上图1 所示,当send_go 为一个时钟周期的高电平时(图3),send_en立马拉高,串口通信位uart_tx立马置低电平,意味着起始位,开始发送数据,直到停止位拉高,之后tx_done立马拉高一个时钟周期(图2),此时send_en立马拉低,至此意味着一个8比特数据的发送结束。同时,可以根据bps_cnt(1,2,3,4,5,6,7,8,9,a)定位查看uart_tx发送的每个比特位,从起始位到停止位对应的是0010000001十位数据,去掉左右两端,再将其翻转正好是0000_0010,也就是02。需要保证tx_done标志位为一个时钟周期,否则会导致数据流发送延迟。