基于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

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

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:负责将并行的sel和seg信号转换为串行信号(ser),通过 HC595 芯片的移位寄存器功能实现并行输出,节省 FPGA 的 IO 资源。
2、数据流向:按键输入 → 选择 32 位待显示数据 → 动态扫描模块生成sel和seg → HC595 驱动模块将sel和seg串行发送 → 外部 HC595 芯片输出并行信号控制数码管。
3、关键功能详解
(1)按键控制:通过 2 位按键key的 4 种组合(00~11),切换data_in的 32 位数据值,实现显示内容的切换(如 12345678、87654321 等十六进制数)。
(2)动态数码管扫描:子模块hex8_dyna_ctrl以 1000Hz 频率轮流点亮 8 位数码管,利用人眼视觉暂留效应实现 “同时显示”,输出的sel(位选)和seg(段选)信号为并行格式。HC595 串并转换:子模块hc595_driver将并行的sel和seg信号转换为串行信号(通过ser、sclk、rclk三根线),控制外部 HC595 芯片将串行数据还原为并行输出,驱动数码管显示,大幅减少 FPGA 的 IO 口占用(从 16 根减少到 3 根)。
浙公网安备 33010602011771号