首页 > 其他分享 >FPGA入门笔记008——数码管动态扫描设计与验证

FPGA入门笔记008——数码管动态扫描设计与验证

时间:2024-03-19 10:44:52浏览次数:44  
标签:disp FPGA Clk seg 数码管 Rst 008 data

# FPGA入门笔记008——数码管动态扫描设计与验证

1、数码管动态扫描原理

​ 8段数码管的结构图如图1所示:

image-20240311143540748
图1——8段数码管结构图(a为共阴极,b为共阳极)

​ 对于共阴数码管需要给对应段以高电平才会使其点亮,而对于共阳极数码管则需要给低电平才会点亮。AC620上板载的是共阳极数码管。

​ 不考虑小数点也就是简化为7段数码管,其共阳极数码管编码译码格式如表1所示(h管脚即为图1的dp管脚):

image-20240311145328186 image-20240311145427986
表1——8段共阳极数码管编码译码表

​ 为了节约 IO 以及成本一般采用如图2所示的电路结构:

image-20240311150323746
图2——三位数码管等效电路图

​ 使用动态显示。动态显示的特点是将所有位数码管的段选线(abcdefgh)并联在一起,由位选线(sel1、sel2、sel3)控制是哪一位数码管有效。

​ 所谓动态扫描显示即轮流向各位数码管送出字形码和相应的位选,利用发光管的余辉和人眼视觉暂留作用,使人的感觉好像各位数码管同时都在显示。

​ 现在举例假设将扫描时间定为1s,这三个数码管一共分成3s:

​ 第1秒时sel数据线上为3'b100,这时数码管0被选中,这时a=0,数码管0 的LED0就可以点亮;

​ 第2秒时sel数据线上为3'b010,这时数码管1被选中,这时b=0,数码管1的LED1就可以点亮;

​ 第3秒时sel数据线上为3'b001,这时数码管2被选中,这时c=0,数码管2的LED2就可以点亮。

​ 这时的效果就会是数码管0的LED0亮一秒后数码管1的LED1亮一秒最后是数码管2的LED2亮一秒,然后往复循环。

​ 这样如果将扫描时间定为1ms,由于数码管的余辉效应以及人的视觉暂留,就会出现数码管0的LED0、数码管1的LED1以及数码管2 的LED2“同时”亮,并不会有闪烁感。

2、模块分配

​ 1、4输入查找表(根据表1——8段共阳极数码管编码译码表,显示0123456789abcdef),8位输出(段码,十六进制格式);

​ 2、分频模块:由于系统时钟过快(50MHz),所以需要分频到1KHz(1ms)的扫描时钟;

​ 3、8选1多路器:设置一个32位的输入数据[31:0]disp_data,其中:

​ 数码管0对应disp_data[3:0];

​ 数码管1对应disp_data[7:4];

​ ......

​ 数码管7对应disp_data[31:28]。并设置待显示内容寄存器data_tmp[3:0]。

​ 当选中数码管0时,data_tmp[3:0]=disp_data[3:0];

​ 当选中数码管1时,data_tmp[3:0]=disp_data[7:4];

​ ......

​ 当选中数码管7时,data_tmp[3:0]=disp_data[31:28]。

​ 该多路器选择端为当前扫描的数码管位置:sel_r[7:0]。

image-20240311163232410
图3——8选1多路器模块逻辑电路图

​ 4、8位循环移位寄存器:利用循环移位寄存器实现0000_0001b→1000_0000b的变化,进而实现数码管的位选,即实现每个扫描时钟周期选择一个数码管。移位寄存器输出值与数码管选通的对应关系如表2所示,其中sel7为高位。

image-20240311162648236
表2——8位循环移位寄存器与数码管对应关系

3、数码管驱动模块总逻辑电路图

​ 其模块总逻辑电路图如图4所示:

image-20240311164213555

图4——数码管驱动模块总逻辑电路图

​ 不使用小数点的话,则为7段数码管:seg[6:0]。

4、各模块代码编写

0、设置输入输出

module HEX8(
    Clk,
    Rst_n,
    En,
    disp_data,
    sel,
    seg
);

    input Clk;  //50M
    input Rst_n;
    input En;   //数码管显示使能,1使能,0关闭

    input [31:0]disp_data;

    output [7:0]sel;    //数码管位选(选择当前要显示的数码管)
    output reg[6:0]seg;    //数码管段选(当前要显示的内容)
    
endmodule

1、分频器模块

​ 将系统时钟(50MHz,20ns)经过分频器模块分为1KHz(1ms)时钟,相当于时钟每500us翻转一次,所以计数器clk_1K计数到每25000次(15位宽)分频时钟翻转一次。故设置计数器[14:0]divider_cnt。

//分频计数器模块(计数器)
    reg [14:0]divider_cnt;  //设置15位计数器cnt,最大计数到24999

    reg clk_1K;

    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        divider_cnt <= 15'd0;
    else if(!En)
        divider_cnt <= 15'd0;
    else if(divider_cnt == 24999)
        divider_cnt <= 15'd0;
    else
        divider_cnt <= divider_cnt + 1'b1;

    //设定出1KHz的时钟
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        clk_1K <= 1'b0;
    else if(divider_cnt == 24999)
        clk_1K <= ~clk_1K;
    else
        clk_1K <= clk_1K;

2、8位循环移位寄存器模块

//8位循环移位寄存器模块
    reg [7:0]sel_r;

    always@(posedge clk_1K or negedge Rst_n)
    if(!Rst_n)
        sel_r <= 8'b0000_0001;
    else if(sel_r == 8'b1000_0000)
        sel_r <= 8'b0000_0001;
    else
        sel_r <= sel_r << 1;    //左移

3、8选1多路器模块

//8选1多路器模块
    reg [3:0]data_tmp;

    always@(*)  //'*'表示所有输入信号都作为敏感信号(适用于组合逻辑)
        case(sel_r)
            8'b0000_0001:data_tmp = disp_data[3:0];
            8'b0000_0010:data_tmp = disp_data[7:4];
            8'b0000_0100:data_tmp = disp_data[11:8];
            8'b0000_1000:data_tmp = disp_data[15:12];
            8'b0001_0000:data_tmp = disp_data[19:16];
            8'b0010_0000:data_tmp = disp_data[23:20];
            8'b0100_0000:data_tmp = disp_data[27:24];
            8'b1000_0000:data_tmp = disp_data[31:28];
            default:data_tmp = 4'b0000;
        endcase

4、LUT模块(4输入查找表)、二选一多路器

//LUT模块(4输入查找表)
    always@(*)
        case(data_tmp)
            4'h0:seg = 7'b100_0000;
            4'h1:seg = 7'b111_1001;
            4'h2:seg = 7'b010_0100;
            4'h3:seg = 7'b011_0000;
            4'h4:seg = 7'b001_1001;
            4'h5:seg = 7'b001_0010;
            4'h6:seg = 7'b000_0010;
            4'h7:seg = 7'b111_1000;
            4'h8:seg = 7'b000_0000;
            4'h9:seg = 7'b001_0000;
            4'ha:seg = 7'b000_1000;
            4'hb:seg = 7'b000_0011;
            4'hc:seg = 7'b100_0110;
            4'hd:seg = 7'b010_0001;
            4'he:seg = 7'b000_0110;
            4'hf:seg = 7'b100_1110;
        endcase

//二选一多路器
    assign sel = (En)?sel_r:8'b0000_0000;

5、仿真

`timescale 1ns/1ns

`define clk_period 20

module HEX8_tb;

    reg Clk;  //50M
    reg Rst_n;
    reg En;   //数码管显示使能,1使能,0关闭

    reg [31:0]disp_data;

    wire [7:0]sel;    //数码管位选(选择当前要显示的数码管)
    wire [6:0]seg;    //数码管段选(当前要显示的内容)

    HEX8 HEX8(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .En(En),
        .disp_data(disp_data),
        .sel(sel),
        .seg(seg)
    );

    initial Clk = 1;
    always#(`clk_period/2) Clk = ~Clk;

    initial begin
        Rst_n = 1'b0;
        En = 1;
        disp_data = 32'h12345678;
        #(`clk_period*20);
        Rst_n = 1;
        #20000000;
        disp_data = 32'h87654321;
        #20000000;
        disp_data = 32'h89abcdef;
        #20000000;
        $stop;
    end

endmodule

6、板级调试与验证

​ 为了进行板级调试,需要编写一个顶层模块HEX8_top.v,该顶层模块将驱动包含进去,同时包含调试模块。

​ 使用Quartus自带的的In system sources and probes editor(ISSP)调试工具进行逻辑功能测试。这样测试整体模块框图就可以简化为如图5所示。

image-20240312103847554
图5——整体测试框图

​ 这里 ISSP 是以 IP 核的形式提供的,ISSP使用步骤如下:

​ 1、单击 Tools→Mega Wizard Plug-In Manager 来启动 Mega Wizard 插件管理器来对 IP 核进行相关操作。

image-20240312104146512

​ 2、在弹出如下图所示的Mega Wizard 插件管理器的参数设置界面中,找到 JTAG-accessible Extensions 下选择 In-System Source and Probes,并将输出目录确定为工程文件夹下的 ip 文 件夹,并以 hex8_data 保存,单击 Next。

image-20240312104625731

​ 3、在弹出如下图所示的配置界面中将驱动源(source)位宽定义为 32,探针(probe)位宽定义为 0,然后单击 Next 即可。

image-20240312105202315

​ 4、simulation library 界面选择 next。

image-20240312105325768

​ 5、生成文件汇总界面选择 next。

image-20240312105351048

​ 6、点击 Finish 后,生成的 IP 核,自动加入了工程。可以在 setting 界面的 files 菜 单下,查看加入工程的 IP 文件是否添加成功。

image-20240312105500639

​ 7、由于 ISSP 依旧属于 IP 核,因此在使用前需要将其在工程文件中进行例化。快捷键Ctrl+o打开文件夹,双击hex8_data.v文件。

image-20240312105755653

​ 8、复制如下图所示的语句,粘贴到顶层文件HEX8_top.v中进行例化,并将HEX8_top.v设置为顶层文件。

image-20240312110026068

​ 代码如下所示:

module HEX8_top(
    Clk,
    Rst_n,
    sel,
    seg
);
    input Clk;  //50M
    input Rst_n;

    output [7:0]sel;    //数码管位选(选择当前要显示的数码管)
    output [6:0]seg;    //数码管段选(当前要显示的内容)

    wire[31:0]disp_data;

//ISSP模块
    hex8_data hex8_data(
        .probe(),
        .source(disp_data)
    );

//数码管驱动
    HEX8 HEX8(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .En(1),
        .disp_data(disp_data),
        .sel(sel),
        .seg(seg)
    );

endmodule

7、串行移位寄存器驱动数码管显示设计与实现(适用于AC620)

​ FPGA 将点亮数码管需要的 16 位段选和位选信号通过 SPI 接口传递给 74HC595 芯片,74HC595可将串行数据转为并行数据。74HC595 芯片再将这 16 位的数据通过 16 个 IO 信号输出,从而驱动数码管的位选和段选。 74HC595 支持级联功能,由于一片74HC595 只能实现 8 位数据的串并转换,因此使用 2 片 74HC595 芯片级联实现,其电路图如图6所示。

image-20240314173229756
图6——FPGA连接串行移位寄存器输出数码管数据

​ 如图为74HC595芯片的组成结构(4bit粗略版):

image-20240318141242197
图7——74HC595芯片组成结构

​ 增加一个模块:HC595_Driver.v,将HEX8.v输出的sel和seg信号(Data)转换为连接74HC595芯片上的LATCH信号(ST_CP)、DATA信号(DS)和CLK(SH_CP)信号。3.3V供电情况下,取SH_CP时钟频率为12.5MHz(80ns),则可以让其每40ns翻转一次。也就是每2个系统时钟周期(CLK:20ns)翻转一次。代码如下:

module HC595_Driver(
    Clk,
    Rst_n,
    Data,
    S_EN,    //移位使能信号,控制是否输出数据
    SH_CP,   //Shift_Clock
    ST_CP,   //LATCH_Clock
    DS
);
    input Clk;
    input Rst_n;
    input [15:0]Data;
    input S_EN;

    output reg SH_CP;
    output reg ST_CP;
    output reg DS;

//使能信号
    reg [15:0]r_data;
    always@(posedge Clk)
    if(S_EN)
        r_data <= Data;

//分频器
    parameter CNT_MAX = 2;  //12.5MHz,CNT_MAX = 2(每两个时钟周期翻转一次);若是6.25MHz,则CNT_MAX = 4(每四个时钟周期翻转一次)。
	
    //计数器
    reg [7:0]divider_cnt;   

    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        divider_cnt <= 0;
    else if(divider_cnt == CNT_MAX - 1'b1)   //每2个时钟周期翻转一次:0、1
        divider_cnt <= 0;
    else
        divider_cnt <= divider_cnt + 1'b1;
	
    //sck_plus脉冲
    wire sck_plus;
    assign sck_plus = (divider_cnt == CNT_MAX - 1'b1);

	//边沿计数器,计数到32
    reg [5:0]SHCP_EDGE_CNT;

    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)
        SHCP_EDGE_CNT <= 0;
    else if(sck_plus)begin
        if(SHCP_EDGE_CNT == 32)
            SHCP_EDGE_CNT <= 0;
        else
            SHCP_EDGE_CNT <= SHCP_EDGE_CNT + 1'b1;
    end
    else
        SHCP_EDGE_CNT <= SHCP_EDGE_CNT;

//产生ST_CP、SH_CP、DS信号
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)begin
        ST_CP <= 1'b0;
        DS <= 1'b0;
        SH_CP <= 1'b0;
    end
    else begin
        case(SHCP_EDGE_CNT)
            0:begin SH_CP <= 0; ST_CP <= 1'd0; DS <= r_data[15];end
            1:begin SH_CP <= 1;end
            2:begin SH_CP <= 0; DS <= r_data[14];end
            3:begin SH_CP <= 1;end

            4:begin SH_CP <= 0; DS <= r_data[13];end
            5:begin SH_CP <= 1;end
            6:begin SH_CP <= 0; DS <= r_data[12];end
            7:begin SH_CP <= 1;end

            8:begin SH_CP <= 0; DS <= r_data[11];end
            9:begin SH_CP <= 1;end
            10:begin SH_CP <= 0; DS <= r_data[10];end
            11:begin SH_CP <= 1;end

            12:begin SH_CP <= 0; DS <= r_data[9];end
            13:begin SH_CP <= 1;end
            14:begin SH_CP <= 0; DS <= r_data[8];end
            15:begin SH_CP <= 1;end

            16:begin SH_CP <= 0; DS <= r_data[7];end
            17:begin SH_CP <= 1;end
            18:begin SH_CP <= 0; DS <= r_data[6];end
            19:begin SH_CP <= 1;end

            20:begin SH_CP <= 0; DS <= r_data[5];end
            21:begin SH_CP <= 1;end
            22:begin SH_CP <= 0; DS <= r_data[4];end
            23:begin SH_CP <= 1;end

            24:begin SH_CP <= 0; DS <= r_data[3];end
            25:begin SH_CP <= 1;end
            26:begin SH_CP <= 0; DS <= r_data[2];end
            27:begin SH_CP <= 1;end

            28:begin SH_CP <= 0; DS <= r_data[1];end
            29:begin SH_CP <= 1;end
            30:begin SH_CP <= 0; DS <= r_data[0];end
            31:begin SH_CP <= 1;end

            32:ST_CP <= 1'd1;
            default:
                begin
                    ST_CP <= 1'b0;
                    DS <= 1'b0;
                    SH_CP <= 1'd0;
                end
        endcase
    end

endmodule

8、串行移位寄存器驱动仿真

`timescale 1ns/1ns
`define clk_period 20

module HC595_Driver_tb;

    reg Clk;
    reg Rst_n;
    reg [15:0]Data;
    reg S_EN;

    wire SH_CP;
    wire ST_CP;
    wire DS;

    HC595_Driver HC595_Driver(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .Data(Data),
        .S_EN(S_EN),    //移位使能信号,控制是否输出数据
        .SH_CP(SH_CP),   //Shift_Clock
        .ST_CP(ST_CP),   //LATCH_Clock
        .DS(DS)
    );

    initial Clk = 1;
    always#(`clk_period/2) Clk = ~Clk;

    initial begin
        Rst_n = 0;
        #201
        Rst_n = 1;
        #200
        S_EN = 0;
        Data = 16'h1234;
        #201;
        S_EN = 1;
        #2000;
        S_EN = 0;
        #200000;
        Data = 16'h8765;
        S_EN = 1;
        #2000;
        S_EN = 0;
        #2000;
        $stop;
    end

endmodule

9、将串行移位寄存器驱动加入到HEX_top.v中

module HEX8_top(
    Clk,
    Rst_n,
    SH_CP,
    ST_CP,
    DS
);
    input Clk;  //50M
    input Rst_n;

    output SH_CP;
    output ST_CP;
    output DS;

    wire [7:0]sel;    //数码管位选(选择当前要显示的数码管)
    wire [6:0]seg;    //数码管段选(当前要显示的内容)

    wire[31:0]disp_data;

//ISSP模块
    hex8_data hex8_data(
        .probe(),
        .source(disp_data)
    );

//数码管驱动
    HEX8 HEX8(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .En(1),
        .disp_data(disp_data),
        .sel(sel),
        .seg(seg)
    );

//74HC595驱动模块
    HC595_Driver(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .Data({1'd0,seg,sel}),
        .S_EN(1),    //移位使能信号,控制是否输出数据
        .SH_CP(SH_CP),   //Shift_Clock
        .ST_CP(ST_CP),   //LATCH_Clock
        .DS(DS)
    );

endmodule

标签:disp,FPGA,Clk,seg,数码管,Rst,008,data
From: https://www.cnblogs.com/little55/p/18082220

相关文章

  • FPGA通过I2C控制AT24C64
    文章目录前言一、代码设计框图二、IIC_drive模块设计2.1、模块接口:2.2、代码功能描述:2.3、IIC协议实现过程:三、EEPROM_ctrl模块设计3.1、模块接口:3.2、代码功能描述四、EEPROM_drive模块五、iic_top模块前言继上一篇FPGA学习_I2C总线协议内容,本文将基于FPGA通过I2......
  • 锁相环技术原理及FPGA实现(第四章4.1)
            经过前面几章的学习,我们已积累了设计锁相环电路的一些基本技能。根据作者的学习经验,这个阶段最期望的一定不是再去理解什么原理公式,学习什么方法思路。好比初次接触到羽毛球时,在网上看了一段中规中矩的教学视频,又刚好买回一支炫丽的球拍,走进球场,实在没有心情......
  • FPGA设计优化(3.7)
            设计规则1:对综合后的设计就要开始进行扇出分析,以尽早发现高扇出的网线,并评估其可能对设计造成的影响。report_high_fanout_nets的具体用法如Tcl代码9-1所示。代码第3行的选项-load_types生成的报告样例如图9-1所示。从此报告中可以看到网线rectify_reset的扇出......
  • 前端学习-vue视频学习008-TS中的接口\泛型\自定义类型
    尚硅谷视频链接使用ts定义,可限制参数的类型新建ts文件//定义接口限制对象属性exportinterfacepersonInter{name:string,age:number,gender:string}//使用泛型//exporttypepersonArr=Array<personInter>//另一种写法exporttypepersonArr=p......
  • STM32 TIM3 定时器应用之数码管显示定时时间
     实现目标1、STM32基于HAL库定时器的使用;2、加强数码管的学习。一、定时器概述?1、生活中哪些场景会用到定时器?2、STM32F1定时器二、原理图设计三、STM32CubeMX配置1.定时器时钟配置2.定时器3、数码管、蜂鸣器的配置  3.开启定时器3中断四、程序......
  • libtorch实现一个数码管数字识别网络
    这里的数字范围是0~9共10个数字,用5×3的数字矩阵表示,把它当成图像那么可以看成5×3的图片。如下图中的数字0,用“1”代表有颜色(亮),“0”代表没颜色(灭)。网络是经典的BP神经网络,15个输入,10个输出。当输入是形状“0”时,输出索引为0的数字最大接近于1;当输入是形状“1”时,输出索引为1的......
  • SqlServer2008(R2)(一)SqlServer2008(R2)经典宝藏操作收集整理
    一、常见操作1、TRUNCATETABLE语句删除表数据TRUNCATETABLE语句比DELET删除表中的所有行更快。从逻辑上讲,TRUNCATETABLE它类似于DELETE没有WHERE子句的语句。TRUNCATETABLE语句从表中删除所有行,但表结构及其列,约束,索引等保持不变。要删除表及其数据,可以使用该DROP......
  • FPGA静态时序分析与约束(二)、时序分析
    系列文章目录FPGA静态时序分析与约束(一)、理解亚稳态FPGA静态时序分析与约束(三)、读懂vivado时序报告文章目录系列文章目录前言一、时序分析基本概念1.1时钟抖动1.2时钟偏斜1.3时钟不确定性Uncertainty1.4建立时间和保持时间1.5启动沿和锁存沿二、时序分析基本步......
  • FPGA的VGA显示驱动部分知识点
    vga显示这边的的的知识点不难,在我写代码的时候却没能显示成功,现在重新设计一遍设计思路。根据下面的这个时序图,可以用计数器的方式来设计,在不同时间段选择显示情况。目前我电脑的副屏是一个1440*900的显示器,在网上找到了他的VGA时序图。根据这个与时序表对应。完成项目代码,最......
  • FPGA开发工具安装
    FPGA从零开始学习第一章工欲善其事必先利其器–各类工具安装FPGA开发工具安装软件配置和可能遇到的问题软件配置和可能遇到的问题FPGA从零开始学习前言一、软件安装中的问题1.Vivado的安装过程中的可能问题2.Modelsim的安装过程中的可能问题二、软件配置1.Mod......