参考:正点原子开拓者 FPGA 开发指南

一、数码管动态显示简介

由于一般的静态驱动操作虽然方便,但占用的I/0口较多,例如要驱动6位8段数码管,以静态驱动方式让数码管各个位显示不同的数值,如“123456”,需要占用6 × 8 = 48个I/O口,虽然对于FPGA这种I/O口较多的芯片而言,在资源允许的情况下可以使用,但一般不建议浪费宝贵的I/O口资源,尤其在I/O口资源紧张的情况下,所以对于多位数码管一般采用动态驱动方式使数码管显示数字。那么什么是动态驱动方式呢?

为了更好的理解数码管动态驱动,我们首先了解下市面上常见的多位数码管的内部连接。
以两位数码管为例,其内部连接如下图。由此图可知,两位8段数码管共10个引脚,每位数码管的阳极连接在一起,为共阳极数码管,每位数码管相同段的led的阴极连接在一起,这样当给第10和第5脚高电平,给第3脚低电平时,两个数码管的发光二极管A都点亮,对于此种数码管以静态方式驱动显然不可能显示像“18”这种个位与十位不同的数字。那么该如何显示数字“18”呢?


既然同时给第10和第5脚高电平不可行,那么是不是可以先给第5脚高电平,第10脚低电平,此时,让其显示数字“8”时,左边的数码管不显示,右边的数码管显示数字“8”;然后给第10脚高电平,第5脚低电平,此时,让其显示数字“1”时,左边的数码管显示数字“1”,右边的数码管不显示,这样就可以显示数字“18”了。但有一个问题,多长时间切换显示的数码管呢,时间如果太长就只能看到数字“8”或数字“1”了,时间太短呢,结果是显示不清晰而且显示亮度不够。由于人眼的视觉暂留(人眼在观察景物时,光信号传人大脑神经,需经过一段短暂的时间,光的作用结束后,视觉形象并不立即消失,这种残留的视觉称“后像”,视觉的这一现象则被称为“视觉暂留”)及发光二极管的余辉效应(当停止向发光二极管供电时,发光二极管亮度仍能维持一段时间),每位数码管的点亮时间为1~2ms时,显示效果能满足使用需要。数码管的这种驱动方式称为数码管的动态驱动,实际上就是分时轮流控制不同数码管的显示。

二、实验任务

本实验任务是使用FPGA开发板上的6位数码管以动态方式从0开始计数,每100ms计数值增加一,当计数值从0增加到999999后重新从0开始计数。

三、 程序设计

由实验任务和动态驱动的原理我们可以知道,若要让6个数码管轮流显示对应的数字,首先需要一个数码管动态显示模块,能够依次点亮6个数码管,并将对应的数据输出至数码管,也就是需要分别控制段选位选信号;同时还需要一个计数模块,能够将0—999999依次输出至数码管动态显示模块。根据实验任务,我们可以大致规划出系统的控制流程:首先我们需要一个数码管动态显示模块在数码管上显示数据,其次需要一个计数控制模块实现从0到999999的变化,并将产生的数值通过数码管动态显示模块在数码管上显示出来。由此画出系统的功能框图如下所示:

程序中各模块端口及信号连接如下图所示:


FPGA顶层(top_seg_led)例化了以下两个模块:计数模块(count)以及数码管动态显示模块(seg_led)。实现各模块之间数据的交互。计数模块将计数值通过data端口传递给数码管动态显示模块,使能信号en使能数码管显示数据,小数点显示信号point控制小数点的显示,符号信号sign可以让数码管显示负号。

计数模块(count):显示的数字每100ms加“1”。

数码管动态显示模块(seg_led):数码管动态显示模块在数码管上以动态方式显示数值。

1、顶层模块的代码
module top_seg_led(//global clockinput            sys_clk  ,       // 全局时钟信号input            sys_rst_n,       // 复位信号(低有效)//seg_led interfaceoutput    [5:0]  seg_sel  ,       // 数码管位选信号output    [7:0]  seg_led          // 数码管段选信号
);//wire define
wire    [19:0]  data;                 // 数码管显示的数值
wire    [ 5:0]  point;                // 数码管小数点的位置
wire            en;                   // 数码管显示使能信号
wire            sign;                 // 数码管显示数据的符号位//*****************************************************
//**                    main code
//*****************************************************//计数器模块,产生数码管需要显示的数据
count u_count(.clk           (sys_clk  ),       // 时钟信号.rst_n         (sys_rst_n),       // 复位信号.data          (data     ),       // 6位数码管要显示的数值.point         (point    ),       // 小数点具体显示的位置,高电平有效.en            (en       ),       // 数码管使能信号.sign          (sign     )        // 符号位
);//数码管动态显示模块
seg_led u_seg_led(.clk           (sys_clk  ),       // 时钟信号.rst_n         (sys_rst_n),       // 复位信号.data          (data     ),       // 显示的数值.point         (point    ),       // 小数点具体显示的位置,高电平有效.en            (en       ),       // 数码管使能信号.sign          (sign     ),       // 符号位,高电平显示负号(-).seg_sel       (seg_sel  ),       // 位选.seg_led       (seg_led  )        // 段选
);endmodule

顶层模块中主要完成对其余模块的例化,并且实现各模块之间信号的交互。计数模块输出的数值data连接至数码管显示模块的输入端口data,数码管显示模块将输入的数据data输出至数码管上显示。

2、计数模块的代码
module count(//mudule clockinput                   clk  ,      // 时钟信号input                   rst_n,      // 复位信号//user interfaceoutput   reg [19:0]     data ,      // 6个数码管要显示的数值output   reg [ 5:0]     point,      // 小数点的位置,高电平点亮对应数码管位上的小数点output   reg            en   ,      // 数码管使能信号output   reg            sign        // 符号位,高电平时显示负号,低电平不显示负号
);//parameter define
parameter  MAX_NUM = 23'd5000_000;      // 计数器计数的最大值//reg define
reg    [22:0]   cnt ;                   // 计数器,用于计时100ms
reg             flag;                   // 标志信号//*****************************************************
//**                    main code
//*****************************************************//计数器对系统时钟计数达10ms时,输出一个时钟周期的脉冲信号
always @ (posedge clk or negedge rst_n) beginif (!rst_n) begincnt <= 23'b0;flag<= 1'b0;endelse if (cnt < MAX_NUM - 1'b1) begincnt <= cnt + 1'b1;flag<= 1'b0;endelse begincnt <= 23'b0;flag <= 1'b1;end
end //数码管需要显示的数据,从0累加到999999
always @ (posedge clk or negedge rst_n) beginif (!rst_n)begindata  <= 20'b0;point <=6'b000000;en    <= 1'b0;sign  <= 1'b0;end else beginpoint <= 6'b000000;             //不显示小数点en    <= 1'b1;                  //打开数码管使能信号sign  <= 1'b0;                  //不显示负号if (flag) begin                 //显示数值每隔0.01s累加一次if(data < 20'd999999) data <= data +1'b1;     elsedata <= 20'b0;end end
end endmodule
3、数码管动态显示模块
module seg_led(input                   clk    ,        // 时钟信号input                   rst_n  ,        // 复位信号input         [19:0]    data   ,        // 6位数码管要显示的数值input         [5:0]     point  ,        // 小数点具体显示的位置,从高到低,高电平有效input                   en     ,        // 数码管使能信号input                   sign   ,        // 符号位(高电平显示"-"号)output   reg  [5:0]     seg_sel,        // 数码管位选,最左侧数码管为最高位output   reg  [7:0]     seg_led         // 数码管段选);//parameter define
localparam  CLK_DIVIDE = 4'd10     ;        // 时钟分频系数
localparam  MAX_NUM    = 13'd5000  ;        // 对数码管驱动时钟(5MHz)计数1ms所需的计数值//reg define
reg    [ 3:0]             clk_cnt  ;        // 时钟分频计数器
reg                       dri_clk  ;        // 数码管的驱动时钟,5MHz
reg    [23:0]             num      ;        // 24位bcd码寄存器
reg    [12:0]             cnt0     ;        // 数码管驱动时钟计数器
reg                       flag     ;        // 标志信号(标志着cnt0计数达1ms)
reg    [2:0]              cnt_sel  ;        // 数码管位选计数器
reg    [3:0]              num_disp ;        // 当前数码管显示的数据
reg                       dot_disp ;        // 当前数码管显示的小数点//wire define
wire   [3:0]              data0    ;        // 十万位数
wire   [3:0]              data1    ;        // 万位数
wire   [3:0]              data2    ;        // 千位数
wire   [3:0]              data3    ;        // 百位数
wire   [3:0]              data4    ;        // 十位数
wire   [3:0]              data5    ;        // 个位数//*****************************************************
//**                    main code
//*****************************************************//提取显示数值所对应的十进制数的各个位
assign  data0 = data % 4'd10;               // 个位数
assign  data1 = data / 4'd10 % 4'd10   ;    // 十位数
assign  data2 = data / 7'd100 % 4'd10  ;    // 百位数
assign  data3 = data / 10'd1000 % 4'd10 ;   // 千位数
assign  data4 = data / 14'd10000 % 4'd10;   // 万位数
assign  data5 = data / 17'd100000;          // 十万位数//对系统时钟10分频,得到的频率为5MHz的数码管驱动时钟dri_clk
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginclk_cnt <= 4'd0;dri_clk <= 1'b1;endelse if(clk_cnt == CLK_DIVIDE/2 - 1'd1) beginclk_cnt <= 4'd0;dri_clk <= ~dri_clk;endelse beginclk_cnt <= clk_cnt + 1'b1;dri_clk <= dri_clk;end
end//将20位2进制数转换为8421bcd码(即使用4位二进制数表示1位十进制数)
always @ (posedge dri_clk or negedge rst_n) beginif (!rst_n)num <= 24'b0;else beginif (data5 || point[5]) begin     //如果显示数据为6位十进制数,num[23:20] <= data5;         //则依次给6位数码管赋值num[19:16] <= data4;num[15:12] <= data3;num[11:8]  <= data2;num[ 7:4]  <= data1;num[ 3:0]  <= data0;endelse begin                         if (data4 || point[4]) begin //如果显示数据为5位十进制数,则给低5位数码管赋值num[19:0] <= {data4,data3,data2,data1,data0};if(sign)                    num[23:20] <= 4'd11; //如果需要显示负号,则最高位(第6位)为符号位elsenum[23:20] <= 4'd10; //不需要显示负号时,则第6位不显示任何字符endelse begin                   //如果显示数据为4位十进制数,则给低4位数码管赋值if (data3 || point[3]) beginnum[15: 0] <= {data3,data2,data1,data0};num[23:20] <= 4'd10; //第6位不显示任何字符if(sign)             //如果需要显示负号,则最高位(第5位)为符号位num[19:16] <= 4'd11;else                 //不需要显示负号时,则第5位不显示任何字符num[19:16] <= 4'd10;endelse begin               //如果显示数据为3位十进制数,则给低3位数码管赋值if (data2 || point[2]) beginnum[11: 0] <= {data2,data1,data0};//第6、5位不显示任何字符num[23:16] <= {2{4'd10}};if(sign)         //如果需要显示负号,则最高位(第4位)为符号位num[15:12] <= 4'd11;else             //不需要显示负号时,则第4位不显示任何字符num[15:12] <= 4'd10;endelse begin           //如果显示数据为2位十进制数,则给低2位数码管赋值if (data1 || point[1]) beginnum[ 7: 0] <= {data1,data0};//第6、5、4位不显示任何字符num[23:12] <= {3{4'd10}};if(sign)     //如果需要显示负号,则最高位(第3位)为符号位num[11:8]  <= 4'd11;else         //不需要显示负号时,则第3位不显示任何字符num[11:8] <=  4'd10;endelse begin       //如果显示数据为1位十进制数,则给最低位数码管赋值num[3:0] <= data0;//第6、5位不显示任何字符num[23:8] <= {4{4'd10}};if(sign)     //如果需要显示负号,则最高位(第2位)为符号位num[7:4] <= 4'd11;else         //不需要显示负号时,则第2位不显示任何字符num[7:4] <= 4'd10;endendendendendend
end//每当计数器对数码管驱动时钟计数时间达1ms,输出一个时钟周期的脉冲信号
always @ (posedge dri_clk or negedge rst_n) beginif (rst_n == 1'b0) begincnt0 <= 13'b0;flag <= 1'b0;endelse if (cnt0 < MAX_NUM - 1'b1) begincnt0 <= cnt0 + 1'b1;flag <= 1'b0;endelse begincnt0 <= 13'b0;flag <= 1'b1;end
end//cnt_sel从0计数到5,用于选择当前处于显示状态的数码管
always @ (posedge dri_clk or negedge rst_n) beginif (rst_n == 1'b0)cnt_sel <= 3'b0;else if(flag) beginif(cnt_sel < 3'd5)cnt_sel <= cnt_sel + 1'b1;elsecnt_sel <= 3'b0;endelsecnt_sel <= cnt_sel;
end//控制数码管位选信号,使6位数码管轮流显示
always @ (posedge dri_clk or negedge rst_n) beginif(!rst_n) beginseg_sel  <= 6'b111111;              //位选信号低电平有效num_disp <= 4'b0;           dot_disp <= 1'b1;                   //共阳极数码管,低电平导通endelse beginif(en) begincase (cnt_sel)3'd0 :beginseg_sel  <= 6'b111110;  //显示数码管最低位num_disp <= num[3:0] ;  //显示的数据dot_disp <= ~point[0];  //显示的小数点end3'd1 :beginseg_sel  <= 6'b111101;  //显示数码管第1位num_disp <= num[7:4] ;dot_disp <= ~point[1];end3'd2 :beginseg_sel  <= 6'b111011;  //显示数码管第2位num_disp <= num[11:8];dot_disp <= ~point[2];end3'd3 :beginseg_sel  <= 6'b110111;  //显示数码管第3位num_disp <= num[15:12];dot_disp <= ~point[3];end3'd4 :beginseg_sel  <= 6'b101111;  //显示数码管第4位num_disp <= num[19:16];dot_disp <= ~point[4];end3'd5 :beginseg_sel  <= 6'b011111;  //显示数码管最高位num_disp <= num[23:20];dot_disp <= ~point[5];enddefault :beginseg_sel  <= 6'b111111;num_disp <= 4'b0;dot_disp <= 1'b1;endendcaseendelse beginseg_sel  <= 6'b111111;          //使能信号为0时,所有数码管均不显示num_disp <= 4'b0;dot_disp <= 1'b1;endend
end//控制数码管段选信号,显示字符
always @ (posedge dri_clk or negedge rst_n) beginif (!rst_n)seg_led <= 8'hc0;else begincase (num_disp)4'd0 : seg_led <= {dot_disp,7'b1000000}; //显示数字 04'd1 : seg_led <= {dot_disp,7'b1111001}; //显示数字 14'd2 : seg_led <= {dot_disp,7'b0100100}; //显示数字 24'd3 : seg_led <= {dot_disp,7'b0110000}; //显示数字 34'd4 : seg_led <= {dot_disp,7'b0011001}; //显示数字 44'd5 : seg_led <= {dot_disp,7'b0010010}; //显示数字 54'd6 : seg_led <= {dot_disp,7'b0000010}; //显示数字 64'd7 : seg_led <= {dot_disp,7'b1111000}; //显示数字 74'd8 : seg_led <= {dot_disp,7'b0000000}; //显示数字 84'd9 : seg_led <= {dot_disp,7'b0010000}; //显示数字 94'd10: seg_led <= 8'b11111111;           //不显示任何字符4'd11: seg_led <= 8'b10111111;           //显示负号(-)default: seg_led <= {dot_disp,7'b1000000};endcaseend
endendmodule

FPGA 动态数码管显示实验相关推荐

  1. FPGA之动态数码管显示实验

    1.试验任务 完成6位数码管以动态方式从0开始计数,没100ms计数值增加1,当计数值从0增加到999999后重新从0开始计数. 2.硬件原理图 3.程序框图 程序设计思想: FPGA顶层(top_s ...

  2. 【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

    1)实验平台:正点原子新起点V2开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113 2)全套实验源码+手册+视频下载地址:ht ...

  3. 共阳数码管段码表_正点原子开拓者FPGA开发板资料连载第十一章 静态数码管显示实验...

    1)实验平台:正点原子开拓者FPGA 开发板 2)摘自<开拓者FPGA开发指南>关注官方微信号公众号,获取更多资料:正点原子 3)全套实验源码+手册+视频下载地址:http://www.o ...

  4. FPGA学习—数码管显示

    FPGA学习--数码管显示 文章目录 FPGA学习--数码管显示 结构框架 一.硬件设计 二.verilog编写 1.计数器 2.译码模块 3.数码管扫描模块 4.顶层模块 实验结果 结构框架 数码管 ...

  5. c语言实现7段数码管显示,七段数码管显示实验.doc

    七段数码管显示实验 单 片 机 实 验 报 告 实验九 七段数码管显示实验 实验目的 学习七段数码管的工作原理: 学习数码管与8051单片机的接口方法: 掌握动态扫描显示技术. 实验原理 如图4.9- ...

  6. 【51单片机】七段数码管显示实验+详细讲解

    [51单片机]七段数码管显示实验 前言 一.兵马未动,粮草先行--认识七段数码管 1.关于显示器 2.七段数码管 3.七段数码管的结构 4.数码管显示方法 二.数码管显示程序 1.静态显示 效果 2. ...

  7. 实验7-1_八段 LED 数码管显示实验

    一.实验要求 用89C51单片机控制并行接口芯片8255实现6 位八段码 LED 动态方式循环显示一组数据. 二.8255接口芯片的简介 (1)8255芯片是什么 8255A是一种可编程并行I/O口的 ...

  8. 51单片机动态数码管显示自己学号

    实验题目:使用51单片机实现动态数码管显示自己8位学号,例如20220018 在此原理图又分两种连接方式: 第一种是8位数码管接38译码器来减少端口使用. 第二种是,没有端口要求接好就可以那就是,把位 ...

  9. 单片机入门——动态数码管显示

    前言 在实际应用中通常都需要显示多位数值,如果采用静态数码管显示就不够好,因此就需要采用另外一种显示方式,即数码管动态显示.开发板上板载2 个四位一体的共阴数码管,接下来我们就来介绍下如何使用51 单 ...

最新文章

  1. php 数组与数组之间去重,PHP开发中一维数组与二维数组去重功能实现教程
  2. linux ejb远程调用,[转载]在容器外使用EJB 3.0 Persistence
  3. C——通过调用函数分配内存
  4. Java数据库篇3——SQL
  5. UIImagePickerController按钮的中文问题
  6. 论文 | 多传感器数据深度图的融合:最近基于深度学习的方法(下)
  7. 【软考】2020年全国计算机技术与软件专业技术资格考试,网络工程师(中级),考纲
  8. mysql 优化 (1)
  9. ERROR:此文件包含病毒,已删除
  10. B、KB、MB、GB、T单位换算
  11. python掷骰子实验报告_python掷骰子游戏
  12. 交换机级联-知识点、注意事项
  13. linux万兆网络传输速度,万兆以太网传输速度实测 (结果: 单向9984Mbps, 双向19808Mbps)...
  14. mes系统故障_mes系统实施失败是什么原因?
  15. 三维模型下构件关联类型系统展示
  16. Android5更新包,Android 5.x OTA Update官方文档(四、OTA更新包解读)
  17. 蓝桥杯 2018 C++ A组 初赛部分题解
  18. Android 读取外接储存设备的数据(如挂载的U盘,SD卡等)
  19. 水仙花数(调用函数)
  20. 微信支付签名失败(几种解决方案)

热门文章

  1. Unreal Engine 4 渲染目标(Render Target)教程之 使用渲染目标绘制(上)
  2. excel怎么设置自动计算_110个算量软件+80个excel自动计算表,提升效率不加班
  3. qt里面kits套件选择
  4. 抓取微信公众号全部文章,采用AnyProxy+Javascript+Java实现
  5. 自动化立体仓库软件(WMSWCS)功能介绍
  6. java 分子 分母_Java写一个类表示分数。分数由分子和分母组成。
  7. 洛谷1072 Hankson 的趣味题
  8. 8.1 正弦波振荡电路(1)
  9. 关于多台三菱PLC数据采集到SQL数据库,对接MES平台
  10. ERP和CRM的分工