如何将16位BCD信号分配至4位Hex以驱动FPGA七段显示?
问题描述
已有二进制转BCD的Verilog模块(输出16位BCD信号,对应4个独立的4位BCD数字)、七段显示译码模块,尝试用简单时钟分频驱动两个数码管,但不清楚如何将16位BCD信号分配给七段显示译码器的4位输入,实现4个数码管的完整动态显示。
解决方案
实现4个七段数码管的动态显示核心是动态扫描机制:通过高频时钟分频得到扫描控制信号,周期性切换数码管的使能状态,同时同步将对应位置的BCD数字送入译码器,利用人眼视觉暂留实现“同时显示”的效果。
1. 改进时钟分频模块(生成2位扫描选择信号)
原分频器仅生成1位信号,无法覆盖4个数码管,需修改为输出2位选择信号,用于切换4个数码管的选通状态:
module clk_divider( input clk, output reg [1:0] sel // 2位选择信号,对应4个数码管的切换 ); reg [15:0] counter; // 计数器位数根据FPGA时钟调整,50MHz时钟下16位计数器约1ms更新一次 always @(posedge clk) begin counter <= counter + 1'b1; end always @(posedge clk) begin sel <= counter[15:14]; // 取计数器高位作为选择信号,控制扫描频率 end endmodule
2. 顶层模块完整实现
顶层模块需要完成模块例化、BCD信号的多路选择、数码管使能控制三个核心功能:
module top_module( input clk, input [13:0] bin, output [6:0] seg, output [3:0] an // 4个数码管的使能信号 ); wire [15:0] bcd; wire [1:0] sel; wire [3:0] hex; // 例化二进制转BCD模块 bin2bcd u_bin2bcd( .bin(bin), .bcd(bcd) ); // 例化时钟分频模块 clk_divider u_clk_divider( .clk(clk), .sel(sel) ); // 根据扫描选择信号,切换对应BCD位到译码器输入 always @(*) begin case(sel) 2'b00: hex = bcd[3:0]; // 第0位数码管显示最低位BCD 2'b01: hex = bcd[7:4]; // 第1位数码管显示次低位BCD 2'b10: hex = bcd[11:8]; // 第2位数码管显示次高位BCD 2'b11: hex = bcd[15:12]; // 第3位数码管显示最高位BCD default: hex = 4'h0; endcase end // 例化七段显示译码模块 hex2seg u_hex2seg( .hex(hex), .seg(seg) ); // 控制数码管使能信号(假设开发板为低电平有效,若为高电平需调整赋值) always @(*) begin case(sel) 2'b00: an = 4'b1110; // 开启第0位,关闭其余 2'b01: an = 4'b1101; // 开启第1位,关闭其余 2'b10: an = 4'b1011; // 开启第2位,关闭其余 2'b11: an = 4'b0111; // 开启第3位,关闭其余 default: an = 4'b1111; // 全部关闭 endcase end endmodule
3. 关键注意事项
- 扫描频率:计数器的高位选择决定扫描频率,建议控制在100Hz1kHz之间,避免肉眼可见闪烁。50MHz时钟下,取计数器第1415位作为选择信号,扫描频率约769Hz,符合视觉要求。
- 使能电平适配:不同FPGA开发板的数码管使能电平可能不同,若为高电平有效,只需将
an的赋值改为对应高电平(例如2'b00: an = 4'b0001)。 - BCD有效性:
bin2bcd模块输出的每4位BCD都是09的有效数字,因此`hex2seg`中AF的分支不会被触发,无需额外处理。
内容的提问来源于stack exchange,提问作者Yiit Ö
相关产品推荐
相关产品推荐

