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)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.仿真结果分析

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

  • 19
    点赞
  • 117
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
FPGA(现场可编程门阵列)和STM32(一种基于ARM架构的微控制器)可以通过SPI(串行外设接口)协议进行通信。在这种通信方式中,FPGA作为SPI通信的从机,而STM32作为主机。 为了实现这种通信,我们需要使用Verilog语言编写从机FPGA的代码。首先,我们需要确定FPGA的SPI接口的基本参数,如数据位宽、时钟频率和时钟极性等。然后,我们可以使用Verilog语言编写从机的SPI控制器,将其连接到FPGA的其他逻辑电路中。 在Verilog代码中,我们需要实现SPI的Slave模式。在SPI通信中,从机始终被动地响应主机的指令,并将数据传送给主机。从机的Verilog代码需要包括两个关键部分:状态机和数据传输。 状态机是从机的控制核心,它根据主机的指令进行状态切换,并管理数据传输过程的流程控制。例如,当主机发起读取指令时,从机会进入接收状态,并将要传输的数据存储到缓冲区中。当主机发起写入指令时,从机会进入发送状态,并将数据从缓冲区传输给主机。 数据传输部分负责实际的数据传输。从机需要实现接收和发送两个功能。接收部分负责接收主机发送数据,并将其存储到缓冲区中。发送部分负责从缓冲区中读取数据,并将其传输给主机。 在编写Verilog代码时,需要注意时序问题和信号同步。SPI通信需要精确的时钟同步,在从机和主机之间共享和交换数据需要遵循一定的时序要求。因此,在设计代码时要特别注意时钟同步和数据的正确传输顺序。 最后,我们需要将Verilog代码综合到FPGA芯片中,并进行功能验证和调试。在验证过程中,我们可以通过观察FPGA输出波形和和STM32的通信结果来判断通信是否成功。如果通信出现问题,我们可以通过调试代码和时序分析来进行故障排查和修复。 通过以上步骤,我们可以实现FPGA作为从机与STM32进行SPI协议通信,并使用Verilog语言完成代码的设计与实现。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

上园村蜻蜓队长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值