一个问题:六位八段数码管(Verilog)

verilog · 浏览次数 : 2

小编点评

```verilog //数字管位选刷新模块 always @(posedge sys_clk or negedge sys_rst)begin if(!sys_rst)begin cnt <= 15'd0; clk_out <= 1'b0; } else if(cnt <(cnt_2khz_max-1'b1))begin cnt <= cnt + 1'b1; clk_out <= 1'b0; } else begin cnt <= 15'd0; verilog clk_out <= 1'b1; end endmodule ```

正文

【基本信息】

需求:verilog程序,显示任意六位字符或数值,包含点号,且能够按需点亮位数。(学习篇)

芯片型号:cyclone Ⅳ EP4CE10F17C8

数码管属性:六位、八段

【最终成果图】

image

经过多轮测试,最后代码程序满足设计要求,但结合仿真发现了一个问题,仿真和上机不匹配,当然还是要以上机为准。

【模块例化图】

image

这里就是简单地赋个初始值,来测试digital模块,最终是要seg_led和seg_sel的显示变化。

【verilog程序】

1、digital模块

模块化设计,六个位上的值直接拆开做处理,分析起来清晰。sel_cnt表示目前要显示的位数(从右往左),即sel_cnt = 6代表全亮。dp_cnt表示点号所在位数,若dp_cnt=0则代表没有点号。clk_2khz为数码管的刷新信号。

module digital
(
    input           sys_clk      ,
    input           sys_rst      ,
    input           clk_2khz     ,
    
    input [3:0]     num6,
    input [3:0]     num5,
    input [3:0]     num4,
    input [3:0]     num3,
    input [3:0]     num2,
    input [3:0]     num1,
    input [2:0]     sel_cnt     , 
    input [2:0]     dp_cnt      ,
   
    output reg[5:0] seg_sel     ,
    output reg[7:0] seg_led      
);
reg[2:0] sel_cnt_tran;
reg[3:0] num;
reg[2:0] tran1;
//endmodule

定义了几个变量,sel_cnt_tran用来根据数码管刷新信号更替数码管的位选,而num用来接引各位上的值,数码管根据num编号段选信号。tran1在过程讨论部分做解释。

always @(posedge sys_clk or negedge sys_rst)
begin
    if(!sys_rst)
        sel_cnt_tran = 3'd0;
    else 
    if(clk_2khz)begin 
        if(sel_cnt_tran < sel_cnt)begin
            tran1 = sel_cnt_tran;
            sel_cnt_tran = sel_cnt_tran + 1'b1;
        end
        else begin
            tran1 = sel_cnt_tran;
            sel_cnt_tran = 3'd0;
        end
    end
end

上述代码就是位选更替,可以直观得到,tran1比sel_cnt_tran要晚上一个数码管刷新周期。

always @(posedge sys_clk or negedge sys_rst)
begin
    if(!sys_rst)
        seg_sel <= 6'b111_111;
    else if(clk_2khz)begin
        case(sel_cnt_tran)
            3'd1:begin
                seg_sel <= 6'b111_110;
                num <= num1;
            end
            3'd2:begin
                seg_sel <= 6'b111_101;
                num <= num2;
            end
            3'd3:begin
                seg_sel <= 6'b111_011;
                num <= num3;
            end
            3'd4:begin
                seg_sel <= 6'b110_111;
                num <= num4;
            end
            3'd5:begin
                seg_sel <= 6'b101_111;
                num <= num5;
            end
            3'd6:begin
                seg_sel <= 6'b011_111;
                num <= num6;
            end
            default:begin
                seg_sel <= 6'b111_111;
            end
        endcase
    end
end

根据原理图,数码管是共阳极,并且位选拉低有效,这里测试是没有问题的。

always @(posedge sys_clk or negedge sys_rst)begin
    if (!sys_rst)
        seg_led = 8'b0;
    else begin
        case (num)
            4'h0 :    seg_led = 8'b1100_0000;
            4'h1 :    seg_led = 8'b1111_1001;
            4'h2 :    seg_led = 8'b1010_0100;
            4'h3 :    seg_led = 8'b1011_0000;
            4'h4 :    seg_led = 8'b1001_1001;
            4'h5 :    seg_led = 8'b1001_0010;
            4'h6 :    seg_led = 8'b1000_0010;
            4'h7 :    seg_led = 8'b1111_1000;
            4'h8 :    seg_led = 8'b1000_0000;
            4'h9 :    seg_led = 8'b1001_0000;
            4'ha :    seg_led = 8'b1000_1000;
            4'hb :    seg_led = 8'b1000_0011;
            4'hc :    seg_led = 8'b1100_0110;
            4'hd :    seg_led = 8'b1010_0001;
            4'he :    seg_led = 8'b1000_0110;
            4'hf :    seg_led = 8'b1000_1110;
            default : seg_led = 8'b1100_0000;//多余
        endcase
        if(dp_cnt == (tran1))begin	//确定点号,这里不是sel_cnt_tran
            seg_led = seg_led & 8'b0111_1111;
        end 
    end
end

2、clk_2khz模块

module clk_2khz
(
    input               sys_clk      ,
    input               sys_rst      ,
    
    output reg          clk_out 
);
parameter   cnt_2khz_max = 25_000;
reg[14:0]   cnt;

clk_2khz时钟产生很简单,cnt_2khz_max = 1/2_000x50_000_000,在这就放下代码。注意两个问题:一个是不要直接posedge clk_2khz方式来读上升沿,另外尽量不要采用取反方式产生分频时钟信号,如果是取反,可以采用wire en;assign en = d1 & d0来读边沿信号。

always @(posedge sys_clk or negedge sys_rst)begin
    if(!sys_rst)begin
        cnt <= 15'd0;
        clk_out <= 1'b0;
    end
    else if(cnt <(cnt_2khz_max-1'b1))begin
        cnt <= cnt + 1'b1;
        clk_out <= 1'b0;
    end
    else begin
        cnt <= 15'd0;verilog
        clk_out <= 1'b1;
    end
end
endmodule

3、顶部例化测试

module digital_top  (  
    input           sys_clk ,  
    input           sys_rst ,  
    output [5:0] seg_sel ,  
    output [7:0] seg_led   
);  
wire clk_2khz_tran;
reg[3:0] num1       = 4'd2;
reg[3:0] num2       = 4'h1;
reg[3:0] num3       = 4'd5;
reg[2:0] sel_cnt    = 3'd6;
reg[2:0] dp_cnt     = 3'd6;
digital digital_inst();  //例化省略
clk_2khz clk_generator1(); 
endmodule

4、Modelsim仿真

`timescale 1ns/1ns
module digital_tb();

parameter   T1 =20 ;
parameter   T2 =200;
parameter max = 14;
reg sys_clk;
reg clk_2khz;
reg sys_rst;
reg[3:0] num1       = 4'h1;
reg[3:0] num2       = 4'h1;
reg[3:0] num3       = 4'h1;
reg[2:0] sel_cnt    = 3'd6;
reg[2:0] dp_cnt     = 3'd6;
reg[3:0] cnt;
wire[7:0] seg_led;
wire[5:0] seg_sel;

always # (T1/2) sys_clk <= ~sys_clk;

always @(posedge sys_clk or negedge sys_rst)begin
    if(!sys_rst)
        cnt <= 4'd0;
    else begin 
        if(cnt < max)begin
            cnt <= cnt + 1'b1;
            clk_2khz <= 1'b0;
        end
        else begin 
            cnt <= 4'd0;
            clk_2khz <= 1'b1;
        end
    end
end

initial begin
    sys_clk              <=1'b0;
    sys_rst              <=1'b0;
    #50 sys_rst          <=1'b1;  
end
    digital digital_inst();//例化省略  
endmodule

【过程讨论】

首先由于自己疏忽,数码管位选刷新位置采用的是非阻塞赋值的方法,通过用sel_cnt_tran判断为真的话,通过仿真(下图)观察到,位选和段选不匹配,就是说,点号的位置不正确,现象是位选落后了。

image

然后想着将赋值方法改为阻塞赋值,还是有问题。强制更改,设定判断条件为if(dp_cnt == (sel_cnt_tran-1’b1))看看,显然,这样虽然有效(图在下面),但只能保证最终的点号位置有限,比如dp_cnt = 6就有问题,因为sel_cnt_tran在6后赋值是0,达不到条件。后面借助变量tran1在sel_cnt_tran刷新处赋值使其落后一个周期,仿真显示不正确,但上机测试有效。

image

与一个问题:六位八段数码管(Verilog)相似的内容:

一个问题:六位八段数码管(Verilog)

【基本信息】 需求:verilog程序,显示任意六位字符或数值,包含点号,且能够按需点亮位数。(学习篇) 芯片型号:cyclone Ⅳ EP4CE10F17C8 数码管属性:六位、八段 【最终成果图】 经过多轮测试,最后代码程序满足设计要求,但结合仿真发现了一个问题,仿真和上机不匹配,当然还是要以上

Python学习之六_同时访问Oracle和Mysql的方法

Python学习之六_同时访问Oracle和Mysql的方法 背景 jaydebeapi 可以访问大部分数据库. 但是他有一个问题是仅能够访问一种类型的数据库. 如果同事连接两种数据库,那么就会出现问题 会有如下的提示: TypeError: Class com.mysql.cj.jdbc.Driv

《优化接口设计的思路》系列:第一篇—接口参数的一些弯弯绕绕

前言 大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。 作为一名从业已达六年的老码农,我的工作主要是开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接

《优化接口设计的思路》系列:第二篇—接口用户上下文的设计与实现

前言 大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。 作为一名从业已达六年的老码农,我的工作主要是开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接

《优化接口设计的思路》系列:第三篇—留下用户调用接口的痕迹

前言 大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。 作为一名从业已达六年的老码农,我的工作主要是开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接

《优化接口设计的思路》系列:第四篇—接口的权限控制

前言 大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。 作为一名从业已达六年的老码农,我的工作主要是开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接

《优化接口设计的思路》系列:第十一篇—表格的导入导出接口优化

一、前言 大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。 作为一名从业已达六年的老码农,我的工作主要是开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,

《优化接口设计的思路》系列:第十篇—网站的静态资源怎么获取?

一、前言 大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。 作为一名从业已达六年的老码农,我的工作主要是开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,

[转帖]Web技术(六):QUIC 是如何解决TCP 性能瓶颈的?

文章目录 一、QUIC 如何解决TCP的队头阻塞问题?1.1 TCP 为何会有队头阻塞问题1.2 QUIC 如何解决队头阻塞问题1.3 QUIC 没有队头阻塞的多路复用 二、QUIC 如何优化TCP 的连接管理机制?2.1 TCP连接的本质是什么2.2 QUIC 如何减少TCP 建立连接的开销2.3

[转帖]Redis进阶实践之六Redis Desktop Manager连接Windows和Linux系统上的Redis服务

https://www.cnblogs.com/PatrickLiu/p/8360057.html 一、引言 今天本来没有打算写这篇文章,但是,今天测试Redis的时候发现了两个问题,第一个问题是:Redis Desktop Manager无法连接虚拟机上Linux系统上的Redis服务,第二个问题