Verilog 学习第八节(数码管段码显示)

共阴极数码管:低电平端接的都是0,高电平端哪里设置为1 ,哪里就亮~
共阳极数码管与之相反~
视觉暂留:
对于三位的共阴极数码管
第0.01s:让数码管0的a段亮,其他数码管全灭
Sel0为高电平,sel1和sel2为低电平
A段为低电平

第0.02s:让数码管1的b、c段亮,其他数码管全灭
Sel1为高电平,sel0和sel2为低电平
B和C段为低电平

第0.03s:让数码管2的e段亮,其他数码管全灭
Sel2为高电平,sel0和sel1为低电平
E段为低电平
数码管动态扫描
所以,通过这种方式,可以节约引脚~
在这里插入图片描述
抽象原理图:
在这里插入图片描述
在fpga设计中尽量使用使能时钟去驱动寄存器,而不是门控时钟,因为门控时钟的质量非常差
在这里插入图片描述
使用门控时钟,将门控时钟直接作为DFF的工作时钟,没有ENA的情况下忽略ENA
在这里插入图片描述

使用使能时钟的情况,DFF的工作时钟继续全局高质量时钟,而将使能时钟作为DFF的使能信号使用

在能使用时序逻辑的情况下,尽量使用时序逻辑
顶层模块:


module hex8_test(
    input Clk,
    input Reset,
    output [7:0]SEL,
    output [7:0]SEG
    );
    wire [31:0]Disp_Data;
    hex8_2 hex8_2(
    Clk,
    Reset,
    Disp_Data,
    SEL,
    SEG
    );
    assign Disp_Data=32'h12359bdf;
    
endmodule

主模块

module hex8(
    input Clk,
    input Reset,
    input [31:0]Disp_Data,
    output reg [7:0]SEL,
    output reg [7:0]SEG//seg[0]对应a,seg[1]对应b~~~~seg[7]对应h
    );
    reg clk_1k;
    reg [14:0]div_cnt;
    always@(posedge Clk or negedge Reset)begin
        if(!Reset)
            div_cnt<=0;
        else if(div_cnt>=25000-1)
            div_cnt<=0;
        else
            div_cnt<=div_cnt+1;
    end
    
    always@(posedge Clk or negedge Reset)begin
         if(!Reset)
            clk_1k<=0;
         else if(div_cnt>=25000-1)
             clk_1k<=~clk_1k;
    end
    
    reg[2:0]reg_num;
    always@(posedge clk_1k or negedge Reset)begin
         if(!Reset)
            reg_num<=0;
         else if(reg_num>=7)
            reg_num<=0;
         else
            reg_num<=reg_num+1;
    end
    
    always@(*)begin
         case(reg_num)
         0:SEL=8'b0000_0001;
         1:SEL=8'b0000_0010;
         2:SEL=8'b0000_0100;
         3:SEL=8'b0000_1000;
         4:SEL=8'b0001_0000;
         5:SEL=8'b0010_0000;
         6:SEL=8'b0100_0000;
         7:SEL=8'b1000_0000;
         endcase
    end
    
    reg[3:0] disp_temp;
    always@(*)begin
             case(reg_num)
             7:disp_temp=Disp_Data[31:28];
             6:disp_temp=Disp_Data[27:24];
             5:disp_temp=Disp_Data[23:20];
             4:disp_temp=Disp_Data[19:16];
             3:disp_temp=Disp_Data[15:12];
             2:disp_temp=Disp_Data[11:8];
             1:disp_temp=Disp_Data[7:4];
             0:disp_temp=Disp_Data[3:0];
             endcase
        end
        
     always@(*)begin
          case(disp_temp)
          0:SEG=8'hc0;
          1:SEG=8'hf9;
          2:SEG=8'ha4;
          3:SEG=8'hb0;
          4:SEG=8'h99;
          5:SEG=8'h92;
          6:SEG=8'h82;
          7:SEG=8'hf8;
          8:SEG=8'h80;
          9:SEG=8'h90;
          4'ha:SEG=8'h88;
          4'hb:SEG=8'h83;
          4'hc:SEG=8'hc6;
          4'hd:SEG=8'ha1;
          4'he:SEG=8'h86;
          4'hf:SEG=8'h8e;
          endcase
     end 
endmodule

改进后的主模块


module hex8_2(
    input Clk,
    input Reset,
    input [31:0]Disp_Data,
    output reg [7:0]SEL,
    output reg [7:0]SEG//seg[0]对应a,seg[1]对应b~~~~seg[7]对应h
    );
    reg clk_1k;
    reg [15:0]div_cnt;
    always@(posedge Clk or negedge Reset)begin
        if(!Reset)
            div_cnt<=0;
        else if(div_cnt>=50000-1)
            div_cnt<=0;
        else
            div_cnt<=div_cnt+1;
    end
    
    always@(posedge Clk or negedge Reset)begin
         if(!Reset)
            clk_1k<=0;
         else if(div_cnt>=50000-1)
            clk_1k<=1;
         else
            clk_1k<=0;
    end
    
    reg[2:0]reg_num;
    always@(posedge Clk or negedge Reset)begin
         if(!Reset)
            reg_num<=0;
         else if(clk_1k)
            reg_num<=reg_num+1;
         else
            reg_num<=reg_num;
    end
    
    always@(posedge Clk)begin
         case(reg_num)
         7:SEL=8'b0000_0001;
         6:SEL=8'b0000_0010;
         5:SEL=8'b0000_0100;
         4:SEL=8'b0000_1000;
         3:SEL=8'b0001_0000;
         2:SEL=8'b0010_0000;
         1:SEL=8'b0100_0000;
         0:SEL=8'b1000_0000;
         endcase
    end
    
    reg[3:0] disp_temp;
    always@(posedge Clk)begin
             case(reg_num)
             0:disp_temp=Disp_Data[31:28];
             1:disp_temp=Disp_Data[27:24];
             2:disp_temp=Disp_Data[23:20];
             3:disp_temp=Disp_Data[19:16];
             4:disp_temp=Disp_Data[15:12];
             5:disp_temp=Disp_Data[11:8];
             6:disp_temp=Disp_Data[7:4];
             7:disp_temp=Disp_Data[3:0];
             endcase
        end
        
     always@(posedge Clk)begin
          case(disp_temp)
          0:SEG=8'hc0;
          1:SEG=8'hf9;
          2:SEG=8'ha4;
          3:SEG=8'hb0;
          4:SEG=8'h99;
          5:SEG=8'h92;
          6:SEG=8'h82;
          7:SEG=8'hf8;
          8:SEG=8'h80;
          9:SEG=8'h90;
          4'ha:SEG=8'h88;
          4'hb:SEG=8'h83;
          4'hc:SEG=8'hc6;
          4'hd:SEG=8'ha1;
          4'he:SEG=8'h86;
          4'hf:SEG=8'h8e;
          endcase
     end 
endmodule

测试模块

`timescale 1ns / 1ps

module hex8_tb(
    );
    reg Clk;
    reg Reset;
    reg [31:0]Disp_Data;
    wire [7:0]SEL;
    wire [7:0]SEG;
    hex8 hex8(
        Clk,
        Reset,
        Disp_Data,
        SEL,
        SEG//seg[0]对应a,seg[1]对应b~~~~seg[7]对应h
        );
      initial Clk=0;
      always#10 Clk=!Clk;
      initial begin
      Reset=0;
      Disp_Data=32'h0000_0000;
      #201
      Reset=1;
      #2000
      Disp_Data=32'h1234_5678;
      #10000000
      Disp_Data=32'h9abc_def0;
      #10000000
      $stop;
      end
endmodule

时钟质量在FPGA设计中重要的原因

1:时钟延迟不确定,而且比较大
2:使得时钟的波形变差
3:驱动能力
在这里插入图片描述

<think>好的,用户让我介绍一下如何使用Verilog HDL实现一个4位8段数码管的选位分秒数字钟。首先,我需要理解用户的需求。他们可能想要一个可以显示分和秒的数字钟,使用四位数码管,比如两位显示分钟,两位显示秒钟。8段数码管通常指的是包括小数点在内的七段数码管,可能用户指的是常见的七段数码管,但写成了8段。需要确认一下,不过通常数码管是7段加小数点,共8段,所以可能用户是正确的。 接下来,我需要考虑数字钟的基本组成部分。通常,数字钟需要计数器来记录时间,分频器将系统时钟转换为1Hz的信号,数码管的驱动电路,以及控制数码管显示扫描模块。用户提到的“选位分秒”可能是指动态扫描,即依次点亮每一位数码管,利用视觉暂留效果显示所有四位数字。 首先,系统需要一个基准时钟,比如50MHz的晶振输入,然后通过分频得到1Hz的时钟信号,用于秒的计数。分频器模块可能需要用计数器来实现,将高频时钟分频到1Hz。 然后是时间计数模块。秒计数器从0到59,当秒达到59时,分钟计数器加1,分钟同样从0到59。这里可能需要两个计数器:一个用于秒(0-59),另一个用于分(0-59)。每个计数器都是60进制。 接下来是数码管显示部分。四位八段数码管需要动态扫描,即每个时刻只点亮一位,依次循环。由于四位共用一组段选信号,需要通过位选信号控制哪一位亮。扫描频率需要足够高,比如几百Hz,以避免闪烁。因此,可能需要另一个分频器来产生扫描时钟,比如1kHz。 数码管的段选信号需要根据当前显示的数字进行译。例如,数字0对应的段是8'b11000000(假设共阳极数码管),数字1是8'b11111001,等等。需要有一个译模块将4位二进制数转换为对应的段。 此外,可能需要将分和秒的数值分解为单独的十进制数字,例如,秒的个位和十位,分的个位和十位。这可以通过除以10取余和取整来实现,或者使用寄存器来保存每一位的值。 现在需要考虑模块划分。可能的模块包括: 1. 分频模块:将系统时钟分频为1Hz(用于计时)和扫描时钟(如1kHz)。 2. 时间计数模块:处理秒和分的计数。 3. 数码管扫描模块:控制位选信号,循环选中每一位。 4. 译模块:将数字转换为段选信号。 5. 顶层模块:连接各个子模块。 在Verilog中,这些模块可以通过多个always块来实现。需要注意同步复位或异步复位,以及时钟边缘的选择(通常使用上升沿)。 接下来,需要处理动态扫描。例如,四位数码管的位选信号可能是一个4位的信号,如an[3:0],每次只有一个位是低电平(假设共阳极)。扫描模块需要每隔一定时间切换到下一位,并输出对应的数字数据到译模块。 对于时间计数,秒和分的计数器可能需要用BCD(二进制编的十进制)来表示,这样便于分解十位和个位。例如,秒的计数器是0-59,当计数到59时归零,并触发分计数器加1。 可能遇到的问题包括分频器的精度,计数器溢出处理,以及动态扫描的频率设置是否合适。如果扫描频率过低,数码管会有闪烁;过高可能会导致功耗增加,但通常几百Hz到1kHz比较合适。 此外,需要考虑硬件资源,例如FPGA的引脚分配,将段选和位选信号连接到正确的IO端口。可能需要使用到多个输出引脚,如8个段选信号和4个位选信号。 最后,测试和验证。可以通过仿真工具如ModelSim验证各个模块的功能,再综合到FPGA开发板上进行实际测试,观察数码管显示是否正确,计时是否准确。 总结步骤: 1. 分频模块生成1Hz和扫描时钟。 2. 时间计数器处理秒和分。 3. 分解秒和分的各位数字。 4. 动态扫描轮流显示各位数字。 5. 译模块转换数字为段。 6. 顶层模块整合所有子模块。 需要注意的细节包括BCD计数器的设计,动态扫描的时序控制,以及段的正确映射。可能需要查阅数码管的段表,确认共阳极还是共阴极,以正确设置段选信号的电平。</think>下面是用Verilog HDL实现4位8段数码管选位分秒数字钟的详细步骤: ```verilog // 模块结构示意图 // 顶层模块:top(clk, rst, seg, an) // ├─ 分频模块 div_clk // ├─ 时间计数模块 time_counter // ├─ 动态扫描模块 scan_control // └─ 译模块 seg_decoder module top( input clk, // 系统时钟(如50MHz) input rst, // 复位信号 output [7:0] seg, // 段选信号(a~g+dp) output [3:0] an // 位选信号(4位) ); // 参数定义 parameter CLK_FREQ = 50_000_000; // 50MHz系统时钟 // 内部信号定义 wire clk_1hz; // 1Hz时钟信号 wire clk_scan; // 扫描时钟(约1kHz) wire [5:0] second; // 秒计数器(0-59) wire [5:0] minute; // 分计数器(0-59) wire [3:0] data; // 当前显示数字 wire [1:0] sel; // 当前扫描位置 // 模块实例化 div_clk #(CLK_FREQ) u_div_clk( .clk (clk), .rst (rst), .clk_1hz (clk_1hz), .clk_scan(clk_scan) ); time_counter u_time_counter( .clk_1hz (clk_1hz), .rst (rst), .second (second), .minute (minute) ); scan_control u_scan_control( .clk (clk_scan), .rst (rst), .minute (minute), .second (second), .data_out (data), .sel (sel) ); seg_decoder u_seg_decoder( .data (data), .seg (seg) ); // 位选信号生成(低电平有效) assign an = (sel == 2'b00) ? 4'b1110 : (sel == 2'b01) ? 4'b1101 : (sel == 2'b10) ? 4'b1011 : 4'b0111; endmodule // 分频模块 module div_clk #(parameter CLK_FREQ = 50_000_000)( input clk, input rst, output reg clk_1hz, output reg clk_scan ); // 1Hz分频计数器 reg [25:0] cnt_1hz; always @(posedge clk or posedge rst) begin if(rst) begin cnt_1hz <= 0; clk_1hz <= 0; end else if(cnt_1hz == CLK_FREQ/2 -1) begin cnt_1hz <= 0; clk_1hz <= ~clk_1hz; end else cnt_1hz <= cnt_1hz + 1; end // 1kHz扫描时钟分频 reg [15:0] cnt_scan; always @(posedge clk or posedge rst) begin if(rst) begin cnt_scan <= 0; clk_scan <= 0; end else if(cnt_scan == CLK_FREQ/(2*1000) -1) begin cnt_scan <= 0; clk_scan <= ~clk_scan; end else cnt_scan <= cnt_scan + 1; end endmodule // 时间计数模块 module time_counter( input clk_1hz, input rst, output reg [5:0] second, output reg [5:0] minute ); always @(posedge clk_1hz or posedge rst) begin if(rst) begin second <= 0; minute <= 0; end else begin if(second == 6'd59) begin second <= 0; if(minute == 6'd59) minute <= 0; else minute <= minute + 1; end else second <= second + 1; end end endmodule // 动态扫描控制模块 module scan_control( input clk, input rst, input [5:0] minute, input [5:0] second, output reg [3:0] data_out, output reg [1:0] sel ); reg [3:0] min_ten; // 分钟十位 reg [3:0] min_one; // 分钟个位 reg [3:0] sec_ten; // 秒十位 reg [3:0] sec_one; // 秒个位 // 数字分解 always @* begin min_ten = minute / 10; min_one = minute % 10; sec_ten = second / 10; sec_one = second % 10; end // 扫描计数器 always @(posedge clk or posedge rst) begin if(rst) sel <= 0; else sel <= sel + 1; end // 数据选择器 always @* begin case(sel) 2'b00: data_out = min_ten; 2'b01: data_out = min_one; 2'b10: data_out = sec_ten; 2'b11: data_out = sec_one; default: data_out = 4'h0; endcase end endmodule // 七段译模块(共阳极数码管) module seg_decoder( input [3:0] data, output reg [7:0] seg ); always @* begin case(data) 4'h0: seg = 8'b11000000; // 0 4'h1: seg = 8'b11111001; // 1 4'h2: seg = 8'b10100100; // 2 4'h3: seg = 8'b10110000; // 3 4'h4: seg = 8'b10011001; // 4 4'h5: seg = 8'b10010010; // 5 4'h6: seg = 8'b10000010; // 6 4'h7: seg = 8'b11111000; // 7 4'h8: seg = 8'b10000000; // 8 4'h9: seg = 8'b10010000; // 9 default: seg = 8'b11111111; // 全灭 endcase end endmodule ``` 实现特点说明: 1. **显示格式**:4位数码管显示MMSS格式(分分秒秒) - 示例:`12:59`表示12分59秒 2. **动态扫描机制**: - 使用1kHz扫描时钟循环点亮数码管 - 位选信号顺序:分十位 → 分个位 → 秒十位 → 秒个位 3. **时间计数**: - 1Hz基准时钟驱动秒计数器 - 60进制计数器(0-59秒/分) - 自动进位机制(59秒→00秒+1分) 4. **硬件资源需求**: - 输入:系统时钟(50MHz)、复位信号 - 输出:8段数码管控制信号(seg[7:0]) 4位位选信号(an[3:0]) 5. **扩展性**: - 可添加按键输入模块实现时间设置 - 可增加小时计数器扩展为完整时钟 - 可通过修改分频参数适配不同系统时钟 实际使用注意事项: 1. 数码管类型:本设计采用共阳极数码管,若使用共阴极需反转段 2. 引脚分配:需根据开发板原理图正确连接seg和an信号 3. 扫描频率:1kHz频率可防止闪烁,实际值可微调 4. 上电初始化:建议通过复位信号初始化计数器 该设计实现了基本的数字钟功能,可通过FPGA开发板直接部署验证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值