二、19【FPGA】数码管动态显示实验

本文介绍了一种基于FPGA的六位数码管动态显示设计,包括二进制到BCD码转换、动态扫描原理及其实现方法。设计能够实现0至999999的计数显示。

前言

学习说明此文档为本人的学习笔记,注重实践,关于理论部分会给出相应的学习链接。

学习视频:是根据野火FPGA视频教程——第二十二讲
https://www.bilibili.com/video/BV1nQ4y1Z7zN?p=3

理论学习

1、数码管动态显示

“天下武功唯快不破”  “看到的不一定为真”

眼睛的视觉暂留:光信号传入大脑需要短暂时间,只要变化的频率大于传输的的频率,那么你看到的事物将是一直存在的,并不会出现闪动,这就是视觉的暂留现象。

数码管的余晖效应:当停止向发光二极管亮度仍然维持一段时间。

动态扫描,当数码管的闪烁时间间隔为1ms时,眼睛将不会察觉有闪烁感。以6ms为一个周期,每个数码管显示1ms,循环显示方法。

2、二进制转BCD码

BCD 码(Binary-Coded Decimal),又称二 - 十进制码,使用 4 位二进制数来表示 1 位十进制数中的 0~9 10 个数码,是一种二进制的数字编码形式,用二进制编码的十进制代码。

分为:有权码和无权码

 将其每位二进制数乘以它的权值然后相加。十进制 5 8421BCD 码为 0101,即:1×1 + 0×2 + 1×4+ 0×8 = 5,每4位代表一个十进制数

 十进制:234            二进制:1110_1010           BCD:0010_0011_0100

如何通过二进制求出每一位的十进制数:

  • stp1:在相应二进制前面补(十进制位数*4),如10则补8位0,234则补12位
  • stp2:判断每四位二进制数(一位十进制数)是否大于4,当>4时每四位二进制数加3(+0011)
  • stp3:向左移1位
  • stp4:循环stp2和stp3
  • stp5:直到将输入二进制数全都移位完成。需要移的位数就是二进制码的位数,得到BCD码每四位的十进制数就是需要显示的数。

实战演练

一、设计规划

1.1 实验要求

让6位数码管显示从十进制数0开始计数,每次0.1s加1,一直加到十进制数999999。到达999999之后回到0开始重新计数。

1.2 硬件资源

和静态显示的硬件资源相同

二、模块框图绘制

2.1 功能模块框图

2.2 Data_gen 数据生成模块

  • 波形图

  • 程序代码 
module data_gen
#(
    parameter CNT_MAX = 23'd4999999,
    parameter DATA_MAX = 20'd999999             
)
(
    input wire sys_clk,
    input wire sys_rst_n,
    output reg [19:0] data,
    output wire [5:0] point,
    output reg seg_en,
    output wire sign
    );
    reg [22:0] cnt_100ms;
    reg  cnt_flag;
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            cnt_100ms <= 23'd0;
        else if(cnt_100ms == CNT_MAX)
            cnt_100ms <= 23'd0;
        else 
            cnt_100ms <= cnt_100ms + 1'b1;
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            cnt_flag <= 1'b0;
        else if(cnt_100ms == CNT_MAX - 1'b1)
            cnt_flag <= 1'b1;
        else 
            cnt_flag <= 1'b0;    
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            data <= 20'd0;
        else if(data == DATA_MAX && cnt_100ms == CNT_MAX)
            data <= 20'd0;
        else if(cnt_flag == 1'b1)
            data <= data + 1'b1;
        else 
            data <= data;   
    assign point = 6'b000000;
    assign sign  = 1'b0;
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)     
            seg_en <= 1'b0;  
        else 
            seg_en <= 1'b1;
endmodule
  •  仿真代码

这里仿真文件只对输入信号进行了输入,输出代码可以通过添加相关代码来查看。但是如果模块框图较多,还是建议仿真时加上想要输入的波形。

`timescale 1ns / 1ns
module tb_data_gen();
    reg sys_clk;
    reg sys_rst_n;
    
    initial begin
        sys_clk = 1'b1;
        sys_rst_n <= 1'b0;
        #20
        sys_rst_n <= 1'b1;
    end 
    always #10 sys_clk = ~sys_clk;


    data_gen
    #(
        .CNT_MAX (9),
        .DATA_MAX(5)             
    )
    data_gen_inst
    (
        .sys_clk  (sys_clk  ),
        .sys_rst_n(sys_rst_n)    
    );
endmodule
  • 仿真波形

通过与绘制的波形对比,可知绘制波形图是正确的

2.3 bcd_8421二进制转十进制模块

  • 波形图

### 实现FPGA控制数码管动态显示0-19的方案 为了实现FPGA控制数码管动态显示0-19的功能,需要设计一个完整的时序逻辑电路。以下是具体的设计思路和代码示例。 #### 1. 数码管显示原理 数码管分为共阳极和共阴极两种类型。对于共阳极数码管,段码为低电平时点亮;对于共阴极数码管,段码为高电平时点亮[^3]。在动态显示中,通过快速扫描多个数码管,并利用人眼的视觉暂留效应,使每个数码管看起来同时显示数字。 #### 2. 设计步骤 设计的核心是生成动态扫描信号和对应的段码信号。以下是一个基于Verilog的实现方案。 #### 3. Verilog代码示例 ```verilog module dynamic_display( input clk, // 系统时钟 input rst_n, // 复位信号,低电平有效 output reg [7:0] seg, // 段码输出 output reg [3:0] dig // 位选输出 ); // 定义显示数据 reg [3:0] counter; // 计数器,用于生成0-19 always @(posedge clk or negedge rst_n) begin if (!rst_n) counter <= 4'd0; else if (counter == 19) counter <= 4'd0; else counter <= counter + 1'b1; end // 数码管段码表 reg [7:0] seg_code [0:15]; initial begin seg_code[0] = 8'h3f; // 0 seg_code[1] = 8'h06; // 1 seg_code[2] = 8'h5b; // 2 seg_code[3] = 8'h4f; // 3 seg_code[4] = 8'h66; // 4 seg_code[5] = 8'h6d; // 5 seg_code[6] = 8'h7d; // 6 seg_code[7] = 8'h07; // 7 seg_code[8] = 8'h7f; // 8 seg_code[9] = 8'h6f; // 9 seg_code[10] = 8'h77; // A seg_code[11] = 8'h7c; // b seg_code[12] = 8'h39; // C seg_code[13] = 8'h5e; // d seg_code[14] = 8'h79; // E seg_code[15] = 8'h71; // F end // 动态扫描逻辑 reg [1:0] digit_select; // 当前选择的数码管 always @(posedge clk or negedge rst_n) begin if (!rst_n) digit_select <= 2'd0; else digit_select <= digit_select + 1'b1; end always @(*) begin case (digit_select) 2'd0: begin seg = seg_code[counter % 10]; // 显示个位 dig = 4'b1110; // 选择第一个数码管 end 2'd1: begin seg = seg_code[counter / 10]; // 显示十位 dig = 4'b1101; // 选择第数码管 end default: begin seg = 8'h00; dig = 4'b1111; end endcase end endmodule ``` #### 4. 代码说明 - **`seg_code`**:定义了数码管的段码表,用于将数字转换为对应的段码。 - **`counter`**:计数器从0到19循环递增,用于生成要显示的数字。 - **`digit_select`**:用于选择当前显示数码管(个位或十位)。 - **`seg` 和 `dig`**:分别表示段码输出和位选输出。 #### 5. 注意事项 - 确保时钟频率足够高以满足动态扫描的需求,通常使用几十MHz的时钟。 - 如果使用的是共阴极数码管,则需要将段码取反[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

追逐者-桥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值