本文基于前一篇串口模块基础版进一步对串口发送数据整理,并通过仿真进行验证,所有代码都可直接跑通,注意各个模块名字。
首先再次回顾一下通信协议UART
本质就是并行数据转串行数据
关键参数:起始位(0)、数据位(非任意位)、停止位(1)
假设发送8位数据,那实际上要发送10位数据,上升沿有效的话,需经历11个上升沿。
波特率计数计算:假设时钟周期为20ns,波特率为115200: 1000000000/115200/20=bps
接口规范: RS232、RS449、RS423、RS422、RS485
工程描述:构建串口发送模块,并进行仿真测试(此部分已在上述链接验证过),串口仿真通过后,采用此串口模块构建40位数据发送模块,并对其上板测试。
具体工程如下:
主要模块 : 两个设计模块:1、8bit数据流串口发送模块 serial(底层) 2、40位数据发送模块 serial_40(顶层) 一个tb模块:serial_40_tb
工程分析:
每次信号到来就引入模块发送五次8位的数据,五次结束后立马要由对应的标志位(trans_done)产生一个时钟周期的脉冲信号
两种情况:
1、没有开始发送(包含上一次的发送已经完成,新的40位数据的发送请求没有出现)
2、来了一个发送40位请求,依次发送数据中,采用状态机
总的来说,循环五次等待最终的trans_go,每次8位数据的发送data40[7:0]给串口模块的uart_tx发送,并产生send_go信号,开始发送,等待tx_done信号。循环5 次,发送完40位数据,回到第一个状态继续等待trans_go。
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_40模块:
`timescale 1ns / 1ns
module serial_40(clk,reset_n,uart_tx,data40,trans_go,trans_done);
input clk;
input reset_n;
output uart_tx;
input [39:0]data40;
output reg trans_done;
reg[7:0] data;
reg send_go;
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),
//可设置8种波特率
.baud_set(4)
);
reg[2:0] state;
always@(posedge clk,negedge reset_n)begin
if(!reset_n)begin
trans_done<=0;
state<=0;
data<=0;
send_go<=0;
end
else if(state==0)begin
trans_done<=0;
if(trans_go)begin
data<=data40[7:0];
send_go<=1;
state<=1;
end
else begin
data<=data;
send_go<=0;
state<=0;
end
end
else if(state==1)begin
if(tx_done)begin
data<=data40[15:8];
send_go<=1;
state<=2;
end
else begin
data<=data;
send_go<=0;
state<=1;
end
end
else if(state==2)begin
if(tx_done)begin
data<=data40[23:16];
send_go<=1;
state<=3;
end
else begin
data<=data;
send_go<=0;
state<=2;
end
end
else if(state==3)begin
if(tx_done)begin
data<=data40[31:24];
send_go<=1;
state<=4;
end
else begin
data<=data;
send_go<=0;
state<=3;
end
end
else if(state==4)begin
if(tx_done)begin
data<=data40[39:32];
send_go<=1;
state<=5;
end
else begin
data<=data;
send_go<=0;
state<=4;
end
end
else if(state==5)begin
if(tx_done)begin
trans_done<=1;
send_go<=0;
state<=0;
end
else begin
data<=data;
send_go<=0;
state<=5;
end
end
end
endmodule
serial_40_tb:
`timescale 1ns / 1ns
module serial_40_tb();
reg clk;
reg reset_n;
wire uart_tx;
reg [39:0]data40;
reg trans_go;
wire trans_done;
serial_40 u5(
clk,
reset_n,
uart_tx,
data40,
trans_go,
trans_done
);
initial clk=1;
always #10 clk=~clk;
initial begin
reset_n=0;
data40=0;
trans_go=0;
#201;
reset_n=1;
#200;
data40=40'h123456789a;
trans_go=1;
#20
trans_go=0;
@(posedge trans_done);
#2000000;
data40=40'h187654329a;
trans_go=1;
#20
trans_go=0;
@(posedge trans_done);
#2000000;
$stop;
end
endmodule
仿真结果图:
图1:
图2:
详细分析:**
通过图1,我们可以看到:当传输数据为187654329A时,当数据开始发送,即uart_tx出现第一个下降沿时,40位数据发送标志位trans_go和底层模块中的8位数据发送标志位send_go都立马拉高一个时钟周期(可见图2),在40位数据发送完成标志位trans_done到来之前,send_go 一共出现五次脉冲,有趣的是,对应时钟周期内的send_en也被拉低,这恰好说明,调用底层模块一共发送5次数据,对应的数据流波形如uart_tx显示那样,每一次send_go和tx_done之间对应10位数据,除去起始位和停止位,共8位数据,而每次穿书都是从高位传到低位,因此靠近左侧的数据流是低位,靠近右侧的是高位,根据uart_tx的波形从左往右可以读出对应数据共50位:0010110011_0010011001_0001010101_0011011101_0000110001 ,每组数据去掉首末位置的01,再将其拼起来,再反转过来为:0001100001110110010101000011001010011010,转换成十进制正好是 187654329A,至此便验证了此次工程的正确性。