本学习笔记主要参考小梅哥B站教学视频,网址如下:
https://www.bilibili.com/video/BV1va411c7Dz?p=1
使用的编译器为Vivado,HDL语言为verilog
一、串口通信之发送数据
原理
设计代码
module uart_byte_tx(
Clk,
Reset_n,
Data,
Send_en,
Baud_set, //波特率选择
uart_tx,
Tx_done,
);
input Clk;
input Reset_n;
input [7:0] Data;
input Send_en;
input [2:0] Baud_set;
output reg uart_tx;
output reg Tx_done;
//Baud_set =0 波特率 = 9600;
//Baud_set =1 波特率 = 19200;
//Baud_set =2 波特率 = 38400;
//Baud_set =3 波特率 = 57600;
//Baud_set =4 波特率 = 115200;
reg [17:0] bps_DR;//定义某一波特率下需要计数的次数
always@(*)begin
case(Baud_set)
3'd0: bps_DR = 1000000000/9600/20;
3'd1: bps_DR = 1000000000/19200/20;
3'd2: bps_DR = 1000000000/38400/20;
3'd3: bps_DR = 1000000000/57600/20;
3'd4: bps_DR = 1000000000/115200/20;
default: bps_DR = 1000000000/9600/20;
endcase
end
reg [17:0] div_cnt; //波特率最小为300时,1s/300=3333333ns发一个bit,3333333/20=166666个周期,18位
//分频得到基本时钟
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
div_cnt <= 0;
else if(Send_en)
if(div_cnt == bps_DR - 1)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1'b1;
else
div_cnt <= 0;
reg[3:0] bps_cnt; //计数11个端点,10位数据
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
bps_cnt <= 0;
else if(div_cnt == bps_DR - 1)begin
if(bps_cnt == 11)
bps_cnt <= 0;
else
bps_cnt <= bps_cnt + 1'b1;
end
always@(posedge Clk or negedge Reset_n)begin //根据bps_cnt的值,给数据
if(!Reset_n)begin
Tx_done <= 0;
uart_tx <= 1'b1; //空闲的时候为高电平
end
else begin
case(bps_cnt)
0: begin uart_tx <= 0;Tx_done <= 0; end
1: uart_tx <= Data[0];
2: uart_tx <= Data[1];
3: uart_tx <= Data[2];
4: uart_tx <= Data[3];
5: uart_tx <= Data[4];
6: uart_tx <= Data[5];
7: uart_tx <= Data[6];
8: uart_tx <= Data[7];
9: uart_tx <= Data[7]; //这里有两个Data[7]。是因为原作者在此处少写了一个状态6
10: uart_tx <= 1;
11: begin uart_tx <= 1; Tx_done <= 1'b1; end//至少维持一个单位的电平
default: uart_tx <= 1;
endcase
end
end
endmodule
测试代码
`timescale 1ns / 1ps
module uart_tx_tb();
reg Clk;
reg Reset_n;
reg [7:0] Data;
reg Send_en;
reg [2:0] Baud_set;
wire uart_tx;
wire Tx_done;
uart_byte_tx uart_byte_tx(
.Clk(Clk),
.Reset_n(Reset_n),
.Data(Data),
.Send_en(Send_en),
.Baud_set(Baud_set), //波特率选择
.uart_tx(uart_tx),
.Tx_done(Tx_done)
);
initial Clk = 1;
always#10 Clk=~Clk;
initial begin
Reset_n = 0;
Data = 0;
Send_en = 0;
Baud_set = 4;
#201;
Reset_n = 1;
#100;
Data = 8'h57;
Send_en = 1;
#20;
@(posedge Tx_done);
Send_en = 0;
#20000;
Data = 8'h75;
Send_en = 1;
#20;
@(posedge Tx_done);
#20000;
Send_en = 0;
$stop;
end
endmodule
仿真结果
发现Send_en拉高之前,uart_tx就置为0了,不符合常理。
转到第二个发送信号处,发现Send_en为1时,uart_tx仍为1,有延迟一个一位数据的时间后,才开始发送起始位:0。
观察代码,bps_cnt = 0时,就让它发送起始位。
但是观察上面的仿真图,bps_cnt在此时仍为11,不是0.
所以bps_cnt也应该受Send_en的控制。
将此处的代码修改为
再次仿真
发现Send_en为1时,bps_cnt却一直为0;
再次修改代码(加个begin end)
仿真
发现 bps_cnt的问题被解决了。再看看Send_en和uart_tx的关系
uart_tx在空闲的时候应该为高电平,但从仿真结果来看,并不是如此。在发送起始位之前,有一段的低电平。
放大仿真图观察,在uart_tx被拉低时,bps_cnt为0.按照代码,uart_tx也被置为0.
修改代码
让bps_cnt从1到12,0在很多时候空闲状态下是这个值,用它会产生一定的麻烦。
仿真
发现Send_en为0时,uart_tx为高电平。但同样又出现了,Send_en为1时,uart_tx滞后一个位的时间,才发送起始位的问题。因为bps_cnt为0时,才开始发送起始位,而此处bps_cnt仍为0.
观察代码,计满才加一次,耽误了一位的时间。
计满需要很长时间,但是计到1不需要很长时间。(这里大体意思是说:原来代码的div_cnt需要计数一个完整的周期才能够将bps_cnt加一,而每一段计数过程都会经过1这个数,所以直接让它跑到值为1的时候就让bps_cnt加1,节省了很多时间,同样也是一段div_cnt的计数周期仅对应一个数值1,也就是一个bps_cnt+1的过程)改为
仿真结果
从结果来看,Send_en变为高电平,两个时钟周期后,Tx开始发起始位。
波特率验证:从仿真结果来看,发一位需要8.68us,符合115200的波特率。
另外:将程序修改为:bps_clk来控制的思路
仿真发现,的确出现了bps_clk一样的脉冲。但是数数发现,12个bps_clk将bps_cnt分成了11段。但算上起始位和终止位,理应10段才行。
再次修改代码
仿真结果
这下终于正常了!!
完整正确代码:
module uart_byte_tx(
Clk,
Reset_n,
Data,
Send_en,
Baud_set, //波特率选择
uart_tx,
Tx_done,
);
input Clk;
input Reset_n;
input [7:0] Data;
input Send_en;
input [2:0] Baud_set;
output reg uart_tx;
output reg Tx_done;
//Baud_set =0 波特率 = 9600;
//Baud_set =1 波特率 = 19200;
//Baud_set =2 波特率 = 38400;
//Baud_set =3 波特率 = 57600;
//Baud_set =4 波特率 = 115200;
reg [17:0] bps_DR;//定义某一波特率下需要计数的次数
always@(*)begin
case(Baud_set)
3'd0: bps_DR = 1000000000/9600/20;
3'd1: bps_DR = 1000000000/19200/20;
3'd2: bps_DR = 1000000000/38400/20;
3'd3: bps_DR = 1000000000/57600/20;
3'd4: bps_DR = 1000000000/115200/20;
default: bps_DR = 1000000000/9600/20;
endcase
end
wire bps_clk;
assign bps_clk = (div_cnt == 1);
reg [17:0] div_cnt; //波特率最小为300时,1s/300=3333333ns发一个bit,3333333/20=166666个周期,18位
//分频得到基本时钟
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
div_cnt <= 0;
else if(Send_en)begin
if(div_cnt == bps_DR - 1)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 0;
reg[3:0] bps_cnt; //计数11个端点,10位数据
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
bps_cnt <= 0;
else if(Send_en)begin
if(bps_clk)begin
if(bps_cnt == 11)
bps_cnt <= 0;
else
bps_cnt <= bps_cnt + 1'b1;
end
end
else
bps_cnt <= 0;
always@(posedge Clk or negedge Reset_n)begin //根据bps_cnt的值,给数据
if(!Reset_n)begin
Tx_done <= 0;
uart_tx <= 1'b1; //空闲的时候为高电平
end
else begin
case(bps_cnt)
1: begin uart_tx <= 0;Tx_done <= 0; end
2: uart_tx <= Data[0];
3: uart_tx <= Data[1];
4: uart_tx <= Data[2];
5: uart_tx <= Data[3];
6: uart_tx <= Data[4];
7: uart_tx <= Data[5];
8: uart_tx <= Data[6];
9: uart_tx <= Data[7];
10: uart_tx <= 1;
11: begin uart_tx <= 1; Tx_done <= 1'b1; end//至少维持一个单位的电平
default: uart_tx <= 1;
endcase
end
end
endmodule
二、每10ms以115200的波特率发送一个数据,每次发送的数据比以前一个数据大一(计数器)
思路:顶层模块调用
设计文件:
module uart_tx_test(
Clk,
Reset_n,
uart_tx
);
input Clk;
input Reset_n;
output uart_tx;
reg Send_en;
reg [7:0] Data;
uart_byte_tx uart_byte_tx(
.Clk(Clk),
.Reset_n(Reset_n),
.Data(Data),
.Send_en(Send_en),
.Baud_set(3'd4), //波特率选择
.uart_tx(uart_tx),
.Tx_done(Tx_done)
);
reg [18:0] counter;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter <= 0;
else if(counter == 500_000-1)
counter <= 0;
else
counter <= counter + 1;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Send_en <= 0;
else if(counter == 1)
Send_en <= 1;
else if(Tx_done)
Send_en <= 0;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Data <= 0;
else if(Tx_done)
Data <= Data + 1'b1;
endmodule
测试文件:
`timescale 1ns / 1ps
module uart_tx_test_tb();
reg Clk;
reg Reset_n;
wire uart_tx;
uart_tx_test uart_tx_test(
.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;
#50_000_000;
end
endmodule
仿真结果:
发现Send_en高电平仅占一个时钟周期,的确是在counter为1时就被拉高,根据代码,Tx_done为1时,Send_en就被拉低。而仿真结果显示,Tx_done一直显示为高电平。
在观察仿真结果前面部分,的确成功发送了一个Data。
再看看Tx_done为何后面不被拉低
bps_cnt等于1时才拉低,由于第一次发送完之后Tx_done=1,然后Send_en变为0,而由于此时Send_en不为1,就不会再计数,bps_cnt也不能变为1,Tx_done就不能变为0.
而由于bps_cnt常态为0,让bps_cnt为0时,不去操作uart_tx,而是去操作Tx_done。
即修改为:
再次观察仿真结果发现
Data发了0之后,直接发3了。
观察代码:
进一步观察细节:
Data很快的加了三次,Tx_done占据了三个机器周期。Tx_done的脉冲时间周期很重要,应该为一个时钟周期的高脉冲。
试着去看看Tx_done部分的语句
当bps_cnt为11的时候,Tx_done被拉高,看看仿真
11持续了三个周期,还是不够短
梳理一下逻辑:
当div_cnt=1时,产生bps_clk这个脉冲,然后让bps_cnt+1,进入了11,才会产生Tx_done为高电平的信号。又因为检测到了Tx_done信号,所以又将Send_en拉低。
bps_cnt要想清零,在下一拍才会生效(非阻塞赋值的缘故)
在下图所示的黄线这一时刻,检测到Send_en=0,bps_cnt清零,下一个时钟沿到来时,检测到bps_cnt=0, Tx_done才置零。
进一步尝试着修改程序(把控制Tx_done的语句单独拿出来写)
观察仿真结果:Tx_done仅维持一个时钟周期
以上过程的最终代码:
设计文件uart_byte_tx:
module uart_byte_tx(
Clk,
Reset_n,
Send_en,
Data,
Baud_set,
uart_tx,
Tx_done
);
input Clk;
input Reset_n;
input Send_en;
input [2:0]Baud_set;
input [7:0] Data;
output reg uart_tx;
output reg Tx_done;
wire bps_clk;
assign bps_clk = (div_cnt == 1);
//波特率选择
reg [17:0] bps_DR; //波特率最小为300时算得
always@(*)begin
case(Baud_set)
3'd0: bps_DR = 1000000000/9600/20;
3'd1: bps_DR = 1000000000/19200/20;
3'd2: bps_DR = 1000000000/38400/20;
3'd3: bps_DR = 1000000000/57600/20;
3'd4: bps_DR = 1000000000/115200/20;
default: bps_DR = 1000000000/9600/20;
endcase
end
//位宽时间计数
reg [17:0] div_cnt;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
div_cnt <= 0;
else if(Send_en)begin
if(div_cnt == bps_DR -1)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1;
end
else
div_cnt <= 0;
//数位数
reg [3:0] bps_cnt;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
bps_cnt <= 0;
else if(Send_en)begin
if(bps_clk)begin
if(bps_cnt == 11)
bps_cnt <= 0;
else
bps_cnt <= bps_cnt + 1;
end
end
else
bps_cnt <= 0;
always@(posedge Clk or negedge Reset_n)begin
if(!Reset_n)begin
uart_tx <= 1;
end
else begin
case(bps_cnt)
1:uart_tx <= 0;
2:uart_tx <= Data[0];
3:uart_tx <= Data[1];
4:uart_tx <= Data[2];
5:uart_tx <= Data[3];
6:uart_tx <= Data[4];
7:uart_tx <= Data[5];
8:uart_tx <= Data[6];
9:uart_tx <= Data[7];
10:uart_tx <= 1;
11:begin uart_tx <= 1; end
default: uart_tx <= 1;
endcase
end
end
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Tx_done <= 0;
else if((bps_clk == 1)&& (bps_cnt == 10))
Tx_done <= 1;
else
Tx_done <= 0;
endmodule
设计文件uart_tx_test:
module uart_tx_test(
Clk,
Reset_n,
uart_tx
);
input Clk;
input Reset_n;
output uart_tx;
reg Send_en;
reg [7:0] Data;
uart_byte_tx uart_byte_tx(
.Clk(Clk),
.Reset_n(Reset_n),
.Data(Data),
.Send_en(Send_en),
.Baud_set(3'd4), //波特率选择
.uart_tx(uart_tx),
.Tx_done(Tx_done)
);
// 10ms
reg [18:0] counter;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter <= 0;
else if(counter == 500_000-1)
counter <= 0;
else
counter <= counter + 1;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Send_en <= 0;
else if(counter == 1)
Send_en <= 1;
else if(Tx_done)
Send_en <= 0;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Data <= 0;
else if(Tx_done)
Data <= Data + 1'b1;
endmodule
测试文件uart_tx_test_tb:
`timescale 1ns / 1ps
module uart_tx_test_tb();
reg Clk;
reg Reset_n;
wire uart_tx;
uart_tx_test uart_tx_test(
.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;
#50_000_000;
end
endmodule
考虑一个数据采集模块,直接将Data_done作为Send_en信号
修改代码,把控制Send_en的代码放在byte_tx内部来完成。
而为了防止数据在发送过程中的变化,需要提前将待发送数据存起来。
仿真结果正确
板级验证
没毛病!
以上过程的最终代码:
设计文件uart_byte_tx:
module uart_byte_tx(
Clk,
Reset_n,
Send_Go,
Data,
Baud_set,
uart_tx,
Tx_done
);
input Clk;
input Reset_n;
input Send_Go;
input [2:0]Baud_set;
input [7:0] Data;
output reg uart_tx;
output reg Tx_done;
reg Send_en;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Send_en <= 0;
else if(Send_Go)
Send_en <= 1;
else if(Tx_done)
Send_en <= 0;
//用来存储数据
reg [7:0]r_Data;
always@(posedge Clk)
if(Send_Go)
r_Data <= Data;
else
r_Data <= r_Data;
wire bps_clk;
assign bps_clk = (div_cnt == 1);
//波特率选择
reg [17:0] bps_DR; //波特率最小为300时算得
always@(*)begin
case(Baud_set)
3'd0: bps_DR = 1000000000/9600/20;
3'd1: bps_DR = 1000000000/19200/20;
3'd2: bps_DR = 1000000000/38400/20;
3'd3: bps_DR = 1000000000/57600/20;
3'd4: bps_DR = 1000000000/115200/20;
default: bps_DR = 1000000000/9600/20;
endcase
end
//位宽时间计数
reg [17:0] div_cnt;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
div_cnt <= 0;
else if(Send_en)begin
if(div_cnt == bps_DR -1)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1;
end
else
div_cnt <= 0;
//数位数
reg [3:0] bps_cnt;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
bps_cnt <= 0;
else if(Send_en)begin
if(bps_clk)begin
if(bps_cnt == 11)
bps_cnt <= 0;
else
bps_cnt <= bps_cnt + 1;
end
end
else
bps_cnt <= 0;
always@(posedge Clk or negedge Reset_n)begin
if(!Reset_n)begin
uart_tx <= 1;
end
else begin
case(bps_cnt)
1:uart_tx <= 0;
2:uart_tx <= r_Data[0];
3:uart_tx <= r_Data[1];
4:uart_tx <= r_Data[2];
5:uart_tx <= r_Data[3];
6:uart_tx <= r_Data[4];
7:uart_tx <= r_Data[5];
8:uart_tx <= r_Data[6];
9:uart_tx <= r_Data[7];
10:uart_tx <= 1;
11:begin uart_tx <= 1; end
default: uart_tx <= 1;
endcase
end
end
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Tx_done <= 0;
else if((bps_clk == 1)&& (bps_cnt == 10))
Tx_done <= 1;
else
Tx_done <= 0;
endmodule
uart_tx_test
module uart_tx_test(
Clk,
Reset_n,
uart_tx
);
input Clk;
input Reset_n;
output uart_tx;
reg Send_Go;
reg [7:0] Data;
uart_byte_tx uart_byte_tx(
.Clk(Clk),
.Reset_n(Reset_n),
.Data(Data),
.Send_Go(Send_Go),
.Baud_set(3'd4), //波特率选择
.uart_tx(uart_tx),
.Tx_done(Tx_done)
);
// 10ms
reg [24:0] counter;//板级验证时,改成100ms发一个
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter <= 0;
else if(counter == 5_000_000-1)
counter <= 0;
else
counter <= counter + 1;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Send_Go <= 0;
else if(counter == 1)
Send_Go <= 1;
else
Send_Go <= 0;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Data <= 0;
else if(Tx_done)
Data <= Data + 1'b1;
endmodule
三、采用状态机实现多字节数据发送
其实也就是发送40位数据到电脑
拆分12位的数据为8位+4位
两种情况:
1.没有开始发送(上一次的发送已经完成,新的40位数据的发送请求没有出现)
2.来了发送40位数据的请求信号
3.依次发送数据的状态
状态机的概念
第一个状态:第一种情况的时候,咱干什么事情?等待传输请求(Trans_Go)的到来
Data[7:0]给到uart_byte_tx的Data,并同时产生Send_Go信号,启动第一个字节的发送
接着等待Tx_done信号的到来
1.40位的数据是否发完了?发完了,回到第一个状态继续等Trans_Go,没发完,启动下一个8位数据的发送。
设计文件uart_tx_data
module uart_tx_data(
Clk,
Reset_n,
Data40, //40位的数据
Trans_Go,
uart_tx,
Trans_Done
);
input Clk;
input Reset_n;
input [39:0]Data40;
input Trans_Go;
output uart_tx;
output reg Trans_Done;
reg [7:0]Data;
reg Send_Go;
wire Tx_done;
uart_byte_tx uart_byte_tx(
.Clk(Clk),
.Reset_n(Reset_n),
.Data(Data),
.Send_Go(Send_Go),
.Baud_set(4),
.uart_tx(uart_tx),
.Tx_done(Tx_done)
);
reg [2:0]state;//定义一个状态信号
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)begin
state <= 0;
Data <= 0;
Send_Go <= 0;
Trans_Done <= 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
Send_Go <= 0;
state <= 0;
Trans_Done <= 1;
end
else begin
Data <= Data;
Send_Go <= 0;
state <= 5;
end
end
endmodule
设计文件uart_byte_tx
module uart_byte_tx(
Clk,
Reset_n,
Send_Go,
Data,
Baud_set,
uart_tx,
Tx_done
);
input Clk;
input Reset_n;
input Send_Go;
input [2:0]Baud_set;
input [7:0] Data;
output reg uart_tx;
output reg Tx_done;
reg Send_en;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Send_en <= 0;
else if(Send_Go)
Send_en <= 1;
else if(Tx_done)
Send_en <= 0;
//用来存储数据
reg [7:0]r_Data;
always@(posedge Clk)
if(Send_Go)
r_Data <= Data;
else
r_Data <= r_Data;
wire bps_clk;
assign bps_clk = (div_cnt == 1);
//波特率选择
reg [17:0] bps_DR; //波特率最小为300时算得
always@(*)begin
case(Baud_set)
3'd0: bps_DR = 1000000000/9600/20;
3'd1: bps_DR = 1000000000/19200/20;
3'd2: bps_DR = 1000000000/38400/20;
3'd3: bps_DR = 1000000000/57600/20;
3'd4: bps_DR = 1000000000/115200/20;
default: bps_DR = 1000000000/9600/20;
endcase
end
//位宽时间计数
reg [17:0] div_cnt;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
div_cnt <= 0;
else if(Send_en)begin
if(div_cnt == bps_DR -1)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1;
end
else
div_cnt <= 0;
//数位数
reg [3:0] bps_cnt;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
bps_cnt <= 0;
else if(Send_en)begin
if(bps_clk)begin
if(bps_cnt == 11)
bps_cnt <= 0;
else
bps_cnt <= bps_cnt + 1;
end
end
else
bps_cnt <= 0;
always@(posedge Clk or negedge Reset_n)begin
if(!Reset_n)begin
uart_tx <= 1;
end
else begin
case(bps_cnt)
1:uart_tx <= 0;
2:uart_tx <= r_Data[0];
3:uart_tx <= r_Data[1];
4:uart_tx <= r_Data[2];
5:uart_tx <= r_Data[3];
6:uart_tx <= r_Data[4];
7:uart_tx <= r_Data[5];
8:uart_tx <= r_Data[6];
9:uart_tx <= r_Data[7];
10:uart_tx <= 1;
11:begin uart_tx <= 1; end
default: uart_tx <= 1;
endcase
end
end
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Tx_done <= 0;
else if((bps_clk == 1)&& (bps_cnt == 10))
Tx_done <= 1;
else
Tx_done <= 0;
endmodule
测试文件uart_tx_data_tb
`timescale 1ns / 1ps
module uart_tx_data_tb(
);
reg Clk;
reg Reset_n;
reg [39:0]Data40;
reg Trans_Go;
wire uart_tx;
uart_tx_data uart_tx_data(
Clk,//隐式例化不用打点
Reset_n,
Data40,
Trans_Go,
uart_tx,
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)
#200000;
Data40 = 40'ha987654321;
Trans_Go = 1;
#20;
Trans_Go = 0;
@(posedge Trans_Done)
#200000;
$stop;
end
仿真结果:
初步正确!
换一种代码风格:
module uart_tx_data2(
Clk,
Reset_n,
Data40, //40位的数据
Trans_Go,
uart_tx,
Trans_Done
);
input Clk;
input Reset_n;
input [39:0]Data40;
input Trans_Go;
output uart_tx;
output reg Trans_Done;
reg [7:0]Data;
reg Send_Go;
wire Tx_done;
uart_byte_tx uart_byte_tx(
.Clk(Clk),
.Reset_n(Reset_n),
.Data(Data),
.Send_Go(Send_Go),
.Baud_set(4),
.uart_tx(uart_tx),
.Tx_done(Tx_done)
);
reg [2:0]state;//定义一个状态信号
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)begin
state <= 0;
Data <= 0;
Send_Go <= 0;
Trans_Done <= 0;
end
else begin
case(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
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
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
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
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
5:
begin
if(Tx_done)begin
Send_Go <= 0;
state <= 0;
Trans_Done <= 1;
end
else begin
Data <= Data;
Send_Go <= 0;
state <= 5;
end
end
default:
begin
Data <= Data;
Send_Go <= 0;
state <= 0;
end
endcase
end
endmodule
四、任务:优化状态机,实现只要2个或3个状态实现发送的功能,并且易于修改为发送任意个字节的数据。
注:该题在骆神的指导下完成,特此致谢!
设计文件uart_tx_data3文章来源:https://www.toymoban.com/news/detail-408296.html
module uart_tx_data3(
Clk,
Reset_n,
Data40, //40位的数据
Trans_Go,
uart_tx,
Trans_Done
);
input Clk;
input Reset_n;
input [39:0]Data40;
input Trans_Go;
output uart_tx;
output reg Trans_Done;
reg [7:0]Data;
reg Send_Go;
wire Tx_done;
uart_byte_tx uart_byte_tx(
.Clk(Clk),
.Reset_n(Reset_n),
.Data(Data),
.Send_Go(Send_Go),
.Baud_set(4),
.uart_tx(uart_tx),
.Tx_done(Tx_done)
);
reg state;//定义一个状态信号
reg [2:0]counter_state;
reg [39:0]Data40_st;//存储数据
parameter Num_Data = 40/8; //待发送的字节数
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)begin
state <= 0;
Data <= 0;
Send_Go <= 0;
Trans_Done <= 0;
counter_state <= 0;
Data40_st <= 0;
end
else begin
case(state)
0:
begin
if(Trans_Go)begin
Trans_Done <= 0;
Data <= Data40[7:0];
Data40_st <= {Data40[7:0],Data40[39:8]};
Send_Go <= 1;
counter_state <= counter_state + 1;
state <= 1;
end
else begin
Data40_st <= 0;
Data <= Data;
Send_Go <= 0;
state <= 0;
counter_state <= 0;
Trans_Done <= 0;
end
end
1:
begin
if(Tx_done)begin
if(counter_state == Num_Data)begin
state <= 0;//发完了回状态0
counter_state <= 0;
Send_Go <= 0;
Data40_st <= 0;
Trans_Done <= 1;
Data <= 0;
end
else begin
Data40_st <= {Data40_st[7:0],Data40_st[39:8]};
Data <= Data40_st[7:0];
Send_Go <= 1;
counter_state <= counter_state + 1;
state <= 1;
end
end
else begin
Data <= Data;
Send_Go <= 0;
state <= 1;
Data40_st <= Data40_st;
counter_state <= counter_state;
end
end
default:
begin
Data <= Data;
Send_Go <= 0;
state <= 0;
counter_state <= 0;
end
endcase
end
endmodule
仿真结果:
显示正确!文章来源地址https://www.toymoban.com/news/detail-408296.html
到了这里,关于FPGA学习笔记(三)——串口通信之发送数据(调试过程)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!