草庐IT

用状态机实现串口多字节数据发送

FPGA与ZYNQ的学习笔记 2023-03-28 原文

这次设计一个多字节(8-256位)且波特率可更改(通过修改例化模块的参数)的串口发送模块。

1、状态机的设定

  • 状态机的设定有空闲、发送、和数据移位三个状态,其中空闲状态为等待多字节发送的信号;
  • 发送状态为给8位串口发送模块传输待发送的8位数据,同时判断是否发送完数据回到空闲状态;
  • 数据移位状态为等到前面8位字节数据发送完后,将接下来待发送的8位数据移动到数据寄存器的低8位中。若数据在发送中则会进行等待;

2、需要的模块

(1)8位串口发送模块

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Engineer: Lclone
// 
// Create Date: 2022/12/10 00:21:40
// Design Name: uart_byte_tx
// Module Name: uart_byte_tx
// Project Name: uart_byte_tx
// Description: 8位串口发送模块,波特率可通过参数设置。
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module uart_byte_tx
    #(
        parameter   TX_BAUD  = 9600,
        parameter   CLK_FQC  = 50_000_000,
        parameter   BAUD_CNT = CLK_FQC/TX_BAUD)
    (
        input       [7:0]   Data,
        input               Send_en,
        input               Clk,
        input               Rst_n,
        output  reg        Uart_Tx,
        output  reg        Tx_done
    );
    


    
    reg   [15:0]   baud_cnt;            
    reg   [ 3:0]   bit_cnt;
    reg            Send_en_r;
    reg            Send_en_rr;
    reg            Tx_flag;
    
    always @(posedge Clk) begin   
        Send_en_r <= Send_en;
        Send_en_rr <= Send_en_r;
    end
       
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            Tx_flag <= 0;
        else if(~Send_en_rr & Send_en_r)
            Tx_flag <= 1'b1;
        else if(bit_cnt == 10 - 1 & baud_cnt == BAUD_CNT - 1)
            Tx_flag <= 1'b0;            
    end   
        
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            baud_cnt <= 0;
        else if(baud_cnt == BAUD_CNT - 1)
            baud_cnt <= 0;
        else if(Tx_flag)
            baud_cnt <= baud_cnt + 1'b1;
    end
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)    
            bit_cnt <= 0;
        else if(bit_cnt == 10 - 1 & baud_cnt == BAUD_CNT - 1)
            bit_cnt <= 0;
        else if(baud_cnt == BAUD_CNT - 1)
            bit_cnt <= bit_cnt + 1'b1;
    end   
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            Uart_Tx <= 1'b1;
        else if(Tx_flag == 0)
            Uart_Tx <= 1'b1;
        else case(bit_cnt)
            0: Uart_Tx <= 1'b0;
            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 <= 1'b1;
            default: Uart_Tx <= 1'b1;
         endcase
     end
    
     always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            Tx_done <= 1'b0;
        else if(bit_cnt == 9 & baud_cnt == BAUD_CNT - 1)
            Tx_done <= 1'b1;
        else
            Tx_done <= 1'b0;            
     end
endmodule

3、设计的模块代码

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2022/12/12 19:14:03
// Design Name: uart_bytes_tx
// Module Name: uart_bytes_tx_3
// Project Name: uart_bytes_tx
// Target Devices: xc7z020clg400-1
// Tool Versions: 2018.3
// Description: 多字节(8-256位)且波特率可更改(通过修改例化模块的参数)的串口发送模块。
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module uart_bytes_tx_3
    #(   parameter              DATA_WIDTH = 40,               //待发送的数据位宽
         parameter              ROUNDS = DATA_WIDTH/8)         //数据发送的次数(例化时不需要设置)
    (
         input                   Clk,                          //时钟信号
         input                   Rst_n,                        //复位信号
         input [DATA_WIDTH-1:0]  Bytes_data,                   //多字节数据端口
         input                   Send_bytes_en,                //多字节发送使能
         output  reg            Tx_bytes_done,                 //多字节发送结束
         output  wire           Uart_Tx                        //串口发送端口
    );
    

    
    reg             [ 2:0]  state;                             //状态寄存器
    reg   [DATA_WIDTH-1:0]  bytes_data_reg;                    //多字节数据寄存器
    reg             [ 7:0]  data_reg;                          //待发送的8位数据寄存器
    reg                     send_en;                           //8位数据的发送使能
    wire                    tx_done;                           //8位数据的发送结束
    reg             [ 4:0]  rounds;                            //需要发送的次数
    
    uart_byte_tx                                               //串口8位发送模块
  # (   .TX_BAUD            (9600),                            //波特率
        .CLK_FQC            (50_000_000))                      //模块的时钟频率
    uart_byte_tx_inst
    (                          
        .Data               (data_reg),
        .Send_en            (send_en),
        .Clk                (Clk),
        .Rst_n              (Rst_n),
        .Uart_Tx            (Uart_Tx),
        .Tx_done            (tx_done)
        );
        
    always @(posedge Clk or negedge Rst_n) begin //数据寄存
        if(Rst_n == 0)
            bytes_data_reg <= 0;
        else if(Send_bytes_en)
            bytes_data_reg <= Bytes_data;
        else
            bytes_data_reg <= bytes_data_reg;
    end
    
    always @(posedge Clk or negedge Rst_n) begin   //状态机
        if(Rst_n == 0) begin
            state <= 0;
            data_reg <= 0;
            send_en <= 0;
            Tx_bytes_done <= 0;
        end
        
        else case(state)
            0: begin
                if(Send_bytes_en)
                
                    state <= 3'b1;
                
                else begin
                    state <= 0;
                    data_reg <= 0;
                    send_en <= 0;
                    Tx_bytes_done <= 0;
                end
            end
            
            
            1: begin
                if(rounds == ROUNDS) begin
                    Tx_bytes_done <= 1'b1;
                    state <= 0;                    
                end
                
                else begin
                    data_reg <= bytes_data_reg[7:0];
                    send_en <= 1'b1;
                    state <= 3'd2;
                end
                
            end
            
            2: begin
                
                send_en <= 0;
                
                if(tx_done) begin
                    bytes_data_reg <= bytes_data_reg >> 8;
                    state <= 3'd1;
                end
                else
                    state <= 3'd2;
            end
            
        endcase
    end
    
    always @(posedge Clk or negedge Rst_n) begin //发送次数计数
        if(Rst_n == 0)
            rounds <= 0;
        else if(rounds == ROUNDS & state == 1)
            rounds <= 0;
        else if(tx_done)
            rounds <= rounds + 1'b1;    
    end
endmodule

4、仿真验证

(1)仿真激励

`timescale 1ns / 1ps

module uart_bytes_tx_tb();

    reg Clk;
    reg Rst_n;
    reg Send_bytes_en;
    reg [63:0]Bytes_data;
    wire Tx_bytes_done;
    wire Uart_Tx;
    
    uart_bytes_tx_3   
  # (   .DATA_WIDTH             (64))
    uart_bytes_tx_inst
    (
        .Clk                    (Clk),
        .Rst_n                  (Rst_n),
        .Bytes_data             (Bytes_data),
        .Send_bytes_en          (Send_bytes_en),
        .Tx_bytes_done          (Tx_bytes_done),
        .Uart_Tx                (Uart_Tx)
    );
    defparam uart_bytes_tx_inst.uart_byte_tx_inst.BAUD_CNT = 10;
    
    initial Clk <= 1'b1;
    always #10 Clk <= ~Clk;
    
    initial begin
        Rst_n <= 0;
        Bytes_data <= 0;
        Send_bytes_en <= 0;
        #200
        Rst_n <= 1'b1;
        #20
        Bytes_data <= 64'h0123456789abcdef;
        Send_bytes_en <= 1'b1;
        #20
        Send_bytes_en <= 0;
        #20000
        Bytes_data <= 64'hfedcba9876543210;
        Send_bytes_en <= 1'b1;
        #20
        Send_bytes_en <= 0;
        @(posedge Tx_bytes_done)
        #100
        $stop;
    end

endmodule

(2)仿真波形

第一轮发送:

两轮发送:

有关用状态机实现串口多字节数据发送的更多相关文章

  1. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby-on-rails - 跳过状态机方法的所有验证 - 2

    当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested

  4. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  5. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  6. jquery - 我的 jquery AJAX POST 请求无需发送 Authenticity Token (Rails) - 2

    rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送

  7. ruby - 字符串文字中的转义状态作为 `String#tr` 的参数 - 2

    对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一

  8. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  9. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  10. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

随机推荐