FPGA_数码管显示

1,数码管介绍

一位数码管:

数码管等效电路(共阴极 和 共阳极)

数码管显示的值:

假设我们需要b,c亮,我们只需要给b,c接高电平,其他接低电平就可。

seg[7:0]  = 8'b0000_0110

对于数码管显示的值,seg值如下图:

多位数码管----->如下图(以3位为例)

假设现在需要LED1亮,那么就让sel0为1,数码管0的LED0-LED7阳极都是高电平,然后再控制a为低电平,那么就实现了数码管0的LED0点亮。

如果是8个数码管呢

8个sel信号

2,位选输出

这个规律就是3-8译码器  我们要按顺序点亮每一位,就需要1个3位的计数器(控制位切换信号):

利用人眼视觉暂留效应,得到多个数码管同时点亮的效果

每个数码管20ms带点亮一次,我们有8个数码管,那么数码管的切换时间就是20/8=2.5ms,保险一点改为1ms切换一个数码管。

1ms计时就设计一个1ms的计数器(预留位宽大一点30位)

设计一个比较器,当技术达到指定要求就输出1,当作cnt_sel的时钟使能信号以及自己本身计数的清0信号。

现在就可以实现8位数码管位选输出了。

3,段选输出

使用查找表形式在实现所要显示的字符。

所谓查找表就是,我预先设定好一个表,在每个表项里面存放需要输出的具体值。把每个字符输出的段码存放在表里面,然后把每个字符的显示内容作为查找表的索引号,只要给出索引号,就能查出对应的值。

16个索引号 4位数据表示temp_data[3:0]

根据当前动态扫描,正在扫描在哪个数码管,就将这个数码管需要显示的内容送到查找表的输入端口去,将需要显示的字符内容作为查找表的索引号输入进去。

8个数码管用一个8选1的多路选择器,利用段选中的cnt_sel作为选通通道--就能实现在扫描对应数码管的时候让段选就是这个数码管。

如果要显示更多字符:

这里 是8个通道,每个通道4位,那么把这8个通道合成一个32位数据data[31:0]

4,代码编写

整体设计符号图:

根据以上描述,编写代码:

module digitial_tube_0(
    Clk,
    Reset_n,
    Disp_Data,
    SEL,
    SEG
    );
    input Clk;
    input Reset_n;
    input [31:0]Disp_Data;
    output reg [7:0]SEL;
    output reg [7:0]SEG;
    
    parameter MCNT_1MS = 1000000/20 -1;
    parameter MCNT_SEL = 8-1;
    reg [15:0]cnt_1ms;
    reg [2:0]cnt_sel;
    reg [7:0]encode_sel;
    reg [7:0]LUT_seg;
    reg [3:0]data_temp;
    //1ms计数器
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        cnt_1ms <= 0;
    else if(cnt_1ms == MCNT_1MS)
        cnt_1ms <= 0;
    else 
        cnt_1ms <= cnt_1ms + 1'b1;
    //位选计数器
    always@(posedge Clk or Reset_n)
    if(!Reset_n)
        cnt_sel <= 0;
    else if(cnt_1ms == MCNT_1MS) begin
        if(cnt_sel == MCNT_SEL)
            cnt_sel <= 0;
        else
            cnt_sel <= cnt_sel + 1'b1;
    end 
    else
        cnt_sel <= cnt_sel;
    
    //3_8译码器
    always@(*)
        case(cnt_sel)
            3'b000:encode_sel = 8'b0000_0001; 
            3'b001:encode_sel = 8'b0000_0010; 
            3'b010:encode_sel = 8'b0000_0100; 
            3'b011:encode_sel = 8'b0000_1000; 
            3'b100:encode_sel = 8'b0001_0000; 
            3'b101:encode_sel = 8'b0010_0000; 
            3'b110:encode_sel = 8'b0100_0000; 
            3'b111:encode_sel = 8'b1000_0000;
        endcase
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        SEL <= 0;
    else
        SEL <= encode_sel;
   //段选

    always@(*)
        case(cnt_sel)
            3'b000:data_temp = Disp_Data[3:0]; 
            3'b001:data_temp = Disp_Data[7:4]; 
            3'b010:data_temp = Disp_Data[11:8]; 
            3'b011:data_temp = Disp_Data[15:12]; 
            3'b100:data_temp = Disp_Data[19:16]; 
            3'b101:data_temp = Disp_Data[23:20]; 
            3'b110:data_temp = Disp_Data[27:24]; 
            3'b111:data_temp = Disp_Data[31:28];
        endcase
    
    always@(*)
    case(data_temp)
        0 : LUT_seg = 8'hc0;
        1 : LUT_seg = 8'hf9;
        2 : LUT_seg = 8'ha4;
        3 : LUT_seg = 8'hb0;
        4 : LUT_seg = 8'h99;
        5 : LUT_seg = 8'h92;
        6 : LUT_seg = 8'h82;
        7 : LUT_seg = 8'hf8;
        8 : LUT_seg = 8'h80;
        9 : LUT_seg = 8'h90;
        4'ha :LUT_seg = 8'h88;
        4'hb :LUT_seg = 8'h83;
        4'hc :LUT_seg = 8'hc6;
        4'hd :LUT_seg = 8'ha1;
        4'he :LUT_seg = 8'h86;
        4'hf :LUT_seg = 8'h8e;
    endcase
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        SEG <= 0;
    else
        SEG <=   LUT_seg;  
            
endmodule

编写testbench,测试代码:

`timescale 1ns / 1ps
module digitial_tubr_1_tb();
    reg Clk;
    reg Reset_n;
    reg [31:0]Disp_Data;
    wire [7:0]SEL;
    wire [7:0]SEG;
    digitial_tube_0 digitial_tube_0(
        .Clk(Clk),
        .Reset_n(Reset_n),
        .Disp_Data(Disp_Data),
        .SEL(SEL),
        .SEG(SEG)
        );
    initial Clk = 1;
    always #10 Clk = ~Clk;
    
    initial begin
        Reset_n = 0;
        Disp_Data = 32'h00000000;
        #201
        Reset_n = 1;
        #200;
        Disp_Data = 32'h12345678;
        #10000000
        Disp_Data = 32'h9abcdef0;
        #10000000
        $stop;       
    end  
endmodule

仿真波形如下:

至此,数码管驱动逻辑编写完毕,但是在实际应用此输出占用的管脚过多,所以为了节省FPGA的管脚,需要将输出信号并转串后(FPGA只需要输出3个管脚),再利用串转并芯片(比如74HC595)连接数码管去显示需要输出的字符。

### FPGA 上实现数码管动态显示的方法 #### 设计概述 数码管动态显示利用了“人眼视觉暂留”现象和“数码管余晖效应”。当每个数码管快速轮流显示,速度达到一定程度时,人眼会认为这些数码管是在同时显示不同数值。为了实现这一功能,在FPGA中通常采用状态机来控制各个位的切换,并配合定时器确保每位显示的时间足够短以避免闪烁[^3]。 #### 关键组件介绍 ##### 1. 状态机控制器 状态机用于管理多个数码管之间的轮询过程。每当到达预设时间间隔(例如1毫秒),状态机会触发一次转换操作,依次选择下一个要激活的数码管位置。这种机制可以有效减少所需的I/O引脚数量,从而节约硬件资源[^1]。 ```verilog // Verilog代码片段:简单状态机定义 module state_machine ( input wire clk, // 主时钟信号 output reg [N-1:0] sel // 当前选中的数码管索引 ); parameter N = 4; // 假设有四个数码管 integer i; always @(posedge clk) begin if (i >= N-1) i <= 0; else i <= i + 1; end assign sel = {N{1'b0}} | (1 << i); // 将当前索引转为one-hot编码形式 endmodule ``` ##### 2. 定时逻辑单元 为了保持稳定无闪烁的效果,每一轮完整的扫描周期应设定在一个合理范围内——一般建议不超过几十毫秒。对于单个数码管而言,则需保证其点亮持续时间为几毫秒级别。这可以通过内部计数器或外部晶振产生的脉冲来进行精确调控[^4]。 ```verilog // Verilog代码片段:基于计数器的延迟生成 module delay_counter( input wire clk, output reg tick_1ms ); reg [9:0] count; always @(posedge clk) begin if(count == 10'd999) begin // 计满溢出重置并发出tick事件 count <= 0; tick_1ms <= ~tick_1ms; end else begin count <= count + 1; end end endmodule ``` ##### 3. 数据映射与译码电路 此部分负责将待显示的信息转化为适合数码管识别的形式。具体来说,就是把十进制数字转换成对应的七段码格式。这部分可以直接用组合逻辑完成,也可以预先存储好所有可能的结果以便查询表的方式调用[^2]。 ```ver_logic // Verilog代码片段:7段LED显示器解码函数 function [6:0] seven_seg_decoder(input [3:0] digit); case(digit) 4'h0 : seven_seg_decoder = 7'b1000000; 4'h1 : seven_seg_decoder = 7'b1111001; ... default: seven_seg_decoder = 7'bx; endcase endfunction ``` #### 测试与验证 开发完成后应当进行全面测试,包括但不限于静态仿真、门级模拟以及实际平台上的运行检验。特别是后者,可借助Xilinx Vivado提供的Virtual Input/Output(VIO)工具实时调整输入参数观察输出变化情况,确保整个系统的正常运作。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值