FPGA项目(12)——基于FPGA的万年历设计

这篇具有很好参考价值的文章主要介绍了FPGA项目(12)——基于FPGA的万年历设计。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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

FPGA项目(12)——基于FPGA的万年历设计,FPGA项目,fpga开发

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

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

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

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

        FPGA项目(12)——基于FPGA的万年历设计,FPGA项目,fpga开发

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

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

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

FPGA项目(12)——基于FPGA的万年历设计,FPGA项目,fpga开发

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

FPGA项目(12)——基于FPGA的万年历设计,FPGA项目,fpga开发

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

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

         

//按键驱动模块
//实现功能:检测按键并转换状态。按键按下为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-->10
	begin
		if(!reset) turn_next_state=2'b00;
		else
			case(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;
			endcase
	end
	//次态到现态转换
	always@(posedge system_clk or negedge reset)
	begin
		if(!reset)  turn_state<=2'b00;
		else        turn_state<=turn_next_state;
	end
	//输出电路
	always@(reset or turn_state)
	begin
		if(!reset)  flag_turn= 2'b00;
		else
			case(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;
			endcase
	end
	
//数码管闪烁选择的状态机
	//次态电路
	always@(negedge key_switch or negedge reset) //敏感信号为选项切换按键(按下按键切换状态) 页面切换状态:00-->01-->10-->11
	begin
		if(!reset) switch_next_state=2'b00;
		else
			case(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;
			endcase
	end
	//次态-->现态转换
	always@(posedge system_clk or negedge reset)
	begin
		if(!reset)  switch_state<=2'b00;
		else        switch_state<=switch_next_state;
	end
	//输出电路
	always@(reset or switch_state)
	begin
		if(!reset)  flag_switch= 2'b00;
		else
			case(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;
			endcase
	end
	
	//增一选择项目
	always@(turn_state or switch_state or reset) //敏感信号为当前页面状态、数码管闪烁选择状态(当选择项目变动时,增一选择项目也随之变化)
	begin
		if(!reset) flag_add=4'b0000;//0000代表空状态(都没选)
		else
			case(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;//选中时
				endcase
			2'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;//选中年
				endcase
			2'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;//选中闹钟时
				endcase
			default:flag_add=4'b0000;
			endcase
	end
	
	//生成增一的专属信号
	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:;                        //其它的都什么也不执行
		endcase
	end
endmodule
		

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

FPGA项目(12)——基于FPGA的万年历设计,FPGA项目,fpga开发

        但是,时、分、秒、年、月、日不仅能够正常的运转(在秒脉冲的驱动下,做正常的时钟运行) ,还能够在设置信号(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;//最大99

reg [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)//敏感信号:系统时钟和复位
begin
	if(!reset)
		p<=0;
	else
		if(p==49999999)
			p<=0;
		else
			p<=p+1;
end

assign pulse_second=(p==49999999 && (select_sign==1'b1));   //秒脉冲

//秒部分
always@(posedge system_clk)
begin
	if(!reset)
		second<=6'd35;
	else 
		if(pulse_second)   //秒脉冲
			if(second>=59)
				second<=0;
			else
				second<=second+1;
		else if(!second_add)  //增一信号
			second<=second_set;
		else
			second<=second;
end
//分脉冲信号的生成
assign pulse_minute=(second==6'd59 && pulse_second==1'b1); //分脉冲
//秒设置
always@(negedge second_add)
begin
		second_set=second;  //先读取当前秒
		if(second_set>=59)
			second_set=0;
		else
			second_set=second_set+1;
end
		
//分部分   
always@(posedge system_clk)
begin
	if(!reset)
		minute<=6'd24;
	else 
		if(pulse_minute)
			if(minute>=59)
				minute<=0;
			else
				minute<=minute+1;	
		else if(!minute_add)
			minute<=minute_set;
		else
			minute<=minute;
end
//小时脉冲的生成
assign pulse_hour=(minute==6'd59 && pulse_minute==1'b1); //小时脉冲
//分设置
always@(negedge minute_add)
begin
		minute_set=minute;
		if(minute_set>=59)
			minute_set=0;
		else
			minute_set=minute_set+1;
end

//小时部分
always@(posedge system_clk)
begin
	if(!reset)
		hour<=5'd16;
	else 
		if(pulse_hour)
			if(hour>=23)
				hour<=0;
			else
				hour<=hour+1;		
		else if(!hour_add)
			hour<=hour_set;
		else
			hour<=hour;
end
//天脉冲的生成
assign pulse_day=(hour==5'd23 && pulse_hour==1'b1);
//小时的设置
always@(negedge hour_add)
begin
		hour_set=hour;
		if(hour_set>=23)
			hour_set=0;
		else
			hour_set=hour_set+1;
end

//天部分
always@(posedge system_clk)
begin
	if(!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;
				else
					day<=day+1;
			else if(month==4 || month==6 ||month==9 || month==11)
				if(day>=30)
					day<=1;
				else
					day<=day+1;
			else if(month==2 && (year%4==0))
				if(day>=29)
					day<=1;
				else
					day<=day+1;
			else
				if(day>=28)
					day<=1;
				else
					day<=day+1;	
		else if(!day_add)
			day<=day_set;
		else
			day<=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)
begin
		day_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;
			else
				day_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;
			else
				day_set<=day_set+1;
		else if(day_month==2 && (day_year%4==0))   //闰年
			if(day_set>=29)
				day_set<=1;
			else
				day_set<=day_set+1;
		else
			if(day_set>=28)
				day_set<=1;
			else
				day_set<=day_set+1;
end
		
//月部分
always@(posedge system_clk)
begin
	if(!reset)
		month<=4'd12;
	else 
		if(pulse_month)
			if(month>=12)
				month<=1;
			else
				month<=month+1;
		else if(!month_add)
			month<=month_set;
		else
			month<=month;
end
//年脉冲的生成
assign pulse_year=(month==4'd12 && pulse_month==1'b1);
//月设置
always@(negedge month_add)
begin
		month_set=month;
		if(month_set>=12)
			month_set=1;
		else
			month_set=month_set+1;
end

//年部分
always@(posedge system_clk)
begin
	if(!reset)
		year<=7'd21;
	else 
		if(pulse_year)
			if(year>=99)
				year<=0;
			else
				year<=year+1;
		else if(!year_add)
			year<=year_set;
		else
			year<=year;
end
//年设置
always@(negedge year_add)
begin
		year_set=year;
		if(year_set>=99)
			year_set=0;
		else
			year_set=year_set+1;
end

endmodule





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

FPGA项目(12)——基于FPGA的万年历设计,FPGA项目,fpga开发

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

FPGA项目(12)——基于FPGA的万年历设计,FPGA项目,fpga开发

        这里的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)
	begin
		if(!reset)
			alarm_second<=6'd30;
		else
			if(alarm_second>=59)
				alarm_second<=0;
			else
				alarm_second<=alarm_second+1;
	end
	//设置分
	always@(negedge alarm_minute_add or negedge reset)
	begin
		if(!reset)
			alarm_minute<=6'd30;
		else
			if(alarm_minute>=59)
				alarm_minute<=0;
			else
				alarm_minute<=alarm_minute+1;
	end
	//设置时
	always@(negedge alarm_hour_add or negedge reset)
	begin
		if(!reset)
			alarm_hour<=6'd12;
		else
			if(alarm_hour>=23)
				alarm_hour<=0;
			else
				alarm_hour<=alarm_hour+1;
	end

endmodule	
			
			
			
			

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

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

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

        

        最后说明,如果您需要完整项目工程,请私信并评论!文章来源地址https://www.toymoban.com/news/detail-725862.html

到了这里,关于FPGA项目(12)——基于FPGA的万年历设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于ego1开发板的万年历自动数字日历设计verilog代码

    名称:基于ego1开发板的万年历自动数字日历设计verilog代码 软件:VIVADO 语言:Verilog 代码功能: 自动数字日历设计  设计自动数字日历,用七段数字显示器显示年(后2位)、月、日和星期数,在计日脉冲的作用下,自动完成1-12月的月、日及星期的计数和显示。 FPGA代码Verilog/VHDL代码

    2024年02月03日
    浏览(42)
  • 47、基于51单片机万年历温度闹钟农历阳历LCD 12864显示系统设计

    本文介绍了基于STC89C52单片机的多功能电子万年历的硬件结构和软硬件设计方法。本设计由数据显示模块、温度采集模块、时间处理模块和调整设置模块四个模块组成。系统以STC89C52单片机为控制器,以串行时钟日历芯片DS1302记录日历和时间,它可以对年、月、日、时、分、秒

    2024年02月09日
    浏览(88)
  • 基于单片机的电子万年历设计与制作系统(设计报告+开题中期报告+仿真文件+程序)

    摘要:本文设计实现了一种基于单片机的电子万年历设计与制作系统。该系统通过单片机的控制,实现了日期、时间和节假日等信息的显示,同时提供了闹钟、定时器和温度显示等功能。实验结果表明,该系统具有较好的稳定性和实用性,能够满足人们对万年历功能的需求。

    2024年02月04日
    浏览(46)
  • 基于Java的万年历(课设)

    资源链接:基于Java的万年历(课设) 摘 要 Java编程语言自诞生十几年来,已经成功地运用在网络计算及移动等各个领域。对于开发者来说,它具有简单、面向对象、健壮、安全、结构中立、可移植和高效能等众多优点。此次我们用JAVA来设计一个万年历程序,该程序以网页形

    2024年02月11日
    浏览(44)
  • 51单片机实训项目之“万年历”代码原理详解

    读者若需要工程源码,可以私信我,收到后会第一时间回复。这是仿真效果  51单片机万年历程序设计(附源码+仿真分享)_哔哩哔哩_bilibili STC89C52 DS18B20(温度传感器) DS1302(时钟芯片) LCD1602液晶显示 独立按键 杜邦线 (一).子程序 EEPROM.h LCD1602.h DS1302时钟模块的三个引脚:

    2024年02月08日
    浏览(48)
  • 基于51单片机的电子钟万年历LCD1602显示

    本设计是51单片机的电子钟万年历LCD1602显示(proteus仿真+程序+报告+器件清单) 仿真图proteus 7.8 程序编译器:keil 4/keil 5 编程语言:C语言 编号C0002 主要功能: 1、采用LCD1602液晶屏显示,DS1302记录日历和时间。 2、按键设置日期、时间、闹钟。 3、当实际的时间达到设定的闹钟时

    2024年02月11日
    浏览(41)
  • 基于AT89C51单片机的电子万年历系统

    点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/87708258 源码获取 主要内容: 电子万年历系统以实时时钟芯片DS1302和AT89C52单片机为主要研究对象,着重进行51单片机控制系统的设计研究和如何读取DS1302内部时钟信息的研究。以及运用18B20进行实

    2024年02月09日
    浏览(48)
  • 基于AT89S52单片机的多功能电子万年历

    基于AT89S52单片机的多功能电子万年历的硬件结构和软硬件设计方法。本设计由数据显示模块、温度采集模块、时间处理模块和调整设置模块四个模块组成。系统以AT89S52单片机为控制器,以串行时钟日历芯片DS1302记录日历和时间,它可以对年、月、日、时、分、秒进行计时,

    2024年02月03日
    浏览(74)
  • STM32制作万年历

        STM32万年历制作指南 一、概述 STM32是一种常用的微控制器,具有强大的处理能力和低功耗特性,非常适合用于制作各种电子设备。本文将介绍如何使用STM32制作一款简易的万年历,帮助您轻松查看日期、时间和农历等信息。 二、所需材料 1. STM32微控制器(建议使用STM32F

    2024年02月03日
    浏览(39)
  • 6-6 万年历显示函数

    题主为武理的学生 网上没有对应答案 仅以学习用途上传 设计一个万年历,当用户输入年份和月份时,显示这个月的日历表。程序重点是这个月的第一天是星期几和这个月有几天,有了这两个值,只需通过排列,就可以显示这个日历。程序要求用户输入的年份是从1900年开始,

    2024年01月18日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包