串行移位寄存器驱动数码管显示

74HC595大概原理
在这里插入图片描述
具有串行=转并功能,通过移位寄存器和输出锁存器实现。驱动开发板上的8位数码管。
FPGA需要通过74HC595这个芯片把16位的数据(SEL + SEG)变为并行端口用来驱动数码管

74HC595的波形图
在这里插入图片描述

如图可知:
输入数据DS,先输入的数据,在8个时间周期之后,在Q7上

数码管结构
在这里插入图片描述
此结构为一个SPI协议接口

第一片74HC595 作为位选信号
第二片作为段选
在这里插入图片描述
要求:对于74HC595芯片,该芯片在SH_CP(SCLK)的上升沿将(DIO)上的数据移入内部寄存器。
目的:因此我们需要保证DS上的数据在SH_CP上升沿前后的一段时间,保持稳定。
手段:FPGA要在SH_CP的下降沿改变DS的值。
设SH_CP时钟的频率为12.5HZ
根据波形,找到SH_CP的最小时间周期,以这个周期为基准来描述DS ST_CP 的变化,类似于此
在这里插入图片描述
则此信号频率应该为SH_CP的二倍

代码如下:

数码管显示模块:

module hex8_2(
    Clk,
    Reset_n,
    Disp_Data,
    SEL,
    SEG
);
    
    input Clk;
    input Reset_n;
    input [31:0]Disp_Data;
    output reg[7:0]SEL;
    output reg[7:0]SEG;//seg[0]-a,seg[1]-b...seg[7]-h
    
    reg clk_1k;
    reg [15:0]div_cnt;
    always@(posedge Clk or negedge Reset_n)    //1000000/20/2*2=50000
    if(!Reset_n)
        div_cnt <= 0;
    else if(div_cnt >= 49999)
        div_cnt <= 0;
    else
        div_cnt <= div_cnt + 1'b1;
        
    always@(posedge Clk or negedge Reset_n)    //使能时钟信号
    if(!Reset_n)
        clk_1k <= 0;
    else if(div_cnt == 49999)
        clk_1k <= 1'b1;
    else
        clk_1k <= 0;
      
    reg [2:0]num_cnt;                         
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        num_cnt <= 0;
    else if(clk_1k)
        num_cnt <= num_cnt + 1'b1;
        
    always@(posedge Clk)
        case(num_cnt)
            0: SEL = 8'b00000001;
            1: SEL = 8'b00000010;
            2: SEL = 8'b00000100;
            3: SEL = 8'b00001000;
            4: SEL = 8'b00010000;
            5: SEL = 8'b00100000;
            6: SEL = 8'b01000000;
            7: SEL = 8'b10000000;
        endcase

    reg[3:0]disp_tmp;
    always@(posedge Clk)
        case(num_cnt)
            7: disp_tmp = Disp_Data[31:28];
            6: disp_tmp = Disp_Data[27:24];
            5: disp_tmp = Disp_Data[23:20];
            4: disp_tmp = Disp_Data[19:16];
            3: disp_tmp = Disp_Data[15:12];
            2: disp_tmp = Disp_Data[11:8];
            1: disp_tmp = Disp_Data[7:4];
            0: disp_tmp = Disp_Data[3:0];
        endcase   

     always@(posedge Clk)
        case(disp_tmp)
            0: SEG = 8'hc0;
            1: SEG = 8'hf9;
            2: SEG = 8'ha4;
            3: SEG = 8'hb0;
            4: SEG = 8'h99;
            5: SEG = 8'h92;
            6: SEG = 8'h82;
            7: SEG = 8'hf8;
            8: SEG = 8'h80;
            9: SEG = 8'h90;
            4'ha: SEG = 8'h88;
            4'hb: SEG = 8'h83;
            4'hc: SEG = 8'hc6;
            4'hd: SEG = 8'ha1;
            4'he: SEG = 8'h86;
            4'hf: SEG = 8'h8e;
        endcase   

endmodule

驱动模块:

`timescale 1ns / 1ps

module HC595_Driver(
    Clk,
    Reset_n,
    Data,
    S_EN,
    SH_CP,
    ST_CP,
    DS
);
    
    input Clk;
    input Reset_n;
    input [15:0]Data;
    input S_EN;
    output reg SH_CP;
    output reg ST_CP;
    output reg DS;
    parameter CNT_MAX = 2;
    
    reg [15:0]r_data;
    always@(posedge Clk)
        if(S_EN)
            r_data <= Data;

    reg [7:0]divider_cnt;//分频计数器,得到最小的那个使能时间单位
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        divider_cnt <= 0;
    else if(divider_cnt == CNT_MAX - 1'd1)
        divider_cnt <= 0;
    else
        divider_cnt <= divider_cnt + 1'b1;
    
    wire sck_plus;      //产生这个两倍时钟信号
    assign sck_plus = (divider_cnt == CNT_MAX - 1'd1);
    
    reg [5:0]SHCP_EDGE_CNT;
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        SHCP_EDGE_CNT <= 0;
    else if(sck_plus)begin
        if(SHCP_EDGE_CNT == 6'd32)            //传16位数据,需要32位计数
            SHCP_EDGE_CNT <= 0;
        else
            SHCP_EDGE_CNT <= SHCP_EDGE_CNT + 1'b1;
    end
        
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)begin
        SH_CP <= 0;
        ST_CP <= 0;
        DS <= 0;   
    end
    else begin
        case(SHCP_EDGE_CNT)
            0: begin SH_CP <= 0;ST_CP <= 1'd0;DS <= r_data[15];end
            1: SH_CP <= 1'd1;
            2: begin SH_CP <= 0;DS <= r_data[14];end
            3: SH_CP <= 1'd1;
            4: begin SH_CP <= 0;DS <= r_data[13];end   
            5: SH_CP <= 1'd1;
            6: begin SH_CP <= 0;DS <= r_data[12];end
            7: SH_CP <= 1'd1;
            8: begin SH_CP <= 0;DS <= r_data[11];end   
            9: SH_CP <= 1'd1;
            10: begin SH_CP <= 0;DS <= r_data[10];end
            11: SH_CP <= 1'd1;
            12: begin SH_CP <= 0;DS <= r_data[9];end  
            13: SH_CP <= 1'd1;
            14: begin SH_CP <= 0;DS <= r_data[8];end
            15: SH_CP <= 1'd1;
            16: begin SH_CP <= 0;DS <= r_data[7];end   
            17: SH_CP <= 1'd1;
            18: begin SH_CP <= 0;DS <= r_data[6];end
            19: SH_CP <= 1'd1;
            20: begin SH_CP <= 0;DS <= r_data[5];end   
            21: SH_CP <= 1'd1;
            22: begin SH_CP <= 0;DS <= r_data[4];end
            23: SH_CP <= 1'd1;
            24: begin SH_CP <= 0;DS <= r_data[3];end   
            25: SH_CP <= 1'd1;
            26: begin SH_CP <= 0;DS <= r_data[2];end
            27: SH_CP <= 1'd1;
            28: begin SH_CP <= 0;DS <= r_data[1];end
            29: SH_CP <= 1'd1;
            30: begin SH_CP <= 0;DS <= r_data[0];end
            31: SH_CP <= 1'd1;
            32: ST_CP <= 1'd1;
            default:
                begin
                    SH_CP <= 0;
                    ST_CP <= 0;
                    DS <= 0;   
                end
        endcase
    end

endmodule

测试模块:

module hex8_test(
    Clk,
    Reset_n,
    SEL,
    SEG,
    SH_CP,
    ST_CP,
    DS
);
    
    input Clk;
    input Reset_n;

    output [7:0]SEL;
    output [7:0]SEG;//seg[0]-a,seg[1]-b...seg[7]-h
    
    output SH_CP;
    output ST_CP;
    output DS;
    
    wire [31:0]Disp_Data;
    
    hex8_2 hex8_2(
        Clk,
        Reset_n,
        Disp_Data,
        SEL,
        SEG
    ); 
    wire [15:0]Data;
    assign Data = {SEG,SEL};
    
    wire S_EN;
    assign S_EN = 1;
    
    HC595_Driver HC595_Driver(
        Clk,
        Reset_n,
        Data,
        S_EN,
        SH_CP,
        ST_CP,
        DS
    );
    
    assign Disp_Data = 32'h13579bdf;    
    
endmodule
TB文件:
`timescale 1ns / 1ps
module hex8_tb();

    reg Clk;
    reg Reset_n;
    reg [31:0]Disp_Data;
    wire [7:0]SEL;
    wire [7:0]SEG;
    
    hex8 hex8(
        Clk,
        Reset_n,
        Disp_Data,
        SEL,
        SEG
    );  
    
    initial Clk = 1;
    always#10 Clk = ~Clk;
    
    initial begin
        Reset_n = 0;
        Disp_Data = 32'h00000000;
        #201;
        Reset_n = 1;
        #2000;
        Disp_Data = 32'h12345678;
        #10000000;
        Disp_Data = 32'h9abcdef0;
        #10000000;
        $stop;
    end
    
endmodule
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值