草庐IT

八段数码管动态显示(输入数据为BCD编码)

归一大师 2023-04-11 原文

八段数码管动态显示(输入数据为BCD编码)

一、数码管概述

图1 八段共阴数码管内部等效原理图
图2 八段共阳数码管内部等效原理图

  上面两图分别是对应八段共阴、共阳的数码管内部等效图,共阴是将八个LED数码管的阴极连接在一起接低,阳极segment信号只需要输入高电平,相应的数码管就被点亮;将上面八个数码管按照下面形状排列,就构成了数码管。

图3 数码管示意图

如何让数码管显示出对应的数据?
  拿八段共阴数码管显示2举例,如上图显示2需要点亮a、b、g、e、d这五个LED,其余LED全部熄灭。所以segment信号应该输出的数据是0101_1011。显示其余数字也是类似的原理,而h一般是用作小数点处理,不需要的时候一般都不点亮这个LED。下面是不包括h段LED的数码管译码对应表。

表一 八段共阴共阳数码管译码数据
显示数字共阳gfedcba 2进制共阳gfedcba 16进制共阴gfedcba 2进制共阴gfedcba 16进制
08’b110000008’hC08’b001111118’h3f
18’b111110018’hF98’b000001108’h06
28’b101001008’hA28’b010110118’h5b
38’b101100008’bB08’b010011118’h4f
48’b100110018’h998’b011001108’b66
58’b100100108’h928’b011011018’h6d
68’b100000108’h828’b011111018’h7d
78’b111110008’hF88’b000001118’h07
88’b100000008’h808’b011111118’h7f
98’b100100008’h908’b011011118’h6f

  经过上面可知,想让数码管显示对应的数据,就必须给此数码管的八个数据线(八个LED输入端)输入对应的数据,将这8根数据线称为一组数据线。那如果需要同时使用两个数码管呢?此时就需要控制16个LED,所以就需要2组数据线。由此需要n个数码管显示n个数字,就需要n组数据线,这会极大消耗数据线资源,但是平常单片机或者FPGA这些开发板上面的数码管很多,却只有一组数据线,这是怎么做到的呢?

  为了解决上面问题,一般是将四个数码管制作成下图所示的三线制数码管,将四个独立数码管四组数据线接在一起,四个数码管共用一组数据线,同时每个数码管增加一个DIG片选信号,如下图当DIG1引脚为低电平时,此时数据线HEX的数据驱动第1个数码管显示对应的数字。通过控制4个DIG引脚实现一组数据线驱动多个数码管,比如20us点亮第一个数码管,之后每隔20us点亮下一个数码管,最后循环点亮四个数码管,在点亮不同数码管的时候控制数据线HEX输入不同的数据,就可以实现一组数据线驱动多个数码管显示不同的数据。

图3 4个八段数码管原理图

二、FPGA实现驱动数码管

1、信号列表

信号位宽输入/输出描述
clk1I系统时钟,1MHz
rst_n1I系统复位,低电平有效
din4*数码管个数I待显示的BCD码数据
segment8O八段数码管的数据线
seg_sel数码管个数O数码管片选信号,电平有效

2、设计思路

  通过参数实现该模块可以驱动任意个数码管,通过自动计算位宽函数自动计算寄存器的位宽。

  该设计实现FPGA可以驱动SEG_NUM个数码管,每个数码管显示的时间为TIME_20MS。所以本设计以20us计数器cnt_20us和点亮的第几个计数器sel为主体架构,其余信号对齐这两个计数器。计数器示意图如下,cnt_20us计数计数时,sel计数器加一,表示点亮下一个数码管。例如使用8个数码管同时显示12345678,则SEG_NUM=8,din=64‘h12345678,之后sel计数器为0时,segment译码显示4’d8,此时第0个数码管显示8,之后计数器cnt_20us计数结束,sel+1,此时点亮第一个数码管,则数据线segment应该将4’d7译码进行输出,之后各位显示类似。

图4 计数器架构

  参数设计以及自动计算位宽函数如下:

parameter       TIME_20US                   =   20              ,//数码管刷新时间,默认20us。
parameter       SEG_NUM                     =   6                //需要显示的数码管个数。
//参数定义
localparam              TIME_W    =     clogb2(TIME_20US);//计算数码管扫描时间的时钟数据位宽;
localparam              SEG_W     =     clogb2(SEG_NUM);
localparam              ZERO      =     8'hC0  ; //8'h3F;前面的数据是共阳数码管使用的,后面数据是共阴数码管使用的;
localparam              ONE       =     8'hF9  ; //8'h06;
localparam              TWO       =     8'hA4  ; //8'hB;
localparam              THREE     =     8'hB0  ; //8'hF;
localparam              FOUR      =     8'h99  ; //8'h66;
localparam              FIVE      =     8'h92  ; //8'h6D;
localparam              SIX       =     8'h82  ; //8'h7D;
localparam              SEVEN     =     8'hF8  ; //8'h07;
localparam              EIGHT     =     8'h80  ; //8'h7F;
localparam              NINE      =     8'h90  ; //8'h6F;
localparam              ERR       =     8'h86  ; //8'h79;

//自动计算位宽的函数;
    function integer clogb2(input integer depth);begin
        if(depth==0)
            clogb2=1;
        else if(depth!=0)
            for(clogb2=0; depth>0;clogb2=clogb2+1)
                depth=depth>>1;
        end
    endfunction

计数器cnt_20us初始值为0,对系统时钟进行计数,计数到TIME_20US-1时结束,对应代码如下:

//20us计数器,用于对一个数码管点亮的持续时间进行计数,计数器初始值为0,对
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin//计数器初始值为0;
            cnt_20us <= 0;
        end
        else if(end_cnt_20us)begin//当计数器计数到20us时,表示一个数码管已经被点亮20US了,将计数器清零;
            cnt_20us <= 0;
        end
        else begin//否则,计数器加一;
            cnt_20us <= cnt_20us + 1'b1;
        end
    end

    //计数器结束条件,当计数器计数到TIME_20US-1时表示20US已经到了,将计数器清零;
    assign end_cnt_20us = cnt_20us == TIME_20US - 

计数器sel初始值为0,表示最初点亮第0个数码管,当计数器cnt_20us计数计数结束时,表示一个数码管被点亮20us了,此时计数器sel加一,开始点亮下一个数码管,当计数器sel计数到SEG_NUM-1并且cnt_20us计数结束时,表示所有数码管都被点亮一次了,将计数器sel清零,从第一个数码管在此循环点亮;

综上:计数器sel初始值为0,加一条件add_sel=end_cnt_20us,结束条件end_sel=add_sel && sel==SEG_NUM-1,对应代码如下:

always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin//初始值为0;
            sel <= 0;
        end
        else if(add_sel) begin
            if(end_sel)//当计数器sel计数结束时,计数器清零;
                sel <= 0;
            else
                sel <= sel + 1;
        end
    end
    assign add_sel = end_cnt_20us;//计数器sel的加一条件是,计数器cnt_20us计数器结束;
    assign end_sel = add_sel && sel == SEG_NUM - 1;//计数器sel计数到SEL_NUM-1时,计数器sel清零;

接下来还需要做两件事:

2.1、输入信号译码成数码管数据信号segment

  输入数据din是BCD码,所以只需要在计数器sel为0是将din[3:0]译码成segment输出给数码管即可。根据这个原理,sel_result信号先根据计数器sel的值从din取出对应的四位数据,代码如下:

//sel_result信号是当前被点亮数码管需要显示的数据,根据计数器sel的值确定此时应该将输入信号的哪几位数据译码输出给数码管进行显示;
    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin//初始值为0;
            sel_result <= 4'd0;
        end
        else begin//取输入信号din[4*sel+3 : 4*sel]信号给译码部分进行译码,之后输出给数码管数据信号驱动数码管显示该数据;
            sel_result <= din[4*sel+3 -: 4];//{din[4*sel+3],din[4*sel+2],din[4*sel+1],din[4*sel]};
        end
    end

之后将sel_result译码成能驱动数码管显示对应数据信号segment,对应代码如下:

//译码器部分,将sel_result十进制信号译码成数码管显示该数字对应的八位数据信号;
    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin//初始上电时,所有数码管显示数据0;
            segment <= ZERO;
        end
        else begin
            case(sel_result)//将sel_result译码成对应的segment数据,segment数据驱动数码管才能显示sel_result代表的数字;
                0: segment <= ZERO ;//想要数码管显示0,就要给数码管数据信号segment输入ZERO数据,其余类似;
                1: segment <= ONE  ;
                2: segment <= TWO  ;
                3: segment <= THREE;
                4: segment <= FOUR ;
                5: segment <= FIVE ;
                6: segment <= SIX  ;
                7: segment <= SEVEN;
                8: segment <= EIGHT;
                9: segment <= NINE ;
                default: segment <= ERR;
            endcase
        end
    end

  对应仿真如下,六个数码管显示24’h044039,sel为0时,sel_result=4’d9,segment将sel_result通过译码表1译码成NINE对应的数据8’h90,则第一个数码管显示9,其余类推;

图5 modelsim部分仿真结果

2.2、根据计数器sel控制数码管片选信号seg_sel

  计数器sel表示此时应该点亮第几个数码管,所以计数器sel=0时,应该将第一个数码管的片选拉低,其余数码管的片选拉高,此时数据线segment的数据用于驱动第一个数码管显示对应数据。segment信号滞后计数器sel变化两个时钟,所以seg_sel也需要滞后sel两个时钟,才能使得segment与seg_sel信号对齐。所以先将sel延迟一个时钟得到sel_ff0,代码如下:

//为了与段选动态扫描,保持同步,此时位选应该打一拍再赋给位选信号 seg_sel
    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            sel_ff0 <= 0;
        end
        else begin
            sel_ff0 <= sel;
        end
    end

  之后根据sel_ff0得到数码管片选信号,将1左移sel_ff0之后取反,就可以将第sel_ff0位数码管片选拉低,其余数码管片选拉高,对应代码如下:

always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin//初始值为0,全部数码管被点亮;
            seg_sel <= {{SEG_NUM}{1'b0}};
        end
        else begin//将1左移sel_ff0位之后取反,seg_sel的第sel_ff0输出低电平,对应的第sel_ff0个数码管被点亮了,其余位输出高电平,对应的数码管熄灭;
            seg_sel <= ~({{{SEG_NUM-1}{1'b0}},1'b1} << sel_ff0);//~(6'h1<<sel_ff0);
        end
    end

三、源文件获取

  在公众号” 数字站 “回复“数码管fpga”获取下载链接。

  下载后解压得到源文件如下图:

图6 解压文件夹下的文件

  其中seg_disp.v是数码管驱动文件,test.v文件是仿真测试激励生成文件。可以直接使用modelsim打开该文件夹下的test.mpf文件进行仿真,如下图操作:

图7 modelsim打开工程

  一定要先修改查找的文件类型,不然很可能找不到该类型的文件,然后选择test.mpf文件打开即可,步骤如下所示:

图8 modelsim选择工程文件

  之后点击Library,然后选中work库里面的test右键选择Simulate即可。

图9 modelsim运行激励文件

  之后可以自己在sim选项卡里面添加文件,也可以加入我之前配置好的页面,下面是使用我之前配置好页面的方法。点击file,之后点击load,最后点击Macro File,如下图所示:

图10 modelsim打开已有波形页面配置文件

  首先还是选择打开文件夹显示所有类型的文件,之后选中wave.do文件打开,如下图所示:

图11 modelsim选择wave.do文件打开

  打开结果如下图所示:

图12 modelsim仿真结果

如果要重新仿真,进行如下图步骤操作即可:

图13 modelsim重新仿真

由于test.v文件有仿真结束设置,所以运行到时间后会自动停止仿真,回到该页面即可。

四、modelsim仿真6个数码管显示

图14 modelsim仿真结果

  仿真结果正确,在使用该模块时,例化时只需要更改SEG_NUM参数确定数码管个数以及是共阴数码管还是共阳数码管即可,其余信号可变位宽可以由自动计算位宽函数自动计算得出,不需要认为更改,该过程是在综合工具编译是完成的,不会消耗额外资源。

五、总结

  1、注意:主要是以两个数码管为主体架构,需要注意数码管数据信号要与片选信号对齐,如果没有对齐则会出现数码管显示数据奇怪的问题,原因在于一个数码管在某个别时钟显示了上一个数码管或者下一个数码管该显示的数据。
  2、扩展:后续可以加入小数点的控制电路,以及使用控制信号选择共阴还是共阳的数码管。

有关八段数码管动态显示(输入数据为BCD编码)的更多相关文章

  1. ruby-on-rails - Rails 编辑表单不显示嵌套项 - 2

    我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby-on-rails - 使用 Sublime Text 3 突出显示 HTML 背景语法中的 ERB? - 2

    所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择

  4. ruby-on-rails - link_to 不显示任何 rails - 2

    我试图在索引页中创建一个超链接,但它没有显示,也没有给出任何错误。这是我的index.html.erb代码。ListingarticlesTitleTextssss我检查了我的路线,我认为它们也没有问题。PrefixVerbURIPatternController#Actionwelcome_indexGET/welcome/index(.:format)welcome#indexarticlesGET/articles(.:format)articles#indexPOST/articles(.:format)articles#createnew_articleGET/article

  5. ruby-on-rails - 如何在 Rails View 上显示错误消息? - 2

    我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c

  6. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  7. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  8. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  9. ruby-on-rails - 复数 for fields_for has_many 关联未显示在 View 中 - 2

    目前,Itembelongs_toCompany和has_manyItemVariants。我正在尝试使用嵌套的fields_for通过Item表单添加ItemVariant字段,但是使用:item_variants不显示该表单。只有当我使用单数时才会显示。我检查了我的关联,它们似乎是正确的,这可能与嵌套在公司下的项目有关,还是我遗漏了其他东西?提前致谢。注意:下面的代码片段中省略了不相关的代码。编辑:不知道这是否相关,但我正在使用CanCan进行身份验证。routes.rbresources:companiesdoresources:itemsenditem.rbclassItemi

  10. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

随机推荐