1.需求分析
关于uart协议实现这部分大家可以参考我上一篇的博客。《FPGA自学笔记--串口通信实现(vivado&verilog版)》。在上一篇博客中,主要实现了将单字节的数据,我们其实就是用上一篇博客的模块来实现多字节数据的发送。
在真实的数据传输过程中,我们不只是发送 6 7 8 位数据,也有可能发送12 16 20位的数据,所以我们需要调用多次串口发送模块来发送多字节数据。通过状态机实现,假设我们这次发送一个40位,5字节的数据,所以朴素的来讲,我们可以有6个状态,0状态是整个模块等待状态,1,2,3,4,5状态分别为5个字节数据的发送状态。
2.总体模块和状态转移图。
上一篇博客中,单字节的串口发送模块为uart_tx。实现的具体功能为,当send_go为高电平时,将data里的并行数据以串行数据发出,发送完成时 tx_done 产生一个单脉冲。我们需要在上层模块中调用这个模块。上层模块的框图如昨图。需要调用的uart_tx模块如右图所示。
所以,需要trans_go启动状态机,状态机内部产身send_go信号,传输一字节数据。发送完成后,uart_tx模块会产生tx_done信号,tx_done信号用来激活状态机的下一个状态,开始发送下一个数据,再次产生一个send_go脉冲,送入data数据,依次送完5字节数据,当最后一个字节发送完成成,最后一个状态5在tx_done下返回第一个状态,并产生五字节发送完成信号,trans_down.回到第一个状态后,等待发送下一个四十位,五字节数据的trans_go。
状态转移图如下。
当然,这个状态转移图也可以简化,由于我目前也是小白状态,只能写出这种比较好理解,简单的写法,大家以后也可以尝试比较高级的写法。即只用两个状态机实现,或者讲后面五个状态总结为一个大状态,
3.设计文件和testbench文件
在top文件中例化uart_byte_tx模块,这部分具体代码请参考上一个博客。代码参考了B站小梅哥的视频。新手强烈推荐。
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2022/04/30 20:20:59
// Design Name:
// Module Name: uart_tx_5byte
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
// 使用串口发送5个字节(40 bit)的数据到电脑
module uart_tx_5byte(
clk,
reset,
data40,
trans_go,
uart_tx,
trans_done
);
input clk;
input reset;
input trans_go;
input [39:0]data40;
output uart_tx;
output reg trans_done;
reg [7:0]data;
reg send_go;
//reg tx_done;
uart_byte_tx uart_byte_tx(
.clk(clk),
.reset(reset),
.send_go(send_go),
.data(data),
.baud_set(4),
.uart_tx(uart_tx),
.tx_done(tx_done)
);
reg [2:0]state;
always@(posedge clk or negedge reset)
if(!reset)begin
state <= 0;
send_go <= 0;
data <= 0;
trans_done <= 0;
end
else if(state == 0)begin
trans_done <= 0;
//if(tx_done)begin // 当发完的时候 发新的
if(trans_go)begin // 由trans_go 点燃第一个状态 启动状态 用tx_done 是无法启动状态机的 tx_done 是 0状态结束标志
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
对应的testbench文件
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2022/04/30 21:51:02
// Design Name:
// Module Name: uart_tx_5byte_tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_tx_5byte_tb(
);
reg clk;
reg reset;
reg [39:0]data40;
reg trans_go;
wire trans_done;
wire uart_tx;
uart_tx_5byte uart_tx_5byte(
.clk(clk),
.reset(reset),
.data40(data40),
.trans_go(trans_go),
.uart_tx(uart_tx),
.trans_done(trans_done)
);
initial clk = 1;
always #10 clk = ~clk;
initial begin
reset = 0;
data40 = 0;
trans_go = 0;
# 201;
# 200;
reset = 1;
data40 = 40'h123456789a;
trans_go = 1;
# 20
trans_go = 0;
@(posedge trans_done);
# 200000;
data40 = 40'habc1234655;
trans_go = 1;
# 20
trans_go = 0;
@(posedge trans_done);
# 200000;
$stop;
end
endmodule
4.仿真结果分析
文章来源:https://www.toymoban.com/news/detail-416002.html
显然,如图所示,一定要注意,多字节发送,每个字节还是存在起始位和终止位的,所以应该还是10位10位的发,看时序图的时候一定不要看错了,去除箭头所示的标志位后,对比数据,并行40位输入和串行输出,结果一致,从低位到高位,完全正确。有兴趣的同学可以直接下载我的vivado工程。文章来源地址https://www.toymoban.com/news/detail-416002.html
到了这里,关于FPGA自学笔记--串口通信发送多字节数据(verilog版)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!