fpga.野火征途开发板实现74HC595串并转换控制数码管

一、写在开头

        这个小项目我是想试一试,如果不跟着野火的步骤:分析->波形图->编辑->仿真->debug,不用波形图纯脑补会用多久的时间,我会遇到什么问题?这个项目是控制数码管显示的,当然我没有看野火的数码管的视频。

        写不写波形图的区别首先是时间上:比用波形图至少慢了4倍,这个时间主要花在了debug上,是真的痛苦。然后是代码上,不用波形图时很多波形的时序都有点想当然了,实际运行的波形跟真正要的波形不是同一个时会先天陷入教条认为自己实际输出的就是对的,要花大量时间比对,更痛苦了。

        总结:老实点先写波形图。

二、开源地址(gitee的项目)

display_d_tube: 点亮使用双级联74HC595点亮数码管的项目

三、代码(博客一般不更新,要看最新的代码可以进gitee看)

1、模块名即文件名

/*
 * @Author: LC [email protected]
 * @Date: 2023-11-05 17:49:32
 * @LastEditors: LC [email protected]
 * @LastEditTime: 2023-11-12 13:31:54
 * @FilePath: \display_d_tube\RTL\digital_tube.v
 * @Description: 使用74来驱动显示数码管
 * 
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved. 
 */
//`include "./IC_74HC595_2023_11.v"

module digital_tube (
    input  clk_50Mhz, rst_n,
    output OE_n, shift_clock_cp, storage_clock_8div_cp, data_s 
);
    reg [7:0] data_disp = 8'b0; //输入到数码管所对应的io输出
    reg [4:0] data_value = 5'd0; //想要显示的数值
    reg [7:0] data_cs = 8'b0; //片选所对应的io输出
    reg [4:0] data_num = 5'd0; //想要选定的数码管,最大支持8个管

/*
显示数字“0”,abcdef亮,状态值00111111——>0x3f
显示数字“1”,bc亮,状态值00000110——>0x06
显示数字“2”,abdeg亮,状态值01011011——>0x5b
显示数字“3”,abcdg亮,状态值01001111——>0x4f
显示数字“4”,bcfg亮,状态值01100110——>0x66
显示数字“5”,acdfg亮,状态值01101101——>0x6d
显示数字“6”,acdefg亮,状态值01111101——>0x7d
显示数字“7”,abc亮,状态值00000111——>0x07
显示数字“8”,abcdefg亮,状态值01111111——>0x7f
显示数字“9”,abcdfg亮,状态值01101111——>0x6f
*/

    /* 转换成对应数码管的数值 */
    always @(posedge clk_50Mhz) begin
        if(rst_n == 1'b0) begin
            data_disp <= 8'b0;
            data_value <= 5'd0;
        end else begin
            case(data_value)
                5'd0: data_disp <= ~8'h3f;
                5'd1: data_disp <= ~8'h06;
                5'd2: data_disp <= ~8'h5b;
                5'd3: data_disp <= ~8'h4f;
                5'd4: data_disp <= ~8'h66;
                5'd5: data_disp <= ~8'h6d;
                5'd6: data_disp <= ~8'h7d;
                5'd7: data_disp <= ~8'h07;
                5'd8: data_disp <= ~8'h7f;
                5'd9: data_disp <= ~8'h6f;
                default: data_disp <= ~8'h00;
            endcase
        end
    end
    /* 转换成片选对应的io输出 */
    always @(posedge clk_50Mhz) begin
        if(rst_n == 1'b0) begin
            data_cs <= 8'b0;
            data_num <= 5'd0;
        end else begin
            case(data_num)
                5'd0: data_cs <= 8'h01;
                5'd1: data_cs <= 8'h02;
                5'd2: data_cs <= 8'h04;
                5'd3: data_cs <= 8'h08;
                5'd4: data_cs <= 8'h10;
                5'd5: data_cs <= 8'h20;
                5'd6: data_cs <= 8'h40;
                5'd7: data_cs <= 8'h80;
                default: data_cs <= 8'h00;
            endcase
        end
    end

    reg switch = 1'b0;
    reg [7:0] data = 1'b0;
    reg [2:0] count = 3'b0;
    reg flag = 1'b1;
    reg sel = 1'b0;

    /* 8计数器 */
    always @(posedge clk_50Mhz) begin
        if(rst_n == 1'b0)begin
            count <= 3'b0;
            flag <= 1'b1;
        end else begin
            if(count == 3'b111) begin
                count <= 3'b0;
                flag <= 1'b1;
            end else begin
                count <= count + 1'b1;
                flag <= 1'b0;
            end
        end
    end

    /* 根据flag依次填入片选跟显示的data */
    always @(posedge clk_50Mhz) begin
        if(rst_n == 1'b0)begin
            switch <= 1'b0;
            data <= 8'b0;
            sel <= 1'b0;
        end else begin
            if(flag == 1'b1) begin
                if(sel == 1'b0) begin //填入显示
                    data <= data_disp;
                    sel <= 1'b1;
                    switch <= 1'b0;
                end else begin //填入片选
                    data <= data_cs;
                    sel <= 1'b0;
                    switch <= 1'b1;
                end
            end
        end
    end

    IC_74HC595_2023_11 DT_6(
        .data                  (data                  ),
        .rst_n                 (rst_n                 ),
        .switch                (switch                ),
        .clk                   (clk_50Mhz             ),
        .OE_n                  (OE_n                  ),
        .MR_n                  (                      ),
        .shift_clock_cp        (shift_clock_cp        ),
        .storage_clock_8div_cp (storage_clock_8div_cp ),
        .data_s                (data_s                )
    );
                
endmodule

/*
 * @Author: LC [email protected]
 * @Date: 2023-11-05 17:50:10
 * @LastEditors: lc [email protected]
 * @LastEditTime: 2023-11-06 17:35:28
 * @FilePath: \display_d_tube\RTL\top.v
 * @Description:  
 * 
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved. 
 */
//`include "./digital_tube.v"

module top (
    input  clk_50Mhz, rst_n,
    output OE_n, shift_clock_cp, storage_clock_8div_cp, data_s 
);
    reg [4:0] count = 5'b0;
    reg flag = 1'b1;

    /* 8计数器 */
    always @(posedge clk_50Mhz) begin
        if(rst_n == 1'b0)begin
            count <= 5'b0;
            flag <= 1'b1;
        end else begin
            if(count == 5'b11010) begin
                count <= 5'b0;
                flag <= 1'b1;
            end else begin
                count <= count + 1'b1;
                flag <= 1'b0;
            end
        end
    end

    digital_tube u_digital_tube(
    	.clk_50Mhz             (flag                  ),
        .rst_n                 (rst_n                 ),
        .OE_n                  (OE_n                  ),
        .shift_clock_cp        (shift_clock_cp        ),
        .storage_clock_8div_cp (storage_clock_8div_cp ),
        .data_s                (data_s                )
    );
    
endmodule

/*
 * @Author: LC [email protected]
 * @Date: 2023-11-05 17:49:02
 * @LastEditors: LC [email protected]
 * @LastEditTime: 2023-11-12 13:28:59
 * @FilePath: \display_d_tube\RTL\IC_74HC595_2023_11.v
 * @Description: 这个是74HC595的驱动元件,8bit串并行转换,最高时钟:100Mhz/8
 * 
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved. 
 */

module IC_74HC595_2023_11 (
    input wire [7:0] data, 
	 input wire rst_n,
    input wire switch, //打开串并行转换
    input clk, //该时钟频率即串并转换时钟
    output reg OE_n, //不改变寄存器,控制输出为高阻
    output reg MR_n, //清除移位寄存器的数据
    output wire shift_clock_cp, //上升沿时提取ds的电平保存到移位寄存器最高位,其余位右移,
    output reg storage_clock_8div_cp, //上升沿时把移位寄存器的值保存到存储寄存器
    output reg data_s //串行数据线
);
    reg flag = 1'b1;
    reg [2:0] count = 3'b0;
    reg [7:0] data_t = 8'b0;

    assign shift_clock_cp = clk;

    /* 8计数器 */
    always @(posedge clk) begin
        if(rst_n == 1'b0) begin
            count <= 3'b0;
            flag <= 1'b1;
        end else begin
            if(count == 3'b111) begin
                flag <= 1'b1;
                count <= 3'b0;
            end else begin
                flag <= 1'b0;
                count <= count + 1'b1;
            end
        end
    end

    /* 控制串行数据转移到移位寄存器 */
    always @(posedge clk) begin
        if(rst_n == 1'b0) begin
            OE_n = 1'b1; //输出高阻
            MR_n = 1'b1; //清除移位寄存器
            data_t = 8'b0;
        end else begin
            OE_n = 1'b0;
            MR_n = 1'b0;
            if(flag == 1'b1) begin
                data_t = data;
                data_s = data_t[7];
            end else begin
				data_t = data_t << 1;
                data_s = data_t[7];
            end
        end
    end
	
	reg cp_flag = 1'b0;
    /* 当flag计数到达,并且允许转换,将触发把移位寄存器数据转移到存储寄存器的动作时钟 */
    always @(posedge clk) begin
        if(rst_n == 1'b0) begin
            cp_flag <= 1'b0;
        end else begin
            if(flag == 1'b1) begin
                if(switch == 1'b1) begin
                    cp_flag <= 1'b1;
                end else begin
                    cp_flag <= 1'b0;
                end
            end else begin
                cp_flag <= 1'b0;
            end
        end 
    end
    always @(posedge clk) begin
        if(rst_n == 1'b0) begin
            storage_clock_8div_cp <= 1'b0;
        end else begin
            if(cp_flag == 1'b1) begin
                storage_clock_8div_cp <= 1'b1;
            end else begin
                storage_clock_8div_cp <= 1'b0;
            end
        end 
    end
endmodule

2、仿真文件

/*
 * @Author: lc [email protected]
 * @Date: 2023-11-06 16:49:00
 * @LastEditors: LC [email protected]
 * @LastEditTime: 2023-11-07 01:15:06
 * @FilePath: \display_d_tube\SIM\sim_DT.v
 * @Description: 例化测试程序
 * 
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved. 
 */
`timescale 10ns/10ns

//`include "../RTL/top.v"

module sim_DT ();
    reg clk_50Mhz;
    reg rst_n;
    wire OE_n;
    wire shift_clock_cp;
    wire storage_clock_8div_cp;
    wire data_s;
    
    initial begin
        clk_50Mhz = 1'b0;
        rst_n = 1'b0;
        #10000; rst_n = 1'b1;
    end

    always #1 clk_50Mhz = !clk_50Mhz;

    top u_top(
        .clk_50Mhz                  (clk_50Mhz             ),
        .rst_n                      (rst_n                 ),
        .OE_n                       (OE_n                  ),
        .shift_clock_cp             (shift_clock_cp        ),
        .storage_clock_8div_cp      (storage_clock_8div_cp ),
        .data_s                     (data_s                )
    );
	
endmodule

三、后记

1、在io口输出时,因为频繁的io电平变动,相当于频繁从vcc拉电流,会强烈干扰其他io口的运行,表现上就是当一个io口跳变时在上下边缘会让其他做输出的io口在该点有一个纹波。如下图:

fpga.野火征途开发板实现74HC595串并转换控制数码管_第1张图片

这种影响不仅体现在fpga,也体现在mcu以及soc上。所以设计时如果是单片机,io口的翻转速度要适合,至于soc、fpga。。。改不了了。

你可能感兴趣的:(fpga开发)