> 文章列表 > FPGA自学笔记--串口通信发送多字节数据(verilog版)

FPGA自学笔记--串口通信发送多字节数据(verilog版)

FPGA自学笔记--串口通信发送多字节数据(verilog版)

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)beginstate <= 0;send_go <= 0;data <= 0;trans_done <= 0;endelse if(state == 0)begintrans_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;endelse begindata <= data;send_go <= 0;state   <= 0;end        endelse if(state == 1)beginif(tx_done)begindata <= data40[15:8];send_go <= 1;state <= 2;endelse begindata <= data;send_go <= 0;state   <= 1;end        endelse if(state == 2)beginif(tx_done)begindata <= data40[23:16];send_go <= 1;state <= 3;endelse begindata <= data;send_go <= 0;state   <= 2;end        endelse if(state == 3)beginif(tx_done)begindata <= data40[31:24];send_go <= 1;state <= 4;endelse begindata <= data;send_go <= 0;state   <= 3;end        endelse if(state == 4)beginif(tx_done)begindata <= data40[39:32];send_go <= 1;state <= 5;endelse begindata <= data;send_go <= 0;state   <= 4;end        endelse if(state == 5)beginif(tx_done)begin               // 当发完的时候 回到初始状态send_go <= 0;state <= 0;trans_done <= 1;endelse 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 beginreset = 0;data40 = 0;trans_go = 0;# 201;# 200;reset = 1;data40 = 40'h123456789a;trans_go = 1;# 20trans_go = 0;@(posedge trans_done);# 200000;data40 = 40'habc1234655;trans_go = 1;# 20trans_go = 0;@(posedge trans_done);# 200000;$stop;end 
endmodule

 4.仿真结果分析

显然,如图所示,一定要注意,多字节发送,每个字节还是存在起始位和终止位的,所以应该还是10位10位的发,看时序图的时候一定不要看错了,去除箭头所示的标志位后,对比数据,并行40位输入和串行输出,结果一致,从低位到高位,完全正确。有兴趣的同学可以直接下载我的vivado工程。