FPGA学习笔记(三)——串口通信之发送数据(调试过程)

这篇具有很好参考价值的文章主要介绍了FPGA学习笔记(三)——串口通信之发送数据(调试过程)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本学习笔记主要参考小梅哥B站教学视频,网址如下:
https://www.bilibili.com/video/BV1va411c7Dz?p=1

使用的编译器为Vivado,HDL语言为verilog

一、串口通信之发送数据

原理

FPGA学习笔记(三)——串口通信之发送数据(调试过程)
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
FPGA学习笔记(三)——串口通信之发送数据(调试过程)

设计代码

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

仿真结果
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
发现Send_en拉高之前,uart_tx就置为0了,不符合常理。

转到第二个发送信号处,发现Send_en为1时,uart_tx仍为1,有延迟一个一位数据的时间后,才开始发送起始位:0。

FPGA学习笔记(三)——串口通信之发送数据(调试过程)
观察代码,bps_cnt = 0时,就让它发送起始位。
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
但是观察上面的仿真图,bps_cnt在此时仍为11,不是0.

所以bps_cnt也应该受Send_en的控制。

FPGA学习笔记(三)——串口通信之发送数据(调试过程)

将此处的代码修改为
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
再次仿真

FPGA学习笔记(三)——串口通信之发送数据(调试过程)
发现Send_en为1时,bps_cnt却一直为0;

再次修改代码(加个begin end)

FPGA学习笔记(三)——串口通信之发送数据(调试过程)
仿真
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
发现 bps_cnt的问题被解决了。再看看Send_en和uart_tx的关系

uart_tx在空闲的时候应该为高电平,但从仿真结果来看,并不是如此。在发送起始位之前,有一段的低电平。

FPGA学习笔记(三)——串口通信之发送数据(调试过程)

放大仿真图观察,在uart_tx被拉低时,bps_cnt为0.按照代码,uart_tx也被置为0.
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
修改代码
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
让bps_cnt从1到12,0在很多时候空闲状态下是这个值,用它会产生一定的麻烦。

仿真
FPGA学习笔记(三)——串口通信之发送数据(调试过程)

发现Send_en为0时,uart_tx为高电平。但同样又出现了,Send_en为1时,uart_tx滞后一个位的时间,才发送起始位的问题。因为bps_cnt为0时,才开始发送起始位,而此处bps_cnt仍为0.

FPGA学习笔记(三)——串口通信之发送数据(调试过程)
观察代码,计满才加一次,耽误了一位的时间。
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
计满需要很长时间,但是计到1不需要很长时间。(这里大体意思是说:原来代码的div_cnt需要计数一个完整的周期才能够将bps_cnt加一,而每一段计数过程都会经过1这个数,所以直接让它跑到值为1的时候就让bps_cnt加1,节省了很多时间,同样也是一段div_cnt的计数周期仅对应一个数值1,也就是一个bps_cnt+1的过程)改为
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
仿真结果
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
从结果来看,Send_en变为高电平,两个时钟周期后,Tx开始发起始位。

波特率验证:从仿真结果来看,发一位需要8.68us,符合115200的波特率。
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
另外:将程序修改为:bps_clk来控制的思路
FPGA学习笔记(三)——串口通信之发送数据(调试过程)

仿真发现,的确出现了bps_clk一样的脉冲。但是数数发现,12个bps_clk将bps_cnt分成了11段。但算上起始位和终止位,理应10段才行。
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
再次修改代码
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
仿真结果
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
这下终于正常了!!

完整正确代码:

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的波特率发送一个数据,每次发送的数据比以前一个数据大一(计数器)

思路:顶层模块调用
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
设计文件:

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

仿真结果:
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
发现Send_en高电平仅占一个时钟周期,的确是在counter为1时就被拉高,根据代码,Tx_done为1时,Send_en就被拉低。而仿真结果显示,Tx_done一直显示为高电平。

FPGA学习笔记(三)——串口通信之发送数据(调试过程)
在观察仿真结果前面部分,的确成功发送了一个Data。
FPGA学习笔记(三)——串口通信之发送数据(调试过程)

再看看Tx_done为何后面不被拉低
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
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。

即修改为:
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
再次观察仿真结果发现
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
Data发了0之后,直接发3了。

观察代码:

FPGA学习笔记(三)——串口通信之发送数据(调试过程)
进一步观察细节:

FPGA学习笔记(三)——串口通信之发送数据(调试过程)

Data很快的加了三次,Tx_done占据了三个机器周期。Tx_done的脉冲时间周期很重要,应该为一个时钟周期的高脉冲。

试着去看看Tx_done部分的语句
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
当bps_cnt为11的时候,Tx_done被拉高,看看仿真

FPGA学习笔记(三)——串口通信之发送数据(调试过程)
11持续了三个周期,还是不够短

梳理一下逻辑:

当div_cnt=1时,产生bps_clk这个脉冲,然后让bps_cnt+1,进入了11,才会产生Tx_done为高电平的信号。又因为检测到了Tx_done信号,所以又将Send_en拉低。
bps_cnt要想清零,在下一拍才会生效(非阻塞赋值的缘故)

FPGA学习笔记(三)——串口通信之发送数据(调试过程)
在下图所示的黄线这一时刻,检测到Send_en=0,bps_cnt清零,下一个时钟沿到来时,检测到bps_cnt=0, Tx_done才置零。

FPGA学习笔记(三)——串口通信之发送数据(调试过程)
进一步尝试着修改程序(把控制Tx_done的语句单独拿出来写)
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
观察仿真结果:Tx_done仅维持一个时钟周期
FPGA学习笔记(三)——串口通信之发送数据(调试过程)

以上过程的最终代码:

设计文件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信号

FPGA学习笔记(三)——串口通信之发送数据(调试过程)
修改代码,把控制Send_en的代码放在byte_tx内部来完成。
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
而为了防止数据在发送过程中的变化,需要提前将待发送数据存起来。
FPGA学习笔记(三)——串口通信之发送数据(调试过程)

FPGA学习笔记(三)——串口通信之发送数据(调试过程)

仿真结果正确
FPGA学习笔记(三)——串口通信之发送数据(调试过程)

板级验证
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
没毛病!

以上过程的最终代码:

设计文件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位数据到电脑

FPGA学习笔记(三)——串口通信之发送数据(调试过程)
拆分12位的数据为8位+4位
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
两种情况:
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
    
    
    

仿真结果:
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
初步正确!

换一种代码风格:

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

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

仿真结果:
FPGA学习笔记(三)——串口通信之发送数据(调试过程)
显示正确!文章来源地址https://www.toymoban.com/news/detail-408296.html

到了这里,关于FPGA学习笔记(三)——串口通信之发送数据(调试过程)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 串口通信-发送和接收数据

    目录 通信方式 硬件电路 软件部分 USART外设(同步/异步收发器) 波特率 引脚复用 初始化基本流程 发送 接收 语言简述 常用的函数 使用的结构体 引脚模式 打印数据的三种方法 使用printf 在实际的串口中,只能发送二进制数,也就是十六进制的最直接数据。如果想发送字符,

    2024年02月12日
    浏览(33)
  • 【FPGA零基础学习之旅#14】串口发送字符串

    🎉欢迎来到FPGA专栏~串口发送字符串 ☆* o(≧▽≦)o *☆ 嗨 ~我是 小夏与酒 🍹 ✨ 博客主页: 小夏与酒的博客 🎈该系列 文章专栏: FPGA学习之旅 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏 📜 欢迎大家关注! ❤️ 🥝 发送Hello: 🥝 发送数字字符并自

    2024年02月08日
    浏览(30)
  • 【FPGA零基础学习之旅#13】串口发送模块设计与验证

    🎉欢迎来到FPGA专栏~串口发送模块 ☆* o(≧▽≦)o *☆ 嗨 ~我是 小夏与酒 🍹 ✨ 博客主页: 小夏与酒的博客 🎈该系列 文章专栏: FPGA学习之旅 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏 📜 欢迎大家关注! ❤️ 🥝 发送测试: 🥝 issp调试测试: 数据

    2024年02月09日
    浏览(45)
  • SpringBoot+RXTXcomm实现Java串口通信 读取串口数据以及发送数据

    记录一下使用SpringBoot+RXTXcomm实现Java串口通信,使用Java语言开发串口,对串口进行读写操作。 案例源码:SpringBoot+RXTXcomm实现Java串口通信 读取串口数据以及发送数据 RXTXcomm.jar这个包支持的系统较多,但是更新太慢,在win系统下使用没有问题,但是在centos的工控机系统里使用

    2024年02月04日
    浏览(30)
  • SpringBoot+jSerialComm实现Java串口通信 读取串口数据以及发送数据

    记录一下使用SpringBoot+jSerialComm实现Java串口通信,使用Java语言开发串口,对串口进行读写操作,在win和linux系统都是可以的,有一点好处是不需要导入额外的文件。 案例demo源码:SpringBoot+jSerialComm实现Java串口通信 读取串口数据以及发送数据 之前使用RXTXcomm实现Java串口通信,这

    2024年02月05日
    浏览(32)
  • Qt+C++串口调试接收发送数据曲线图

    程序示例精选 Qt+C++串口调试接收发送数据曲线图 如需安装运行环境或远程调试,见文章底部个人 QQ 名片,由专业技术人员远程协助! 这篇博客针对Qt+C++串口调试接收发送数据曲线图编写代码,代码整洁,规则,易读。 学习与应用推荐首选。 一、所需工具软件 二、使用步骤

    2024年02月11日
    浏览(31)
  • Unity串口通信、接受和发送数据、C#

    串行接口(串口)通常指COM接口,是采用串行通信方式的扩展接口。串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。特别适用于远距离通信。 查看串口:右键 我的电脑-管理-设备管理器

    2023年04月13日
    浏览(33)
  • FPGA入门学习笔记(十)Vivado设计状态机实现UART多字节数据发送

    使用串口发送5个字节数据到电脑 1、ADC采样的结果为12位,如何使用串口发送 2、16位数据,如何通过串口发送 3、多个字节的数据,如何通过串口发送 UART规定,发送的数据位只能有6、7、8位,若直接修改发送位数,接收模块将不适配。 两种情况: 1、没有开始发送(上一次的

    2024年02月12日
    浏览(34)
  • ZYNQ 7020 之 FPGA知识点重塑笔记一——串口通信

    目录 一:串口通信简介 二:三种常见的数据通信方式—RS232串口通信 2.1 实验任务 2.2 串口接收模块的设计 2.2.1 代码设计 2.3 串口发送模块的设计 2.3.1 代码设计 2.4 顶层模块编写 2.4.1 代码设计 2.4.2 仿真验证代码 2.4.3 仿真结果 2.4.4 板上验证        通信方式一般分为 串行

    2024年02月03日
    浏览(36)
  • 蓝牙串口调试助手通过PC蓝牙发送数据给ESP32同时在串口上显示

    OK,好久没有更新Blog啦 今天把之前积累的代码放上,给需要学习的程序猿们使用 我还是不太喜欢写文字,倒是比较喜欢客套,哈哈 硬件图: ESP32和USB-micro-B数据线一根 蓝牙串口调试助手通过PC蓝牙发送数据给ESP32同时在串口上显示 具体代码如下:

    2024年02月09日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包