基于74HC595的动态数码管设计(FPGA)

1、 8 位动态数码管控制模块,功能:将 32 位数据转换为动态扫描的位选和段选信号;

 1 module hex8_dyna_ctrl(
 2     input    wire            clk        ,    // 系统时钟(默认50MHz)
 3     input     wire            rst_n    ,    // 复位信号,低电平有效
 4     input    wire     [31:0]    data    ,    // 待显示的32位数据(分8个4位十六进制数)
 5     input    wire            disp_en    ,    // 显示使能信号(高电平有效,控制是否显示)
 6     output    reg        [7:0]    sel        ,    // 8位数码管位选信号(高电平有效,控制哪一位点亮)
 7     output    reg        [7:0]    seg            // 8段位选信号(低电平有效,共阳型)
 8 );
 9 
10 // 参数定义:用于配置扫描频率和分频系数    
11 parameter        CLOCK_FREQ     = 50_000_000;    // 系统时钟频率(50MHz)
12 parameter        SCAN_FREQ    = 1_000;        // 数码管扫描频率(1000Hz,人眼无闪烁)
13 parameter        MCNT         = CLOCK_FREQ / SCAN_FREQ - 1;    // 分频计数器最大值(50MHz/1000Hz -1 = 49999)
14 
15 // 内部寄存器定义
16 reg        [29:0]    div_cnt;    // 分频计数器:用于将系统时钟分频为扫描时钟
17 reg        [2:0]    cnt_sel;    // 位选计数器:0~7循环,选择当前点亮的数码管
18 reg        [3:0]    data_temp;    // 临时数据寄存器:存储当前要显示的4位十六进制数
19 
20 // 1. 分频计数器:生成扫描时钟(1000Hz)
21 always@(posedge clk or negedge rst_n)
22     if(rst_n == 1'b0)
23         div_cnt <= 30'd0;    // 复位时计数器清零
24     else if(disp_en == 1'b0)
25         div_cnt <= 30'd0;    // 显示关闭时,计数器清零
26     else begin
27         if(div_cnt == MCNT)    // 计数到最大值(49999)
28             div_cnt <= 30'd0;
29         else
30             div_cnt <= div_cnt + 1'b1;    // 未到最大值时,计数器累加
31     end
32 
33 // 2. 位选计数器:循环选择8位数码管(每扫一次切换一位)    
34 always@(posedge clk or negedge rst_n)
35     if(rst_n == 1'b0)
36         cnt_sel <= 3'd0;        // 复位时计数器清零
37     else if(div_cnt == MCNT)begin    // 每分频计数器计满一次(即1ms),切换到下一位
38         if(cnt_sel == 3'd7)    
39             cnt_sel <= 3'd0;     // 计数到7(最后一位)时,重置为0
40         else
41             cnt_sel <= cnt_sel + 1'b1;    // 未到最后一位时,计数器累加(0→1→...→7循环)
42     end    
43     else
44         cnt_sel <= cnt_sel;        // 未满足切换条件时,保持当前值
45 
46 // 3. 位选信号控制与数据选择:根据当前位选计数器,选择对应的数据位并激活相应数码管
47 always@(posedge clk or negedge rst_n)
48     if(rst_n == 1'b0)begin
49         sel <= 8'b0000_0000;data_temp <= 4'd0;    end    // 复位时,位选信号全灭,临时数据清零
50     else 
51         case(cnt_sel)
52             3'd0    : begin    sel <= 8'b0000_0001;     data_temp <= data[3:0];    end
53             3'd1    : begin    sel <= 8'b0000_0010;     data_temp <= data[7:4];    end
54             3'd2    : begin    sel <= 8'b0000_0100;     data_temp <= data[11:8];    end
55             3'd3    : begin    sel <= 8'b0000_1000;     data_temp <= data[15:12];    end
56             3'd4    : begin    sel <= 8'b0001_0000;     data_temp <= data[19:16];    end
57             3'd5    : begin    sel <= 8'b0010_0000;     data_temp <= data[23:20];    end
58             3'd6    : begin    sel <= 8'b0100_0000;     data_temp <= data[27:24];    end
59             3'd7    : begin    sel <= 8'b1000_0000;     data_temp <= data[31:28];    end
60             default:  begin    sel <= 8'b0000_0000;     data_temp <= 4'd0;    end    
61         endcase
62 
63 // 4. 七段数码管译码:将4位十六进制数转换为段选信号(共阳极数码管,低电平有效)        
64 always@(*)begin
65     case(data_temp)
66         4'h0: seg = 8'b11000000;  // 0
67         4'h1: seg = 8'b11111001;  // 1
68         4'h2: seg = 8'b10100100;  // 2
69         4'h3: seg = 8'b10110000;  // 3
70         4'h4: seg = 8'b10011001;  // 4
71         4'h5: seg = 8'b10010010;  // 5
72         4'h6: seg = 8'b10000010;  // 6
73         4'h7: seg = 8'b11111000;  // 7
74         4'h8: seg = 8'b10000000;  // 8
75         4'h9: seg = 8'b10010000;  // 9
76         4'ha: seg = 8'b10001000;  // A
77         4'hb: seg = 8'b10000011;  // B
78         4'hc: seg = 8'b11000110;  // C
79         4'hd: seg = 8'b10100001;  // D
80         4'he: seg = 8'b10000110;  // E
81         4'hf: seg = 8'b10001110;  // F
82         default: seg = 8'b11111111; // 全灭
83     endcase
84 end
85 endmodule

 

 1 `timescale 1ns / 1ps
 2 module hex8_dyna_ctrl_tb( );
 3 
 4 reg                 clk        ;
 5 reg                 rst_n    ;
 6 reg     [31:0]        data    ;
 7 reg                 disp_en    ;
 8 wire    [7:0]         sel    ;
 9 wire    [7:0]         seg    ;
10 
11 hex8_dyna_ctrl hex8_dyna_isnt (
12     .clk             (clk)     ,
13     .rst_n             (rst_n)   ,
14     .data             (data) ,    
15     .disp_en         (1'b1)    ,
16     .sel             (sel)     ,
17     .seg             (seg) 
18 );
19 
20 initial clk = 0;
21 always #10 clk = ~clk;
22 
23 initial begin
24     rst_n = 0;
25     disp_en = 1;
26     data = 32'h12345678;
27     #200;
28     rst_n = 1;
29     #20_000_000; //延时20ms,数码管循环一次1ms*8=8ms
30     data = 32'h20250817;
31     #20_000_000; //延时20ms,数码管循环一次1ms*8=8ms        
32     $stop;
33 end
34   
35 endmodule

 2025-08-17_231725

2、HC595 驱动模块,功能将并行的位选和段选信号转换为串行信号,通过 HC595 芯片输出;

 1 /*
 2 该模块是HC595 串并转换芯片的驱动电路,用于将并行的数码管段选(seg)和位选(sel)信号转换为串行信号,
 3 通过 3 根线(ser、sclk、rclk)控制 HC595,从而节省 FPGA 的 IO 资源;
 4 */
 5 module hc595_driver(
 6     input    wire            clk        ,    // 系统时钟(默认50MHz)
 7     input    wire            rst_n    ,    // 复位信号,低电平有效
 8     input    wire    [7:0]    sel        ,    // 8位数码管位选信号
 9     input    wire    [7:0]    seg        ,    // 8段数码管段选信号
10     output    reg                ser        ,    // HC595串行数据输入(Serial Data)
11     output    reg                sclk    ,    // HC595移位时钟(Shift Clock)
12     output    reg                rclk        // HC595锁存时钟(Latch Clock)
13 );
14 
15 // 参数定义:配置HC595的移位时钟频率和分频系数
16 parameter        CLOCK_FREQ     = 50_000_000;    // 系统时钟频率(50MHz)
17 parameter        SCLK_FREQ    = 12_500_000;    // HC595移位时钟频率(12.5MHz)
18 // 分频计数器最大值:将系统时钟分频为SCLK_FREQ(占空比50%)
19 parameter        MCNT         = CLOCK_FREQ / (SCLK_FREQ * 2) - 1;
20 
21 // 内部寄存器定义
22 reg         div_cnt;    // 分频计数器:用于生成移位时钟(sclk)
23 reg    [4:0]    cnt_bit;    // 位计数器:0~31循环,控制串行数据发送的位序
24 
25 // 1. 分频计数器:生成移位时钟(sclk)的分频信号
26 always@(posedge clk or negedge rst_n)
27     if(rst_n == 1'b0)
28         div_cnt <= 1'b0;
29     else if(div_cnt == MCNT )
30         div_cnt <= 1'b0;
31     else
32         div_cnt <= div_cnt + 1'b1;
33 
34 // 2. 位计数器:控制串行发送的位数(共32位:8位seg + 8位sel,每位含高低电平切换)        
35 always@(posedge clk or negedge rst_n)
36     if(rst_n == 1'b0)
37         cnt_bit <= 5'd0;
38     else if(div_cnt == MCNT)begin
39         if(cnt_bit == 5'd31)
40             cnt_bit <= 5'd0;
41         else
42             cnt_bit <= cnt_bit + 1'b1;
43     end
44     else
45         cnt_bit <= cnt_bit;
46 
47 // 3. HC595控制信号生成:按位发送seg和sel数据,控制sclk和rclk时序
48 // 工作原理:先发送seg的8位(高位到低位),再发送sel的8位(高位到低位),
49 // 每发送1位数据,sclk产生一次高低电平跳变(上升沿触发移位),
50 // 全部数据发送完成后,rclk产生高电平锁存数据。        
51 always@(posedge clk or negedge rst_n)
52     if(rst_n == 1'b0)begin
53         sclk <= 1'b0;  ser <= 1'b0; rclk <= 1'b0;
54     end
55     else begin
56         case(cnt_bit)
57             5'd0    :  begin sclk <= 1'b0;    ser <= seg[7]; rclk <= 1'b1; end
58             5'd1    :  begin sclk <= 1'b1;    rclk <= 1'b0;     end
59             5'd2    :  begin sclk <= 1'b0;    ser <= seg[6];     end
60             5'd3    :  begin sclk <= 1'b1;    end
61             5'd4    :  begin sclk <= 1'b0;    ser <= seg[5];     end
62             5'd5    :  begin sclk <= 1'b1;    end
63             5'd6    :  begin sclk <= 1'b0;    ser <= seg[4];     end
64             5'd7    :  begin sclk <= 1'b1;    end
65             5'd8    :  begin sclk <= 1'b0;    ser <= seg[3];     end
66             5'd9    :  begin sclk <= 1'b1;    end
67             5'd10    :  begin sclk <= 1'b0;    ser <= seg[2];     end
68             5'd11    :  begin sclk <= 1'b1;    end
69             5'd12    :  begin sclk <= 1'b0;    ser <= seg[1];     end
70             5'd13    :  begin sclk <= 1'b1;    end
71             5'd14    :  begin sclk <= 1'b0;    ser <= seg[0];     end
72             5'd15    :  begin sclk <= 1'b1;    end
73             
74             5'd16    :  begin sclk <= 1'b0;    ser <= sel[7];  end
75             5'd17    :  begin sclk <= 1'b1;    end
76             5'd18    :  begin sclk <= 1'b0;    ser <= sel[6];     end
77             5'd19    :  begin sclk <= 1'b1;    end
78             5'd20    :  begin sclk <= 1'b0;    ser <= sel[5];     end
79             5'd21    :  begin sclk <= 1'b1;    end
80             5'd22    :  begin sclk <= 1'b0;    ser <= sel[4];     end
81             5'd23    :  begin sclk <= 1'b1;    end
82             5'd24    :  begin sclk <= 1'b0;    ser <= sel[3];     end
83             5'd25    :  begin sclk <= 1'b1;    end
84             5'd26    :  begin sclk <= 1'b0;    ser <= sel[2];     end
85             5'd27    :  begin sclk <= 1'b1;    end
86             5'd28    :  begin sclk <= 1'b0;    ser <= sel[1];     end
87             5'd29    :  begin sclk <= 1'b1;    end
88             5'd30    :  begin sclk <= 1'b0;    ser <= sel[0];     end
89             5'd31    :  begin sclk <= 1'b1;    end    
90             default: begin sclk <= 1'b0;  ser <= 1'b0; rclk <= 1'b0;end
91         endcase
92     end
93         
94 endmodule

 

 1 `timescale 1ns / 1ps
 2 module hc595_driver_tb( );
 3 
 4 reg                 clk        ;
 5 reg                 rst_n    ;
 6 reg        [7:0]        sel        ;
 7 reg        [7:0]        seg        ;    
 8 wire                ser        ;
 9 wire                sclk    ;
10 wire                rclk    ;
11 
12 hc595_driver hc595_driver_inst (
13     .clk             (clk)   ,
14     .rst_n             (rst_n) ,
15     .sel             (sel)     ,
16     .seg             (seg)   ,
17     .ser             (ser)   ,
18     .sclk             (sclk)  ,
19     .rclk            (rclk )  
20 );
21 
22 initial clk = 0;
23 always #10 clk = ~clk;
24 
25 initial begin
26     rst_n = 0;
27     sel = 8'b0000_0001;
28     seg = 8'b0101_0101;
29     #200;
30     rst_n = 1;
31     #5_000; //延时5000ns,32*20*2=1280ns
32     sel = 8'b0000_0010;
33     seg = 8'b1010_1010;
34     #5_000;    //延时5000ns,32*20*2=1280ns
35     $stop;
36 end
37 
38 endmodule

 2025-08-17_232016

3、顶层模块,功能通过整合动态数码管扫描控制与 HC595 串并转换驱动,实现了通过按键切换显示不同十六进制数据的功能;

 1 module hex8_hc595(
 2     input   wire            clk     ,
 3     input   wire            rst_n   ,
 4     input   wire    [1:0]   key     ,
 5     output  wire            ser     ,
 6     output  wire            sclk    ,
 7     output  wire            rclk
 8 );
 9 
10 reg    [31:0]      data_in;
11 wire    [7:0]       sel;
12 wire    [7:0]       seg;
13 
14 hex8_dyna_ctrl hex8_dyna_isnt (
15     .clk             (clk)     ,
16     .rst_n             (rst_n)   ,
17     .data             (data_in) ,    
18     .disp_en         (1'b1)    ,
19     .sel             (sel)     ,
20     .seg             (seg) 
21 );
22 
23 hc595_driver hc595_driver_inst (
24     .clk             (clk)   ,
25     .rst_n             (rst_n) ,
26     .sel             (sel)     ,
27     .seg             (seg)   ,
28     .ser             (ser)   ,
29     .sclk             (sclk)  ,
30     .rclk            (rclk )  
31 );
32 
33 always@(*)begin
34     case(key)
35         2'b00   :  data_in = 32'h12345678;
36         2'b01   :  data_in = 32'h87654321;
37         2'b10   :  data_in = 32'h09abcdef;
38         2'b11   :  data_in = 32'h20250817;
39         default:   data_in = 32'h00000000;
40     endcase
41 end
42 
43 endmodule

 相关说明:

1、模块层级关系:顶层模块hex8_hc595通过例化两个子模块实现完整功能:

(1)hex8_dyna_ctrl:负责将 32 位并行数据转换为动态扫描的位选(sel)和段选(seg)信号(时分复用方式,依次点亮 8 位数码管)。

(2)hc595_driver:负责将并行的selseg信号转换为串行信号(ser),通过 HC595 芯片的移位寄存器功能实现并行输出,节省 FPGA IO 资源。

2、数据流向:按键输入 → 选择 32 位待显示数据 → 动态扫描模块生成selseg HC595 驱动模块将selseg串行发送 → 外部 HC595 芯片输出并行信号控制数码管。

3、关键功能详解

(1)按键控制:通过 2 位按键key4 种组合(00~11),切换data_in32 位数据值,实现显示内容的切换(如 1234567887654321 等十六进制数)。

(2)动态数码管扫描:子模块hex8_dyna_ctrl1000Hz 频率轮流点亮 8 位数码管,利用人眼视觉暂留效应实现 “同时显示”,输出的sel(位选)和seg(段选)信号为并行格式。HC595 串并转换:子模块hc595_driver将并行的selseg信号转换为串行信号(通过sersclkrclk三根线),控制外部 HC595 芯片将串行数据还原为并行输出,驱动数码管显示,大幅减少 FPGA IO 口占用(从 16 根减少到 3 根)。

 

posted @ 2025-08-17 23:07  FPGA9161  阅读(89)  评论(0)    收藏  举报