基于Verilog与VIVADO的七段数码管动态扫描与BCD码显示技术实现

FPGA实战:基于Verilog的BCD码动态扫描显示系统设计 本文详细介绍了基于Verilog的BCD码动态扫描显示系统设计,涵盖二进制转BCD模块、数码管刷新电路和七段译码器等核心组件。通过FPGA实战案例,展示了如何优化资源利用和解决常见问题,如鬼影现象和亮度不均,适用于控制器设计和上机实验。 阅读详情

1. 七段数码管控制器的核心设计思路

七段数码管是嵌入式系统和FPGA开发中最常用的显示设备之一,它通过控制7个LED段的亮灭来显示数字和部分字母。在实际项目中,我们经常需要控制多个数码管,如果每个数码管都独立控制,会占用大量的IO资源。动态扫描技术就是为了解决这个问题而生的。

动态扫描的基本原理是利用人眼的视觉暂留特性,通过快速轮流点亮多个数码管,让它们看起来像是同时亮起的。比如我们有8个数码管,可以在极短的时间内(比如每1ms)切换一个数码管,由于切换速度很快,人眼无法分辨这种快速切换,就会看到所有数码管都在同时显示。

在Verilog中实现动态扫描,我们需要设计一个状态机来控制数码管的位选信号(选择哪个数码管亮)和段选信号(显示什么内容)。位选信号通过一个循环计数器来生成,段选信号则根据当前选中的数码管和要显示的内容来确定。

BCD码(Binary-Coded Decimal)是一种用4位二进制数表示1位十进制数的编码方式。比如十进制数123,用BCD码表示就是0001_0010_0011。在数码管显示中,我们经常需要将二进制数转换为BCD码,然后再将BCD码转换为七段数码管的段选信号。

2. VIVADO环境搭建与工程创建

开始设计之前,我们需要先搭建好开发环境。VIVADO是Xilinx公司推出的FPGA开发软件,它提供了从设计输入到比特流生成的全套工具链。如果你还没有安装VIVADO,可以去Xilinx官网下载安装包,选择WebPACK版本就可以,这个版本是免费的,功能也足够我们使用。

安装完成后,打开VIVADO,点击"Create Project"开始创建新工程。给工程起个有意义的名字,比如"seven_segment_display",选择工程存放路径。在项目类型选择时,选择"RTL Project",这样我们可以从Verilog代码开始设计。

在选择器件时,需要根据你实际使用的FPGA开发板来选择。比如我使用的是Basys3开发板,就选择Artix-7系列的XC7A35T-1CPG236C。如果你不确定自己的开发板用什么芯片,可以查看开发板的文档或者原理图。

工程创建完成后,我们需要添加源文件。右键点击"Design Sources",选择"Add Sources",然后创建新的Verilog文件。我给主模块起名为"seven_segment",这个模块将包含我们所有的控制逻辑。

除了设计文件,我们还需要添加约束文件。约束文件的作用是将Verilog代码中的信号映射到FPGA的实际管脚上。不同的开发板管脚定义不同,所以一定要根据自己使用的开发板来编写约束文件。

3. Verilog代码实现详解

让我们深入看看Verilog代码的具体实现。首先定义模块的输入输出端口:

module seven_segment(
    input clk,           // 系统时钟,通常是100MHz
    input reset_n,       // 复位信号,低电平有效
    input button,        // 按钮输入,用于控制显示
    input switch,        // 开关输入,用于模式选择
    output reg [7:0] segment,  // 段选信号,控制显示内容
    output reg [7:0] digit     // 位选信号,选择哪个数码管
);

接下来我们需要生成5Hz的时钟信号用于计数。使用100MHz的系统时钟分频得到5Hz:

reg [31:0] div_cnt = 32'd0;
reg clk_5hz = 0;

always @(posedge clk) begin
    if (div_cnt >= 32'd10000000) begin  // 100MHz / 10,000,000 = 10Hz
        div_cnt <= 32'd0;
        clk_5hz <= ~clk_5hz;           // 翻转得到5Hz
    end else begin
        div_cnt <= div_cnt + 32'd1;
    end
end

这里有个实用技巧:在仿真时,我们可以把分频系数改小,比如改成200,这样仿真时就不用等待太长时间。但在实际下载到FPGA时,一定要改回正确的分频系数。

计数器的实现相对简单,但要注意计数范围是2到254:

reg [7:0] count = 8'd2;

always @(posedge clk) begin
    if (!reset_n) begin
        count <= 8'd2;
    end else if (clk_5hz) begin
        if (count >= 8'd254)
            count <= 8'd2;
        else
            count <= count + 8'd1;
    end
end

二进制到BCD码的转换是重点也是难点。我们可以使用移位加3算法来实现:

module binary_to_bcd (
    input clk,
    input [7:0] binary,
    output reg [3:0] hundreds,
    output reg [3:0] tens,
    output reg [3:0] ones
);
    
    reg [19:0] bcd_reg = 0;
    integer i;
    
    always @(posedge clk) begin
        bcd_reg = {12'd0, binary};
        for (i = 0; i < 8; i = i + 1) begin
            // 对每一位进行加3调整
            if (bcd_reg[19:16] >= 5) bcd_reg[19:16] = bcd_reg[19:16] + 3;
            if (bcd_reg[15:12] >= 5) bcd_reg[15:12] = bcd_reg[15:12] + 3;
            if (bcd_reg[11:8] >= 5) bcd_reg[11:8] = bcd_reg[11:8] + 3;
            if (bcd_reg[7:4] >= 5) bcd_reg[7:4] = bcd_reg[7:4] + 3;
            bcd_reg = bcd_reg << 1;
        end
        hundreds = bcd_reg[19:16];
        tens = bcd_reg[15:12];
        ones = bcd_reg[11:8];
    end
endmodule

这个算法可能看起来有点复杂,但其核心思想是通过移位和条件调整来逐步将二进制数转换为BCD码。我在第一次实现时也花了不少时间才理解透彻。

4. 动态扫描与显示控制

动态扫描的实现需要两个计数器:一个用于控制扫描频率,一个用于选择当前显示的数码管。扫描频率通常设置在1kHz左右,这样既不会让数码管闪烁,又能够保证亮度。

reg [15:0] scan_cnt = 0;
reg [2:0] digit_sel = 0;

always @(posedge clk) begin
    if (scan_cnt >= 16'd100000) begin  // 100MHz/100000 = 1kHz
        scan_cnt <= 0;
        digit_sel <= digit_sel + 1;
        if (digit_sel >= 3'd7)
            digit_sel <= 0;
    end else begin
        scan_cnt <= scan_cnt + 1;
    end
end

根据当前选中的数码管,我们需要生成相应的位选信号:

always @(*) begin
    case (digit_sel)
        3'd0: digit = 8'b11111110;
        3'd1: digit = 8'b11111101;
        3'd2: digit = 8'b11111011;
        3'd3: digit = 8'b11110111;
        3'd4: digit = 8'b11101111;
        3'd5: digit = 8'b11011111;
        3'd6: digit = 8'b10111111;
        3'd7: digit = 8'b01111111;
        default: digit = 8'b11111111;
    endcase
end

段选信号的生成需要根据当前模式和要显示的内容来决定。我们有两种显示模式:显示相同数字和显示计数器值。

reg [3:0] display_data [0:7];
reg [3:0] current_digit;

always @(*) begin
    if (switch == 1) begin  // 显示相同数字模式
        current_digit = uniform_data;
    end else begin  // 显示计数器模式
        case (digit_sel)
            3'd0: current_digit = hundreds;
            3'd1: current_digit = tens;
            3'd2: current_digit = ones;
            default: current_digit = 4'd0;
        endcase
    end
end

最后将BCD数字转换为七段数码管的段选信号:

always @(*) begin
    case (current_digit)
        4'd0: segment = 7'b1000000;  // 0
        4'd1: segment = 7'b1111001;  // 1
        4'd2: segment = 7'b0100100;  // 2
        4'd3: segment = 7'b0110000;  // 3
        4'd4: segment = 7'b0011001;  // 4
        4'd5: segment = 7'b0010010;  // 5
        4'd6: segment = 7'b0000010;  // 6
        4'd7: segment = 7'b1111000;  // 7
        4'd8: segment = 7'b0000000;  // 8
        4'd9: segment = 7'b0010000;  // 9
        default: segment = 7'b1111111; // 全灭
    endcase
end

在实际调试时,我发现段选信号的编码方式与数码管的共阴/共阳特性有关。我的开发板使用的是共阳数码管,所以0表示亮,1表示灭。如果你的开发板是共阴的,需要将段选信号取反。

5. 仿真测试与验证方法

仿真测试是FPGA开发中非常重要的一环,它可以帮助我们在下载到硬件之前发现和修复问题。在VIVADO中,我们可以编写Testbench来模拟实际的工作环境。

首先创建仿真文件,通常以_tb结尾,比如seven_segment_tb.v:

module seven_segment_tb;
    reg clk;
    reg reset_n;
    reg button;
    reg switch;
    wire [7:0] segment;
    wire [7:0] digit;
    
    // 实例化被测试模块
    seven_segment uut (
        .clk(clk),
        .reset_n(reset_n),
        .button(button),
        .switch(switch),
        .segment(segment),
        .digit(digit)
    );
    
    // 生成时钟信号
    initial begin
        clk = 0;
        forever #5 clk = ~clk;  // 100MHz时钟,周期10ns
    end
    
    // 测试过程
    initial begin
        // 初始化
        reset_n = 0;
        button = 1;
        switch = 0;
        
        // 释放复位
        #100 reset_n = 1;
        
        // 测试计数器模式
        #2000000;  // 等待一段时间观察计数
        
        // 测试相同数字模式
        switch = 1;
        #1000000;
        
        // 测试按钮功能
        button = 0;
        #100000;
        button = 1;
        
        #1000000;
        $finish;
    end
endmodule

在仿真时,为了减少仿真时间,我们可以修改分频系数。比如把5Hz的分频系数从20000000改为200,这样仿真时就能更快地观察到计数变化。

运行仿真后,我们需要查看波形图来验证设计是否正确。重点关注以下几个方面:

  1. 时钟分频:检查5Hz时钟是否正常生成
  2. 计数器:检查计数是否在2到254范围内循环
  3. BCD转换:检查二进制数是否正确转换为BCD码
  4. 动态扫描:检查位选信号是否按预期循环
  5. 段选信号:检查显示内容是否正确

如果发现任何问题,需要回到设计代码中进行调试。我建议使用VIVADO的ILA(Integrated Logic Analyzer)IP核进行在线调试,它可以在FPGA运行时捕获内部信号,非常实用。

6. 实际部署与调试技巧

代码通过仿真测试后,就可以部署到实际的FPGA开发板上了。首先需要编写约束文件,将Verilog中的信号映射到具体的管脚。

以Basys3开发板为例,约束文件可能长这样:

set_property PACKAGE_PIN W5 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]

set_property PACKAGE_PIN U18 [get_ports reset_n]
set_property IOSTANDARD LVCMOS33 [get_ports reset_n]

set_property PACKAGE_PIN T18 [get_ports button]
set_property IOSTANDARD LVCMOS33 [get_ports button]

set_property PACKAGE_PIN U17 [get_ports switch]
set_property IOSTANDARD LVCMOS33 [get_ports switch]

# 段选信号
set_property PACKAGE_PIN W7 [get_ports {segment[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segment[0]}]
# ... 其他段选信号

# 位选信号
set_property PACKAGE_PIN U2 [get_ports {digit[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {digit[0]}]
# ... 其他位选信号

生成比特流文件后,通过USB连接开发板,点击"Program device"将设计下载到FPGA中。

在实际调试时,可能会遇到一些问题。比如数码管显示闪烁,这可能是扫描频率太低导致的。或者显示内容不正确,可能是段选信号的编码方式错了。我遇到过最棘手的问题是数码管显示有重影,这是因为位选信号的切换时机和段选信号的更新时机没有配合好。

解决重影问题的方法是在切换位选信号之前,先关闭所有段选信号,切换到位选后再更新段选信号:

always @(posedge clk) begin
    if (scan_cnt == 16'd99900) begin
        segment <= 8'b11111111;  // 先关闭显示
    end else if (scan_cnt == 16'd99950) begin
        digit <= next_digit;      // 切换位选
    end else if (scan_cnt == 16'd0) begin
        segment <= next_segment;  // 更新段选
    end
end

另一个常见问题是显示亮度不均匀。这通常是因为不同数码管的点亮时间不一致,或者驱动能力不足。可以通过调整扫描时序或增加驱动电路来解决。

我在实际项目中还发现,按键输入需要做防抖动处理,否则可能会检测到多次按下。简单的防抖动可以在Verilog中实现:

reg [19:0] debounce_cnt = 0;
reg button_stable = 1;

always @(posedge clk) begin
    if (debounce_cnt >= 20'd1000000) begin  // 10ms防抖动
        button_stable <= button;
        debounce_cnt <= 0;
    end else begin
        debounce_cnt <= debounce_cnt + 1;
    end
end

这些调试经验都是在实际项目中一点点积累的,希望对你有所帮助。FPGA开发就是这样,理论知识和实践经验同样重要。多动手实践,遇到问题时耐心调试,你会发现自己的技能在不断提升。

用Vivado和Basys3 FPGA板,手把手教你实现数码管动态显示(Verilog实战) 本文详细介绍了如何使用Vivado和Basys3 FPGA板实现数码管动态显示系统,涵盖Verilog HDL编程、BCD码转换、动态扫描控制器设计等关键技术。通过实战案例,帮助读者掌握FPGA开发中的数码管驱动原理与实现方法,适用于嵌入式系统设计和电子竞赛项目。 阅读详情

相关推荐

FPGA数码管动态扫描Verilog实现与优化

数码管驱动是FPGA开发中的基础实践项目,通过动态扫描技术可显著减少IO资源占用。其核心原理是利用人眼视觉暂留效应,通过快速轮询点亮各位数码管形成稳定显示。Verilog硬件描述语言实现涉及时钟分频、扫描时序控制和段码解码等关键技术,在Nexys4 DDR开发板上仅需12个IO引脚即可驱动8位数码管。该技术广泛应用于工业控制面板、仪器仪表显示等领域,特别适合资源受限的FPGA设计。通过消除鬼影、亮度均衡等优化手段,可进一步提升显示质量,其中扫描频率设置在3.8kHz左右能获得最佳视觉效果。

weixin_29188997的博客 276

【FPGA】Verilog:组合逻辑电路应用 | 数码管 | 8421BCD编码 | 转换七段数码管段码

【FPGA】Verilog:组合逻辑电路应用 | 数码管 | 8421BCD编码 | 转换七段数码管段码

m0_66307842的博客 1万+

FPGA二进制转BCD码:移位加三算法与Verilog实现详解

在数字电路与嵌入式系统中,二进制与十进制之间的转换是连接硬件数据处理与人机交互界面的关键技术。其核心原理在于通过高效的硬件算法替代软件中简单的除法运算,以实现低延迟、高吞吐的数据转换。移位加三算法(Double Dabble)正是为此而生,它利用移位和条件加法模拟十进制进位,完美适配FPGA的并行架构。该算法的技术价值在于显著优化了时序与资源利用率,使其广泛应用于仪器仪表显示、工业控制器状态反馈及通信设备参数配置等场景。本文聚焦于该算法的Verilog硬件实现,深入剖析了组合逻辑展开与流水线设计等工程实践,

weixin_34060299的博客 542

FPGA学习笔记2.2——用Verilog实现七段管的工作逻辑

FPGA实验学习

nimo观察者 3949

FPGA数字时钟设计:Verilog实现七段管驱动与动态扫描

数字逻辑设计是嵌入式系统和硬件开发的基础,其核心在于通过计数器、状态机等基本单元构建时序电路。在FPGA开发中,Verilog HDL是实现这些逻辑的关键语言,它允许工程师以模块化方式描述硬件行为。模块化设计不仅提升了代码的可维护性,还便于功能复用和调试,是工程实践中的核心方法。其技术价值在于能够高效实现从简单计数器到复杂处理器的各类数字系统。一个典型的应用场景是驱动七段数码管显示,这需要结合时钟分频、动态扫描等关键技术。动态扫描技术通过快速轮流点亮多个数码管,利用人眼视觉暂留效应实现稳定显示,能极大节省F

weixin_30945039的博客 343

verilog七段数码管

verilog 写的七段数码管的显示 老师布置的作业,写了好几天!刚开始学verilog

verilog 7段数码管译码器

sed_dec.v: // 2022-1-20 verilog学习 // 七段码译码器 a-g7根管 `timescale 1ns/10ps; module sed_dec( num, a_g ); input[3:0] num; output[6:0] a_g; // a_g -->{a,b,c,d,e,f,g} reg[6:0] a_g; // always赋值需要定义为reg变量 always@(num)b..

踩坑记录 7577

Verilog HDL 七段数码管倒计时效果

这是大学期间我上VerilogHDL的七段数码管倒计时效果实验报告,报告中除了包括正确测试后的程序代码,我还加入了非常详细的注释。为了让读者更好理解程序代码和编写代码的思路,我还特意精心绘制了交通灯程序模块间的结构图。

FPGA基础知识极简教程(9)七段数码管显示的Verilog简单设计

七段显示器是FPGA设计人员通常用来向用户显示信息的指示器。在VHDL和Verilog中可以轻松完成将二进制文件转换为兼容七段显示器的代码。有许多应用程序可能需要使用一个或多个八段显示器,例如: * 闹钟 * 秒表 * 按钮计数指示器 * 电压测量(从模拟到数字转换器)

Reborn Lee 4万+

Verilog编写的七段数码管显示的源码

用Verilog实现FPGA七段数码管的显示。

7段数码管显示Verilog程序

此压缩文件为计数器和7段数码管显示功能,使用Verilog语言描述的模块

Verilog——7段数码管译码器

组合逻辑代码设计

weixin_41788560的博客 2万+

七段数码管Verilog文件

七段数码管Verilog文件,可以直接使用

FPGA实战:用二进制转BCD码驱动数码管,从仿真到上板(Vivado工程分享)

本文详细介绍了FPGA实战中二进制转BCD码驱动数码管的完整实现过程,包括项目架构设计、二进制转BCD模块优化、数码管驱动系统实现及Vivado工程实战。通过Verilog代码示例和性能对比,帮助开发者掌握从仿真到上板的完整流程,特别适合FPGA初学者和硬件工程师参考。

weixin_30819085的博客 354

Temporal Fusion Transformer(TFT)与扩散模型融合:时间序列预测新范式

本文探讨了Temporal Fusion Transformer(TFT)与扩散模型融合的时间序列预测新范式。该融合方案利用TFT强大的特征提取与可解释性,为扩散模型提供精准的“导航图”,从而在噪声数据中实现鲁棒预测,并原生支持不确定性量化,输出完整的概率分布与置信区间,为金融、能源等领域的风险决策提供更可靠的依据。

weixin_29096377的博客 292

基于springboot生鲜食品订购系统的设计与实现

近年来互联网络的迅猛发展和电子终端设备的普及,赋予了各行业充足的发展空间。微信小程序的O2O生鲜食品订购相比于传统信息技术,时效性是它最大的特色,已经在电子娱乐、经济等中发挥着举足轻重的作用。短时间内迅速扩大了线上管理系统的规模。尽管O2O生鲜食品订购已经有了很大程度的发展,但在微信小程序的O2O生鲜食品订购领域上却少有建设。

1024

基于Verilog与VIVADO的七段数码管动态扫描与BCD计数仿真实现

本文详细介绍了基于Verilog与VIVADO平台实现七段数码管动态扫描与BCD计数仿真的完整流程。通过核心模块设计、testbench仿真验证和硬件实现,展示了如何利用动态扫描技术节省IO资源并实现稳定显示,为数字电路设计提供实用参考。

desk3的博客 973

RACECAR底层平台安装:Jetson Nano+ROS Melodic全栈部署指南

移动机器人开发中,底层平台安装是将硬件转化为可编程移动平台的关键起点。其核心在于嵌入式系统、ROS中间件与传感器驱动的深度协同——需兼顾内核模块加载、GPIO/PWM精确控制、I2C设备初始化及ROS工作空间构建等多层技术耦合。尤其在竞速级小车如RACECAR场景下,Jetson Nano的硬件加速能力、MPU6050 IMU固件加载、TB6612FNG电机驱动配置等细节,直接决定运动控制精度与实时性。本文聚焦Ubuntu 18.04 + ROS Melodic + JetPack 4.4.1技术栈,覆盖从

weixin_32025789的博客 257

七段数码管的艺术:Verilog设计中的时序与显示优化策略

本文深入探讨了七段数码管在Verilog设计中的时序与显示优化策略,涵盖了刷新机制、视觉暂留优化、跨时钟域同步处理以及Vivado中的时序约束方法。通过动态亮度补偿算法和状态机共享技术,有效解决了显示闪烁和资源利用率问题,为FPGA项目提供了可靠的显示解决方案。

alice7model的博客 1039

Frida Stalker实战:如何用指令级追踪破解OLLVM混淆的AES算法

本文深入探讨了如何使用Frida Stalker进行指令级追踪,破解经过OLLVM混淆的AES算法。通过动态分析技术,逆向工程师可以精准识别控制流平坦化等混淆手段,还原算法逻辑,特别适用于移动安全领域的加密算法分析。文章提供了详细的配置指南、核心机制解析和实战案例,帮助读者掌握对抗高级代码混淆的有效方法。

weixin_29325955的博客 477

Stata做DID平行趋势检验,用coefplot画图时遇到的3个坑及解决办法

本文详细解析了使用Stata进行DID平行趋势检验时,利用coefplot绘图可能遇到的三个常见问题及其解决方案。包括基线期选择陷阱、置信区间变形和图形元素错位,提供专业调试步骤和高阶技巧,帮助研究者高效完成学术级图表绘制。

weixin_42530458的博客 327

FPGA七段数码管驱动设计:从动态扫描原理到Vivado实战

七段数码管是数字系统中最基础的人机交互显示器件,其驱动原理涉及数字逻辑设计中的译码、时序控制等核心概念。通过理解共阴/共阳极的硬件差异,掌握静态驱动与动态扫描两种方式,开发者可以在FPGA上实现高效稳定的显示控制。动态扫描技术利用人眼视觉暂留效应,通过分时复用IO引脚,解决了多位数码管显示时的引脚资源紧张问题,体现了数字系统设计中资源与性能的经典权衡。在工程实践中,使用Verilog/VHDL硬件描述语言,结合Vivado等开发工具,可以构建参数化的驱动模块,并通过时钟分频、状态机设计实现稳定扫描。针对常见

weixin_29184075的博客 278

Unity游戏开发实战:用Flow Field实现RTS百人军团流畅寻路(附完整代码)

本文深入探讨了在Unity中利用流场寻路技术解决RTS游戏大规模单位移动性能瓶颈的实战方案。通过解析从Dijkstra算法生成成本场、积分场到最终向量场的核心原理,并结合分块、缓存及与DOTS/ECS架构集成的优化策略,实现了百人军团的流畅群体寻路。文章提供了完整的代码示例,助力开发者构建高性能的RTS寻路系统。

weixin_28363123的博客 589
上一篇: 从零到一:STM32出租车计价器开发中的硬件选型与模块化设计陷阱
下一篇: 直流充电桩控制单元安全防护机制深度剖析——以JC-6511硬件架构为例
vim8coder
博客等级 码龄1天 0粉丝 1693原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值