当前位置: 首页 > article >正文

FPGA 26,数码管动态显示,解析与实现( 使用 Xilinx Vivado 实现数码管动态显示 )

目录

前言
一. 基本原理
1.1 数码管的结构
1.2 动态显示原理
二. 设计流程
三. 代码实现
1.show.v 模块
2. seg.v 模块
四. 段选位选
4.1 段选信号
4.2 位选信号
五. 注意事项
六. 文本总结
七. 更多操作

前言

数码管是电子设备中常见的显示组件,广泛应用于时钟、计数器、温度计等设备中。数码管显示分为静态显示和动态显示两种方式。静态显示简单直接,但占用资源较多;动态显示通过分时复用技术,能够以较少的硬件资源,实现多位数字的显示。上期分享了数码管的静态显示,这里来详细介绍如何使用Verilog HDL实现数码管的动态显示,并结合代码进行详细讲解。

看下实现的数码管动态显示效果:

一. 基本原理

数码管动态显示,通常涉及将多个数码管逐个点亮,而不是同时点亮所有数码管。这意味着在任何给定时间,实际上只有一个数码管是亮着的。它是通过高速切换各个数码管的显示内容,使人眼感觉所有的数码管都在同时显示不同的数字。

1.1 数码管的结构

数码管通常由多个发光二极管(LED)组成,分为共阴极和共阳极两种类型:

  1. 共阴极:所有LED的阴极连接在一起,阳极分别控制。

  2. 共阳极:所有LED的阳极连接在一起,阴极分别控制。

1.2 动态显示原理

数码管的动态显示,通过快速切换数码管的位选信号,依次点亮每个数码管,并显示对应的数字。由于切换速度非常快(通常几十毫秒),人眼无法察觉到闪烁,从而看到稳定的显示效果。这里实际上利用的就是人的视觉暂留效应。

视觉暂留科学实验证明,人眼在某个视像消失后,仍可使该物像在视网膜上滞留0.1 - 0.4秒左右。因此当切换各个数码管显示内容足够快的时候,你可以同时看到它们的显示内容,达到一个所有数码管常亮的效果。


二. 设计流程

1. 设计思路

数码管动态显示,设计思路如下

  1. 数据拆分:将需要显示的数字拆分为个位、十位、百位和千位。

  2. 状态机设计:使用状态机控制数码管的显示顺序,每个状态对应一个数码管。

  3. 延时控制:每个数码管显示一段时间(如1ms),然后切换到下一个数码管。

  4. 段选信号生成:根据当前显示的数字生成对应的段选信号。

  5. 位选信号控制:通过位选信号选择当前显示的数码管。

整体流程就是首先将4位数据进行拆分,分别拆分为个,十,百,千,四个数据。然后,通过状态机设计1ms显示切换,第一毫秒时,将个位数据进行显示,第二毫秒时,将十位数据进行显示,第三毫秒时,将百位数据进行显示,第四毫秒时,将千位数据进行显示,依次进行切换。 

2. 模块划分

数码管动态显示设计,主要分为 show.v 和 seg.v 两个模块,每个模块负责不同的功能,协同工作以实现稳定的多位数字显示。

show.v 模块

  • 功能:根据输入的数字生成对应的段选信号。
  • 输入:4位宽的数字。
  • 输出:8位宽的段选信号。

seg.v 模块

  • 功能:控制数码管的动态显示,调用 show.v 模块生成段选信号。
  • 输入:时钟信号和复位信号。
  • 输出:位选信号和段选信号。


三. 代码实现

以下是完整的Verilog代码实现,包含详细的注释:

1.show.v 模块

show.v 模块负责根据输入的数字生成对应的段选信号,用于控制数码管的显示

`timescale 1ns / 1ps
// 静态数码管显示模块
module show(
    input           clk,       // 时钟信号
    input           rst_n,     // 复位信号,低电平有效
    input [3:0]     number,    // 输入的数字,4位宽,范围0-9
    output reg [7:0] seg       // 数码管段选信号,8位宽,控制数码管的各段
);

assign dig = 4'b0000; // 数码管位选信号,共阴极数码管,此处设置为全低电平(选中所有数码管)

always @(*) begin
    case (number) // 根据输入的数字选择对应的段选信号
        0: seg = 8'hfc; // 数字0对应的段选信号
        1: seg = 8'h60; // 数字1对应的段选信号
        2: seg = 8'hda; // 数字2对应的段选信号
        3: seg = 8'hf2; // 数字3对应的段选信号
        4: seg = 8'h66; // 数字4对应的段选信号
        5: seg = 8'hb6; // 数字5对应的段选信号
        6: seg = 8'hbe; // 数字6对应的段选信号
        7: seg = 8'he0; // 数字7对应的段选信号
        8: seg = 8'hfe; // 数字8对应的段选信号
        9: seg = 8'hf6; // 数字9对应的段选信号
        default: seg = 8'hfc; // 默认显示数字0
    endcase
end

endmodule

代码说明:

  1. 输入信号

    • clk:时钟信号。

    • rst_n:复位信号,低电平有效。

    • number:4位宽的数字输入,范围是0-9。

  2. 输出信号

    • seg:8位宽的段选信号,用于控制数码管的各段显示。

  3. 段选信号

    • 根据输入的数字 number,生成对应的段选信号 seg

    • 例如,8'hfc 表示数码管的 a、b、c、d、e、f 段亮,g 和 dp 段灭,显示数字0。

  4. 默认值

    • 如果 number 的值不在0-9范围内,默认显示数字0。

2. seg.v 模块

seg.v 模块负责控制数码管的动态显示,通过状态机依次显示个位、十位、百位和千位数字

`timescale 1ns / 1ps
module seg(
    input       clk,       // 时钟信号
    input       rst_n,     // 复位信号,低电平有效
    output reg [3:0] dig,  // 位选信号,控制数码管的位选
    output [7:0] seg       // 段选信号,控制数码管的段选
);

// 定义常量
parameter show_number = 1234; // 要显示的数字
parameter delay_1ms = 50_000; // 1ms 延迟对应的时钟周期数

// 定义状态编码
parameter idle = 5'b0_0001, // 空闲状态
          s0 = 5'b0_0010,   // 显示个位
          s1 = 5'b0_0100,   // 显示十位
          s2 = 5'b0_1000,   // 显示百位
          s3 = 5'b1_0000;   // 显示千位

// 计算各位数字
wire [3:0] number0 = show_number % 10;       // 个位
wire [3:0] number1 = (show_number / 10) % 10; // 十位
wire [3:0] number2 = (show_number / 100) % 10; // 百位
wire [3:0] number3 = show_number / 1000;      // 千位

// 状态寄存器
reg [4:0] cur_state, next_state;

// 1ms 计数器
reg [15:0] cnt_1ms;

// 当前显示的数字
reg [3:0] number;

// 状态转移逻辑
always @(posedge clk) begin
    if (!rst_n)
        cur_state <= idle; // 复位时进入空闲状态
    else
        cur_state <= next_state; // 更新当前状态
end

// 状态机逻辑
always @(*) begin
    case (cur_state)
        idle: begin
            next_state = s0; // 空闲状态后进入显示个位状态
        end
        s0: begin
            if (cnt_1ms == delay_1ms - 1)
                next_state = s1; // 1ms 后进入显示十位状态
            else
                next_state = s0;
        end
        s1: begin
            if (cnt_1ms == delay_1ms - 1)
                next_state = s2; // 1ms 后进入显示百位状态
            else
                next_state = s1;
        end
        s2: begin
            if (cnt_1ms == delay_1ms - 1)
                next_state = s3; // 1ms 后进入显示千位状态
            else
                next_state = s2;
        end
        s3: begin
            if (cnt_1ms == delay_1ms - 1)
                next_state = s0; // 1ms 后回到显示个位状态
            else
                next_state = s3;
        end
        default: next_state = s0; // 默认回到显示个位状态
    endcase
end

// 位选和数字选择逻辑
always @(posedge clk) begin
    if (!rst_n) begin
        dig <= 4'b1111; // 复位时关闭所有数码管
        number <= 0;
    end else begin
        case (cur_state)
            idle: begin
                dig <= 4'b1111; // 空闲状态关闭所有数码管
                number <= 0;
            end
            s0: begin
                dig <= 4'b1110; // 选择个位数码管
                number <= number0; // 显示个位数字
            end
            s1: begin
                dig <= 4'b1101; // 选择十位数码管
                number <= number1; // 显示十位数字
            end
            s2: begin
                dig <= 4'b1011; // 选择百位数码管
                number <= number2; // 显示百位数字
            end
            s3: begin
                dig <= 4'b0111; // 选择千位数码管
                number <= number3; // 显示千位数字
            end
            default: begin
                dig <= 4'b1111; // 默认关闭所有数码管
                number <= 0;
            end
        endcase
    end
end

// 1ms 计数器逻辑
always @(posedge clk) begin
    if (!rst_n)
        cnt_1ms <= 0; // 复位时计数器清零
    else begin
        case (cur_state)
            s0, s1, s2, s3: begin
                if (cnt_1ms == delay_1ms - 1)
                    cnt_1ms <= 0; // 达到 1ms 后计数器清零
                else
                    cnt_1ms <= cnt_1ms + 1; // 计数器加 1
            end
            default: cnt_1ms <= 0; // 其他状态计数器清零
        endcase
    end
end

// 实例化数码管显示模块
show show_u(
    .clk (clk),
    .rst_n (rst_n),
    .number (number),
    .seg (seg)
);

endmodule

代码说明:

  1. 输入信号

    • clk:时钟信号。

    • rst_n:复位信号,低电平有效。

  2. 输出信号

    • dig:4位宽的位选信号,控制数码管的位选。

    • seg:8位宽的段选信号,控制数码管的段选。

  3. 状态机设计

    • 使用状态机控制数码管的显示顺序,依次显示个位、十位、百位和千位。

    • 每个状态对应一个数码管,显示时间为1ms。

  4. 数字拆分

    • 将需要显示的数字拆分为个位、十位、百位和千位,分别存储在 number0number1number2 和 number3 中。

  5. 延时控制

    • 使用计数器实现1ms的延时,确保每个数码管显示时间一致。

  6. 实例化 show 模块

    • 调用 show 模块,根据当前数字生成段选信号。

show.v 和 seg.v 这两个模块共同实现了数码管的动态显示功能。show.v 模块将输入的数字转换为段选信号,控制数码管各段的亮灭。seg.v 模块通过状态机依次选择数码管,并调用 show.v 生成段选信号,实现动态显示。两个模块协作,高效完成多位数字的稳定显示。


四. 段选位选与引脚绑定

段选信号和位选信号,是数码管显示设计中的关键控制信号。段选信号,通过控制数码管的各段亮灭来决定显示的内容,而位选信号,则用于选择当前显示的数码管。

4.1 段选信号

段选信号控制数码管各段(a、b、c、d、e、f、g、dp)的亮灭,决定显示的数字或字符。它是一个8位二进制信号,每位对应一段,通过不同编码显示0-9等字符。

以下是段选信号的引脚定义:

  • U15-a:段a

  • W15-b:段b

  • U17-c:段c

  • V18-d:段d

  • W18-e:段e

  • U19-f:段f

  • U14-g:段g

  • Y14-dp:小数点

4.2 位选信号

位选信号用于选择当前显示的数码管。在动态显示中,多个数码管共享段选信号后,通过位选信号依次选择每个数码管。它是一个4位二进制信号,每位对应一个数码管,快速切换实现稳定显示。

以下是位选信号的引脚定义:

  • P15-W1:数码管1

  • V15-W2:数码管2

  • V17-W3:数码管3

  • V18-W4:数码管4

段选: U15-a W15-b U17-c V18-d W18-e U19-f U14-g Y14-dp

位选: P15-W1 V15-W2 V17-W3 V18-W4


五. 注意事项

  1. 时钟频率:代码中的 delay_1ms 需要根据实际时钟频率进行调整,以确保1ms的延时准确。

  2. 复位信号:复位信号 rst_n 为低电平有效,复位时应将所有状态和计数器清零。

  3. 数码管类型:代码中默认使用共阴极数码管,如果使用共阳极数码管,需要调整位选和段选信号的逻辑。

  4. 段选信号定义:段选信号 seg 的具体值需要根据数码管的段选编码表进行调整,确保显示正确。

  5. 资源占用:动态显示虽然节省了硬件资源,但需要较高的切换频率,可能会增加功耗。


六. 文本总结

本文详细介绍了如何使用Verilog实现数码管的动态显示,通过状态机控制数码管的显示顺序,并结合延时计数器实现稳定的显示效果。动态显示技术不仅节省了硬件资源,还能够实现多位数字的显示,是数字电路设计中的常用技术。希望这里的内容,能够帮助你更好地理解和应用数码管动态显示技术。


七. 更多操作

完整FPGA系列,请看

FPGA系列,文章目录https://blog.csdn.net/weixin_65793170/article/details/144185217?spm=1001.2014.3001.5501https://blog.csdn.net/weixin_65793170/article/details/144185217?spm=1001.2014.3001.5501https://blog.csdn.net/weixin_65793170/article/details/144185217?spm=1001.2014.3001.5501https://blog.csdn.net/weixin_65793170/article/details/144185217?spm=1001.2014.3001.5501https://blog.csdn.net/weixin_65793170/article/details/144185217?spm=1001.2014.3001.5501


http://www.kler.cn/a/522292.html

相关文章:

  • gesp(C++六级)(6)洛谷:P10109:[GESP202312 六级] 工作沟通
  • css中的animation
  • doris:Bitmap
  • 系统思考—心智模式
  • 十年筑梦,再创鲸彩!庆祝和鲸科技十周年
  • postgres基准测试工具pgbench如何使用自定义的表结构和自定义sql
  • 计算机网络之计算机网络基本概念
  • 【Leetcode 每日一题】45. 跳跃游戏 II
  • Linux 命令之技巧(Tips for Linux Commands)
  • QT 笔记
  • 深入探讨防抖函数中的 this 上下文
  • 论文笔记(六十五)Schmidt-EKF-based Visual-Inertial Moving Object Tracking
  • LeetCode-175. 组合两个表
  • H2 Database安装部署
  • VMware 中Ubuntu无网络连接/无网络标识解决方法【已解决】
  • PHP Error处理与优化指南
  • volatile之四类内存屏障指令 内存屏障 面试重点 底层源码
  • 多模态论文笔记——TECO
  • 已解决:Win10任务状态栏卡死点击无响应的解决方案
  • 【SAP-PP】生产订单和计划订单
  • DeepSeek-R1试用
  • AI在Facebook平台中的安全应用探索
  • JAVA 接口、抽象类的关系和用处 详细解析
  • Python-基础环境(01) 虚拟环境,Python 基础环境之虚拟环境,一篇文章助你完全搞懂!
  • 通过案例研究二项分布和泊松分布之间关系(2)
  • Lucene中DocValues 和 Stored Fields 的用法