【数电实验】基于Verilog HDL的数码管动态扫描与学号显示优化

张开发
2026/4/8 22:22:53 15 分钟阅读

分享文章

【数电实验】基于Verilog HDL的数码管动态扫描与学号显示优化
1. 数码管动态扫描的核心原理数码管动态扫描本质上是一种分时复用技术。想象一下你面前有四个数码管如果同时给它们供电显示不同数字需要至少4×728根控制线。但通过快速轮流点亮每个数码管每秒几十次利用人眼的视觉暂留效应就能实现同时显示的效果而实际只需要7根段选线4根位选线共11根线。我在实际项目中做过测试当扫描频率低于50Hz时即每个数码管点亮间隔超过20ms人眼会明显感觉到闪烁。经过多次实验验证将刷新率控制在60-100Hz范围最为理想。这就像老式显像管电视的成像原理只不过我们是用Verilog来控制时序。动态扫描的核心硬件电路包含两部分段选信号控制显示的具体数字或字符a-g七段小数点dp位选信号决定当前点亮的是哪一个数码管2. Verilog实现动态扫描的关键模块2.1 时钟分频模块开发板上的晶振通常是50MHz直接用于扫描会导致切换过快。我们需要分频产生1kHz左右的扫描时钟module clk_divider( input clk_50M, // 50MHz主时钟 output reg clk_1k // 1kHz扫描时钟 ); reg [15:0] counter; always (posedge clk_50M) begin if(counter 24999) begin // 50MHz/(2*1kHz)-1 clk_1k ~clk_1k; counter 0; end else begin counter counter 1; end end endmodule这个参数我调整过多次实测发现当扫描频率在800Hz-1.2kHz之间时显示效果最稳定。频率太高会导致数码管亮度不足太低则会出现肉眼可见的扫描线。2.2 位选计数器模块采用2位计数器循环生成00→01→10→11的状态对应四个数码管module digit_counter( input clk_1k, output reg [1:0] digit_sel ); always (posedge clk_1k) begin digit_sel digit_sel 1; end endmodule这里有个优化技巧如果开发板支持三态输出可以在位选信号切换时加入短暂的全灭间隔约1us能有效消除鬼影现象。3. 学号显示的特殊处理3.1 数据存储方案学号通常包含8位数字我们需要设计存储方案。推荐两种实现方式寄存器数组方案reg [3:0] student_id [0:7]; // 8个4位寄存器 initial begin student_id[0] 2; // 学号示例20231101 student_id[1] 0; student_id[2] 2; student_id[3] 3; student_id[4] 1; student_id[5] 1; student_id[6] 0; student_id[7] 1; end移位寄存器方案reg [31:0] student_id_reg 32h20231101; // 32位存储8个BCD码我在实际测试中发现当需要频繁更新显示内容时寄存器数组方案更灵活而固定显示时移位寄存器方案更节省资源。3.2 数字切换逻辑根据位选信号选择当前显示的数字module digit_selector( input [1:0] sel, input [31:0] id_reg, output reg [3:0] digit_out ); always (*) begin case(sel) 2b00: digit_out id_reg[3:0]; 2b01: digit_out id_reg[7:4]; 2b10: digit_out id_reg[11:8]; 2b11: digit_out id_reg[15:12]; default: digit_out 4b0; endcase end endmodule这里有个常见坑点Verilog的case语句一定要写全所有可能情况否则会生成锁存器latch导致综合后的电路行为异常。4. 七段译码器的优化设计4.1 基础译码实现module seg_decoder( input [3:0] num, output reg [6:0] seg ); always (*) begin case(num) 4d0: seg 7b1000000; // g f e d c b a 4d1: seg 7b1111001; 4d2: seg 7b0100100; 4d3: seg 7b0110000; 4d4: seg 7b0011001; 4d5: seg 7b0010010; 4d6: seg 7b0000010; 4d7: seg 7b1111000; 4d8: seg 7b0000000; 4d9: seg 7b0010000; default: seg 7b1111111; // 全灭 endcase end endmodule4.2 亮度调节技巧通过PWM调节显示亮度在扫描周期内控制点亮时间比例reg [2:0] brightness 3b100; // 5级亮度控制 reg [2:0] pwm_cnt; always (posedge clk_1k) begin pwm_cnt pwm_cnt 1; if(pwm_cnt brightness) begin seg 7b1111111; // 熄灭 end end这个技巧在环境光线变化大的场合特别有用我曾在智能家居面板项目中用类似方法实现了自动亮度调节。5. 层次化设计实践5.1 顶层模块整合module student_id_display( input clk_50M, output [6:0] seg, output [3:0] digit_sel ); wire clk_1k; wire [1:0] sel; wire [3:0] current_num; clk_divider div(.clk_50M(clk_50M), .clk_1k(clk_1k)); digit_counter cnt(.clk_1k(clk_1k), .digit_sel(sel)); digit_selector ds(.sel(sel), .id_reg(32h20231101), .digit_out(current_num)); seg_decoder decoder(.num(current_num), .seg(seg)); assign digit_sel {2b11, ~sel}; // 位选信号译码 endmodule5.2 仿真测试要点测试平台需要验证三个关键点扫描频率是否准确位选信号是否循环切换段选输出是否符合预期initial begin // 检查1ms时钟 #500000; // 0.5ms if(clk_1k ! 1b1) $error(Clock error); #500000; // 1ms if(clk_1k ! 1b0) $error(Clock error); // 检查位选信号 #2000000; // 2ms if(digit_sel ! 2b01) $error(Digit select error); end6. 常见问题与调试技巧6.1 鬼影现象处理鬼影产生的原因是段选信号和位选信号不同步。解决方法在位选切换前先关闭所有段选切换位选后延迟约1us再打开新段选使用带锁存功能的驱动芯片如74HC5956.2 显示亮度不均可能原因及解决方案扫描频率过高 → 降低到800Hz-1kHz限流电阻过大 → 改为100-200Ω数码管老化 → 更换新数码管6.3 资源优化建议当需要显示更多位数时采用时分复用技术交替显示不同信息使用片内ROM存储显示内容利用FPGA的PLL生成精确时钟7. 扩展应用实例7.1 滚动显示实现通过添加位移寄存器实现文字滚动效果reg [31:0] shift_reg; always (posedge scroll_clk) begin shift_reg {shift_reg[27:0], shift_reg[31:28]}; end7.2 按键输入交互结合消抖电路实现学号修改功能module debounce( input clk, input btn, output reg btn_out ); reg [19:0] cnt; always (posedge clk) begin if(btn ^ btn_out) begin if(cnt) btn_out ~btn_out; else cnt cnt 1; end else begin cnt 0; end end endmodule在实际调试中我发现按键消抖时间设置在10-20ms效果最佳既能防止误触发又不会影响操作体验。

更多文章