FPGA初学记录——数字时钟系统搭建(上)

野火征途Pro开发板教程——数码管动态展示拓展训练,数字时钟系统搭建


文章目录

  • FPGA初学记录——数字时钟系统搭建(上)
  • 前言
  • 一、问题简述
  • 二、功能解析和源代码
    • 1.功能解析
    • 2.源代码——简单乘法器
    • 2.源代码——数字时钟的乘法器
    • 4.源代码——数据产生data_gen
  • 三、总结

前言

最近在学习FPGA,因为研究生需要用到,所用的开发板是野火的征途Pro,也是在跟着野火的教程学习,学到了数码管动态展示,看到拓展训练,是一个挺适合练手的题目,主要是利用各个模块,比较锻炼模块化设计的思路,感觉有必要记录下来,说不定以后会有用。
整个rtl代码网盘链接:

链接:https://pan.baidu.com/s/1HiunRrvUADUQ4vlM0RRmaA
提取码:0101
–来自百度网盘超级会员V4的分享

一、问题简述

1、更改数据生成模块,生成一个时钟(时、分、秒)显示在数码管上。
2、在1的基础上加入按键信号,可通过按键去设置时钟值。
本文先解决第一个问题

二、功能解析和源代码

1.功能解析

1、首先思路呢,学习完野火给的动态显示数码管之后,我们会明白如何在数码管上动态显示0-999999的数字,然后再看这个题目。会发现很多模块可以直接拿过来用,只需要更改data_gen这个模块,更改数据的产生方式就ok。
2、例程从0-99999的思路很简单,就是计时然后累加,但是数字时钟的思路会稍微复杂一些,这里就提一下我自己思考的一个思路:
a、参考例程的思路,把想要展示的数据用二进制表示出来,然后传入到一系列模块(下文统称为显示模块)中,就能把数据以10进制的方式展示出来,所以我们第一个想法是怎么把我们想要的数据用二进制表示出来。
b、0-999999,思路就是不停累加,而我们想要的数据比较特殊,将这个数据分为3个部分,时、分、秒,秒一直累加,一直到59归零,同时分加1,当分加到59的时候也归零,时加1,当时加到24的时候归零,从头开始,好,我们的主要思路就是这样定了。
c、得到了时、分、秒各自的数字,怎么把他们弄成一块呢,我的思路就是,最终的数据=时10000+分100+秒,当然了,这是10进制的,二进制也是一样的,但是二进制我们需要考虑的问题就是verilog没有直接的乘法,得自己写一个乘法器。
d、按照上述的说法,写一个基本的乘法器,然后加上亿点点细节,从乘法器然后往上推,就可以最后实现这个功能啦。
3、综上呢,本文主要分享数据生成data_gen代码和乘法器multiply代码(备注,为了更好了看到问题,所以缩短了数据更新时间,正常时钟的话是每1s变化,但代码里我设成了比较短,好像只有100ms还是10ms,之后改的话只需要改CNT_MAX就可以了)

2.源代码——简单乘法器

先把简单乘法器代码奉上:非最终代码里的内容,只是一个基础,最终乘法器代码是在该代码的基础上修改的

module multiply
(input wire sys_clk,input wire sys_rst_n,input wire [19:0] data1,//被乘数input wire [19:0] data2,//乘数output wire [19:0] result
);
reg [4:0] num;//判断第几位
reg [19:0] data_reg;//存储中间变量
reg [19:0] sum;//结果reg [19:0] data1_reg;
reg [19:0] data2_reg;
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)num <= -1'd1;else if(num == 5'd20)num <= 5'd20;elsenum <= num + 1'b1;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)data_reg <= 20'd0;else if(data2[num] == 1'b0)data_reg <= 20'd0;else if(data2[num] == 1'b1)data_reg <= data1 << (num);elsedata_reg <= data_reg;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)sum <= 20'd0;else if(num == 5'd20)sum <= sum;elsesum <= sum + data_reg;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)data1_reg <= 20'd0;else    data1_reg <= data1;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)data2_reg <= 20'd0;else    data2_reg <= data2;
assign result = sum;endmodule

乘法器的思路比较简单,简单来说就是:判断,移位,相加,可以直接手写几个二进制相乘的例子,仔细观察就可以发现规律,本人实在太懒,这里就不再花大篇内容阐述乘法器的思路了,以后有空了再说吧。然后就是想验证自己写的乘法器是否正确的话,可以写一个仿真验证代码的代码,缩短调试时间,观察波形和结果来验证,这个过程还是能学到很多的。

2.源代码——数字时钟的乘法器

同样先把代码奉上

module multiply
#(parameter CNT_MAX        = 25'd499_99_99, // 1000ms计数值parameter DATA_CARRY_MAX = 20'd10  // 进位最大值
)
(input  wire        sys_clk               , // 时钟信号input  wire        sys_rst_n             , // 复位信号input  wire [19:0] data_multiplicand     , // 被乘数input  wire [19:0] data_multiplier       , // 乘数output wire [19:0] result                  // 计算结果
);
/***乘法器变量***/
reg [4:0]   num              ; // 乘数的位的索引,因为乘数和被乘数都有20位,所以num设定为5位,最大值为31,大于20
reg [19:0]  data_reg         ; // 存储计算过程中移位的中间变量
reg [19:0]  sum              ; // 累加结果/***针对数码管显示时间的进位变量***/
reg [19:0]  data_carry       ; // 进位数据
reg         data_carry_flag  ; // 进位的标志位/***计时器变量***/
reg [24:0]  cnt_100ms        ; // 100ms计数器
reg         cnt_flag         ; // 100ms标志信号// cnt_100ms:用50MHz时钟从0到4999_999计数即为100ms
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_100ms <= 25'd0;else if(cnt_100ms == CNT_MAX)cnt_100ms <= 25'd0;elsecnt_100ms <= cnt_100ms + 1'b1;// cnt_flag:每100ms产生一个标志信号
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_flag <= 1'b0;else if(cnt_100ms == CNT_MAX - 1'b1)cnt_flag <= 1'b1;elsecnt_flag <= 1'b0;// 最小单元显示的数据:0-60
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0) data_carry <= 20'd0;else if(data_carry == DATA_CARRY_MAX && cnt_flag == 1'b1)data_carry <= 20'd0;else if(cnt_flag == 1'b1)data_carry <= data_carry + 1'b1;elsedata_carry <= data_carry;// data_carry_flag——进位的标志位
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)data_carry_flag <= 1'b0;else if(data_carry == DATA_CARRY_MAX && cnt_flag == 1'b1)data_carry_flag <= 1'b1;elsedata_carry_flag <= 1'b0;// num:每过一个时钟上升沿就累加,每次完成一次进位就清零
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)num <= -1'd1;else if(num == 5'd20 && data_carry_flag == 1'b1)num <= 5'd0;else if(num == 5'd20)num <= 5'd20;elsenum <= num + 1'b1;// num:如果data_multiplier[num] == 1'b0,data_reg为0;如果data_multiplier[num] == 1'b1,data_reg为data_multiplicand左移num位
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)data_reg <= 20'd0;else if(data_multiplier[num] == 1'b0)data_reg <= 20'd0;else if(data_multiplier[num] == 1'b1)data_reg <= data_multiplicand << (num);elsedata_reg <= data_reg;//每次移位之后对data_reg进行累加,并且在每一次进位的时候清零
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)sum <= 20'd0;else if(num == 20'd20 && data_carry_flag == 1'b1)sum <= 20'd0;else if(num == 20'd20)sum <= sum;elsesum <= sum + data_reg;
assign result = sum;endmodule

最终乘法器的代码是在简单乘法器的基础上加了亿点点细节,这些细节真的是永远的痛,一开始学的时候我还觉得看波形没必要,直到经过这个题目之后,只能说:“真香”,很多看不到的东西可以通过仿真,看波形,直观的看出来,一开始我简单乘法器功能实现了,但不知道为什么一和数字时钟结合就出各种各样的问题,最后盯着波形一个个参数的看,结合代码分析问题,一步步把问题解决,也加了亿点点细节。

4.源代码——数据产生data_gen

同样先把代码奉上

module data_gen
#(parameter CNT_MAX         = 25'd49_99_99  , // 1000ms计数值parameter DATA_MAX        = 20'd999_999   , // 显示的最大值parameter DATA_SECOND_MAX = 20'd59        , // 秒的最大值parameter DATA_MINUTE_MAX = 20'd59        , // 分钟的最大值parameter DATA_HOUR_MAX   = 20'd24          // 小时的最大值// parameter DATA_MAX1 = 18'd250_000)
(input  wire         sys_clk   , // 系统时钟,频率50MHzinput  wire         sys_rst_n , // 复位信号,低电平有效output wire  [19:0] data      , // 数码管要显示的值output wire  [5:0]  point     , // 小数点显示,高电平有效output reg          seg_en    , // 数码管使能信号,高电平有效output wire         sign      // 符号位,高电平显示负号);reg [24:0]  cnt_100ms           ; // 100ms计数器
reg         cnt_flag            ; // 100ms标志信号reg [19:0]  data_sencond        ; // 秒的数据  reg [19:0]  data_hour           ; // 小时的数据
reg [19:0]  hour_multiplicand   ; // 小时的被乘数reg [19:0]  data_minute         ; // 分钟的数据
reg [19:0]  minute_multiplicand ; // 分钟的被乘数reg         sencond_minute_flag ; // 秒-分钟的标志位
reg         minute_hour_flag    ; // 分钟-小时的标志位wire [19:0] result_minute       ; // 计算的分钟的结果
wire [19:0] result_hour         ; // 计算的小时的结果// 不显示小数点以及负数
assign point = 6'b010_100 ;
assign sign  = 1'b0       ;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)minute_multiplicand <= 20'd0;elseminute_multiplicand <= 20'd100;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)hour_multiplicand <= 20'd0;elsehour_multiplicand <= 20'd10000;// cnt_100ms:用50MHz时钟从0到4999_999计数即为100ms
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_100ms <= 25'd0;else if(cnt_100ms == CNT_MAX)cnt_100ms <= 25'd0;elsecnt_100ms <= cnt_100ms + 1'b1;// cnt_flag:每100ms产生一个标志信号
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)cnt_flag <= 1'b0;else if(cnt_100ms == CNT_MAX - 1'b1)cnt_flag <= 1'b1;elsecnt_flag <= 1'b0;// 秒显示的数据:0-60
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0) data_sencond <= 20'd0;else if(data_sencond == DATA_SECOND_MAX && cnt_flag == 1'b1)data_sencond <= 20'd0;else if(cnt_flag == 1'b1)data_sencond <= data_sencond + 1'b1;elsedata_sencond <= data_sencond;//sencond_minute_flag:秒-分钟的标志位
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)sencond_minute_flag <= 1'b0;else if(data_sencond == DATA_SECOND_MAX && cnt_flag == 1'b1)sencond_minute_flag <= 1'b1;elsesencond_minute_flag <= 1'b0;// 分钟显示的数据:0-60
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0) data_minute <= 20'd0;else if(data_minute == DATA_MINUTE_MAX && sencond_minute_flag == 1'b1)data_minute <= 20'd0;else if(sencond_minute_flag == 1'b1)data_minute <= data_minute + 1'b1;elsedata_minute <= data_minute;//minute_hour_flag:分钟-小时的标志位
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)minute_hour_flag <= 1'b0;else if(data_minute == DATA_MINUTE_MAX && sencond_minute_flag == 1'b1)minute_hour_flag <= 1'b1;elseminute_hour_flag <= 1'b0;// 小时显示的数据:0-24
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0) data_hour <= 20'd0;else if(data_hour == DATA_HOUR_MAX && minute_hour_flag == 1'b1)data_hour <= 20'd0;else if(minute_hour_flag == 1'b1)data_hour <= data_hour + 1'b1;elsedata_hour <= data_hour;    // 数码管使能信号给高即可
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)seg_en <= 1'b0;elseseg_en <= 1'b1;        assign   data = result_hour + result_minute + data_sencond;multiply
#(.DATA_CARRY_MAX(DATA_MINUTE_MAX)
)
multiply_inst
(.sys_clk(sys_clk),.sys_rst_n(sys_rst_n),.data_multiplier(data_minute),.data_multiplicand(minute_multiplicand),.result(result_minute)
);multiply
#(.DATA_CARRY_MAX(DATA_HOUR_MAX)
)
multiply_inst0
(.sys_clk(sys_clk),.sys_rst_n(sys_rst_n),.data_multiplier(data_hour),.data_multiplicand(hour_multiplicand),.result(result_hour)
);endmodule

代码思路就跟一开始的思路一样,先分时、分、秒,然后利用乘法器,然后再相加获得想要展示的数据。

三、总结

大体内容就是这样啦,因为不是很熟练,整个功能的全部实现花了比较多的时间,每个功能或许能单独实现,但是整合在一起总会出现一些小问题,不过问题解决了心情还是很好滴,代码格式得注意可读性,一开始代码太乱了,给自己看糊涂了,最后整理下代码,定义定义放一块,功能功能放一块,看起来舒服多了.
这道题的第一问总算完成了,很多细节都没有写出来,一方面是我懒,一方面是我还不清楚怎么组织起来会更有条理,之后把思路理清楚再说叭。这道题学到的最多的呢是看波形调试代码,还有写仿真代码,过程艰辛,不过还是搞出来啦,欧耶,接着搞第二个问题去了。

FPGA初学记录——数字时钟系统搭建(上)相关推荐

  1. 基于FPGA的遥控数字时钟设计

    基于FPGA的遥控数字时钟设计报告 ​ ​ Author:张宏宇 摘要 ​ 数字时钟是一种通过数字显示时间的计时装置,本次项目采用Cyclone Ⅳ系列芯片,使用QuartusII开发环境,使用Ver ...

  2. 单片机课程学习的数字时钟系统模块化设计研究

    摘要:随着人们生活环境的不断改善和美化,人们希望通过在生活中得到更多的乐趣,来满足我们日益增长的精神需求.单片机课程学习的数字时钟系统模块化设计研究能够给人们的生活带来丰富多彩的滋味,低廉的造价以及控 ...

  3. 医院数字时钟系统-智慧医院|医院数字化转型

    引言:随着新兴技术蓬勃发展,我国已进入数字经济时代,各行各业也向着数字化转型发展.我国"十四五规划"还专门强调了数字化概念,并提出加快数字化发展,建设数字中国的战略任务,医院作为民 ...

  4. 基于单片机的数字时钟系统

    设计简介: 本设计是基于单片机的数字时钟系统,主要实现以下功能: 可通过LCD1602显示时间.温度: 可通过按键切换到倒计时.秒表的界面: 可通过按键设置闹钟时间: 可通过按键语音播报时间和温度. ...

  5. FPGA期末项目 | 数字时钟

    戳这里下载整个项目包(已上传到CSDN资源库) 一.实验设备 FPGA开发平台.计算机.其它外接器件 二.需求分析(选题的意义.功能要求等...这里有点水,小伙伴们可以选择性跳过) 选题的意义:个人认 ...

  6. 基于FPGA的简易数字时钟

    基于FPGA的可显示数字时钟,设计思路为自底向上,包括三个子模块:时钟模块,进制转换模块,led显示模块.所用到的FPGA晶振频率为50Mhz,首先利用它得到1hz的时钟然后然后得到时钟模块,把时钟模 ...

  7. FPGA数字时钟系统-设计教程

    博主福利:100G+电子设计学习资源包! http://mp.weixin.qq.com/mp/homepage?__biz=MzU3OTczMzk5Mg==&hid=7&sn=ad5 ...

  8. 51单片机的数字时钟系统【含仿真+程序+报告+原理图】

    1.主要功能 该系统由AT89C51单片机+DS1302时钟模块+按键模块+LCD显示模块构成. 利用51单片机实现电子时钟的功能.使用DS1302芯片作为计时设备,用LCD1602作为显示设备,按键 ...

  9. (Windows,ten2.0)python0-9数字识别系统搭建(MNIST数据集)

    一.MNIST数据集介绍:是由0〜9手写数字图片和数字标签所组成的,由60000个训练样本和10000个测试组成. 二.tf2.0.0导入MNIST数据集的方法: from tensorflow.ex ...

最新文章

  1. 自保护、人机互动柔性织物传感器研究取得进展
  2. 使用VS2008开发及部署Excel AddIn 心得
  3. Xamarin XAML语言教程基本页面ContentPage占用面积(二)
  4. Apache ZooKeeper - 选举Leader源码流程深度解析
  5. 几个常用的文本处理shell 命令:find、grep、xargs、sort、uniq、tr、cut、paste、wc、sed、awk
  6. 前端学习(1439):vue的helloworld
  7. Opencv visual studio c++ 环境搭建
  8. php安装了openssl扩展,php如何安装openssl扩展?
  9. Ghost in IE6.web标准网页IE6中的幽灵。
  10. Kubernetes的Service外部访问方式:NodePort和LoadBalancer
  11. 阿里达摩院赵昆:获评“中国客户服务领袖”
  12. 代码整洁之道 python_代码整洁之道的 7 个方法
  13. 企业运维实践-Nginx使用geoip2模块并利用MaxMind的GeoIP2数据库实现处理不同国家或城市的访问最佳实践指南...
  14. C/C++函数库 之 ctype.h
  15. 以时间作为文件名的后缀
  16. 免费图片验证码识别方法,支持旋转图片识别
  17. 如何编写一个简单的 Python 程序
  18. 解决Carla同步模式下Runtime Error的问题(tick hangs)
  19. AMiner论文推荐
  20. 让人可怕的团队是怎样炼成的?

热门文章

  1. java设计按月每天签到_签到功能java实现
  2. mybatis批量添加案例
  3. 养龟经验总结,原创哦
  4. 20款响应式bootstrap后台模板源码下载
  5. 高并发抢红包案列以及使用锁,版本号,redis缓存解决,项目可运行,详细注释(一)
  6. C语言输出字符的ASCII码
  7. 华科计算机学院导师吕新桥,华中科技大学计算机学院导师和科研方向.doc
  8. vue剩余时间倒计时展示
  9. php键盘按键,键盘常用15个功能键
  10. 微信小程序开发15 项目实战 基于云开发开发一个在线商城小程序