本设计是本学期的课程设计,也没有正式上过课,全凭自学摸索完成本课程设计,在摸索的过程中也看了csdn上众多博客,给予了本人很大的帮助,本人做完也前来分享。若有不对错误之处也请大家多多理解指正。本设计可以实现正常计数(区分大小月和闰年),备忘提醒,按键调整,按键消抖,星期几显示等功能。代码分为顶层模块,分频模块,计数模块,备忘模块,译码显示模块,按键消抖模块,8位二进制转BCD码模块。也希望此文也能给大家带来一定的帮助。(若有本课程设计的同学请勿照搬照抄)
本设计的系统框架图:
顶层模块:
- module calendar(
- input clk_100MHz,
- input clr,//清零信号
- input model,//换位信号
- input add,//加位信号
- input sub,//减位信号
- input mem,//备忘信号
- input pause,//暂停信号
- output [7:0]a_to_h_1,//输出数码管段选信号
- output [7:0]a_to_h_2,//输出数码管段选信号
- //两个段选信号
- output [7:0]an,//输出8个数码管使能信号
- //数码管使能信号
- output memo,//输出备忘信号
- output [6:0]xqj //周几信号
- );
-
- wire clk_200Hz;
- wire clk_1Hz;
- //wire clk_24H;
- wire clk_5Hz;
- wire model_out;
- wire add_out;
- wire sub_out;
- wire [1:0]flag_1;
- wire [1:0]flag_2;
- wire[7:0] M;//月份
- wire[7:0] D;//日
- wire[7:0]Y_H;//年份高位
- wire[7:0]Y_L;//年份低位
- wire[7:0] M_m;//月份
- wire[7:0] D_m;//日
- wire[7:0]Y_H_m;//年份高位
- wire[7:0]Y_L_m;//年份低位
- wire[7:0] a_to_g_M;
- wire[7:0] a_to_g_D;
- wire[7:0] a_to_g_Y_H;
- wire[7:0] a_to_g_Y_L;
- wire[7:0] a_to_g_M_m;
- wire[7:0] a_to_g_D_m;
- wire[7:0] a_to_g_Y_H_m;
- wire[7:0] a_to_g_Y_L_m;
-
- clk_200Hz u_clk_200Hz(
- .clk_100MHz(clk_100MHz),//100MHz信号输入模块clk_200Hz
- .clk_200Hz(clk_200Hz)//clk_200Hz信号输出
-
- );
- clk_1Hz u_clk_1Hz(
- .clk_100MHz(clk_100MHz),//100MHz信号输入模块clk_1Hz
- .clk_1Hz(clk_1Hz)//clk_1Hz信号输出
- );
-
- //clk_24H u_clk_24H(
- // .clk_100MHz(clk_100MHz),//100MHz信号输入模块clk_24H
- // .clk_24H(clk_24H)
- // );
- clk_5Hz u_clk_5Hz(
- .clk_100MHz(clk_100MHz),
- .clk_5Hz(clk_5Hz)
- );
-
- count u_count(
- .clk_1Hz(clk_1Hz),
- .clk_5Hz(clk_5Hz),
- .clk_200Hz(clk_200Hz),
- .clr(clr),
- .model(model_out),
- .pause(pause),
- .add(add_out),
- .sub(sub_out),
- .mem(mem),
- .M(M),
- .D(D),
- .Y_H(Y_H),
- .Y_L(Y_L),
- .flag(flag_1),
- .xqj(xqj)
- );
-
- bin8_to_bcd u_bin8_to_bcd(
- .bin(M),
- .bcd(a_to_g_M)
- );
-
- bin8_to_bcd u1_bin8_bcd(
- .bin(D),
- .bcd(a_to_g_D)
- );
-
- bin8_to_bcd u2_bin8_bcd(
- .bin(Y_H),
- .bcd(a_to_g_Y_H)
- );
-
- bin8_to_bcd u3_bin8_bcd(
- .bin(Y_L),
- .bcd(a_to_g_Y_L)
- );
- bin8_to_bcd u4_bin8_bcd(
- .bin(M_m),
- .bcd(a_to_g_M_m)
- );
-
- bin8_to_bcd u5_bin8_bcd(
- .bin(D_m),
- .bcd(a_to_g_D_m)
- );
-
- bin8_to_bcd u6_bin8_bcd(
- .bin(Y_H_m),
- .bcd(a_to_g_Y_H_m)
- );
-
- bin8_to_bcd u7_bin8_bcd(
- .bin(Y_L_m),
- .bcd(a_to_g_Y_L_m)
- );
-
- smg u_smg(
- .flag_1(flag_1),
- .flag_2(flag_2),
- .clk(clk_200Hz),
- .x({a_to_g_Y_H,a_to_g_Y_L,a_to_g_M,a_to_g_D}),
- .x_m({a_to_g_Y_H_m,a_to_g_Y_L_m,a_to_g_M_m,a_to_g_D_m}),
- .mem(mem),
- .dp(8'b00010100),
- .a_to_h_1(a_to_h_1),
- .a_to_h_2(a_to_h_2),
- .an(an)
- );
-
- key_debounce u_key_debounce(
- .clk_200Hz(clk_200Hz),
- .clr(clr),
- .btn_in({model,add,sub}),
- .btn_out({model_out,add_out,sub_out})
- );
-
- memory u_memory(
- .clk_5Hz(clk_5Hz),
- .M(M),
- .D(D),
- .Y_H(Y_H),
- .Y_L(Y_L),
- .clr(clr),
- .model(model_out),
- .add(add_out),
- .sub(sub_out),
- .mem(mem),
- .M_m(M_m),
- .D_m(D_m),
- .Y_H_m(Y_H_m),
- .Y_L_m(Y_L_m),
- .memo(memo),
- .flag(flag_2)
- );
- endmodule
分频模块:分出1Hz(为方便显示用1Hz,正常时为24H),5Hz,200Hz(数码管扫描时钟)。
- module clk_1Hz(
- //module clk_24H(
- input clk_100MHz,
- output clk_1Hz
- //output clk_24H
- );
- reg[25:0] cnt_1Hz;//原时钟上升沿计数到49999999时,时钟电平输出翻转即得1Hz时钟信号。d49999999=h2faf07f需要26位二进制数
- //reg[41:0] cnt_24H;//原时钟上升沿计数到4319999999999时,时钟电平输出翻转即得24H时钟信号。d44319999999999=3edd410bfff需要42位二进制数
- reg clk_1Hz_reg;//1Hz时钟电平输出
- //reg clk_24H_reg;//24H时钟电平输出
- initial//初始化
- begin
- cnt_1Hz=0;
- //cnt_24H=0;
- clk_1Hz_reg=0;
- //clk_24H_reg=0;
- end
- always@(posedge clk_100MHz)
- begin
- if(cnt_1Hz==26'h2faf07f)
- // if(cnt_24H==26'h3edd410bfff)
- begin
- clk_1Hz_reg<=~clk_1Hz_reg;
- // clk_24H_reg<=~clk_24H_reg;
- cnt_1Hz<=0;
- // cnt_24H<=0;
- end
- else
- cnt_1Hz<= cnt_1Hz+1;
- //cnt_24H<= cnt_24H+1
- end
- assign clk_1Hz=clk_1Hz_reg;
- //assign clk_24H=clk_24H_reg;
-
- endmodule
计数模块:是本设计的核心,实现计数功能,按键调整功能,周几显示功能。数码管在默认情况下进行计时功能,进行正常的日期变更。
通过case语句实现对月份的讨论。分别讨论大月(31天)、小月(30天)、且考虑闰年年份非100倍数且能被4整除、100倍数且能被400整除)和平年的二月天数(闰年2月29天,平年28天)的情况。
在暂停信号(pause)的触发下可以停止计数,且进行调数时,计数功能也停止计数。在位选信号(model)的触发下,可依次分别调整年份、月份和日。年、月、日调整时由两个信号(add、sub)分别实现数字的增加和减少。且当加减信号(add,sub)和位选信号(model)持续输入时,模式切换和数字调整均以0.2s的速率变换。当按下复位信号(clr)时,无论是计时状态还是调数状态,数码管示数均变回默认示数2020.01.01。
判断星期几功能:由蔡勒(Zeller)公式:w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1可由日期推导出这天星期几。公式中的符号含义如下,w:星期;c:世纪;y:年(两位数); m:月(m大于等于3,小于等于14,即在蔡勒公式中,某年的1、2月要看作上一年的13、14月来计算,比如2003年1月1日要看作2002年的13月1日来计算);d:日;[ ]代表取整,即只要整数部分。由7位的信号控制相应LED灯的亮灭,即可显示周几。
- always@(posedge clk_200Hz)//判段星期几
- begin
- if(M==1||M==2)
- xq<=(Y_L-1+(Y_L-1-(Y_L-1)%4)/4+(Y_H-Y_H%4)/4-2*Y_H+(26*(M+13)-(26*(M+13))%10)/10+D-1)%7;
- else
- xq<=(Y_L+(Y_L-Y_L%4)/4+(Y_H-Y_H%4)/4-2*Y_H+(26*(M+1)-(26*(M+1))%10)/10+D-1)%7;
- case(xq)
- 3'd0:xqj<=7'b0000001;
- 3'd1:xqj<=7'b1000000;
- 3'd2:xqj<=7'b0100000;
- 3'd3:xqj<=7'b0010000;
- 3'd4:xqj<=7'b0001000;
- 3'd5:xqj<=7'b0000100;
- 3'd6:xqj<=7'b0000010;
- endcase
- end
8位二进制转BCD码模块:
为了使count模块的年月日数字,更好地在数码管显示,需进行二进制到BCD码的转换。分别将年份用4个8位二进制数(Y_H表示年份高位,Y_L表示年份低位,M表示月份,D表示日)表示,传入该模块,分别转换成BCD码的数码管段选信号a_to_g_N_H、a_to_g_N_L、a_to_g_H和a_to_g_L传入数码管显示模块,进行数码管显示。
数码管显示模块:
输入200Hz扫描时钟,在该时钟下使变量s在0-7循环,控制8个数码管显示,通过模式切换按键的信号输入选择显示的部分(年月日、年、月、日)四种状态显示,实现在调整年份数字时,月、日数码管停止显示,调整月份数字时,停止年、日的数码管显示,调整日的时候,年月数码管停止显示。
备忘模块:
在备忘信号mem触发下,模式切换为备忘模式,可以进行年月日时间调整,确定备忘日期。再次按下mem按键,模式切换回计时模式,并从切换为备忘模式前的日期开始计时,当时间到达备忘日期时,备忘LED灯亮起。
实物调试结果:
其中前7个LED灯显示周几,第8个LED灯为备忘提示灯。
设计中遇到的主要问题及解决方法:
问题一:计数和调数同时使用1Hz频率时,调数的频率太低,需要长按,调整不方便。
解决方法:使用时钟切换,assign clk_f=x?clk_5Hz:clk_1Hz,在clk_f时钟下进行计时和调数,以模式切换变量flag的值为判断依据,给x赋值从而选择时钟,x=1,选择clk_5Hz,x=0,选择clk_1Hz(实际运用为clk_24H)。使得计数时采用1Hz时钟(实际运用24小时周期时钟),调数和模式切换为5Hz时钟,0.2s变化一次,同时实现按钮按下不松开时,数字持续增或减。
问题二:调数时希望使相应位数低频率闪烁,未调位数持续显示。而数码管显示采用动态扫描的方式,不容易实现两个时钟来控制数码管的显示。
解决方法:在调数模式下,让调整数字常亮,其余数字熄灭。从控制s的循环入手,控制相应数码管亮灭的状态。
问题三:备忘模块刚开始时采用1Hz的时钟,而计数模块也采用1Hz的时钟,导致备忘LED灯有延迟。
解决方法:备忘模块采用5Hz的时钟,可以解决问题不会出现LED灯延迟。
问题四:用LED灯来显示周几,最初的想法是定义一个变量来计数经过多少天,即知道经过多少天和初始日期是周几就可计算出该日期周几。但是满足不了日期调整情况。或者由日期直接进行计算,但又涉及到闰年的讨论问题,很复杂。
解决方法:查阅资料,发现了可以由日期直接计算出周几。相关的公式也有很多,这里用的是蔡勒(Zeller)公式。
问题五:为了让程序更加整洁,我用按键输入信号作为敏感信号。但调试的过程中一直报错,经过反复的检查,一直没有检查出语法和逻辑上的问题。
解决方法:最后查询了官网才知道要在引脚约束文件中添加set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets ]语句。