目录

  • 一、数码管原理
  • 二、基础篇
    • 2.1 原理及代码
    • 2.2 验证结果
  • 三、进阶篇
    • 3.1 原理及代码
    • 3.2 验证结果
  • 四、数字时钟
    • 4.1 原理及代码
    • 4.2 验证结果

本文内容:基于 FPGA 实现数字时钟,如果后续有时间可以添加一些额外的功能,比如设置时间、闹钟等等
中间的基础篇和进阶篇主要训练数码管的灵活应用,如果熟悉了并完全掌握的话,可以更加熟练的实现数字时钟

一、数码管原理

  • 我使用的开发板型号为 EP4CE6F17C8,它的数码管有六位,原理图如下:
  • 主要是由 DIG 和 SEL 这两个信号控制 6 位数码管显示,高电平灭,低电平亮,下面主要介绍如何控制

SEL 信号

  • SEL 信号主要用来控制数码管的每一位,共有 6 位,SEL 位宽也就是 6 位,如下图所示:

    举个例子,在代码中位宽的表示是低位在右,高位在左,所以在代码中写成 SEL = 6’b111_110 时,实际上就是第 0 位数码管亮,其余五位灭,如下图所示:

    当 SEL = 6’b101_110 时,实际上就是第 0 位和第 4 位数码管亮,如下图所示:

    但是只有一个 SEL 信号不足以完全控制每一位数码管中的每一段

DIG 信号

  • DIG 信号主要控制每一位数码管中的 8 个段的数码管亮灭,如下图所示:
  • 当 DIG = 8’b1010_0100 时,一位数码管就显示数字 2,如下图所示:

    其它每个数字所对应的 DIG 值就不一一列举出来了
  • 这两个信号时需要相互配合才可以在数码管上呈现我们想要的效果
  • 比如说当 SEL = 6’b111_101 且 DIG = 8’b1010_0100 时,前面 SEL 表示第 1 位(从 0 开始的)数码管亮,配合 DIG 显示数字 2,那么在开发板上就可以呈现第 1 位数码管显示数字 2 其余数码管灭,代码如下:
module display(input                   clk         ,input                   rst_n       ,output      [7:0]       DIG         ,output      [5:0]       SEL
);assign SEL = 6'b111_101;assign DIG = 8'b1010_0100;endmodule
  • 效果图如下:

二、基础篇

2.1 原理及代码

  • 如何让数码管依次显示 123456 呢?首先要知道 SEL 和 DIG 如何配合控制数码管显示的
  • 原理图:

    (1) 首先设置 SEL 显示第 0 位,也就是 SEL = 6’b111_110,再设置 DIG 显示 1 的比特值,这样的话,数码管就第 0 位显示数字 1 了,但是其它位是灭的
    (2) 然后设置 SEL 显示第 1 位,也就是 SEL = 6’b111_101,再设置 DIG 显示 2 的比特值,这样的话,数码管就第 1 位显示数字 2 了,但是其它位是灭的
    (3) 按照上面的套路,依次让每一位显示相应数字,其它位灭,当 SEL 的值改变的速度不断的增加,那么就可以连续显示 123456 了,这主要应用到了视觉残留的机制
  • 仔细好好的理一下逻辑
  • 代码也是十分简单的,当然可以不用 SEL_num 来做选择,直接对 SEL 使用拼接运算符也可以

方法一:普通版

module display #(parameter MS_1 = 17'd100_000)(input                   clk         ,input                   rst_n       ,output  reg [7:0]       DIG         ,output  reg [5:0]       SEL
);// 参数定义parameter SEL_MAX   = 3'd6          ;   // 数码管位数// 信号定义reg     [ 2:0]          SEL_num     ;   // SEL序号选择reg     [16:0]          cnt_flicker ;   // 数码管闪烁频率计数器// 逻辑实现// 闪烁频率计数器always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_flicker <= 17'd0;endelse if (cnt_flicker >= MS_1 - 17'd1) begincnt_flicker <= 17'd0;endelse begincnt_flicker <= cnt_flicker + 17'd1; endend// SEL序号选择always @(posedge clk or negedge rst_n) beginif (!rst_n) beginSEL_num <= 3'd0;endelse if (cnt_flicker >= MS_1 - 17'd1) beginif (SEL_num >= SEL_MAX - 3'd1) beginSEL_num <= 3'd0;endelse beginSEL_num <= SEL_num + 3'd1;endendelse beginSEL_num <= SEL_num;endend// SEL信号输出always @(*) begincase (SEL_num)3'd0 : SEL = 6'b111_110;3'd1 : SEL = 6'b111_101;3'd2 : SEL = 6'b111_011;3'd3 : SEL = 6'b110_111;3'd4 : SEL = 6'b101_111;3'd5 : SEL = 6'b011_111;default: SEL = 6'b111_111;endcaseend// DIG信号输出always @(*) begincase (SEL_num)3'd0 : DIG = 8'b1111_1001;3'd1 : DIG = 8'b0010_0100;3'd2 : DIG = 8'b1011_0000;3'd3 : DIG = 8'b0001_1001;3'd4 : DIG = 8'b1001_0010;3'd5 : DIG = 8'b1000_0010;default: DIG = 8'b1111_1111; endcaseendendmodule

方法二:拼接运算版

module display #(parameter MS_1 = 17'd100_000)(input                               clk         ,input                               rst_n       ,output  reg     [7:0]               DIG         ,output  reg     [5:0]               SEL
);// 信号定义reg             [16:0]              cnt_flicker ;   // 数码管闪烁频率计数器wire            [ 0:0]              SEL_change  ;// 逻辑实现// 闪烁频率计数器always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_flicker <= 17'd0;endelse if (SEL_change) begincnt_flicker <= 17'd0;endelse begincnt_flicker <= cnt_flicker + 17'd1; endendassign SEL_change = cnt_flicker >= MS_1 - 17'd1 ? 1'b1 : 1'b0;// SEL信号输出always @(posedge clk or negedge rst_n) beginif (!rst_n) beginSEL <= 6'b111_110;endelse if (SEL_change) beginSEL <= {SEL[4:0], SEL[5]};endelse beginSEL <= SEL;endend// DIG信号输出always @(*) begincase (SEL)6'b111_110 : DIG = 8'b1111_1001;6'b111_101 : DIG = 8'b0010_0100;6'b111_011 : DIG = 8'b1011_0000;6'b110_111 : DIG = 8'b0001_1001;6'b101_111 : DIG = 8'b1001_0010;6'b011_111 : DIG = 8'b1000_0010;default: DIG = 8'b1111_1111; endcaseendendmodule

2.2 验证结果

  • 效果图如下:

三、进阶篇

3.1 原理及代码

  • 该部分主要进一步探究数码管 SEL 与 DIG 之间配合显示

实现目标:让 6 位数码管从右往左滑动显示 0 - 9,当显示 9 时,后面连续显示 “-”,直到 9 消失,又从 0 开始滑动

  • 可以自己画图分析如何实现,这里我将数码管显示分为 16 个状态,每经过 0.5 s 的时间就向左滑动一下,也就是从一个状态跳转到下一个状态
  • 将一些特殊的情况罗列出来,可以发现用状态序号+位选号来确定每一位所对应的 DIG 信号
  • 进一步分析,随着状态的转移,它们之间的和就大于 9 了,那么就需要分情况讨论
  • 和 <= 9
    直接输出对应的 DIG 二进制
    15>= 和 >= 10
    输出 “-” 对应的 DIG 二进制
    和 >= 16
    输出 和 - 16 对应的 DIG 二进制
  • 现在思路清晰了,实现代码如下:
module dig_demo #(parameter MS_1    = 17'd100_000,MS_200  = 25'd2500_0000)(input                               clk                     ,   // 50MHz时钟input                               rst_n                   ,   // 复位信号output  reg     [ 7:0]              DIG                     ,   // 输出DIGoutput  reg     [ 5:0]              SEL                         // 输出SEL
);// 信号定义reg             [16:0]              cnt_flicker             ;   // SEL刷新频率计数器wire            [ 0:0]              end_cnt_flicker         ;   // cnt_flicker停止计数信号reg             [24:0]              cnt_200ms               ;   // 200ms计数器wire            [ 0:0]              end_cnt_200ms           ;   // cnt_200ms停止计数使能信号reg             [ 3:0]              cnt_16state             ;   // 16 个状态计数器wire            [ 0:0]              end_cnt_16state         ;   // 结束计时reg             [ 2:0]              SEL_now                 ;   // SEL现态wire            [ 4:0]              cnt_16state_and_SEL_now ;   // cnt_16state + SEL_nowreg             [ 1:0]              DIG_now_status          ;   // DIG现态所处的情况reg             [ 3:0]              DIG_now                 ;   // DIG现态// 逻辑实现// SEL刷新频率计数器/*每过10_0000个时钟周期刷新SEL值*/always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_flicker <= 17'd0;endelse if (end_cnt_flicker) begincnt_flicker <= 17'd0;endelse begincnt_flicker <= cnt_flicker + 17'd1; endendassign end_cnt_flicker = cnt_flicker >= MS_1 - 17'd1 ? 1'b1 : 1'b0;// SEL现态/*当cnt_flicker计数到最大值10_0000个时钟周期后SEL就从第n位跳到第n+1位当跳到第5位后,就回到第0位*/always @(posedge clk or negedge rst_n) beginif (!rst_n) beginSEL_now <= 'd0;endelse if (end_cnt_flicker) beginif (SEL_now >= 'd5) beginSEL_now <= 'd0;endelse beginSEL_now <= SEL_now + 'd1;endendelse beginSEL_now <= SEL_now;endend// SEL信号输出/*根据SEL_now选择SEL输出二进制*/always @(*) begincase (SEL_now)3'd0 : SEL = 6'b111_110;3'd1 : SEL = 6'b111_101;3'd2 : SEL = 6'b111_011;3'd3 : SEL = 6'b110_111;3'd4 : SEL = 6'b101_111;3'd5 : SEL = 6'b011_111;default: SEL = 6'b111_111;endcaseend// cnt_200ms/*计数200ms每过200ms,数码管就向左滑动一下*/always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_200ms <= 'd0;endelse if (end_cnt_200ms) begincnt_200ms <= 'd0;endelse begincnt_200ms <= cnt_200ms + 'd1;endendassign end_cnt_200ms = cnt_200ms >= MS_200 - 'd1 ? 1'b1 : 1'b0;// cnt_16state/*数码管滑动分为16个状态每个状态持续200ms*/always @(posedge clk or negedge rst_n) beginif (!rst_n) begincnt_16state <= 'd0;endelse if (end_cnt_200ms) beginif (end_cnt_16state) begincnt_16state <= 'd0;endelse begincnt_16state <= cnt_16state + 'd1;endendelse begincnt_16state <= cnt_16state;endendassign end_cnt_16state = cnt_16state >= 4'd15 ? 1'b1 : 1'b0;// 计算当前16个状态中的一个状态值与SEL现在位数的和assign cnt_16state_and_SEL_now = cnt_16state + SEL_now;// DIG现态值判断/*根据上面计算出的和判断当前数码管处于3个状态中的哪一个状态0:数码管每一位显示数字1:数码管前面显示数字,后面显示"-"2:数码管前面显示"-",后面显示数字*/always @(*) beginif (cnt_16state_and_SEL_now > 9 && cnt_16state_and_SEL_now < 16) beginDIG_now_status <= 'd1;endelse if (cnt_16state_and_SEL_now >= 16) beginDIG_now_status <= 'd2;endelse beginDIG_now_status <= 'd0;endend// DIG现态/*根据前面状态的判断计算出当前SEL所对应的DIG的值*/always @(*) beginif (DIG_now_status == 'd0) beginDIG_now = cnt_16state_and_SEL_now;endelse if (DIG_now_status == 'd1) beginDIG_now = 'd10;endelse if (DIG_now_status == 'd2) beginDIG_now = cnt_16state_and_SEL_now - 'd16;endelse beginDIG_now = DIG_now;endend// DIG对应数字输出always @(*) begincase (DIG_now)4'd0  : DIG = 8'b1100_0000;4'd1  : DIG = 8'b1111_1001;4'd2  : DIG = 8'b1010_0100;4'd3  : DIG = 8'b1011_0000;4'd4  : DIG = 8'b1001_1001;4'd5  : DIG = 8'b1001_0010;4'd6  : DIG = 8'b1000_0010;4'd7  : DIG = 8'b1111_1000;4'd8  : DIG = 8'b1000_0000;4'd9  : DIG = 8'b1001_0000;4'd10 : DIG = 8'b1011_1111;default : DIG = 8'b1111_1111;endcaseendendmodule

3.2 验证结果

数码管滑动显示

四、数字时钟

4.1 原理及代码

需求分析:

  1. 数码管显示拥有多个界面
    (1)时分秒:显示界面、设置界面
    (2)年月日:显示界面、设置界面
    (3)闹钟:状态界面(开启/关闭)、设置界面
  2. 年月日:
    (1)默认主界面为时分秒显示界面,通过按键可调出年月日界面,并显示三秒钟后自动跳转回主界面(时分秒界面)
    (2)进入到年月日界面后,可通过设置按键设置年月日的值,设置完成后自动保存
  3. 时分秒:
    (1)默认主界面为时分秒显示界面,设置按键可设置当前显示界面时分秒的值,设置完成后自动保存
  4. 闹钟:
    (1)闹钟设置为闹钟显示界面,如果开启闹钟,则显示设置的闹钟时间,如果关闭闹钟,则显示连续的 “-”,表示关闭了闹钟
    (2)当时间达到闹钟设的值时,蜂鸣器播放歌曲,播放歌曲期间按键任意按键即可关闭闹钟

原理讲解:

  • 说实话,这原理其实真没啥好讲的,根据上面写出来的需求一步一步添加相应的条件或者使能信号就实现出来了
  • 上面两部分倒是有些原理可讲,如果能熟练写出上面的代码,那么对于一些信号的灵活应用以及代码编写的能力也能提高很多,而这时钟是真不好讲,所以只说说模块划分吧
  • 数字时钟,无非就是用按键设置时钟的值,这里空口白话的说也理不清楚里面的逻辑关系,涉及到很多使能信号之间的条件关系
  • 可以仿真出来看看,就很清楚明白了
  • 工程文件链接:https://pan.baidu.com/s/1NR6dzZ2G6TEWzoaGswhYww?pwd=30gh——提取码:30gh
  • 简单说一下工程内的文件
  • 源码主要是在 rtl 下面的 .v 文件
  • 看看上面那张系统设计的图,就可以知道每个文件之间的依赖关系
  • 贴一张代码图片,看着还不算脑袋疼吧,有点些许的强迫症

4.2 验证结果

  • 最后来看看在开发板上的效果吧

FPGA多功能数字时钟

  • 视频中可以看到设置闹钟的分,递减到 59 就减不下去了,这是由于代码中闹钟模块设置分的部分,条件写岔劈了,有点粗心大意,这个我改了,将大于(>)改成了小于(<)
  • 同时有一个小 bug ,在 display.v 模块中,也就是数码管显示模块,处于闹钟关闭状态下,如果一直按设置键,即使当前闹钟显示关闭状态,也还是可以设值,只是我们看不到而已
  • 这是由于给 select_place 信号赋值的条件少写了,这个我还没改了,有时间可以自行研究

基于 FPGA 实现滑动显示、多功能数字时钟【设置年月日时分秒以及闹钟】相关推荐

  1. 基于FPGA的多功能数字时钟设计报告

    作品基于intel Cyclone IV E EP4CE10F17C8 FPGA板卡,主要开发环境为Quartus Ⅱ,编程并实现了多功能温湿度电子钟.本作品在实现显示实时时间的基础上,设计并完成了设 ...

  2. 数字系统设计(FPGA)课程设计: 多功能数字钟

    一.目的: 实现多功能数字钟,具备下列功能: 1.数字钟:能计时,实现小时.分钟.秒的显示: 2.数字跑表:精度至0.01秒 比如显示12.97秒: 3.闹钟: 可以设定闹钟,用试验箱上的蜂鸣器作为闹 ...

  3. 基于FPGA的HDMI显示(二)

    基于FPGA的720P HDMI显示 1.4.1 HDMI 硬件电路分析   本次设计采用了 IO 模拟的方式实现 HDMI 的功能.与采用专用 HDMI 芯片相比,此方案具有成本更低.效果不输于采用 ...

  4. FPGA - 基于FPGA的HDMI显示

    写在前面 HDMI接口很早之前就想调试了,由于没有时间,就拖到了现在,而且毕业设计也是和视频处理系统有关,就趁这个机会把这个接口调试下. 开发环境 vivado 18.3 pynq - z2 HDMI ...

  5. 基于FPGA的VGA显示对贪吃蛇游戏的设计

    基于FPGA的VGA显示对贪吃蛇游戏的设计 摘要 目前,电子数码产品已经进入了人生活的方方面面,而大多数电子产品都依靠显示屏来传递信息,由此可见用电路对显示屏进行控制的研究有很大的实用价值和市场需求. ...

  6. rgb转yuv422 matlab,基于FPGA的RGB到YUV422的数字视频转换.pdf

    基于FPGA的RGB到YUV422的数字视频转换.pdf Parts & applications 文章编号: ( ) 1002-8692 2009 S2-0105-02 实用技术 基于FPG ...

  7. 基于FPGA的通信显示系统设计

    一.系统介绍 本设计给出了一种基于FPGA的通信显示系统设计方法,采用EDA作为开发工具,QuartusII作为运行程序的平台,结合所学知识设计一个基于FPGA的通信显示系统设计,通过串口调试器发送图 ...

  8. 多功能数字钟软件C语言,多功能数字时钟

    内容介绍 原文档由会员 你的样子 发布 多功能数字时钟 ①页数 19 ②字数 6932 ③摘要 摘 要: 随着电子技术的发展,在诸如计时.控制等领域,设计出应用具有时间设置(小时和分钟),闹钟时间设置 ...

  9. quartus 修改 时钟_Clock Quartus II 开发的多功能数字时钟,有计时、调 闹铃、警报等 Other systems 其他 274万源代码下载- www.pudn.com...

    文件名称: Clock下载  收藏√  [ 5  4  3  2  1 ] 开发工具: Others 文件大小: 530 KB 上传时间: 2017-04-20 下载次数: 0 提 供 者: 墨离非 ...

最新文章

  1. Android典型界面设计(4)——使用ActionBar+Fragment实现tab切换
  2. 实现费用管理 mysql_移动电费房租管理系统的设计与实现(IDEA,MySQL)
  3. RabbitMQ(一)helloworld
  4. ubuntu java 编译器_Ubuntu 16.04配置Java开发环境
  5. DirectShow学习
  6. IE下 c00ce56e 错误竟然是nginx 字符设置的问题
  7. mysql子查询总分最高的学生_mysql高级内容
  8. Linux下ip route、ip rule、iptables的关系(转
  9. mac下HTTP与HTTPS抓包
  10. Windows 10 预览版安装
  11. ArcPad 10 的安装部署
  12. 数学方法论的含义和研究意义
  13. walking机器人仿真教程-激光导航-TEB算法导航
  14. matlab DSB-AM与SSB-AM的调制与解调
  15. 深度学习初学者必须知道的25个专业名词
  16. sklearn基础篇(十)-- 非负矩阵分解与t-SNE
  17. 有图书馆有计算机教室的英语,以my school为题的英语作文有图书馆,办公室,音乐室,教室,体育馆...
  18. PS存储为和导出为的区别
  19. python爬取王者荣耀高清图
  20. Linux运维技术之详解任务计划(crontab命令)

热门文章

  1. supervise 用来监控服务,自动启动
  2. iOS 14.4 Beta 2更新内容 iOS 14.4 Beta 2升级方法
  3. Kaggle_NCFM鱼品种识别
  4. C++包扩展_USB、Type-C全能转换器-扩展坞
  5. ARM架构IMX6核心板在全自动生化分析仪中的应用
  6. 微信公众平台订阅号和服务号和企业号的区别
  7. 贵州二级计算机考试题库,2018贵州事业单位考试题库:事业单位模拟试卷试题及答案解析六...
  8. python处理滑块验证码(企查查)实例
  9. linux_命令行助手:(manpages supplement)/获取linux命令用法示例的若干辅助命令行工具(cheat/tldr/eg/manly)
  10. 软件测试之第三方快捷支付_支付相关测试方法