FPGA学习笔记(四)——串口通信之接收数据(调试过程)

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

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

一、串口接收原理与思路

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
二、串口接收程序设计与调试

设计文件uart_byte_rx:

module uart_byte_rx(
    Clk,
    Reset_n,
    Baud_Set,
    uart_rx,
    Data,
    Rx_Done

    );
    input Clk;
    input Reset_n;
    input [2:0]Baud_Set;
    input uart_rx;
    output reg[7:0]Data;
    output reg Rx_Done;
  
    
    //边沿检测
    //存储数据
    reg [1:0]uart_rx_r; //定义两个D触发器,判断前后的值
    always@(posedge Clk)begin
        uart_rx_r[0] <= uart_rx;
        uart_rx_r[1] <= uart_rx_r[0]; //[1]是前一刻的值
    end
    
    wire pedge_uart_rx;//上升沿检测
    //assign pedge_uart_rx = (uart_rx_r[1] == 0) && (uart_rx_r[0] == 1);
    assign pedge_uart_rx = (uart_rx_r == 2'b01);
    
    wire nedge_uart_rx;//下降沿检测
    assign nedge_uart_rx = (uart_rx_r == 2'b10);
    
    //波特率设置
    //1位数据分成了16小段,9600的波特率:1_000_000_000/9600/16/20=325 
    reg[8:0] bps_DR;
    always@(*)
        case(Baud_Set)
            0:bps_DR = 1_000_000_000/9600/16/20 -1;
            1:bps_DR = 1_000_000_000/19200/16/20 -1;
            2:bps_DR = 1_000_000_000/38400/16/20 -1;
            3:bps_DR = 1_000_000_000/57600/16/20 -1;
            4:bps_DR = 1_000_000_000/115200/16/20 -1;
            default:bps_DR = 1_000_000_000/9600/16/20 -1;
        endcase
/
    //产生bps_CLK
    wire bps_clk_16x;
    assign bps_clk_16x = (div_cnt == bps_DR / 2);  //16段的每一小段也要在中心处采样
    
    reg RX_EN;
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        RX_EN <= 0;
    else if(nedge_uart_rx)
        RX_EN <= 1;
    else if(Rx_Done || (sta_bit >= 4))  //sta_bit >= 4是错误的起始位,是一个干扰
        RX_EN <= 0;
        
    
    reg [8:0] div_cnt; //分频计数器
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        div_cnt <= 0;
    else if(RX_EN)begin //nedge_uart_rx仅一个脉冲,不足以使得div_cnt一直加,所以这里用前面定义的RX_EN。
        if(div_cnt == bps_DR)
            div_cnt <= 0;
        else
            div_cnt <= div_cnt + 1'b1;
    end
        

    //采样数据
    reg[7:0] bps_cnt;          //16*10=160个bps_clk,10是10位(包括起始和终止)
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        bps_cnt <= 0;
    else if(RX_EN)begin
        if(bps_clk_16x)begin
            if(bps_cnt == 160 - 1)
                bps_cnt <= 0;
            else
                bps_cnt <= bps_cnt + 1'b1; 
        end
        else
            bps_cnt <= bps_cnt;
    end
    else
        bps_cnt <= 0;
        
    
    //接收部分
    reg [2:0] r_data[7:0]; //累加每一位处获得的值,8位;(二维数组)    reg width name number
    reg [2:0] sta_bit; //单独定义起始位
    reg [2:0] sto_bit; //停止位
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)begin
        sta_bit <= 0;
        sto_bit <= 0;
        r_data[0] <= 0; //只能一个个的赋值
        r_data[1] <= 0;
        r_data[2] <= 0;
        r_data[3] <= 0;
        r_data[4] <= 0;
        r_data[5] <= 0;
        r_data[6] <= 0;
        r_data[7] <= 0;
    end
    else if(bps_clk_16x)begin
        case(bps_cnt)
            0:begin   //等于零的时刻 把这些清零,防止出错
                sta_bit <= 0;
                sto_bit <= 0;
                r_data[0] <= 0; 
                r_data[1] <= 0;
                r_data[2] <= 0;
                r_data[3] <= 0;
                r_data[4] <= 0;
                r_data[5] <= 0;
                r_data[6] <= 0;
                r_data[7] <= 0;
            end
            5,6,7,8,9,10,11:sta_bit <= sta_bit + uart_rx;  //7次采样,16次中,主要是中间的几次有效和稳定
            21,22,23,24,25,26,27:r_data[0] <= r_data[0] + uart_rx; 
            37,38,39,40,41,42,43:r_data[1] <= r_data[1] + uart_rx; 
            53,54,55,56,57,58,59:r_data[2] <= r_data[2] + uart_rx; 
            69,70,71,72,73,74,75:r_data[3] <= r_data[3] + uart_rx; 
            85,86,87,88,89,90,91:r_data[4] <= r_data[4] + uart_rx; 
            101,102,103,104,105,106,107:r_data[5] <= r_data[5] + uart_rx; 
            117,118,119,120,121,122,123:r_data[6] <= r_data[6] + uart_rx; 
            133,134,135,136,137,138,139:r_data[7] <= r_data[7] + uart_rx; 
            149,150,151,152,153,154,155:sto_bit <= sto_bit + uart_rx; 
            default:; //什么都不做
        endcase
    end
      
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        Data <= 0;
    else if(bps_clk_16x && (bps_cnt == 159))begin  //bps_cnt为159的时候就可以输出结果了
        Data[0] <= (r_data[0] >= 4)?1'b1:1'b0;
        Data[1] <= (r_data[1] >= 4)?1'b1:1'b0;  
        Data[2] <= (r_data[2] >= 4)?1'b1:1'b0;  
        Data[3] <= (r_data[3] >= 4)?1'b1:1'b0;  
        Data[4] <= (r_data[4] >= 4)?1'b1:1'b0;  
        Data[5] <= (r_data[5] >= 4)?1'b1:1'b0;  
        Data[6] <= (r_data[6] >= 4)?1'b1:1'b0;  
        Data[7] <= (r_data[7] >= 4)?1'b1:1'b0;     
    end
                
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        Rx_Done <= 0;
    else if(bps_clk_16x && (bps_cnt == 159))
        Rx_Done <= 1;
    else
        Rx_Done <= 0;
        

endmodule

测试文件uart_byte_rx_tb:

`timescale 1ns / 1ps

module uart_byte_rx_tb();

    reg Clk;
    reg Reset_n;
    wire [2:0]Baud_Set;
    reg uart_rx;
    wire [7:0]Data;
    wire Rx_Done;
    
    assign Baud_Set = 4;
    
    uart_byte_rx uart_byte_rx(
    Clk,
    Reset_n,
    Baud_Set,
    uart_rx,
    Data,
    Rx_Done
    );
    
    initial Clk = 1;
    always#10 Clk = ~Clk;
    
    initial begin
        Reset_n = 0;
        uart_rx = 1;
        #201;
        Reset_n = 1;
        #200;
        uart_tx_byte(8'h5a); //调用task
        @(posedge Rx_Done);
        #5000;
        uart_tx_byte(8'ha5); 
        @(posedge Rx_Done);
        #5000;
        uart_tx_byte(8'h86); 
        @(posedge Rx_Done);
        #5000;
        $stop;
    end
    
    task uart_tx_byte; //创建任务
        input [7:0]tx_data;
        begin
            uart_rx = 1;
            #20;
            uart_rx = 0;
            #8680; //一位的发送时间
            uart_rx = tx_data[0];
            #8680; 
            uart_rx = tx_data[1];
            #8680; 
            uart_rx = tx_data[2];
            #8680; 
            uart_rx = tx_data[3];
            #8680; 
            uart_rx = tx_data[4];
            #8680; 
            uart_rx = tx_data[5];
            #8680; 
            uart_rx = tx_data[6];
            #8680; 
            uart_rx = tx_data[7];
            #8680; 
            uart_rx = 1;
            #8680; 
            
        end  
    endtask
           
endmodule

仿真结果:
在这里插入图片描述
发现仅仅接受了一位,也就是5a,而没有进行到下一步。进一步观察:
在这里插入图片描述
可知uart_rx最后一位开始到Rx_Done之间的时间为8.059us,而测试文件最后一位要延迟8680ns之后,才结束一个task,这个时候才会执行@(posedge Rx_Done),而这时早已过了Rx_Done的脉冲。

在这里插入图片描述
回到代码,寻找有关Rx_Done的原因

在这里插入图片描述
其中bps_clk_16x的部分
在这里插入图片描述
是说在每一小段的中间就已经产生了Rx_Done,早了一点。
在这里插入图片描述
修改一下,当div_cnt计满时,才拉高Rx_Done.
在这里插入图片描述

再次仿真,观察仿真结果,发现Rx_Done更加提前了,仍然是不对的
在这里插入图片描述
加入子模块的信号深入探究:

初步看,没啥毛病
在这里插入图片描述
细看最后一位,只有15位
在这里插入图片描述

观察前面的位数,是16个
在这里插入图片描述
应该计数到160,修改原程序试试。

在这里插入图片描述

观察仿真结果,发现还是不够长,而且160的宽度要比其他的窄。bps_cnt为159时,div_cnt从14到下一个13;而bps_cnt为160时,div_cnt从14到下一个1.

在这里插入图片描述
下面看看div_cnt的结束条件:
在这里插入图片描述
再次修改
在这里插入图片描述

在这里插入图片描述
重新仿真
在这里插入图片描述

发现时长还是有一些不够

verilog中只要整数,有可能实际不是测试文件中写的8680us了。

在这里插入图片描述
看仿真结果
在这里插入图片描述
一位是540ns,540*16=8640ns<8680ns,导致Rx_Done提前到来。

因此,@(posedge Rx_Done);的方法可能不行了,直接采用延时。

将tb中部分代码修改为:
在这里插入图片描述

观察仿真结果:
在这里插入图片描述
从Data来看,初步正确

然后Rx_Done也在Data之后

在这里插入图片描述

完整修改后的代码:

uart_byte_rx:

module uart_byte_rx(
    Clk,
    Reset_n,
    Baud_Set,
    uart_rx,
    Data,
    Rx_Done

    );
    input Clk;
    input Reset_n;
    input [2:0]Baud_Set;
    input uart_rx;
    output reg[7:0]Data;
    output reg Rx_Done;
  
    
    //边沿检测
    //存储数据
    reg [1:0]uart_rx_r; //定义两个D触发器,判断前后的值
    always@(posedge Clk)begin
        uart_rx_r[0] <= uart_rx;
        uart_rx_r[1] <= uart_rx_r[0]; //[1]是前一刻的值
    end
    
    wire pedge_uart_rx;//上升沿检测
    //assign pedge_uart_rx = (uart_rx_r[1] == 0) && (uart_rx_r[0] == 1);
    assign pedge_uart_rx = (uart_rx_r == 2'b01);
    
    wire nedge_uart_rx;//下降沿检测
    assign nedge_uart_rx = (uart_rx_r == 2'b10);
    
    //波特率设置
    //1位数据分成了16小段,9600的波特率:1_000_000_000/9600/16/20=325 
    reg[8:0] bps_DR;
    always@(*)
        case(Baud_Set)
            0:bps_DR = 1_000_000_000/9600/16/20 -1;
            1:bps_DR = 1_000_000_000/19200/16/20 -1;
            2:bps_DR = 1_000_000_000/38400/16/20 -1;
            3:bps_DR = 1_000_000_000/57600/16/20 -1;
            4:bps_DR = 1_000_000_000/115200/16/20 -1;
            default:bps_DR = 1_000_000_000/9600/16/20 -1;
        endcase
/
    //产生bps_CLK
    wire bps_clk_16x;
    assign bps_clk_16x = (div_cnt == bps_DR / 2);  //16段的每一小段也要在中心处采样
    
    reg RX_EN;
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        RX_EN <= 0;
    else if(nedge_uart_rx)
        RX_EN <= 1;
    else if(Rx_Done || (sta_bit >= 4))  //sta_bit >= 4是错误的起始位,是一个干扰
        RX_EN <= 0;
        
    
    reg [8:0] div_cnt; //分频计数器
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        div_cnt <= 0;
    else if(RX_EN)begin //nedge_uart_rx仅一个脉冲,不足以使得div_cnt一直加,所以这里用前面定义的RX_EN。
        if(div_cnt == bps_DR)
            div_cnt <= 0;
        else
            div_cnt <= div_cnt + 1'b1;
    end
    else
        div_cnt <= 0;
        

    //采样数据
    reg[7:0] bps_cnt;          //16*10=160个bps_clk,10是10位(包括起始和终止)
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        bps_cnt <= 0;
    else if(RX_EN)begin
        if(bps_clk_16x)begin
            if(bps_cnt == 160)
                bps_cnt <= 0;
            else
                bps_cnt <= bps_cnt + 1'b1; 
        end
        else
            bps_cnt <= bps_cnt;
    end
    else
        bps_cnt <= 0;
        
    
    //接收部分
    reg [2:0] r_data[7:0]; //累加每一位处获得的值,8位;(二维数组)    reg width name number
    reg [2:0] sta_bit; //单独定义起始位
    reg [2:0] sto_bit; //停止位
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)begin
        sta_bit <= 0;
        sto_bit <= 0;
        r_data[0] <= 0; //只能一个个的赋值
        r_data[1] <= 0;
        r_data[2] <= 0;
        r_data[3] <= 0;
        r_data[4] <= 0;
        r_data[5] <= 0;
        r_data[6] <= 0;
        r_data[7] <= 0;
    end
    else if(bps_clk_16x)begin
        case(bps_cnt)
            0:begin   //等于零的时刻 把这些清零,防止出错
                sta_bit <= 0;
                sto_bit <= 0;
                r_data[0] <= 0; 
                r_data[1] <= 0;
                r_data[2] <= 0;
                r_data[3] <= 0;
                r_data[4] <= 0;
                r_data[5] <= 0;
                r_data[6] <= 0;
                r_data[7] <= 0;
            end
            5,6,7,8,9,10,11:sta_bit <= sta_bit + uart_rx;  //7次采样,16次中,主要是中间的几次有效和稳定
            21,22,23,24,25,26,27:r_data[0] <= r_data[0] + uart_rx; 
            37,38,39,40,41,42,43:r_data[1] <= r_data[1] + uart_rx; 
            53,54,55,56,57,58,59:r_data[2] <= r_data[2] + uart_rx; 
            69,70,71,72,73,74,75:r_data[3] <= r_data[3] + uart_rx; 
            85,86,87,88,89,90,91:r_data[4] <= r_data[4] + uart_rx; 
            101,102,103,104,105,106,107:r_data[5] <= r_data[5] + uart_rx; 
            117,118,119,120,121,122,123:r_data[6] <= r_data[6] + uart_rx; 
            133,134,135,136,137,138,139:r_data[7] <= r_data[7] + uart_rx; 
            149,150,151,152,153,154,155:sto_bit <= sto_bit + uart_rx; 
            default:; //什么都不做
        endcase
    end
      
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        Data <= 0;
    else if(bps_clk_16x && (bps_cnt == 159))begin  //bps_cnt为159的时候就可以输出结果了
        Data[0] <= (r_data[0] >= 4)?1'b1:1'b0;
        Data[1] <= (r_data[1] >= 4)?1'b1:1'b0;  
        Data[2] <= (r_data[2] >= 4)?1'b1:1'b0;  
        Data[3] <= (r_data[3] >= 4)?1'b1:1'b0;  
        Data[4] <= (r_data[4] >= 4)?1'b1:1'b0;  
        Data[5] <= (r_data[5] >= 4)?1'b1:1'b0;  
        Data[6] <= (r_data[6] >= 4)?1'b1:1'b0;  
        Data[7] <= (r_data[7] >= 4)?1'b1:1'b0;     
    end
                
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        Rx_Done <= 0;
    else if((div_cnt == bps_DR/2) && (bps_cnt == 160))
        Rx_Done <= 1;
    else
        Rx_Done <= 0;
        

endmodule

uart_byte_rx_tb:

`timescale 1ns / 1ps

module uart_byte_rx_tb();

    reg Clk;
    reg Reset_n;
    wire [2:0]Baud_Set;
    reg uart_rx;
    wire [7:0]Data;
    wire Rx_Done;
    
    assign Baud_Set = 4;
    
    uart_byte_rx uart_byte_rx(
    Clk,
    Reset_n,
    Baud_Set,
    uart_rx,
    Data,
    Rx_Done
    );
    
    initial Clk = 1;
    always#10 Clk = ~Clk;
    
    initial begin
        Reset_n = 0;
        uart_rx = 1;
        #201;
        Reset_n = 1;
        #200;
        uart_tx_byte(8'h5a); //调用task
        #90000;
        uart_tx_byte(8'ha5); 
        #90000;
        uart_tx_byte(8'h86); 
        #90000;
        $stop;
    end
    
    task uart_tx_byte; //创建任务
        input [7:0]tx_data;
        begin
            uart_rx = 1;
            #20;
            uart_rx = 0;
            #8680; //一位的发送时间
            uart_rx = tx_data[0];
            #8680; 
            uart_rx = tx_data[1];
            #8680; 
            uart_rx = tx_data[2];
            #8680; 
            uart_rx = tx_data[3];
            #8680; 
            uart_rx = tx_data[4];
            #8680; 
            uart_rx = tx_data[5];
            #8680; 
            uart_rx = tx_data[6];
            #8680; 
            uart_rx = tx_data[7];
            #8680; 
            uart_rx = 1;
            #8680; 
            
        end  
    endtask
           
endmodule

二、巧用位操作优化串口接受逻辑设计

在这里插入图片描述

三、串口接收模块的项目应用案例

中间控制单元
在这里插入图片描述

在这里插入图片描述

八位数据与协议进行比较
在这里插入图片描述

程序文件
在这里插入图片描述
uart_rx_ctrl_led

module uart_rx_ctrl_led(
    Clk,
    Reset_n,
    Led,
    uart_rx
    );
    
    input Clk;
    input Reset_n;
    output Led;
    input uart_rx;
    
    wire [7:0] ctrl;//模块连接模块,中间只需要导线
    wire [31:0] time_set;
    wire [7:0] rx_data;
    wire rx_done;
    
    counter_led_3 counter_led(
        .Clk(Clk),
        .Reset_n(Reset_n),
        .Ctrl(ctrl),
        .Time(time_set),
        .Led(Led)
   
    );
    
    uart_byte_rx uart_byte_rx(
    .Clk(Clk),
    .Reset_n(Reset_n),
    .Baud_Set(4),
    .uart_rx(uart_rx),
    .Data(rx_data),
    .Rx_Done(rx_done)

    );
    
    uart_cmd uart_cmd(
    .Clk(Clk),
    .Reset_n(Reset_n),
    .rx_data(rx_data),
    .rx_done(rx_done),
    .ctrl(ctrl),
    .time_set(time_set)
    
    );
    
    
endmodule

counter_led_3

module counter_led_3(
    Clk,
    Reset_n,
    Ctrl,
    Time,
    Led

    );
    
    input Clk;
    input Reset_n;
    input [7:0]Ctrl;
    input [31:0]Time;
    output reg Led;
    
    reg [31:0] counter;
    
    always@(posedge Clk or negedge Reset_n)
        if(!Reset_n)
            counter <= 0;
        else if(counter == Time-1)
            counter <=0;
        else
            counter <= counter + 1'b1;
            
    reg [2:0] counter2;       
    always@(posedge Clk or negedge Reset_n)
        if(!Reset_n)
            counter2 <= 0;
        else if(counter == Time - 1)
            counter2 <= counter2 + 1'b1;
            
            
 
     always@(posedge Clk or negedge Reset_n)
        if(!Reset_n)
            Led <= 0;
        else case(counter2)                                            
            0:Led <= Ctrl[0];
            1:Led <= Ctrl[1];
            2:Led <= Ctrl[2];
            3:Led <= Ctrl[3];
            4:Led <= Ctrl[4];
            5:Led <= Ctrl[5];   
            6:Led <= Ctrl[6];      
            7:Led <= Ctrl[7]; 
            default Led <= Led;       
         endcase   
                 
endmodule

uart_byte_rx

module uart_byte_rx(
    Clk,
    Reset_n,
    Baud_Set,
    uart_rx,
    Data,
    Rx_Done

    );
    input Clk;
    input Reset_n;
    input [2:0]Baud_Set;
    input uart_rx;
    output reg[7:0]Data;
    output reg Rx_Done;
  
    
    //边沿检测
    //存储数据
    reg [1:0]uart_rx_r; //定义两个D触发器,判断前后的值
    always@(posedge Clk)begin
        uart_rx_r[0] <= uart_rx;
        uart_rx_r[1] <= uart_rx_r[0]; //[1]是前一刻的值
    end
    
    wire pedge_uart_rx;//上升沿检测
    //assign pedge_uart_rx = (uart_rx_r[1] == 0) && (uart_rx_r[0] == 1);
    assign pedge_uart_rx = (uart_rx_r == 2'b01);
    
    wire nedge_uart_rx;//下降沿检测
    assign nedge_uart_rx = (uart_rx_r == 2'b10);
    
    //波特率设置
    //1位数据分成了16小段,9600的波特率:1_000_000_000/9600/16/20=325 
    reg[8:0] bps_DR;
    always@(*)
        case(Baud_Set)
            0:bps_DR = 1_000_000_000/9600/16/20 -1;
            1:bps_DR = 1_000_000_000/19200/16/20 -1;
            2:bps_DR = 1_000_000_000/38400/16/20 -1;
            3:bps_DR = 1_000_000_000/57600/16/20 -1;
            4:bps_DR = 1_000_000_000/115200/16/20 -1;
            default:bps_DR = 1_000_000_000/9600/16/20 -1;
        endcase
/
    //产生bps_CLK
    wire bps_clk_16x;
    assign bps_clk_16x = (div_cnt == bps_DR / 2);  //16段的每一小段也要在中心处采样
    
    reg RX_EN;
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        RX_EN <= 0;
    else if(nedge_uart_rx)
        RX_EN <= 1;
    else if(Rx_Done || (sta_bit >= 4))  //sta_bit >= 4是错误的起始位,是一个干扰
        RX_EN <= 0;
        
    
    reg [8:0] div_cnt; //分频计数器
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        div_cnt <= 0;
    else if(RX_EN)begin //nedge_uart_rx仅一个脉冲,不足以使得div_cnt一直加,所以这里用前面定义的RX_EN。
        if(div_cnt == bps_DR)
            div_cnt <= 0;
        else
            div_cnt <= div_cnt + 1'b1;
    end
    else
        div_cnt <= 0;
        

    //采样数据
    reg[7:0] bps_cnt;          //16*10=160个bps_clk,10是10位(包括起始和终止)
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        bps_cnt <= 0;
    else if(RX_EN)begin
        if(bps_clk_16x)begin
            if(bps_cnt == 160)
                bps_cnt <= 0;
            else
                bps_cnt <= bps_cnt + 1'b1; 
        end
        else
            bps_cnt <= bps_cnt;
    end
    else
        bps_cnt <= 0;
        
    
    //接收部分
    reg [2:0] r_data[7:0]; //累加每一位处获得的值,8位;(二维数组)    reg width name number
    reg [2:0] sta_bit; //单独定义起始位
    reg [2:0] sto_bit; //停止位
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)begin
        sta_bit <= 0;
        sto_bit <= 0;
        r_data[0] <= 0; //只能一个个的赋值
        r_data[1] <= 0;
        r_data[2] <= 0;
        r_data[3] <= 0;
        r_data[4] <= 0;
        r_data[5] <= 0;
        r_data[6] <= 0;
        r_data[7] <= 0;
    end
    else if(bps_clk_16x)begin
        case(bps_cnt)
            0:begin   //等于零的时刻 把这些清零,防止出错
                sta_bit <= 0;
                sto_bit <= 0;
                r_data[0] <= 0; 
                r_data[1] <= 0;
                r_data[2] <= 0;
                r_data[3] <= 0;
                r_data[4] <= 0;
                r_data[5] <= 0;
                r_data[6] <= 0;
                r_data[7] <= 0;
            end
            5,6,7,8,9,10,11:sta_bit <= sta_bit + uart_rx;  //7次采样,16次中,主要是中间的几次有效和稳定
            21,22,23,24,25,26,27:r_data[0] <= r_data[0] + uart_rx; 
            37,38,39,40,41,42,43:r_data[1] <= r_data[1] + uart_rx; 
            53,54,55,56,57,58,59:r_data[2] <= r_data[2] + uart_rx; 
            69,70,71,72,73,74,75:r_data[3] <= r_data[3] + uart_rx; 
            85,86,87,88,89,90,91:r_data[4] <= r_data[4] + uart_rx; 
            101,102,103,104,105,106,107:r_data[5] <= r_data[5] + uart_rx; 
            117,118,119,120,121,122,123:r_data[6] <= r_data[6] + uart_rx; 
            133,134,135,136,137,138,139:r_data[7] <= r_data[7] + uart_rx; 
            149,150,151,152,153,154,155:sto_bit <= sto_bit + uart_rx; 
            default:; //什么都不做
        endcase
    end
      
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        Data <= 0;
    else if(bps_clk_16x && (bps_cnt == 159))begin  //bps_cnt为159的时候就可以输出结果了
        Data[0] <= (r_data[0] >= 4)?1'b1:1'b0;
        Data[1] <= (r_data[1] >= 4)?1'b1:1'b0;  
        Data[2] <= (r_data[2] >= 4)?1'b1:1'b0;  
        Data[3] <= (r_data[3] >= 4)?1'b1:1'b0;  
        Data[4] <= (r_data[4] >= 4)?1'b1:1'b0;  
        Data[5] <= (r_data[5] >= 4)?1'b1:1'b0;  
        Data[6] <= (r_data[6] >= 4)?1'b1:1'b0;  
        Data[7] <= (r_data[7] >= 4)?1'b1:1'b0;     
    end
                
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        Rx_Done <= 0;
    else if((div_cnt == bps_DR/2) && (bps_cnt == 160))
        Rx_Done <= 1;
    else
        Rx_Done <= 0;
        

endmodule

uart_cmd

module uart_cmd(
    Clk,
    Reset_n,
    rx_data,
    rx_done,
    ctrl,
    time_set
    
    );
    
    input Clk;
    input Reset_n;
    input [7:0] rx_data;
    input rx_done;
    output reg[7:0] ctrl;
    output reg[31:0] time_set;
    
    reg [7:0] data_str[7:0];
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)begin
        data_str[0] <= 0;
        data_str[1] <= 0;
        data_str[2] <= 0;
        data_str[3] <= 0;
        data_str[4] <= 0;
        data_str[5] <= 0;
        data_str[6] <= 0;
        data_str[7] <= 0;
    end
    else if(rx_done)begin
        data_str[7] <= rx_data;
        data_str[6] <= data_str[7];
        data_str[5] <= data_str[6];
        data_str[4] <= data_str[5];
        data_str[3] <= data_str[4];
        data_str[2] <= data_str[3];
        data_str[1] <= data_str[2];
        data_str[0] <= data_str[1];        
    end
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)begin
        ctrl <= 0;
        time_set <= 0;
    end
    else if(rx_done)begin
        if((data_str[0] == 8'h55) && (data_str[1] == 8'hA5) && (data_str[7] == 8'hF0))begin
            time_set[7:0] <= data_str[2];
            time_set[15:8] <= data_str[3];
            time_set[23:16] <= data_str[4];
            time_set[31:24] <= data_str[5];
            ctrl <= data_str[6];
            
        end

    end
        
endmodule

测试文件uart_rx_ctrl_led_tb

`timescale 1ns / 1ps

module uart_rx_ctrl_led_tb(

    );
    
    reg Clk;
    reg Reset_n;
    wire Led;
    reg uart_rx;    
    
    uart_rx_ctrl_led uart_rx_ctrl_led(
    .Clk(Clk),
    .Reset_n(Reset_n),
    .Led(Led),
    .uart_rx(uart_rx)
    );
    
    initial Clk = 1;
    always#10 Clk = ~Clk;
    
    initial begin
        Reset_n = 0;
        uart_rx = 1;
        #201;
        Reset_n = 1;
        #200;
        uart_tx_byte(8'h55); //调用task
        #90000;
        uart_tx_byte(8'ha5); 
        #90000;
        uart_tx_byte(8'h12); 
        #90000;
        uart_tx_byte(8'h34); 
        #90000;        
        uart_tx_byte(8'h56); 
        #90000;        
        uart_tx_byte(8'h78); 
        #90000 ;       
        uart_tx_byte(8'h9a); 
        #90000;
        uart_tx_byte(8'hf0); 
        #90000;               
        $stop;
    end
    
    task uart_tx_byte; //创建任务
        input [7:0]tx_data;
        begin
            uart_rx = 1;
            #20;
            uart_rx = 0;
            #8680; //一位的发送时间
            uart_rx = tx_data[0];
            #8680; 
            uart_rx = tx_data[1];
            #8680; 
            uart_rx = tx_data[2];
            #8680; 
            uart_rx = tx_data[3];
            #8680; 
            uart_rx = tx_data[4];
            #8680; 
            uart_rx = tx_data[5];
            #8680; 
            uart_rx = tx_data[6];
            #8680; 
            uart_rx = tx_data[7];
            #8680; 
            uart_rx = 1;
            #8680; 
            
        end  
    endtask    
    
endmodule

仿真结果:
在这里插入图片描述
发现ctrl和time_set的值都是0,没有发生变化。

而程序中,是将这些数据存在了data_str中,观察它的数据正确与否。
在这里插入图片描述
好像没什么问题,但ctrl和time_set的值却没有得到更新

然后重新看给ctrl和time_set赋值的部分代码
在这里插入图片描述
在这里插入图片描述
rx_done是在黄线处被捕获到,但此时的data_str还没有变为下一个值,还是前一个值。

像这种如果一个数据同时用来控制数据和使用数据结果时,会存在这样的问题。解决的思路:使rx_done晚一拍

使用如下代码,也就是寄存器,使其慢一拍
在这里插入图片描述

再者,如果单单是移位寄存器,复位操作没有任何意义。(即红框内的可以删除)

在这里插入图片描述

继续运行仿真,发现前几个值变成红色,这是因为:没有复位值,最开始是什么值不知道,移到八个数据之后,都是已知的了,就不是红色的了。
在这里插入图片描述
修改tb测试文件,看看能否依然更新结果(反面测试
在这里插入图片描述
运行仿真,发现因为后一组数据不正确,而没有刷新上一组的值
在这里插入图片描述
整体逻辑图

在这里插入图片描述

板级验证

分配管脚

在这里插入图片描述
若亮灭时间间隔设置为0.5s,则time_set对应值为25000000,8’h017D7840;ctrl的值为10101010(8‘hAA),循环闪烁。

所以发送内容应该为:55 A5 40 78 7D 01 AA F0

然后发现,Led灯亮都不亮。

重新仿真看看,并把counter_led的波形添加进仿真结果中。

发现刚一上电,Time的值是0

**加粗样式**
看一下代码

在这里插入图片描述
counter=0-1,也就是全是fffffff…非常大才会清零,否则自加。也就是让counter一直在自加,加到很大

在这里插入图片描述

在这里插入图片描述
而在板级验证时,当time更新值到了25000000时,counter早就自加到了大于25000000的地方,那个条件判断依然不会满足,counter依然会不断自加

之所以等待几十秒后,灯才会闪烁是因为:counter不断自加,把32位的那个数据跑满了,才满足清零条件,这个时候才能进入那个判断,让灯闪烁起来。

在这里插入图片描述

一般用后者的方式清零

在这里插入图片描述
修改代码
在这里插入图片描述

然后再板级验证

  • 9
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值