基于FPGA的电子日历设计

本设计是本学期的课程设计,也没有正式上过课,全凭自学摸索完成本课程设计,在摸索的过程中也看了csdn上众多博客,给予了本人很大的帮助,本人做完也前来分享。若有不对错误之处也请大家多多理解指正。本设计可以实现正常计数(区分大小月和闰年),备忘提醒,按键调整,按键消抖,星期几显示等功能。代码分为顶层模块,分频模块,计数模块,备忘模块,译码显示模块,按键消抖模块,8位二进制转BCD码模块。也希望此文也能给大家带来一定的帮助。(若有本课程设计的同学请勿照搬照抄)

本设计的系统框架图:

基于FPGA的电子日历设计_第1张图片

顶层模块:

  1. module calendar(  
  2. input clk_100MHz,  
  3. input clr,//清零信号  
  4. input model,//换位信号  
  5. input add,//加位信号  
  6. input sub,//减位信号  
  7. input mem,//备忘信号  
  8. input pause,//暂停信号  
  9. output [7:0]a_to_h_1,//输出数码管段选信号  
  10. output [7:0]a_to_h_2,//输出数码管段选信号  
  11.   //两个段选信号  
  12. output [7:0]an,//输出8个数码管使能信号  
  13.   //数码管使能信号  
  14. output memo,//输出备忘信号  
  15. output [6:0]xqj  //周几信号
  16.     );  
  17.     
  18. wire clk_200Hz;  
  19. wire clk_1Hz;  
  20. //wire clk_24H;  
  21. wire clk_5Hz;  
  22. wire model_out;  
  23. wire add_out;  
  24. wire sub_out;  
  25. wire [1:0]flag_1;  
  26. wire [1:0]flag_2;    
  27. wire[7:0] M;//月份  
  28. wire[7:0] D;//  
  29. wire[7:0]Y_H;//年份高位  
  30. wire[7:0]Y_L;//年份低位  
  31. wire[7:0] M_m;//月份  
  32. wire[7:0] D_m;//  
  33. wire[7:0]Y_H_m;//年份高位  
  34. wire[7:0]Y_L_m;//年份低位  
  35. wire[7:0] a_to_g_M;  
  36. wire[7:0] a_to_g_D;  
  37. wire[7:0] a_to_g_Y_H;  
  38. wire[7:0] a_to_g_Y_L;  
  39. wire[7:0] a_to_g_M_m;  
  40. wire[7:0] a_to_g_D_m;  
  41. wire[7:0] a_to_g_Y_H_m;  
  42. wire[7:0] a_to_g_Y_L_m;  
  43.   
  44. clk_200Hz u_clk_200Hz(      
  45.                 .clk_100MHz(clk_100MHz),//100MHz信号输入模块clk_200Hz  
  46.                 .clk_200Hz(clk_200Hz)//clk_200Hz信号输出  
  47.        
  48.         );  
  49. clk_1Hz u_clk_1Hz(   
  50.                 .clk_100MHz(clk_100MHz),//100MHz信号输入模块clk_1Hz  
  51.                 .clk_1Hz(clk_1Hz)//clk_1Hz信号输出  
  52.                 );  
  53.     
  54. //clk_24H u_clk_24H(  
  55. // .clk_100MHz(clk_100MHz),//100MHz信号输入模块clk_24H  
  56. // .clk_24H(clk_24H)  
  57. //            );  
  58. clk_5Hz u_clk_5Hz(  
  59.                 .clk_100MHz(clk_100MHz),  
  60.                 .clk_5Hz(clk_5Hz)  
  61.                 );  
  62.     
  63. count u_count(     
  64.             .clk_1Hz(clk_1Hz),  
  65.             .clk_5Hz(clk_5Hz),  
  66.             .clk_200Hz(clk_200Hz),  
  67.             .clr(clr),  
  68.             .model(model_out),  
  69.             .pause(pause),  
  70.             .add(add_out),  
  71.             .sub(sub_out),  
  72.             .mem(mem),  
  73.             .M(M),  
  74.             .D(D),  
  75.             .Y_H(Y_H),  
  76.             .Y_L(Y_L),  
  77.             .flag(flag_1),  
  78.             .xqj(xqj)  
  79.         );  
  80.     
  81. bin8_to_bcd u_bin8_to_bcd(  
  82.   .bin(M),  
  83.   .bcd(a_to_g_M)  
  84.                );  
  85.     
  86. bin8_to_bcd u1_bin8_bcd(  
  87.   .bin(D),  
  88.   .bcd(a_to_g_D)  
  89.                );  
  90.     
  91. bin8_to_bcd u2_bin8_bcd(  
  92.   .bin(Y_H),  
  93.   .bcd(a_to_g_Y_H)  
  94.                );  
  95.     
  96. bin8_to_bcd u3_bin8_bcd(  
  97.   .bin(Y_L),  
  98.   .bcd(a_to_g_Y_L)  
  99.                );  
  100. bin8_to_bcd u4_bin8_bcd(  
  101.   .bin(M_m),  
  102.   .bcd(a_to_g_M_m)  
  103.                );  
  104.                    
  105. bin8_to_bcd u5_bin8_bcd(  
  106.    .bin(D_m),  
  107.    .bcd(a_to_g_D_m)  
  108.                 );  
  109.                    
  110. bin8_to_bcd u6_bin8_bcd(  
  111.    .bin(Y_H_m),  
  112.    .bcd(a_to_g_Y_H_m)  
  113.                 );  
  114.                    
  115. bin8_to_bcd u7_bin8_bcd(  
  116.     .bin(Y_L_m),  
  117.     .bcd(a_to_g_Y_L_m)  
  118.                 );    
  119.     
  120. smg u_smg(  
  121.                .flag_1(flag_1),  
  122.                .flag_2(flag_2),  
  123.                .clk(clk_200Hz),  
  124.   .x({a_to_g_Y_H,a_to_g_Y_L,a_to_g_M,a_to_g_D}),  
  125.   .x_m({a_to_g_Y_H_m,a_to_g_Y_L_m,a_to_g_M_m,a_to_g_D_m}),                 
  126.                .mem(mem),  
  127.                .dp(8'b00010100),  
  128.                .a_to_h_1(a_to_h_1),  
  129.                .a_to_h_2(a_to_h_2),  
  130.                .an(an)  
  131.            );     
  132.     
  133. key_debounce u_key_debounce(  
  134.            .clk_200Hz(clk_200Hz),  
  135.             .clr(clr),  
  136.    .btn_in({model,add,sub}),  
  137.    .btn_out({model_out,add_out,sub_out})  
  138.             );  
  139.     
  140. memory u_memory(  
  141.          .clk_5Hz(clk_5Hz),  
  142.          .M(M),  
  143.          .D(D),  
  144.          .Y_H(Y_H),  
  145.          .Y_L(Y_L),  
  146.          .clr(clr),  
  147.          .model(model_out),  
  148.          .add(add_out),  
  149.          .sub(sub_out),  
  150.          .mem(mem),  
  151.          .M_m(M_m),  
  152.          .D_m(D_m),  
  153.          .Y_H_m(Y_H_m),  
  154.          .Y_L_m(Y_L_m),  
  155.          .memo(memo),  
  156.          .flag(flag_2)  
  157.                         );  
  158. endmodule  

分频模块:分出1Hz(为方便显示用1Hz,正常时为24H),5Hz,200Hz(数码管扫描时钟)。

  1. module clk_1Hz(  
  2. //module clk_24H(  
  3. input clk_100MHz,  
  4. output clk_1Hz  
  5. //output clk_24H  
  6.     );  
  7. reg[25:0] cnt_1Hz;//原时钟上升沿计数到49999999时,时钟电平输出翻转即得1Hz时钟信号。d49999999=h2faf07f需要26位二进制数  
  8. //reg[41:0] cnt_24H;//原时钟上升沿计数到4319999999999时,时钟电平输出翻转即得24H时钟信号。d44319999999999=3edd410bfff需要42位二进制数  
  9.  reg clk_1Hz_reg;//1Hz时钟电平输出  
  10.  //reg clk_24H_reg;//24H时钟电平输出  
  11.  initial//初始化  
  12.  begin  
  13.    cnt_1Hz=0;  
  14.    //cnt_24H=0;  
  15.    clk_1Hz_reg=0;  
  16.    //clk_24H_reg=0;  
  17.  end  
  18.  always@(posedge clk_100MHz)  
  19.  begin  
  20.    if(cnt_1Hz==26'h2faf07f)  
  21.    // if(cnt_24H==26'h3edd410bfff)  
  22.      begin   
  23.        clk_1Hz_reg<=~clk_1Hz_reg;  
  24.        // clk_24H_reg<=~clk_24H_reg;  
  25.        cnt_1Hz<=0;  
  26.        // cnt_24H<=0;  
  27.      end  
  28.    else  
  29.      cnt_1Hz<= cnt_1Hz+1;  
  30.      //cnt_24H<= cnt_24H+1  
  31.    end  
  32. assign clk_1Hz=clk_1Hz_reg;  
  33. //assign clk_24H=clk_24H_reg;  
  34.       
  35. 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灯的亮灭,即可显示周几。

  1. always@(posedge clk_200Hz)//判段星期几  
  2. begin  
  3.     if(M==1||M==2)  
  4.     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;  
  5.     else  
  6.     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;  
  7.     case(xq)  
  8.     3'd0:xqj<=7'b0000001;  
  9.     3'd1:xqj<=7'b1000000;  
  10.     3'd2:xqj<=7'b0100000;  
  11.     3'd3:xqj<=7'b0010000;  
  12.     3'd4:xqj<=7'b0001000;  
  13.     3'd5:xqj<=7'b0000100;  
  14.     3'd6:xqj<=7'b0000010;  
  15.     endcase  
  16. 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灯亮起。

实物调试结果

基于FPGA的电子日历设计_第2张图片

基于FPGA的电子日历设计_第3张图片

其中前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 ]语句。

 

 

你可能感兴趣的:(FPGA,verilog,fpga)