? ? ? ? 首先稱述一下所實(shí)現(xiàn)的功能:可以顯示年、月、日、時(shí)、分、秒,有鬧鐘設(shè)置功能,鬧鐘時(shí)間到時(shí),蜂鳴器響,報(bào)警。用6位數(shù)碼管進(jìn)行顯示,分三個(gè)顯示頁(yè)面,第一個(gè)頁(yè)面顯示年月日,第二個(gè)界面顯示時(shí)分秒,第三個(gè)頁(yè)面顯示鬧鐘時(shí)間??梢杂冒存I進(jìn)行翻頁(yè),按鍵進(jìn)行時(shí)間、日期設(shè)置、鬧鐘設(shè)置。
? ? ? ? 本次做的設(shè)計(jì),使用了正點(diǎn)原子的開拓者FPGA開發(fā)板,并且在開發(fā)板上驗(yàn)證了功能,通過了實(shí)物測(cè)試。實(shí)物圖片如下:
? ? ? ? 對(duì)于本次設(shè)計(jì),我還拍了實(shí)物演示視頻,視頻播放鏈接如下:
基于FPGA的萬(wàn)年歷設(shè)計(jì)_嗶哩嗶哩_bilibilihttps://www.bilibili.com/video/BV1FT4y1i7YJ/?spm_id_from=333.999.list.card_archive.click&vd_source=392747917e381eaafdf7756cf4b87612
? ? ? ? ?看完實(shí)物視頻后,接下來就是思路及代碼的講解了。
? ? ? ? 還是使用自頂層向下的設(shè)計(jì)思想,將整個(gè)設(shè)計(jì)分為許多個(gè)模塊,分別進(jìn)行設(shè)計(jì),最后將它們?nèi)烤C合起來,形成一個(gè)整體。先給出系統(tǒng)的rtl視圖如下,因?yàn)閺膔tl視圖可以清楚的看到整個(gè)設(shè)計(jì)由哪幾個(gè)模塊組成,各個(gè)模塊都有哪些端口,各端口之間是如何連接的。
????????
? ? ? ? 從上圖可以看出,整個(gè)系統(tǒng)首先使用了按鍵消抖模塊,因?yàn)殚_拓者板子上的按鍵為機(jī)械按鍵,抖動(dòng)非常嚴(yán)重,如果不使用按鍵消抖模塊,那么按下一次按鍵就會(huì)被程序識(shí)別為按下n多次,就導(dǎo)致程序無(wú)法正常運(yùn)行。(關(guān)于按鍵消抖模塊的具體思路及其代碼,可以參考我的另一篇文章)
?FPGA項(xiàng)目(3)--按鍵消抖_嵌入式小李的博客-CSDN博客https://blog.csdn.net/guangali/article/details/130674206?spm=1001.2014.3001.5501
? ? ? ? 經(jīng)過消抖的按鍵信號(hào)直接輸出給了按鍵處理模塊,在這個(gè)模塊里面,設(shè)置了很多加信號(hào),以及狀態(tài)標(biāo)志位。比如:
? ? ? ? 這里面的flag_turn就是頁(yè)面標(biāo)志位,因?yàn)榭偣卜譃槿齻€(gè)頁(yè)面,這個(gè)標(biāo)志就是用于記錄當(dāng)前處于哪個(gè)頁(yè)面,數(shù)碼管根據(jù)當(dāng)前所處的頁(yè)面,控制輸出的顯示信息。此外還有一系列的加信號(hào)(年月日的加1信號(hào),時(shí)分秒的加1信號(hào),鬧鐘的加1信號(hào)等等)如下所示:
? ? ? ? 這些信號(hào)分別輸出到時(shí)鐘處理模塊和鬧鐘處理模塊。
? ? ? ? 按鍵模塊的具體代碼如下:
?????????
//按鍵驅(qū)動(dòng)模塊
//實(shí)現(xiàn)功能:檢測(cè)按鍵并轉(zhuǎn)換狀態(tài)。按鍵按下為0
//輸入信號(hào):系統(tǒng)時(shí)鐘、復(fù)位、翻頁(yè)按鍵、數(shù)碼管閃爍選擇按鍵、加一按鍵
//輸出信號(hào):翻頁(yè)選擇狀態(tài)、數(shù)碼管閃爍選擇狀態(tài)、時(shí)分秒和年月日和鬧鐘時(shí)分秒的加一脈沖信號(hào)
//當(dāng)按下翻頁(yè)按鍵時(shí),轉(zhuǎn)換翻頁(yè)狀態(tài)、當(dāng)按下數(shù)碼管閃爍選擇按鍵時(shí)轉(zhuǎn)換當(dāng)前數(shù)碼管閃爍選擇狀態(tài)、當(dāng)按下加一按鍵時(shí)生成每個(gè)部分的專屬增一脈沖信號(hào)(秒分時(shí)....的增一信號(hào)都分開)
//顯示狀態(tài)有三個(gè)(一共有三頁(yè))狀態(tài)轉(zhuǎn)換為:00-->01-->10
//數(shù)碼管顯示狀態(tài)有四個(gè)(一個(gè)沒選中和三個(gè)不同得選擇)狀態(tài)轉(zhuǎn)換:00-->01-->10-->11(00是誰(shuí)也沒選的狀態(tài))
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; //翻頁(yè)狀態(tài)機(jī)(分別是輸出、現(xiàn)態(tài)、次態(tài))
reg [1:0] flag_switch=0,switch_state=0,switch_next_state=0;//當(dāng)前選擇數(shù)碼管狀態(tài)機(jī)
assign select_sign=(flag_add==4'b0000);
//頁(yè)面切換的狀態(tài)機(jī)
//次態(tài)電路
always@(negedge key_turn or negedge reset) //敏感信號(hào)為頁(yè)面切換按鍵(按下按鍵切換狀態(tài)) 頁(yè)面切換狀態(tài):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
//次態(tài)到現(xiàn)態(tài)轉(zhuǎn)換
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
//數(shù)碼管閃爍選擇的狀態(tài)機(jī)
//次態(tài)電路
always@(negedge key_switch or negedge reset) //敏感信號(hào)為選項(xiàng)切換按鍵(按下按鍵切換狀態(tài)) 頁(yè)面切換狀態(tài):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
//次態(tài)-->現(xiàn)態(tài)轉(zhuǎn)換
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
//增一選擇項(xiàng)目
always@(turn_state or switch_state or reset) //敏感信號(hào)為當(dāng)前頁(yè)面狀態(tài)、數(shù)碼管閃爍選擇狀態(tài)(當(dāng)選擇項(xiàng)目變動(dòng)時(shí),增一選擇項(xiàng)目也隨之變化)
begin
if(!reset) flag_add=4'b0000;//0000代表空狀態(tài)(都沒選)
else
case(turn_state)//當(dāng)前頁(yè)面
2'b00: //第一頁(yè)(時(shí)分秒)
case(switch_state)//當(dāng)前數(shù)碼管閃爍選擇
2'b00:flag_add=4'b0000;
2'b01:flag_add=4'b0001;//選中秒
2'b10:flag_add=4'b0010;//選中分
2'b11:flag_add=4'b0011;//選中時(shí)
endcase
2'b01: //第二頁(yè)(年月日)
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: //第三頁(yè)(鬧鐘)
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;//選中鬧鐘時(shí)
endcase
default:flag_add=4'b0000;
endcase
end
//生成增一的專屬信號(hào)
always@(key_add)//敏感信號(hào)為增一按鍵,當(dāng)按鍵按下時(shí),專屬增一脈沖為0;當(dāng)按鍵抬起時(shí),專屬增一脈沖為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:; //其它的都什么也不執(zhí)行
endcase
end
endmodule
?????????接下來就是時(shí)鐘處理模塊,這個(gè)模塊里面的內(nèi)容并不是很難。首先就是對(duì)系統(tǒng)時(shí)鐘進(jìn)行分頻,產(chǎn)生1S的脈沖信號(hào),然后在這個(gè)脈沖的驅(qū)動(dòng)下,驅(qū)使時(shí)、分、秒、年、月、日的正常邏輯運(yùn)轉(zhuǎn)。部分示例代碼如下:
? ? ? ? 但是,時(shí)、分、秒、年、月、日不僅能夠正常的運(yùn)轉(zhuǎn)(在秒脈沖的驅(qū)動(dòng)下,做正常的時(shí)鐘運(yùn)行)?,還能夠在設(shè)置信號(hào)(second_add,minute_add,hour_add,day_add,month_add,year_add)等的驅(qū)使下,隨著按鍵按下一次,數(shù)值增加一次,起到一個(gè)按鍵調(diào)節(jié)時(shí)鐘的效果。
? ? ? ? 這部分的所有代碼如下:
//時(shí)鐘模塊
//實(shí)現(xiàn)功能:計(jì)時(shí)功能及其時(shí)鐘設(shè)置功能
//輸入信號(hào):系統(tǒng)時(shí)鐘、復(fù)位按鍵、時(shí)分秒年月日的加一信號(hào)
//輸出信號(hào):年月日和時(shí)分秒、秒脈沖
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;//小時(shí)脈沖
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)//敏感信號(hào):系統(tǒng)時(shí)鐘和復(fù)位
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) //增一信號(hào)
second<=second_set;
else
second<=second;
end
//分脈沖信號(hào)的生成
assign pulse_minute=(second==6'd59 && pulse_second==1'b1); //分脈沖
//秒設(shè)置
always@(negedge second_add)
begin
second_set=second; //先讀取當(dāng)前秒
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
//小時(shí)脈沖的生成
assign pulse_hour=(minute==6'd59 && pulse_minute==1'b1); //小時(shí)脈沖
//分設(shè)置
always@(negedge minute_add)
begin
minute_set=minute;
if(minute_set>=59)
minute_set=0;
else
minute_set=minute_set+1;
end
//小時(shí)部分
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);
//小時(shí)的設(shè)置
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));
//天的設(shè)置
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);
//月設(shè)置
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
//年設(shè)置
always@(negedge year_add)
begin
year_set=year;
if(year_set>=99)
year_set=0;
else
year_set=year_set+1;
end
endmodule
? ? ? ? 接著就是鬧鐘模塊。這個(gè)模塊更為簡(jiǎn)單,就是在鬧鐘設(shè)置信號(hào)(alarm_second_add,alarm_minute_add,alarm_hour_add)的驅(qū)動(dòng)下,控制鬧鐘時(shí)分秒的增加而已。然后判斷當(dāng)前時(shí)間與鬧鐘時(shí)間是否相等,如果相等了,那么蜂鳴器響,進(jìn)行報(bào)警。此外,報(bào)警功能還收一個(gè)按鍵控制,這個(gè)按鍵控制鬧鐘功能是否使能。只有使能了,鬧鐘功能才起作用,否則不起作用。
? ? ? ? 這里的?switch_alarm就是控制鬧鐘正常工作的按鍵,高電平有效。如果它為低電平,那么鬧鐘功能不使能,無(wú)效。
? ? ? ? 這里的LED燈用于指示,是否啟用了鬧鐘功能。
? ? ? ? 該模塊所有代碼如下:
????????文章來源:http://www.zghlxwxcb.cn/news/detail-725862.html
//鬧鐘模塊
//實(shí)現(xiàn)功能:設(shè)置鬧鐘,鬧鐘時(shí)間到時(shí)鬧鐘響1分鐘
//輸入信號(hào):撥碼開關(guān)(使能信號(hào))、時(shí)鐘模塊的時(shí)分秒、按鍵驅(qū)動(dòng)模塊的時(shí)分秒專屬增一脈沖信號(hào)
//輸出信號(hào):LED燈、蜂鳴器、當(dāng)前的設(shè)置時(shí)間(給顯示模塊來顯示)
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; //撥碼開關(guān)開時(shí)亮
assign beep=((minute==alarm_minute&&hour==alarm_hour&&switch_alarm==1'b1)||(minute==6'd59 && second==6'd59));
//設(shè)置秒
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
//設(shè)置分
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
//設(shè)置時(shí)
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驅(qū)動(dòng)數(shù)碼管進(jìn)行動(dòng)態(tài)顯示的代碼,可以參考我的這篇博客:
????????FPGA項(xiàng)目(5)--FPGA控制數(shù)碼管動(dòng)態(tài)顯示的原理_fpga數(shù)碼管顯示實(shí)驗(yàn)原理_嵌入式小李的博客-CSDN博客https://blog.csdn.net/guangali/article/details/130754726?spm=1001.2014.3001.5501????????
? ? ? ? 雖然不是一模一樣的,但是稍加修改,即可完整使用。
????????
? ? ? ? 最后說明,如果您需要完整項(xiàng)目工程,請(qǐng)私信并評(píng)論!文章來源地址http://www.zghlxwxcb.cn/news/detail-725862.html
到了這里,關(guān)于FPGA項(xiàng)目(12)——基于FPGA的萬(wàn)年歷設(shè)計(jì)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!