明德扬论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信扫一扫,快捷登录!

查看: 118|回复: 0

【每周FPGA案例】至简设计系列_闹钟

[复制链接]
发表于 2020-7-22 15:05:58 | 显示全部楼层 |阅读模式

马上注册,看完整文章,学更多FPGA知识。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
至简设计系列_闹钟
【上板现象】

闹钟在MP801开发板效果


闹钟在点拨开发板上板效果




1.1 总体设计1.1.1 概述
数字时钟是采用数字电路技术实现时、分、秒计时显示的装置,可以用数字同时显示时,分,秒的精确时间并实现准确校时,具备体积小、重量轻、抗干扰能力强、对环境要求高、高精确性、容易开发等特性,在工业控制系统、智能化仪器表、办公自动化系统等诸多领域取得了极为广泛的应用,诸如自动报警、按时自动打铃、时间程序自动控制、定时广播、自定启闭路灯、定时开关烘箱、通断动力设备、甚至各种定时电器的自动启用等。与传统表盘式机械时钟相比,数字时钟具有更高的准确性和直观性,由于没有机械装置,其使用寿命更长。
1.1.2 设计目标
设计一款具有闹钟功能的数字时钟,具体要求如下
1、  用8个数码管实现,四个一组,每组有分钟和秒。左边一组是时间显示,右边一组用来做闹钟时间。
2、  当左边时间等于右边时,蜂鸣器响5秒。
3、  闹钟时间和显示时间均可通过3个按键设置。设置方法:按下按键1,时钟暂停,跳到设置时间状态,再按下按键1,回到正常状态。通过按键2,选择要设置的位置,初始设置秒个位,按一下,设置秒十位,再按下,设置分个位,以此类推,循环设置。通过按键3,设置数值,按一下数值加1,如果溢出则重新变为0。
1.1.3 系统结构框图
系统结构框图如下所示:
结构图共分两个,如果使用的开发板上是矩阵键盘的时候,对应的结构图是图一。如果使用的开发板上是普通按键的时候,对应的结构图是图二。
01.JPG

图一
02.JPG


图二
1.1.4模块功能Ø  按键检测模块实现功能
1、将外来异步信号打两拍处理,将异步信号同步化。
2、实现20ms按键消抖功能,并输出有效按键信号。
Ø  矩阵键盘模块实现功能
1、将外来异步信号打两拍处理,将异步信号同步化。
2、实现20ms按键消抖功能。
3、实现矩阵键盘的按键检测功能,并输出有效按键信号。
Ø  时间产生模块实现功能
1、  产生显示时间数据。
2、  产生闹钟时间数据,
3、根据接收到的不同的按键信号,产生暂停、开启、设置时间的功能。
Ø  数码管显示模块实现功能
1、  对接收到的时间数据进行译码。
Ø  蜂鸣器模块实现功能
1、  将接受到的显示时间数据与闹钟时间数据进行比较,控制蜂鸣器的开启。

1.1.5顶层信号
  
信号名
  
接口方向
定义
clk
输入
系统时钟,50Mhz
rst_n
输入
低电平复位信号
Key
输入
3位按键信号,开发板按键为矩阵键盘时,不需要该信号
Key_col
输入
4位矩阵键盘列信号,默认高电平,开发板按键为普通按键时,不需要该信号
Key_row
输出
4位矩阵键盘行信号,默认低电平,开发板按键为普通按键时,不需要该信号
Segment
输出
8位数码管段选信号
Seg_sel
输出
8位数码管位选信号
beep
输出
1位蜂鸣器控制信号

1.1.6参考代码

下面是使用普通按键的顶层代码:
  1. module  alarm_clock(
  2.     clk       ,
  3.     rst_n     ,
  4.     key       ,
  5.     segment   ,
  6.     seg_sel   ,
  7.     beep
  8. );
  9. input                   clk             ;
  10. input                   rst_n           ;
  11. input   [2:0 ]          key             ;
  12. output  [7:0 ]          segment         ;
  13. output  [7:0 ]          seg_sel         ;
  14. output                  beep            ;

  15. wire    [7:0 ]          segment         ;
  16. wire    [7:0 ]          seg_sel         ;
  17. wire                    beep            ;
  18. wire    [3:0 ]          xs_sec_low      ;
  19. wire    [3:0 ]          xs_sec_high     ;
  20. wire    [3:0 ]          xs_min_low      ;
  21. wire    [3:0 ]          xs_min_high     ;
  22. wire    [3:0 ]          sec_low         ;
  23. wire    [3:0 ]          sec_high        ;
  24. wire    [3:0 ]          min_low         ;
  25. wire    [3:0 ]          min_high        ;
  26. wire    [25:0]          counter         ;
  27. wire    [3:0 ]          key_vld         ;
  28. wire                    flag_set        ;

  29. key_module u0(
  30.              .clk         (clk         ),
  31.              .rst_n       (rst_n       ),
  32.              .key_in      (key         ),
  33.              .key_vld     (key_vld     )
  34.              );
  35. time_data u1(
  36.              .clk         (clk         ),
  37.              .rst_n       (rst_n       ),
  38.              .key_vld     (key_vld     ),
  39.              .flag_set    (flag_set    ),
  40.              .end_counter (end_counter ),
  41.              .sec_low     (sec_low     ),
  42.              .sec_high    (sec_high    ),
  43.              .min_low     (min_low     ),
  44.              .min_high    (min_high    ),
  45.              .xs_sec_low  (xs_sec_low  ),
  46.              .xs_sec_high (xs_sec_high ),
  47.              .xs_min_low  (xs_min_low  ),
  48.              .xs_min_high (xs_min_high )
  49.              );
  50. beep u2(
  51.              .clk         (clk         ),
  52.              .rst_n       (rst_n       ),
  53.              .flag_set    (flag_set    ),
  54.              .end_counter (end_counter ),
  55.              .beep        (beep        ),
  56.              .sec_low     (sec_low     ),
  57.              .sec_high    (sec_high    ),
  58.              .min_low     (min_low     ),
  59.              .min_high    (min_high    ),
  60.              .xs_sec_low  (xs_sec_low  ),
  61.              .xs_sec_high (xs_sec_high ),
  62.              .xs_min_low  (xs_min_low  ),
  63.              .xs_min_high (xs_min_high )
  64.              );
  65. seg_disp u3(
  66.              .clk         (clk         ),
  67.              .rst_n       (rst_n       ),
  68.              .segment_data({xs_min_high,xs_min_low,xs_sec_high,xs_sec_low,min_high,min_low,sec_high,sec_low}),
  69.              .segment     (segment     ),
  70.              .seg_sel     (seg_sel     )  
  71. );


  72. endmodule
复制代码



下面是使用矩阵键盘的顶层代码:
  1. module  alarm_clock_jvzhen(
  2.     clk       ,
  3.     rst_n     ,
  4.     key_col   ,
  5.     key_row   ,
  6.     segment   ,
  7.     seg_sel   ,
  8.     beep
  9. );
  10. input                   clk             ;
  11. input                   rst_n           ;
  12. input   [3:0 ]          key_col         ;
  13. output  [3:0 ]          key_row         ;
  14. output  [7:0 ]          segment         ;
  15. output  [7:0 ]          seg_sel         ;
  16. output                  beep            ;

  17. wire    [7:0 ]          segment         ;
  18. wire    [7:0 ]          seg_sel         ;
  19. wire                    beep            ;
  20. wire    [3:0 ]          xs_sec_low      ;
  21. wire    [3:0 ]          xs_sec_high     ;
  22. wire    [3:0 ]          xs_min_low      ;
  23. wire    [3:0 ]          xs_min_high     ;
  24. wire    [3:0 ]          sec_low         ;
  25. wire    [3:0 ]          sec_high        ;
  26. wire    [3:0 ]          min_low         ;
  27. wire    [3:0 ]          min_high        ;
  28. wire                    end_counter     ;
  29. wire    [3:0 ]          key_vld         ;
  30. wire                    flag_set        ;
  31. wire    [15:0]          key_out         ;

  32. key_scan  u0(
  33.              .clk         (clk         ),
  34.              .rst_n       (rst_n       ),
  35.              .key_col     (key_col     ),
  36.              .key_row     (key_row     ),
  37.              .key_en      (key_vld     )
  38.              );
  39. time_data u1(
  40.              .clk         (clk         ),
  41.              .rst_n       (rst_n       ),
  42.              .key_vld     (key_vld     ),
  43.              .flag_set    (flag_set    ),
  44.              .end_counter (end_counter ),
  45.              .sec_low     (sec_low     ),
  46.              .sec_high    (sec_high    ),
  47.              .min_low     (min_low     ),
  48.              .min_high    (min_high    ),
  49.              .xs_sec_low  (xs_sec_low  ),
  50.              .xs_sec_high (xs_sec_high ),
  51.              .xs_min_low  (xs_min_low  ),
  52.              .xs_min_high (xs_min_high )
  53.              );
  54. beep u2(
  55.              .clk         (clk         ),
  56.              .rst_n       (rst_n       ),
  57.              .flag_set    (flag_set    ),
  58.              .end_counter (end_counter ),
  59.              .beep        (beep        ),
  60.              .sec_low     (sec_low     ),
  61.              .sec_high    (sec_high    ),
  62.              .min_low     (min_low     ),
  63.              .min_high    (min_high    ),
  64.              .xs_sec_low  (xs_sec_low  ),
  65.              .xs_sec_high (xs_sec_high ),
  66.              .xs_min_low  (xs_min_low  ),
  67.              .xs_min_high (xs_min_high )
  68.              );
  69. seg_disp u3(
  70.              .clk         (clk         ),
  71.              .rst_n       (rst_n       ),
  72.              .segment_data({xs_min_high,xs_min_low,xs_sec_high,xs_sec_low,min_high,min_low,sec_high,sec_low}),
  73.              .segment     (segment     ),
  74.              .seg_sel     (seg_sel     )  
  75. );


  76. endmodul
复制代码



1.2 按键检测模块设计1.2.1 接口信号
  
信号
  
接口方向
定义
clk
输入
系统时钟
rst_n
输入
低电平复位信号
key_in
输入
按键输入
key_vld
输出
按键按下指示信号

1.2.2 设计思路
在前面的案例中已经有按键检测的介绍,所以这里不在过多介绍,详细介绍请看下方链接:

1.2.3参考代码
使用明德扬的计数器模板,可以很快速很熟练地写出按键消抖模块。
  1. always  @(posedge clk or negedge rst_n)begin
  2.     if(rst_n==1'b0)begin
  3.         cnt <= 20'b0;
  4.     end
  5.     else if(add_cnt)begin
  6.         if(end_cnt)
  7.             cnt <= 20'b0;
  8.         else
  9.             cnt <= cnt + 1'b1;
  10.     end
  11.     else begin
  12.         cnt <= 0;
  13.     end
  14. end

  15. assign add_cnt = flag_add==1'b0 && (&key_in_ff1==0);
  16. assign end_cnt = add_cnt && cnt == TIME_20MS - 1;




  17. always  @(posedge clk or negedge rst_n)begin
  18.     if(rst_n==1'b0)begin
  19.         flag_add <= 1'b0;
  20.     end
  21.     else if(end_cnt)begin
  22.         flag_add <= 1'b1;
  23.     end
  24.     else if(&key_in_ff1==1)begin
  25.         flag_add <= 1'b0;
  26.     end
  27. end




  28. always  @(posedge clk or negedge rst_n)begin
  29.     if(rst_n==1'b0)begin
  30.         key_in_ff0 <= {{KEY_W}{1'b1}};
  31.         key_in_ff1 <= {{KEY_W}{1'b1}};
  32.     end
  33.     else begin
  34.         key_in_ff0 <= key_in    ;
  35.         key_in_ff1 <= key_in_ff0;
  36.     end
  37. end




  38. always  @(posedge clk or negedge rst_n)begin
  39.     if(rst_n==1'b0)begin
  40.         key_vld <= 0;
  41.     end
  42.     else if(end_cnt)begin
  43.         key_vld <= ~key_in_ff1;
  44.     end
  45.     else begin
  46.         key_vld <= 0;
  47.     end
  48. end



  49. endmodule
复制代码


1.3 矩阵键盘模块设计1.3.1接口信号
  
信号
  
接口方向
定义
clk
输入
系统时钟
rst_n
输入
低电平复位信号
key_col
输入
矩阵键盘列输入信号
Key_row
输出
矩阵键盘行输出信号
Key_en
输出
按键按下指示信号

1.3.2 设计思路
在前面的案例中已经有矩阵键盘的介绍,所以这里不在过多介绍,详细介绍请看下方链接:

1.3.3参考代码
  1. always  @(posedge clk or negedge rst_n)begin
  2.     if(rst_n==1'b0)begin
  3.         key_col_ff0 <= 4'b1111;
  4.         key_col_ff1 <= 4'b1111;
  5.     end
  6.     else begin
  7.         key_col_ff0 <= key_col    ;
  8.         key_col_ff1 <= key_col_ff0;
  9.     end
  10. end


  11. always @(posedge clk or negedge rst_n) begin
  12.     if (rst_n==0) begin
  13.         shake_cnt <= 0;
  14.     end
  15.     else if(add_shake_cnt) begin
  16.         if(end_shake_cnt)
  17.             shake_cnt <= 0;
  18.         else
  19.             shake_cnt <= shake_cnt+1 ;
  20.    end
  21. end
  22. assign add_shake_cnt = key_col_ff1!=4'hf;
  23. assign end_shake_cnt = add_shake_cnt  && shake_cnt == TIME_20MS-1 ;


  24. always  @(posedge clk or negedge rst_n)begin
  25.     if(rst_n==1'b0)begin
  26.         state_c <= CHK_COL;
  27.     end
  28.     else begin
  29.         state_c <= state_n;
  30.     end
  31. end

  32. always  @(*)begin
  33.     case(state_c)
  34.         CHK_COL: begin
  35.                      if(col2row_start )begin
  36.                          state_n = CHK_ROW;
  37.                      end
  38.                      else begin
  39.                          state_n = CHK_COL;
  40.                      end
  41.                  end
  42.         CHK_ROW: begin
  43.                      if(row2del_start)begin
  44.                          state_n = DELAY;
  45.                      end
  46.                      else begin
  47.                          state_n = CHK_ROW;
  48.                      end
  49.                  end
  50.         DELAY :  begin
  51.                      if(del2wait_start)begin
  52.                          state_n = WAIT_END;
  53.                      end
  54.                      else begin
  55.                          state_n = DELAY;
  56.                      end
  57.                  end
  58.         WAIT_END: begin
  59.                      if(wait2col_start)begin
  60.                          state_n = CHK_COL;
  61.                      end
  62.                      else begin
  63.                          state_n = WAIT_END;
  64.                      end
  65.                   end
  66.        default: state_n = CHK_COL;
  67.     endcase
  68. end
  69. assign col2row_start = state_c==CHK_COL  && end_shake_cnt;
  70. assign row2del_start = state_c==CHK_ROW  && row_index==3 && end_row_cnt;
  71. assign del2wait_start= state_c==DELAY    && end_row_cnt;
  72. assign wait2col_start= state_c==WAIT_END && key_col_ff1==4'hf;

  73. always  @(posedge clk or negedge rst_n)begin
  74.     if(rst_n==1'b0)begin
  75.         key_row <= 4'b0;
  76.     end
  77.     else if(state_c==CHK_ROW)begin
  78.         key_row <= ~(1'b1 << row_index);
  79.     end
  80.     else begin
  81.         key_row <= 4'b0;
  82.     end
  83. end





  84. always @(posedge clk or negedge rst_n) begin
  85.     if (rst_n==0) begin
  86.         row_index <= 0;
  87.     end
  88.     else if(add_row_index) begin
  89.         if(end_row_index)
  90.             row_index <= 0;
  91.         else
  92.             row_index <= row_index+1 ;
  93.    end
  94.    else if(state_c!=CHK_ROW)begin
  95.        row_index <= 0;
  96.    end
  97. end
  98. assign add_row_index = state_c==CHK_ROW && end_row_cnt;
  99. assign end_row_index = add_row_index  && row_index == 4-1 ;


  100. always @(posedge clk or negedge rst_n) begin
  101.     if (rst_n==0) begin
  102.         row_cnt <= 0;
  103.     end
  104.     else if(add_row_cnt) begin
  105.         if(end_row_cnt)
  106.             row_cnt <= 0;
  107.         else
  108.             row_cnt <= row_cnt+1 ;
  109.    end
  110. end
  111. assign add_row_cnt = state_c==CHK_ROW || state_c==DELAY;
  112. assign end_row_cnt = add_row_cnt  && row_cnt == 16-1 ;



  113. always  @(posedge clk or negedge rst_n)begin
  114.     if(rst_n==1'b0)begin
  115.         key_col_get <= 0;
  116.     end
  117.     else if(state_c==CHK_COL && end_shake_cnt ) begin
  118.         if(key_col_ff1==4'b1110)
  119.             key_col_get <= 0;
  120.         else if(key_col_ff1==4'b1101)
  121.             key_col_get <= 1;
  122.         else if(key_col_ff1==4'b1011)
  123.             key_col_get <= 2;
  124.         else
  125.             key_col_get <= 3;
  126.     end
  127. end


  128. always  @(posedge clk or negedge rst_n)begin
  129.     if(rst_n==1'b0)begin
  130.         key_out <= 0;
  131.     end
  132.     else if(state_c==CHK_ROW && end_row_cnt)begin
  133.         key_out <= {row_index,key_col_get};
  134.     end
  135.     else begin
  136.         key_out <= 0;
  137.     end
  138. end

  139. always  @(posedge clk or negedge rst_n)begin
  140.     if(rst_n==1'b0)begin
  141.         key_vld <= 1'b0;
  142.     end
  143.     else if(state_c==CHK_ROW && end_row_cnt && key_col_ff1[key_col_get]==1'b0)begin
  144.         key_vld <= 1'b1;
  145.     end
  146.     else begin
  147.         key_vld <= 1'b0;
  148.     end
  149. end


  150. always  @(*)begin
  151.     if(rst_n==1'b0)begin
  152.         key_en = 0;
  153.     end
  154.     else if(key_vld && key_out==0)begin
  155.         key_en = 4'b0001;
  156.     end
  157.     else if(key_vld && key_out==1)begin
  158.         key_en = 4'b0010;
  159.     end
  160.     else if(key_vld && key_out==2)begin
  161.         key_en = 4'b0100;
  162.     end
  163.     else begin
  164.         key_en = 0;
  165.     end
  166. end


  167. endmodule
复制代码


1.4 时间产生模块设计1.4.1接口信号
  
信号
  
接口方向
定义
clk
输入
系统时钟
rst_n
输入
低电平复位信号
key_vld
输入
按键按下指示信号
end_counter
输出
时钟计数器结束条件,表示1秒的时间
Xs_sec_low
输出
显示的秒低位计数器
Xs_sec_high
输出
显示的秒高位计数器
Xs_min_low
输出
显示的分低位计数器
Xs_min_high
输出
显示的分高位计数器
Sec_low
输出
闹钟的秒低位计数器
Sec_high
输出
闹钟的秒高位计数器
Min_low
输出
闹钟的分低位计数器
Min_high
输出
闹钟的分高位计数器
Flag_set
输出
设置状态指示信号

1.4.2设计思路

根据题目功能要求可知,要显示的时间就是在完整的数字时钟的基础上,减少了时的高位和低位的显示,再介绍架构之前,先了解一下本模块其他几个信号的作用。
设置状态指示信号flag_set:该信号初始状态为低电平,表示模块处于正常工作状态,当按下按键key1时,设置状态指示信号进行翻转,变为高电平,表示进入到设置状态。
设置位计数器sel_cnt:该计数器表示要设置的位,初始状态为0,表示可以设置闹钟的秒低位,当其为1时表示可以设置闹钟的秒的高位,按照这样的顺序依次类推,当其为7的时候,表示可以设置显示时间的分高位。加一条件为key_vld[1]==1’b1,表示按下按键key2的时候加一;结束条件为8,显示时间的四个数码管加上闹钟的四个数码管共8个,所以数8个就清零。
由此可提出5个计数器的架构,如下图所示:

该架构由5个计数器组成:时钟计数器counter、秒低位计数器xs_sec_low、秒高位计数器xs_sec_high、分低位计数器xs_min_low、分高位计数器xs_min_high
时钟计数器counter:用于计算1秒的时钟个数,加一条件为flag_set==1'b0,表示刚上电时开始计数,key1按下之后,进入设置模式,停止计数,再按下又重新开始计数;结束条件为50000000,表示数到1秒就清零。
秒低位计数器xs_sec_low:用于对1秒进行计数,加一条件为(sel_cnt==5-1&& set_en) || end_counter,表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到1秒就加1;结束条件为10,表示数到10秒就清零。
秒高位计数器xs_sec_high:用于对10秒进行计数,加一条件为(sel_cnt==6-1&& set_en) || end_xs_sec_low,表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到10秒就加1;结束条件为6,表示数到60秒就清零。
分低位计数器xs_min_low:用于对1分进行计数,加一条件为(sel_cnt==7-1&& set_en) || end_xs_sec_high,表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到1分就加1;结束条件为10,表示数到10分就清零。
分高位计数器xs_min_high:用于对10分进行计数,加一条件为(sel_cnt==8-1&& set_en) || end_xs_min_low,表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到10分就加1;结束条件为6,表示数到60分就清零。
上面介绍了显示时间的计数器架构,下面我们来思考一下闹钟部分的架构。
我们都知道闹钟的工作原理,它本身不会自动计数,需要我们手动设置。根据本设计的功能要求,有四个数码管来显示设置的闹钟秒的高低位和分的高低位,因此我们提出四个计数器组成的架构,这四个计数器相互独立,互不干涉,结构图如下:

该架构由4个计数器组成:秒低位计数器sec_low、秒高位计数器sec_high、分低位计数器min_low、分高位计数器min_high
秒低位计数器sec_low:用于对闹钟秒的低位进行计数,加一条件为sel_cnt==1-1 && set_en,表示在设置状态下通过按键key3来控制加一  ;结束条件为10,表示最大能设置为9,超过之后便清零。
秒高位计数器sec_high:用于对闹钟秒的高位进行计数,加一条件为sel_cnt==2-1 && set_en,表示在设置状态下可通过按键key3来控制加一;结束条件为6,表示最大能设置为5,超过之后便清零。
分低位计数器min_low:用于对闹钟分的低位进行计数,加一条件为sel_cnt==3-1 && set_en,表示在设置状态下可通过按键key3来控制加一;结束条件为10,表示最大能设置为9,超过之后便清零。
分高位计数器min_high:用于对闹钟分高位进行计数,加一条件为sel_cnt==4-1 && set_en,表示在设置状态下可通过按键key3来控制加一;结束条件为6,表示最大能设置为5,超过之后便清零。


1.4.3参考代码
使用明德扬的计数器模板,可以很快速很熟练地写出时间产生模块。
  1. always  @(posedge clk or negedge rst_n)begin
  2.     if(rst_n==1'b0)begin
  3.         flag_set<=1'b0;
  4.     end
  5.     else if(key_vld[0]==1'b1)begin
  6.         flag_set<=~flag_set;
  7.     end
  8.     else begin
  9.         flag_set<=flag_set;
  10.     end
  11. end


  12. always @(posedge clk or negedge rst_n) begin
  13.     if (rst_n==0) begin
  14.         sel_cnt <= 0;
  15.     end
  16.     else if(add_sel_cnt) begin
  17.         if(end_sel_cnt)
  18.             sel_cnt <= 0;
  19.         else
  20.             sel_cnt <= sel_cnt+1 ;
  21.    end
  22. end
  23. assign add_sel_cnt = key_vld[1]==1'b1;
  24. assign end_sel_cnt = add_sel_cnt  && sel_cnt == 8-1 ;




  25. always  @(posedge clk or negedge rst_n)begin
  26.     if(rst_n==1'b0)begin
  27.         set_en<=1'b0;
  28.     end
  29.     else if(flag_set==1'b1 && key_vld[2]==1'b1)begin
  30.         set_en<=1'b1;
  31.     end
  32.     else begin
  33.         set_en<=1'b0;
  34.     end
  35. end


  36. always @(posedge clk or negedge rst_n) begin
  37.     if (rst_n==0) begin
  38.         counter <= 0;
  39.     end
  40.     else if(add_counter) begin
  41.         if(end_counter)
  42.             counter <= 0;
  43.         else
  44.             counter <= counter+1 ;
  45.    end
  46. end
  47. assign add_counter = flag_set==1'b0;
  48. assign end_counter = add_counter  && counter == 26'd5000_0000-1;


  49. always @(posedge clk or negedge rst_n) begin
  50.     if (rst_n==0) begin
  51.         sec_low <= 0;
  52.     end
  53.     else if(add_sec_low) begin
  54.         if(end_sec_low)
  55.             sec_low <= 0;
  56.         else
  57.             sec_low <= sec_low+1 ;
  58.    end
  59. end
  60. assign add_sec_low = sel_cnt==1-1 && set_en;
  61. assign end_sec_low = add_sec_low  && sec_low == 10-1 ;


  62. always @(posedge clk or negedge rst_n) begin
  63.     if (rst_n==0) begin
  64.         sec_high <= 0;
  65.     end
  66.     else if(add_sec_high) begin
  67.         if(end_sec_high)
  68.             sec_high <= 0;
  69.         else
  70.             sec_high <= sec_high+1 ;
  71.    end
  72. end
  73. assign add_sec_high = sel_cnt==2-1 && set_en;
  74. assign end_sec_high = add_sec_high  && sec_high == 6-1 ;


  75. always @(posedge clk or negedge rst_n) begin
  76.     if (rst_n==0) begin
  77.         min_low <= 0;
  78.     end
  79.     else if(add_min_low) begin
  80.         if(end_min_low)
  81.             min_low <= 0;
  82.         else
  83.             min_low <= min_low+1 ;
  84.    end
  85. end
  86. assign add_min_low = sel_cnt==3-1 && set_en;
  87. assign end_min_low = add_min_low  && min_low == 10-1 ;

  88. always @(posedge clk or negedge rst_n) begin
  89.     if (rst_n==0) begin
  90.         min_high <= 0;
  91.     end
  92.     else if(add_min_high) begin
  93.         if(end_min_high)
  94.             min_high <= 0;
  95.         else
  96.             min_high <= min_high+1 ;
  97.    end
  98. end
  99. assign add_min_high = sel_cnt==4-1 && set_en;
  100. assign end_min_high = add_min_high  && min_high == 6-1 ;




  101. always @(posedge clk or negedge rst_n) begin
  102.     if (rst_n==0) begin
  103.         xs_sec_low <= 0;
  104.     end
  105.     else if(add_xs_sec_low) begin
  106.         if(end_xs_sec_low)
  107.             xs_sec_low <= 0;
  108.         else
  109.             xs_sec_low <= xs_sec_low+1 ;
  110.    end
  111. end
  112. assign add_xs_sec_low = (sel_cnt==5-1 && set_en) || end_counter;
  113. assign end_xs_sec_low = add_xs_sec_low && xs_sec_low == 10-1 ;




  114. always @(posedge clk or negedge rst_n) begin
  115.     if (rst_n==0) begin
  116.         xs_sec_high <= 0;
  117.     endalways  @(posedge clk or negedge rst_n)begin
  118.     if(rst_n==1'b0)begin
  119.         key_col_ff0 <= 4'b1111;
  120.         key_col_ff1 <= 4'b1111;
  121.     end
  122.     else begin
  123.         key_col_ff0 <= key_col    ;
  124.         key_col_ff1 <= key_col_ff0;
  125.     end
  126. end


  127. always @(posedge clk or negedge rst_n) begin
  128.     if (rst_n==0) begin
  129.         shake_cnt <= 0;
  130.     end
  131.     else if(add_shake_cnt) begin
  132.         if(end_shake_cnt)
  133.             shake_cnt <= 0;
  134.         else
  135.             shake_cnt <= shake_cnt+1 ;
  136.    end
  137. end
  138. assign add_shake_cnt = key_col_ff1!=4'hf;
  139. assign end_shake_cnt = add_shake_cnt  && shake_cnt == TIME_20MS-1 ;


  140. always  @(posedge clk or negedge rst_n)begin
  141.     if(rst_n==1'b0)begin
  142.         state_c <= CHK_COL;
  143.     end
  144.     else begin
  145.         state_c <= state_n;
  146.     end
  147. end

  148. always  @(*)begin
  149.     case(state_c)
  150.         CHK_COL: begin
  151.                      if(col2row_start )begin
  152.                          state_n = CHK_ROW;
  153.                      end
  154.                      else begin
  155.                          state_n = CHK_COL;
  156.                      end
  157.                  end
  158.         CHK_ROW: begin
  159.                      if(row2del_start)begin
  160.                          state_n = DELAY;
  161.                      end
  162.                      else begin
  163.                          state_n = CHK_ROW;
  164.                      end
  165.                  end
  166.         DELAY :  begin
  167.                      if(del2wait_start)begin
  168.                          state_n = WAIT_END;
  169.                      end
  170.                      else begin
  171.                          state_n = DELAY;
  172.                      end
  173.                  end
  174.         WAIT_END: begin
  175.                      if(wait2col_start)begin
  176.                          state_n = CHK_COL;
  177.                      end
  178.                      else begin
  179.                          state_n = WAIT_END;
  180.                      end
  181.                   end
  182.        default: state_n = CHK_COL;
  183.     endcase
  184. end
  185. assign col2row_start = state_c==CHK_COL  && end_shake_cnt;
  186. assign row2del_start = state_c==CHK_ROW  && row_index==3 && end_row_cnt;
  187. assign del2wait_start= state_c==DELAY    && end_row_cnt;
  188. assign wait2col_start= state_c==WAIT_END && key_col_ff1==4'hf;

  189. always  @(posedge clk or negedge rst_n)begin
  190.     if(rst_n==1'b0)begin
  191.         key_row <= 4'b0;
  192.     end
  193.     else if(state_c==CHK_ROW)begin
  194.         key_row <= ~(1'b1 << row_index);
  195.     end
  196.     else begin
  197.         key_row <= 4'b0;
  198.     end
  199. end





  200. always @(posedge clk or negedge rst_n) begin
  201.     if (rst_n==0) begin
  202.         row_index <= 0;
  203.     end
  204.     else if(add_row_index) begin
  205.         if(end_row_index)
  206.             row_index <= 0;
  207.         else
  208.             row_index <= row_index+1 ;
  209.    end
  210.    else if(state_c!=CHK_ROW)begin
  211.        row_index <= 0;
  212.    end
  213. end
  214. assign add_row_index = state_c==CHK_ROW && end_row_cnt;
  215. assign end_row_index = add_row_index  && row_index == 4-1 ;


  216. always @(posedge clk or negedge rst_n) begin
  217.     if (rst_n==0) begin
  218.         row_cnt <= 0;
  219.     end
  220.     else if(add_row_cnt) begin
  221.         if(end_row_cnt)
  222.             row_cnt <= 0;
  223.         else
  224.             row_cnt <= row_cnt+1 ;
  225.    end
  226. end
  227. assign add_row_cnt = state_c==CHK_ROW || state_c==DELAY;
  228. assign end_row_cnt = add_row_cnt  && row_cnt == 16-1 ;



  229. always  @(posedge clk or negedge rst_n)begin
  230.     if(rst_n==1'b0)begin
  231.         key_col_get <= 0;
  232.     end
  233.     else if(state_c==CHK_COL && end_shake_cnt ) begin
  234.         if(key_col_ff1==4'b1110)
  235.             key_col_get <= 0;
  236.         else if(key_col_ff1==4'b1101)
  237.             key_col_get <= 1;
  238.         else if(key_col_ff1==4'b1011)
  239.             key_col_get <= 2;
  240.         else
  241.             key_col_get <= 3;
  242.     end
  243. end


  244. always  @(posedge clk or negedge rst_n)begin
  245.     if(rst_n==1'b0)begin
  246.         key_out <= 0;
  247.     end
  248.     else if(state_c==CHK_ROW && end_row_cnt)begin
  249.         key_out <= {row_index,key_col_get};
  250.     end
  251.     else begin
  252.         key_out <= 0;
  253.     end
  254. end

  255. always  @(posedge clk or negedge rst_n)begin
  256.     if(rst_n==1'b0)begin
  257.         key_vld <= 1'b0;
  258.     end
  259.     else if(state_c==CHK_ROW && end_row_cnt && key_col_ff1[key_col_get]==1'b0)begin
  260.         key_vld <= 1'b1;
  261.     end
  262.     else begin
  263.         key_vld <= 1'b0;
  264.     end
  265. end


  266. always  @(*)begin
  267.     if(rst_n==1'b0)begin
  268.         key_en = 0;
  269.     end
  270.     else if(key_vld && key_out==0)begin
  271.         key_en = 4'b0001;
  272.     end
  273.     else if(key_vld && key_out==1)begin
  274.         key_en = 4'b0010;
  275.     end
  276.     else if(key_vld && key_out==2)begin
  277.         key_en = 4'b0100;
  278.     end
  279.     else begin
  280.         key_en = 0;
  281.     end
  282. end


  283. endmodule;
复制代码


1.5 数码管显示模块设计1.5.1接口信号
  
信号
  
接口方向
定义
clk
输入
系统时钟
rst_n
输入
低电平复位信号
Segment_data
输入
时间数据
Segment
输出
数码管段选信号
Seg_sel
输出
数码管位选信号


1.5.2设计思路

在前面的案例中已经有数码管显示的介绍,所以这里不在过多介绍,详细介绍请看下方链接:

1.5.3参考代码
  1. always @(posedge clk or negedge rst_n) begin
  2.     if (rst_n==0) begin
  3.         delay <= 0;
  4.     end
  5.     else if(add_delay) begin
  6.         if(end_delay)
  7.             delay <= 0;
  8.         else
  9.             delay <= delay+1 ;
  10.    end
  11. end
  12. assign add_delay = 1;
  13. assign end_delay = add_delay  && delay == 2000-1 ;




  14. always @(posedge clk or negedge rst_n) begin
  15.     if (rst_n==0) begin
  16.         delay_time <= 0;
  17.     end
  18.     else if(add_delay_time) begin
  19.         if(end_delay_time)
  20.             delay_time <= 0;
  21.         else
  22.             delay_time <= delay_time+1 ;
  23.    end
  24. end
  25. assign add_delay_time = end_delay;
  26. assign end_delay_time = add_delay_time  && delay_time == 8-1 ;


  27. assign segment_tmp  = segment_data[(1+delay_time)*4-1 -:4];
  28. always  @(posedge clk or negedge rst_n)begin
  29.     if(rst_n==1'b0)begin
  30.         segment <= ZERO;
  31.     end
  32.     else begin
  33.         case(segment_tmp)
  34.             4'd0:segment <= ZERO;
  35.             4'd1:segment <= ONE  ;
  36.             4'd2:segment <= TWO  ;
  37.             4'd3:segment <= THREE;
  38.             4'd4:segment <= FOUR ;
  39.             4'd5:segment <= FIVE ;
  40.             4'd6:segment <= SIX  ;
  41.             4'd7:segment <= SEVEN;
  42.             4'd8:segment <= EIGHT;
  43.             4'd9:segment <= NINE ;
  44.             default:begin
  45.                 segment <= segment;
  46.             end
  47.         endcase
  48.     end
  49. end


  50. always  @(posedge clk or negedge rst_n)begin
  51.     if(rst_n==1'b0)begin
  52.         seg_sel <= 8'b1111_1111;
  53.     end
  54.     else begin
  55.         seg_sel <= ~(8'b1<<delay_time);
  56.     end
  57. end


  58. endmodule
复制代码


1.6 蜂鸣器模块设计1.6.1接口信号
  
信号
  
接口方向
定义
clk
输入
系统时钟
rst_n
输入
低电平复位信号
Key1_func
输入
设置状态指示信号
end_counter
输入
时钟计数器结束条件,表示1秒的时间
Xs_sec_low
输入
显示的秒低位计数器
Xs_sec_high
输入
显示的秒高位计数器
Xs_min_low
输入
显示的分低位计数器
Xs_min_high
输入
显示的分高位计数器
Sec_low
输入
闹钟的秒低位计数器
Sec_high
输入
闹钟的秒高位计数器
Min_low
输入
闹钟的分低位计数器
Min_hign
输入
闹钟的分高位计数器
beep
输出
蜂鸣器信号
1.6.2设计思路
本模块主要通过将显示时间与设置的闹钟时间进行比较,如果相同的话,就控制beep拉低,持续时间为5秒。由此提出一个计数器的架构,如下图所示。
03.JPG

该架构由蜂鸣器控制信号beep、秒计数器miao和闹钟触发指示信号flag_add组成。
秒计数器秒:用于对5秒的时间进行计数,加一条件为flag_add && end_counter,表示当闹钟被触发,并且经过1秒的时间就加一;结束条件为5,表示数完5秒就清零。
闹钟触发指示信号flag:当其为高电平时表示闹钟被触发,低电平表示没有被触发。初始状态为低电平,从低变高的条件为sec_low==xs_sec_low&&sec_high==xs_sec_high&&min_low==xs_min_low&&min_high==xs_min_high&&init&&!key1_func,表示当显示时间的秒高低位、分高低位和闹钟设置的秒高低位、分高低位相等,同时不处于刚上电的初始状态和设置状态时,闹钟被触发;从高变低的条件为end_miao,表示当5秒数完之后,就拉低。
蜂鸣器控制信号beep:当其为低电平时,控制蜂鸣器响,为高电平时不响。初始状态为高电平,从高变低的条件为flag_add,表示计数器开始计数之后便将其拉低,当检测到flag_add=0的时候,便将其拉高。

1.6.3参考代码
  1. always  @(posedge clk or negedge rst_n)begin
  2.     if(rst_n==1'b0)begin
  3.         flag_add <= 0;
  4.     end
  5.     else if(sec_low==xs_sec_low&&sec_high==xs_sec_high&&min_low==xs_min_low&&min_high==xs_min_high&&init&&flag_set==0)begin
  6.         flag_add <= 1;
  7.     end
  8.     else if(end_miao)begin
  9.         flag_add <= 0;
  10.     end
  11. end


  12. always@(*)begin
  13.     if(!sec_low&&!sec_high&&!min_low&&!min_high)begin
  14.         init=0;
  15.     end
  16.     else begin
  17.         init=1;
  18.     end
  19. end


  20. always @(posedge clk or negedge rst_n) begin
  21.     if (rst_n==0) begin
  22.         miao <= 0;
  23.     end
  24.     else if(add_miao) begin
  25.         if(end_miao)
  26.             miao <= 0;
  27.         else
  28.             miao <= miao+1 ;
  29.    end
  30. end
  31. assign add_miao = flag_add && end_counter;
  32. assign end_miao = add_miao  && miao == 5-1 ;


  33. always@(posedge clk or negedge rst_n)begin
  34.      if(rst_n==1'b0)begin
  35.         beep<=1'b1;
  36.     end
  37.     else if(flag_add)begin
  38.         beep<=1'b0;
  39.     end
  40.     else
  41.         beep<=1'b1;
  42. end


  43. endmodule
复制代码



1.7 效果和总结下图是该工程在mp801开发板上的现象
其中按键s4控制数字时钟的暂停与开始,按键s3来选择需要设置的位,按键s2设置数值。左边四个数码管显示的是时钟的时间,右边四个数码管显示的是闹钟设置的时间。
04.png

下图是该工程在db603开发板上的现象
其中按键s1控制数字时钟的暂停与开始,按键s2来选择需要设置的位,按键s3设置数值。左边四个数码管显示的是时钟的时间,右边四个数码管显示的是闹钟设置的时间。
05.png

下图是该工程在ms980试验箱上的现象
其中按键s1控制数字时钟的暂停与开始,按键s2来选择需要设置的位,按键s3设置数值。左边四个数码管显示的是时钟的时间,右边四个数码管显示的是闹钟设置的时间。

06.png



由于该项目的上板现象是动态的,开始、暂停、时间设置等现象无法通过图片表现出来,想观看完整现象的朋友可以看一下现象演示的视频。


【设计教程下载】

至简设计系列_闹钟.pdf (1.35 MB, 下载次数: 0)
FPGA视频课程  培训班 FPGA学习资料
吴老师 18022857217(微信同号) Q1241003385
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|MDYBBS ( 粤ICP备16061416号-1

GMT+8, 2020-8-6 05:42 , Processed in 0.567941 second(s), 13 queries , File On.

Powered by Discuz! X3.4

本论坛由广州健飞通信有限公司所有

© 2001-2019 Comsenz Inc.

快速回复 返回顶部 返回列表