首先称述一下所实现的功能:可以显示年、月、日、时、分、秒,有闹钟设置功能,闹钟时间到时,蜂鸣器响,报警。用6位数码管进行显示,分三个显示页面,第一个页面显示年月日,第二个界面显示时分秒,第三个页面显示闹钟时间。可以用按键进行翻页,按键进行时间、日期设置、闹钟设置。

本次做的设计,使用了正点原子的开拓者FPGA开发板,并且在开发板上验证了功能,通过了实物测试。实物图片如下:

对于本次设计,我还拍了实物演示视频,视频播放链接如下:

基于FPGA的万年历设计_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1FT4y1i7YJ/?spm_id_from=333.999.list.card_archive.click&vd_source=392747917e381eaafdf7756cf4b87612

看完实物视频后,接下来就是思路及代码的讲解了。

还是使用自顶层向下的设计思想,将整个设计分为许多个模块,分别进行设计,最后将它们全部综合起来,形成一个整体。先给出系统的rtl视图如下,因为从rtl视图可以清楚的看到整个设计由哪几个模块组成,各个模块都有哪些端口,各端口之间是如何连接的。

从上图可以看出,整个系统首先使用了按键消抖模块,因为开拓者板子上的按键为机械按键,抖动非常严重,如果不使用按键消抖模块,那么按下一次按键就会被程序识别为按下n多次,就导致程序无法正常运行。(关于按键消抖模块的具体思路及其代码,可以参考我的另一篇文章)

FPGA项目(3)--按键消抖_嵌入式小李的博客-CSDN博客https://blog.csdn.net/guangali/article/details/130674206?spm=1001.2014.3001.5501

经过消抖的按键信号直接输出给了按键处理模块,在这个模块里面,设置了很多加信号,以及状态标志位。比如:

这里面的flag_turn就是页面标志位,因为总共分为三个页面,这个标志就是用于记录当前处于哪个页面,数码管根据当前所处的页面,控制输出的显示信息。此外还有一系列的加信号(年月日的加1信号,时分秒的加1信号,闹钟的加1信号等等)如下所示:

这些信号分别输出到时钟处理模块和闹钟处理模块。

按键模块的具体代码如下:

//按键驱动模块
//实现功能:检测按键并转换状态。按键按下为0
//输入信号:系统时钟、复位、翻页按键、数码管闪烁选择按键、加一按键
//输出信号:翻页选择状态、数码管闪烁选择状态、时分秒和年月日和闹钟时分秒的加一脉冲信号
//当按下翻页按键时,转换翻页状态、当按下数码管闪烁选择按键时转换当前数码管闪烁选择状态、当按下加一按键时生成每个部分的专属增一脉冲信号(秒分时....的增一信号都分开)
//显示状态有三个(一共有三页)状态转换为:00-->01-->10
//数码管显示状态有四个(一个没选中和三个不同得选择)状态转换:00-->01-->10-->11(00是谁也没选的状态)module key_drive_module(system_clk,reset,key_turn,key_switch,key_add,flag_switch,flag_turn,second_add,minute_add,hour_add,day_add,month_add,year_add,alarm_second_add,alarm_minute_add,alarm_hour_add,select_sign);
input key_turn,key_switch,key_add,system_clk,reset;
output [1:0] flag_switch,flag_turn;
output select_sign;
output second_add,minute_add,hour_add,day_add,month_add,year_add,alarm_second_add,alarm_minute_add,alarm_hour_add;reg [3:0] flag_add=0;
reg second_add,minute_add,hour_add,day_add,month_add,year_add,alarm_second_add,alarm_minute_add,alarm_hour_add;
reg [1:0] flag_turn=0,turn_state=0,turn_next_state=0;  //翻页状态机(分别是输出、现态、次态)
reg [1:0] flag_switch=0,switch_state=0,switch_next_state=0;//当前选择数码管状态机assign select_sign=(flag_add==4'b0000);//页面切换的状态机//次态电路always@(negedge key_turn or negedge reset) //敏感信号为页面切换按键(按下按键切换状态) 页面切换状态:00-->01-->10beginif(!reset) turn_next_state=2'b00;elsecase(turn_state)2'b00:turn_next_state=2'b01;2'b01:turn_next_state=2'b10;2'b10:turn_next_state=2'b00;default:turn_next_state=2'b00;endcaseend//次态到现态转换always@(posedge system_clk or negedge reset)beginif(!reset)  turn_state<=2'b00;else        turn_state<=turn_next_state;end//输出电路always@(reset or turn_state)beginif(!reset)  flag_turn= 2'b00;elsecase(turn_state)2'b00:flag_turn=2'b00;2'b01:flag_turn=2'b01;2'b10:flag_turn=2'b10;default:flag_turn=2'b00;endcaseend//数码管闪烁选择的状态机//次态电路always@(negedge key_switch or negedge reset) //敏感信号为选项切换按键(按下按键切换状态) 页面切换状态:00-->01-->10-->11beginif(!reset) switch_next_state=2'b00;elsecase(switch_state)2'b00:switch_next_state=2'b01;2'b01:switch_next_state=2'b10;2'b10:switch_next_state=2'b11;2'b11:switch_next_state=2'b00;default:switch_next_state=2'b00;endcaseend//次态-->现态转换always@(posedge system_clk or negedge reset)beginif(!reset)  switch_state<=2'b00;else        switch_state<=switch_next_state;end//输出电路always@(reset or switch_state)beginif(!reset)  flag_switch= 2'b00;elsecase(switch_state)2'b00:flag_switch=2'b00;2'b01:flag_switch=2'b01;2'b10:flag_switch=2'b10;2'b11:flag_switch=2'b11;default:switch_next_state=2'b00;endcaseend//增一选择项目always@(turn_state or switch_state or reset) //敏感信号为当前页面状态、数码管闪烁选择状态(当选择项目变动时,增一选择项目也随之变化)beginif(!reset) flag_add=4'b0000;//0000代表空状态(都没选)elsecase(turn_state)//当前页面2'b00:      //第一页(时分秒)case(switch_state)//当前数码管闪烁选择2'b00:flag_add=4'b0000;2'b01:flag_add=4'b0001;//选中秒2'b10:flag_add=4'b0010;//选中分2'b11:flag_add=4'b0011;//选中时endcase2'b01:    //第二页(年月日)case(switch_state)2'b00:flag_add=4'b0000;2'b01:flag_add=4'b0101;//选中日2'b10:flag_add=4'b0110;//选中月2'b11:flag_add=4'b0111;//选中年endcase2'b10:   //第三页(闹钟)case(switch_state)2'b00:flag_add=4'b0000;2'b01:flag_add=4'b1001;//选中闹钟秒2'b10:flag_add=4'b1010;//选中闹钟分2'b11:flag_add=4'b1011;//选中闹钟时endcasedefault:flag_add=4'b0000;endcaseend//生成增一的专属信号always@(key_add)//敏感信号为增一按键,当按键按下时,专属增一脉冲为0;当按键抬起时,专属增一脉冲为1;begin  case(flag_add)4'b0001:second_add=key_add;4'b0010:minute_add=key_add;4'b0011:hour_add=key_add;4'b0101:day_add=key_add;4'b0110:month_add=key_add;4'b0111:year_add=key_add;4'b1001:alarm_second_add=key_add;4'b1010:alarm_minute_add=key_add;4'b1011:alarm_hour_add=key_add;default:;                        //其它的都什么也不执行endcaseend
endmodule

接下来就是时钟处理模块,这个模块里面的内容并不是很难。首先就是对系统时钟进行分频,产生1S的脉冲信号,然后在这个脉冲的驱动下,驱使时、分、秒、年、月、日的正常逻辑运转。部分示例代码如下:

但是,时、分、秒、年、月、日不仅能够正常的运转(在秒脉冲的驱动下,做正常的时钟运行) ,还能够在设置信号(second_add,minute_add,hour_add,day_add,month_add,year_add)等的驱使下,随着按键按下一次,数值增加一次,起到一个按键调节时钟的效果。

这部分的所有代码如下:

//时钟模块
//实现功能:计时功能及其时钟设置功能
//输入信号:系统时钟、复位按键、时分秒年月日的加一信号
//输出信号:年月日和时分秒、秒脉冲module clock(system_clk,reset,select_sign,second_add,minute_add,hour_add,day_add,month_add,year_add,second,minute,hour,day,month,year);
input system_clk,reset,second_add,minute_add,hour_add,day_add,month_add,year_add;
input select_sign;
output [5:0] second;//最大59
output [5:0] minute;//最大59
output [4:0] hour;//最大23
output [4:0] day;//最大31
output [3:0] month;//最大12
output [6:0] year;//最大99reg [31:0]p;//最大24999999
wire pulse_second;//秒脉冲reg [5:0] second;
reg [5:0] second_set;
wire pulse_minute; //分脉冲reg [5:0] minute;
reg [5:0] minute_set;
wire pulse_hour;//小时脉冲reg [4:0] hour;
reg [4:0] hour_set;
wire pulse_day;//日脉冲reg [4:0] day=5'd8;
reg [4:0] day_set;
reg [3:0] day_month;
reg [6:0] day_year;
wire pulse_month;//月脉冲reg [3:0] month;
reg [3:0] month_set;
wire pulse_year;//年脉冲reg [6:0] year;
reg [6:0] year_set;always@(posedge system_clk or negedge reset)//敏感信号:系统时钟和复位
beginif(!reset)p<=0;elseif(p==49999999)p<=0;elsep<=p+1;
endassign pulse_second=(p==49999999 && (select_sign==1'b1));   //秒脉冲//秒部分
always@(posedge system_clk)
beginif(!reset)second<=6'd35;else if(pulse_second)   //秒脉冲if(second>=59)second<=0;elsesecond<=second+1;else if(!second_add)  //增一信号second<=second_set;elsesecond<=second;
end
//分脉冲信号的生成
assign pulse_minute=(second==6'd59 && pulse_second==1'b1); //分脉冲
//秒设置
always@(negedge second_add)
beginsecond_set=second;  //先读取当前秒if(second_set>=59)second_set=0;elsesecond_set=second_set+1;
end//分部分
always@(posedge system_clk)
beginif(!reset)minute<=6'd24;else if(pulse_minute)if(minute>=59)minute<=0;elseminute<=minute+1;   else if(!minute_add)minute<=minute_set;elseminute<=minute;
end
//小时脉冲的生成
assign pulse_hour=(minute==6'd59 && pulse_minute==1'b1); //小时脉冲
//分设置
always@(negedge minute_add)
beginminute_set=minute;if(minute_set>=59)minute_set=0;elseminute_set=minute_set+1;
end//小时部分
always@(posedge system_clk)
beginif(!reset)hour<=5'd16;else if(pulse_hour)if(hour>=23)hour<=0;elsehour<=hour+1;       else if(!hour_add)hour<=hour_set;elsehour<=hour;
end
//天脉冲的生成
assign pulse_day=(hour==5'd23 && pulse_hour==1'b1);
//小时的设置
always@(negedge hour_add)
beginhour_set=hour;if(hour_set>=23)hour_set=0;elsehour_set=hour_set+1;
end//天部分
always@(posedge system_clk)
beginif(!reset)day<=5'd8;else if(pulse_day) //天脉冲if(month==1 || month==3 ||month==5 || month==7 || month==8 || month==10 || month==12)if(day>=31)day<=1;elseday<=day+1;else if(month==4 || month==6 ||month==9 || month==11)if(day>=30)day<=1;elseday<=day+1;else if(month==2 && (year%4==0))if(day>=29)day<=1;elseday<=day+1;elseif(day>=28)day<=1;elseday<=day+1;  else if(!day_add)day<=day_set;elseday<=day;
end
//月脉冲的生成
assign pulse_month=((day==5'd28 && month==4'd2 && (year%4!=0) && pulse_day==1'b1)
||(day==5'd29 && month==4'd2 && (year%4==0) && pulse_day==1'b1)
||(day==5'd30 && (month==4'd4 || month==4'd6 ||month==4'd9 || month==4'd11) && pulse_day==1'b1)
||(day==5'd31 && (month==4'd1 || month==4'd3 ||month==4'd5 || month==4'd7 || month==4'd8 || month==4'd10 || month==4'd12) && pulse_day==1'b1));
//天的设置
always@(negedge day_add)
beginday_set=day;day_month=month;day_year=year;if(day_month==1 || day_month==3 ||day_month==5 || day_month==7 || day_month==8 || day_month==10 || day_month==12)if(day_set>=31)day_set<=1;elseday_set<=day_set+1;else if(day_month==4 || day_month==6 ||day_month==9 || day_month==11)if(day_set>=30)day_set<=1;elseday_set<=day_set+1;else if(day_month==2 && (day_year%4==0))   //闰年if(day_set>=29)day_set<=1;elseday_set<=day_set+1;elseif(day_set>=28)day_set<=1;elseday_set<=day_set+1;
end//月部分
always@(posedge system_clk)
beginif(!reset)month<=4'd12;else if(pulse_month)if(month>=12)month<=1;elsemonth<=month+1;else if(!month_add)month<=month_set;elsemonth<=month;
end
//年脉冲的生成
assign pulse_year=(month==4'd12 && pulse_month==1'b1);
//月设置
always@(negedge month_add)
beginmonth_set=month;if(month_set>=12)month_set=1;elsemonth_set=month_set+1;
end//年部分
always@(posedge system_clk)
beginif(!reset)year<=7'd21;else if(pulse_year)if(year>=99)year<=0;elseyear<=year+1;else if(!year_add)year<=year_set;elseyear<=year;
end
//年设置
always@(negedge year_add)
beginyear_set=year;if(year_set>=99)year_set=0;elseyear_set=year_set+1;
endendmodule

接着就是闹钟模块。这个模块更为简单,就是在闹钟设置信号(alarm_second_add,alarm_minute_add,alarm_hour_add)的驱动下,控制闹钟时分秒的增加而已。然后判断当前时间与闹钟时间是否相等,如果相等了,那么蜂鸣器响,进行报警。此外,报警功能还收一个按键控制,这个按键控制闹钟功能是否使能。只有使能了,闹钟功能才起作用,否则不起作用。

这里的 switch_alarm就是控制闹钟正常工作的按键,高电平有效。如果它为低电平,那么闹钟功能不使能,无效。

这里的LED灯用于指示,是否启用了闹钟功能。

该模块所有代码如下:

//闹钟模块
//实现功能:设置闹钟,闹钟时间到时闹钟响1分钟
//输入信号:拨码开关(使能信号)、时钟模块的时分秒、按键驱动模块的时分秒专属增一脉冲信号
//输出信号:LED灯、蜂鸣器、当前的设置时间(给显示模块来显示)module alarm(sys_clk,reset,second,minute,hour,switch_alarm,alarm_second_add,alarm_minute_add,alarm_hour_add,led,beep,alarm_second,alarm_minute,alarm_hour);
input sys_clk,reset,switch_alarm,alarm_second_add,alarm_minute_add,alarm_hour_add;
input [5:0] second;
input [5:0] minute;
input [4:0] hour;
output led,beep;
output [5:0] alarm_second;
output [5:0] alarm_minute;
output [4:0] alarm_hour; reg [5:0] alarm_second;
reg [5:0] alarm_minute;
reg [4:0] alarm_hour; assign led=switch_alarm;  //拨码开关开时亮
assign beep=((minute==alarm_minute&&hour==alarm_hour&&switch_alarm==1'b1)||(minute==6'd59 && second==6'd59)); //设置秒always@(negedge alarm_second_add or negedge reset)beginif(!reset)alarm_second<=6'd30;elseif(alarm_second>=59)alarm_second<=0;elsealarm_second<=alarm_second+1;end//设置分always@(negedge alarm_minute_add or negedge reset)beginif(!reset)alarm_minute<=6'd30;elseif(alarm_minute>=59)alarm_minute<=0;elsealarm_minute<=alarm_minute+1;end//设置时always@(negedge alarm_hour_add or negedge reset)beginif(!reset)alarm_hour<=6'd12;elseif(alarm_hour>=23)alarm_hour<=0;elsealarm_hour<=alarm_hour+1;endendmodule    

最后就是显示模块了。FPGA驱动数码管进行动态显示的代码,可以参考我的这篇博客:

FPGA项目(5)--FPGA控制数码管动态显示的原理_fpga数码管显示实验原理_嵌入式小李的博客-CSDN博客https://blog.csdn.net/guangali/article/details/130754726?spm=1001.2014.3001.5501

虽然不是一模一样的,但是稍加修改,即可完整使用。

最后说明,如果您需要完整项目工程,请私信并评论!

FPGA项目(12)——基于FPGA的万年历设计相关推荐

  1. imut FPGA课设 基于FPGA的VGA弹球游戏设计 *秋昊

    写在前面的话: 本文主要呈现了一篇IMUT的FPGA课设报告. 课设报告内容(word版),视频演示,程序源码,专业创新实践简介,专业创新实践指导书均已放入下面的百度云链接中,也不大,总共不到20MB ...

  2. 【FPGA实例】基于FPGA的DDS信号发生器设计

    原文链接来源:www.runoob.com 基于FPGA的DDS信号发生器设计 DDS 原理 ------DDS(直接频率合成) 技术是根据奈奎斯特抽样定理及数字处理技术,把一系列的模拟信号进行不失真 ...

  3. 多通路fpga 通信_基于FPGA的多路卫星信号处理系统的设计实现

    龙源期刊网 http://www.qikan.com.cn 基于 FPGA 的多路卫星信号处理系统的设 计实现 作者:曹晨 唐灵丽 来源:<中国新通信> 2014 年第 02 期 [摘要] ...

  4. 基于verilog的万年历设计

    万年历设计如下:(使用verilog语言) 适用于quartusII 或者 vivado 一 digital_clock设计需求 本设计采用FPGA,实现核心控制.利用独立按键当作输入,利用六位一体的 ...

  5. Matlab与FPGA图像处理系列——基于FPGA的实时边缘检测系统设计,sobel边缘检测流水线实现

    注:下载链接的资源是图片存 ROM 后读取进行 Sobel 检测显示在 VGA上,可供参考. 摘要:本文设计了一种基于 FPGA 的实时边缘检测系统,使用OV5640 摄像头模块获取实时的视频图像数据 ...

  6. 【FPGA,MPPT】基于FPGA的MPPT最大功率跟踪系统verilog开发

    1.软件版本 MATLAB2019a,ISE14.7 2.本算法理论知识 MPPT,我们采用的是 FPGA的设计结构如下: 这里, 第一,使用MATLAB模拟出光伏设备PV输出的电流和电压数据,尽量贴 ...

  7. 使用VisualStudio读写NI FPGA板卡实例(基于FPGA Interface C API Generator)

    实验平台说明:安装了NI LabVIEW 2015 32bit版本,安装了NI FPGA Interface C API Generator,安装了硬件PCIe-7842R:安装了Visual Stu ...

  8. 基于zynq的千兆网udp项目_基于FPGA的千兆网UDP通信分析

    千兆网UDP通信 以太网帧格式 图8‑12 以太网帧格式 表8‑5 以太网帧格式说明 UDP协议分析 为什么UDP协议在FPGA实现时很受欢迎,最主要一个原因就是简单,简答到什么地步呢?UDP协议只是 ...

  9. 项目记录 / 基于FPGA实现数字温度计

    目录 1.设计目标 2.硬件部分 3.程序设计 4.仿真及测量效果 1.设计目标 1)温度测量范围0~125℃: 2)led数码管显示温度: 3)存在报警温度(可修改). 2.硬件部分 主要由三个部分 ...

  10. 【FPGA数控】基于FPGA的小型步进电机数控装置的设计与实现

    1.软件版本 modelsim6.1d 2.算法仿真描述 步进电机是一种离散运动的装置,步进电机驱动器通过外加控制脉冲,并按环形分配器决定的分配方式,控制步进电机各相绕组的导通或截止,从而使电机产生步 ...

最新文章

  1. noj数据结构稀疏矩阵的加法十字链表_数据结构学习(C )稀疏矩阵(十字链表1)
  2. [Music]若是每个人都彼此关怀
  3. 二叉树的非递归遍历(c/c++)
  4. Leetcode 112. 路径总和 (每日一题 20210910)
  5. matlab错误:Variable 'a' cannot be saved to a MAT-file whose version is older than 7.3.
  6. LeetCode——双指针
  7. Leetcode--16. 最接近的三数之和
  8. 向上取整函数_向上取整函数ROUNDUP在BOM表里面的实际应用
  9. 数据科学家必会的六大 NLP 技术!
  10. mysql定制化_【MySQL技巧】定制你的MySQL命令行
  11. 2019年2月22日 深入理解计算机系统(CS:APP)第一章读书笔记
  12. 税务会计实务【17】
  13. 发展恐怖故事–邮件炸弹
  14. DROOPYCTF WALKTHROUGH
  15. 姐妹素数java_Java之姐妹素数
  16. 黑马程序员JS学习第一天
  17. 利用selenium与etree抓取必应图片
  18. 上海市大学生CTF(华东杯CTF) Crypto 全解
  19. 【2309. 兼具大小写的最好英文字母】
  20. 有N个台阶,一步可以走一梯或者两梯,请问有多少种走法

热门文章

  1. 05.用两个栈实现队列
  2. 通过js代码调整网页video内视频播放速度
  3. 神操作,居然有人用 Python 在 Excel 中画画,你不会是最后一个知道的吧
  4. 【电力电子技术DC-DC】Buck降压式变换器Simulink仿真
  5. 小程序、公众号开发报code been used(40163)或invalid code(40029)错误,解决方案--之--搞清楚微信的登录机制,保存登录状态!!!
  6. Filter及FilterChain的使用详解
  7. ERROR 1044 (42000): Access denied for user ”@’localhost’ to database ‘mysql’
  8. 销售员日常工作中需要什么样的销售工具
  9. 用户行为统计分析工具比较
  10. Emotion英语——Point