–作者:小黑同学
本文为明德扬原创及录用文章,转载请注明出处!

1.1 总体设计
1.1.1 概述
数字时钟是采用数字电路技术实现时、分、秒计时显示的装置,可以用数字同时显示时,分,秒的精确时间并实现准确校时,具备体积小、重量轻、抗干扰能力强、对环境要求高、高精确性、容易开发等特性,在工业控制系统、智能化仪器表、办公自动化系统等诸多领域取得了极为广泛的应用,诸如自动报警、按时自动打铃、时间程序自动控制、定时广播、自定启闭路灯、定时开关烘箱、通断动力设备、甚至各种定时电器的自动启用等。与传统表盘式机械时钟相比,数字时钟具有更高的准确性和直观性,由于没有机械装置,其使用寿命更长。

1.1.2 设计目标
本设计要求实现可设置的数字时钟(速度快10倍,每过0.1s,秒数加1),具体要求如下:

  1. 按下按键key1,时钟暂停,跳到设置时间状态,在按按键key1,回到正常状态。
  2. 通过按键key2,选择要设置的位置,初始时设置秒低位,按一下,设置秒高位,再按下,设置分低位,依次类推,循环设置。
  3. 通过按键key3,设置数值,按一下数值加1,如果溢出,则重新变为0。
  4. 通过数码管将时间实时显示出来。
  5. 如果开发板上的按键是矩阵键盘,那么要产生需要的按键信号,需要通过例化矩阵键盘模块来产生。

1.1.3 系统结构框图
系统结构框图如下所示:
结构图共分两个,如果使用的开发板上是普通按键的时候,对应的结构图是图一。如果使用的开发板上是矩阵键盘的时候,对应的结构图是图二。

图一


图二

1.1.4 模块功能
按键检测模块实现功能
将外来异步信号打两拍处理,将异步信号同步化;
实现20ms按键消抖功能,并输出有效按键信号。
矩阵键盘模块实现功能
将外来异步信号打两拍处理,将异步信号同步化;
实现20ms按键消抖功能;
实现矩阵键盘的按键检测功能,并输出有效按键信号。
时间产生模块实现功能
产生时间数据;
根据接收到的不同的按键信号,产生暂停、开启、设置时间的功能。
数码管显示模块实现功能
对接收到的时间数据进行译码。

1.1.5 顶层信号

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

1.1.6 参考代码
下面是使用普通按键的顶层代码:

1.module  key_clock(
2.    clk    ,
3.    rst_n  ,
4.    key    ,
5.    segment,
6.    seg_sel
7.);
8.
9.parameter   COUNT_TIME      =   23'd500_0000;
10.parameter   DELAY_TIME      =   10000       ;
11.parameter   SEG_WID         =   8           ;
12.parameter   SEG_SEL         =   6           ;
13.
14.parameter   KEY_S           =   4           ;
15.parameter   KEY_W           =   3           ;
16.
17.input                   clk         ;
18.input                   rst_n       ;
19.input   [ 2:0]          key         ;
20.output  [ 7:0]          segment     ;
21.output  [ 6:0]          seg_sel     ;
22.
23.wire    [ 2:0]          key_vld     ;
24.wire    [23:0]          segment_data;
25.wire    [ 3:0]          cnt2        ;
26.wire    [ 3:0]          cnt3        ;
27.wire    [ 3:0]          cnt4        ;
28.wire    [ 3:0]          cnt5        ;
29.wire    [ 3:0]          cnt6        ;
30.wire    [ 3:0]          cnt7        ;
31.
32.
33.             key_module  uut0(
34.                .clk     (clk    ),
35.                .rst_n   (rst_n  ),
36.                .key_in  (key    ),
37.                .key_vld (key_vld)
38.             );
39.
40.
41.             time_data  uut1(
42.                .clk      (clk    ),
43.                .rst_n    (rst_n  ),
44.                .key_vld  (key_vld),
45.                .cnt2     (cnt2   ),
46.                .cnt3     (cnt3   ),
47.                .cnt4     (cnt4   ),
48.                .cnt5     (cnt5   ),
49.                .cnt6     (cnt6   ),
50.                .cnt7     (cnt7   )
51.
52.             );
53.
54.
55.             seg_disp  uut2(
56.                 .clk          (clk                          ),
57.                 .rst_n        (rst_n                        ),
58.                 .segment      (segment                      ),
59.                 .seg_sel      (seg_sel                      ),
60.                 .segment_data (cnt7,cnt6,cnt5,cnt4,cnt3,cnt2)
61.
62.             );
63.
64.
65.endmodule
下面是使用矩阵键盘的顶层代码:
66.module  key_clock_jvzhen(
67.    clk    ,
68.    rst_n  ,
69.    key_col,
70.    key_row,
71.    segment,
72.    seg_sel
73.);
74.
75.parameter   COUNT_TIME      =   23'd500_0000;
76.parameter   DELAY_TIME      =   10000       ;
77.parameter   SEG_WID         =   8           ;
78.parameter   SEG_SEL         =   6           ;
79.
80.parameter   KEY_S           =   4           ;
81.parameter   KEY_W           =   3           ;
82.
83.input                   clk         ;
84.input                   rst_n       ;
85.input   [ 3:0]          key_col     ;
86.output  [ 3:0]          key_row     ;
87.output  [ 7:0]          segment     ;
88.output  [ 6:0]          seg_sel     ;
89.
90.wire    [ 3:0]          key_vld     ;
91.wire    [ 3:0]          cnt2        ;
92.wire    [ 3:0]          cnt3        ;
93.wire    [ 3:0]          cnt4        ;
94.wire    [ 3:0]          cnt5        ;
95.wire    [ 3:0]          cnt6        ;
96.wire    [ 3:0]          cnt7        ;
97.
98.
99.             key_scan  uut0(
100.                .clk     (clk    ),
101.                .rst_n   (rst_n  ),
102.                .key_col (key_col),
103.                .key_row (key_row),
104.                .key_en  (key_vld)
105.             );
106.
107.
108.             time_data  uut1(
109.                .clk      (clk    ),
110.                .rst_n    (rst_n  ),
111.                .key_vld  (key_vld),
112.                .cnt2     (cnt2   ),
113.                .cnt3     (cnt3   ),
114.                .cnt4     (cnt4   ),
115.                .cnt5     (cnt5   ),
116.                .cnt6     (cnt6   ),
117.                .cnt7     (cnt7   )
118.
119.             );
120.
121.
122.             seg_disp  uut2(
123.                 .clk          (clk                            ),
124.                 .rst_n        (rst_n                          ),
125.                 .segment      (segment                        ),
126.                 .seg_sel      (seg_sel                        ),
127.                 .segment_data ({cnt7,cnt6,cnt5,cnt4,cnt3,cnt2})
128.             );
129.
130.
131.endmodule

1.2 按键检测模块设计
1.2.1 接口信号

信号 接口方向 定义
clk 输入 系统时钟
rst_n 输入 低电平复位信号
key_in 输入 按键输入
key_vld 输出 按键按下指示信号

1.2.2 设计思路
硬件电路

独立式按键工作原理如上图所示,4条输入线连接到FPGA的IO口上,当按键S1按下时,3.3V的电源通过电阻R53再通过按键S1最终进入GND形成一条通路,这条线路的全部电压都加在R53上,则KS0是低电平。当松开按键后,线路断开,就不会有电流通过,KS0应该是3.3V,为高电平。我们可以通过KS0这个IO口的高低电平状态来判断是否有按键按下。其他按键原理与S1一致。
从图上可以看出,如果我们按下按键,那么按键就会接通并连接到低电平GND,如果我们没有按下,那么按键就会断开并接到3.3V,因此按键为低电平有效。通常的按键所用开关为机械弹性开关,当机械触点断开或者闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而机械式按键在闭合及断开的瞬间均伴随有一连串的抖动,如果不进行处理,会使系统识别到抖动信号而进行不必要的反应,导致模块功能不正常,为了避免这种现象的产生,需要进行按键消抖的操作

按键消抖

按键消抖主要分为硬件消抖和软件消抖。两个“与非”门构成一个RS触发器为常用的硬件消抖。软件方法消抖,即检测出键闭合后执行一个延时程序,抖动时间的长短由按键的机械特性决定,一般为5ms~20ms,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认按下按键操作有效。当检测到按键释放后,也要给5ms~20ms的延时,待后沿抖动消失后才能转入该键的处理程序。经过按键消抖的行人优先按键,判断按键有效后,按键信号传递给控制系统,控制系统再进入相应的处理程序。

由于按键按下去的时间一般都会大于20ms,为了达到不管按键按下多久,都视为按下一次的效果,提出以下计数器架构,如下图所示:

消抖计数器cnt:用于计算20ms的时间,加一条件为flag==0 &&(&key_in_ff1 == 0),表示当某个按键按下就开始计数;结束条件为100000,表示数到20ms就结束
按键:表示被按下的按键,没被按下时为高电平,按下后为低电平。
Flag_add:20ms指示信号,默认为低电平,当按键按键按下20ms后变为高电平,直到按键信号变为高电平,重新拉低。

1.2.3 参考代码
使用明德扬的计数器模板,可以很快速很熟练地写出按键消抖模块。

132.module key_module(
133.    clk    ,
134.    rst_n  ,
135.    key_in ,
136.    key_vld
137.);
138.parameter                           DATA_W    = 20          ;
139.parameter                           KEY_W     = 3           ;
140.parameter                           TIME_20MS = 1_000_000   ;
141.
142.input                               clk                     ;
143.input                               rst_n                   ;
144.input           [KEY_W-1 :0]        key_in                  ;
145.output          [KEY_W-1 :0]        key_vld                 ;
146.reg             [KEY_W-1 :0]        key_vld                 ;
147.reg             [DATA_W-1:0]        cnt                     ;
148.wire                                add_cnt                 ;
149.wire                                end_cnt                 ;
150.reg                                 flag_add                ;
151.reg             [KEY_W-1 :0]        key_in_ff1              ;
152.reg             [KEY_W-1 :0]        key_in_ff0              ;
153.
154.
155.always  @(posedge clk or negedge rst_n)begin
156.    if(rst_n==1'b0)begin
157.        cnt <= 20'b0;
158.    end
159.    else if(add_cnt)begin
160.        if(end_cnt)
161.            cnt <= 20'b0;
162.        else
163.            cnt <= cnt + 1'b1;
164.    end
165.    else begin
166.        cnt <= 0;
167.    end
168.end
169.
170.assign add_cnt = flag_add==1'b0 && (&key_in_ff1==0);
171.assign end_cnt = add_cnt && cnt == TIME_20MS - 1;
172.
173.
174.always  @(posedge clk or negedge rst_n)begin
175.    if(rst_n==1'b0)begin
176.        flag_add <= 1'b0;
177.    end
178.    else if(end_cnt)begin
179.        flag_add <= 1'b1;
180.    end
181.    else if(&key_in_ff1==1)begin
182.        flag_add <= 1'b0;
183.    end
184.end
185.
186.
187.always  @(posedge clk or negedge rst_n)begin
188.    if(rst_n==1'b0)begin
189.        key_in_ff0 <= {{KEY_W}{1'b1}};
190.        key_in_ff1 <= {{KEY_W}{1'b1}};
191.    end
192.    else begin
193.        key_in_ff0 <= key_in    ;
194.        key_in_ff1 <= key_in_ff0;
195.    end
196.end
197.
198.
199.always  @(posedge clk or negedge rst_n)begin
200.    if(rst_n==1'b0)begin
201.        key_vld <= 0;
202.    end
203.    else if(end_cnt)begin
204.        key_vld <= ~key_in_ff1;
205.    end
206.    else begin
207.        key_vld <= 0;
208.    end
209.end
210.
211.
212.endmodule

1.3 矩阵键盘模块设计
1.3.1 接口信号

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

1.3.2 设计思路
在前面的案例中已经有矩阵键盘的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
http://fpgabbs.com/forum.php?mod=viewthread&tid=310

1.3.3 参考代码

1.module  key_scan(
2.                 clk    ,
3.                 rst_n  ,
4.                 key_col,
5.                 key_row,
6.                 key_en
7.               );
8.
9.
10.    parameter      KEY_W    =   4      ;
11.    parameter      CHK_COL  =   0      ;
12.    parameter      CHK_ROW  =   1      ;
13.    parameter      DELAY    =   2      ;
14.    parameter      WAIT_END =   3      ;
15.    parameter      COL_CNT  =   16     ;
16.    parameter      TIME_20MS=   1000000;
17.
18.    input               clk              ;
19.    input               rst_n            ;
20.    input [3:0]         key_col          ;
21.
22.    output[3:0]         key_en           ;
23.    output[KEY_W-1:0]   key_row          ;
24.
25.    reg   [3:0]         key_out          ;
26.    reg   [KEY_W-1:0]   key_row          ;
27.    reg                 key_vld          ;
28.
29.
30.    reg   [3:0]         key_col_ff0      ;
31.    reg   [3:0]         key_col_ff1      ;
32.    reg   [1:0]         key_col_get      ;
33.    reg   [3:0]         key_en           ;
34.    wire                end_shake_cnt    ;
35.    reg                 end_shake_cnt_ff0;
36.    reg   [3:0]         state_c          ;
37.    reg   [19:0]        shake_cnt        ;
38.    reg   [3:0]         state_n          ;
39.    reg   [1:0]         row_index        ;
40.    reg   [15:0]        row_cnt          ;
41.    wire                col2row_start    ;
42.    wire                row2del_start    ;
43.    wire                del2wait_start   ;
44.    wire                wait2col_start   ;
45.    wire                add_row_cnt      ;
46.    wire                end_row_cnt      ;
47.    wire                add_shake_cnt    ;
48.    wire                add_row_index    ;
49.    wire                end_row_index    ;
50.
51.
52.always  @(posedge clk or negedge rst_n)begin
53.    if(rst_n==1'b0)begin
54.        key_col_ff0 <= 4'b1111;
55.        key_col_ff1 <= 4'b1111;
56.    end
57.    else begin
58.        key_col_ff0 <= key_col    ;
59.        key_col_ff1 <= key_col_ff0;
60.    end
61.end
62.
63.
64.always @(posedge clk or negedge rst_n) begin
65.    if (rst_n==0) begin
66.        shake_cnt <= 0;
67.    end
68.    else if(add_shake_cnt) begin
69.        if(end_shake_cnt)
70.            shake_cnt <= 0;
71.        else
72.            shake_cnt <= shake_cnt+1 ;
73.   end
74.end
75.assign add_shake_cnt = key_col_ff1!=4'hf;
76.assign end_shake_cnt = add_shake_cnt  && shake_cnt == TIME_20MS-1 ;
77.
78.
79.always  @(posedge clk or negedge rst_n)begin
80.    if(rst_n==1'b0)begin
81.        state_c <= CHK_COL;
82.    end
83.    else begin
84.        state_c <= state_n;
85.    end
86.end
87.
88.always  @(*)begin
89.    case(state_c)
90.        CHK_COL: begin
91.                     if(col2row_start )begin
92.                         state_n = CHK_ROW;
93.                     end
94.                     else begin
95.                         state_n = CHK_COL;
96.                     end
97.                 end
98.        CHK_ROW: begin
99.                     if(row2del_start)begin
100.                         state_n = DELAY;
101.                     end
102.                     else begin
103.                         state_n = CHK_ROW;
104.                     end
105.                 end
106.        DELAY :  begin
107.                     if(del2wait_start)begin
108.                         state_n = WAIT_END;
109.                     end
110.                     else begin
111.                         state_n = DELAY;
112.                     end
113.                 end
114.        WAIT_END: begin
115.                     if(wait2col_start)begin
116.                         state_n = CHK_COL;
117.                     end
118.                     else begin
119.                         state_n = WAIT_END;
120.                     end
121.                  end
122.       default: state_n = CHK_COL;
123.    endcase
124.end
125.assign col2row_start = state_c==CHK_COL  && end_shake_cnt;
126.assign row2del_start = state_c==CHK_ROW  && row_index==3 && end_row_cnt;
127.assign del2wait_start= state_c==DELAY    && end_row_cnt;
128.assign wait2col_start= state_c==WAIT_END && key_col_ff1==4'hf;
129.
130.always  @(posedge clk or negedge rst_n)begin
131.    if(rst_n==1'b0)begin
132.        key_row <= 4'b0;
133.    end
134.    else if(state_c==CHK_ROW)begin
135.        key_row <= ~(1'b1 << row_index);
136.    end
137.    else begin
138.        key_row <= 4'b0;
139.    end
140.end
141.
142.
143.
144.
145.
146.always @(posedge clk or negedge rst_n) begin
147.    if (rst_n==0) begin
148.        row_index <= 0;
149.    end
150.    else if(add_row_index) begin
151.        if(end_row_index)
152.            row_index <= 0;
153.        else
154.            row_index <= row_index+1 ;
155.   end
156.   else if(state_c!=CHK_ROW)begin
157.       row_index <= 0;
158.   end
159.end
160.assign add_row_index = state_c==CHK_ROW && end_row_cnt;
161.assign end_row_index = add_row_index  && row_index == 4-1 ;
162.
163.
164.always @(posedge clk or negedge rst_n) begin
165.    if (rst_n==0) begin
166.        row_cnt <= 0;
167.    end
168.    else if(add_row_cnt) begin
169.        if(end_row_cnt)
170.            row_cnt <= 0;
171.        else
172.            row_cnt <= row_cnt+1 ;
173.   end
174.end
175.assign add_row_cnt = state_c==CHK_ROW || state_c==DELAY;
176.assign end_row_cnt = add_row_cnt  && row_cnt == 16-1 ;
177.
178.
179.
180.always  @(posedge clk or negedge rst_n)begin
181.    if(rst_n==1'b0)begin
182.        key_col_get <= 0;
183.    end
184.    else if(state_c==CHK_COL && end_shake_cnt ) begin
185.        if(key_col_ff1==4'b1110)
186.            key_col_get <= 0;
187.        else if(key_col_ff1==4'b1101)
188.            key_col_get <= 1;
189.        else if(key_col_ff1==4'b1011)
190.            key_col_get <= 2;
191.        else
192.            key_col_get <= 3;
193.    end
194.end
195.
196.
197.always  @(posedge clk or negedge rst_n)begin
198.    if(rst_n==1'b0)begin
199.        key_out <= 0;
200.    end
201.    else if(state_c==CHK_ROW && end_row_cnt)begin
202.        key_out <= {row_index,key_col_get};
203.    end
204.    else begin
205.        key_out <= 0;
206.    end
207.end
208.
209.always  @(posedge clk or negedge rst_n)begin
210.    if(rst_n==1'b0)begin
211.        key_vld <= 1'b0;
212.    end
213.    else if(state_c==CHK_ROW && end_row_cnt && key_col_ff1[key_col_get]==1'b0)begin
214.        key_vld <= 1'b1;
215.    end
216.    else begin
217.        key_vld <= 1'b0;
218.    end
219.end
220.
221.
222.always  @(*)begin
223.    if(rst_n==1'b0)begin
224.        key_en = 0;
225.    end
226.    else if(key_vld && key_out==0)begin
227.        key_en = 4'b0001;
228.    end
229.    else if(key_vld && key_out==1)begin
230.        key_en = 4'b0010;
231.    end
232.    else if(key_vld && key_out==2)begin
233.        key_en = 4'b0100;
234.    end
235.    else begin
236.        key_en = 0;
237.    end
238.end
239.
240.
241.Endmodule

1.4 时间产生模块设计
1.4.1 接口信号

信号 接口方向 定义
clk 输入 系统时钟
rst_n 输入 低电平复位信号
key_vld 输入 按键按下指示信号
Cnt2 输出 秒低位计数器
Cnt3 输出 秒高位计数器
Cnt4 输出 分低位计数器
Cnt5 输出 分高位计数器
Cnt6 输出 时低位计数器
Cnt7 输出 时高位计数器

1.4.2 设计思路
根据题目功能要求可知,要设计数字时钟,由此我们可以提出7个计数器的架构,如下图所示:

该架构由7个计数器组成:时钟计数器cnt1、秒低位计数器cnt2、秒高位计数器cnt3、分低位计数器cnt4、分高位计数器cnt5、时低位计数器cnt6、时高位计数器cnt7。
时钟计数器cnt1:用于计算0.1秒的时钟个数,加一条件为key1_func == 0,表示刚上电时开始计数,key1按下之后停止计数,再按下又重新开始计数;结束条件为5000000,表示数到0.1秒就清零。
秒低位计数器cnt2:用于对“1秒“(实际为0.1秒)进行计数,加一条件为(key1_func &&cnt0 == 0 &&key3_func)||(key1_func == 0 &&end_cnt1),表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到1秒就加1;结束条件为10,表示数到10秒就清零。
秒高位计数器cnt3:用于对10秒进行计数,加一条件为(key1_func &&cnt0== 1 &&key3_func)||(key1_func== 0 &&end_cnt2),表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到10秒就加1;结束条件为6,表示数到60秒就清零。
分低位计数器cnt4:用于对1分进行计数,加一条件为(key1_func &&cnt0== 2 &&key3_func)||(key1_func== 0 &&end_cnt3),表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到1分就加1;结束条件为10,表示数到10分就清零。
分高位计数器cnt5:用于对10分进行计数,加一条件为(key1_func &&cnt0== 3 &&key3_func)||(key1_func== 0 &&end_cnt4),表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到10分就加1;结束条件为6,表示数到60分就清零。
时低位计数器cnt6:用于对1小时进行计数,加一条件为(key1_func &&cnt0== 4 &&key3_func)||(key1_func== 0 &&end_cnt5),表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到1小时就加1;结束条件为x,表示数到x小时就清零。
时高位计数器cnt7:用于对10小时进行计数,加一条件为(key1_func &&cnt0== 5 &&key3_func)||(key1_func== 0 &&end_cnt6),表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到10小时就加1;结束条件为y,表示数到y*10小时就清零。

1.4.3 参考代码
使用明德扬的计数器模板,可以很快速很熟练地写出时间产生模块。

1.module time_data(
2.    clk      ,
3.    rst_n    ,
4.    key_vld  ,
5.    cnt2     ,
6.    cnt3     ,
7.    cnt4     ,
8.    cnt5     ,
9.    cnt6     ,
10.    cnt7
11.);
12.input                   clk         ;
13.input                   rst_n       ;
14.input   [ 3:0]          key_vld     ;
15.output  [ 3:0]          cnt2        ;
16.output  [ 3:0]          cnt3        ;
17.output  [ 3:0]          cnt4        ;
18.output  [ 3:0]          cnt5        ;
19.output  [ 3:0]          cnt6        ;
20.output  [ 3:0]          cnt7        ;
21.
22.reg                     key1_func   ;
23.reg                     key3_func   ;
24.reg     [ 2:0]          cnt0        ;
25.wire                    add_cnt0    ;
26.wire                    end_cnt0    ;
27.reg     [ 23:0]          cnt1       ;
28.wire                    add_cnt1    ;
29.wire                    end_cnt1    ;
30.reg     [ 3:0]          cnt2        ;
31.wire                    add_cnt2    ;
32.wire                    end_cnt2    ;
33.reg     [ 3:0]          cnt3        ;
34.wire                    add_cnt3    ;
35.wire                    end_cnt3    ;
36.reg     [ 3:0]          cnt4        ;
37.wire                    add_cnt4    ;
38.wire                    end_cnt4    ;
39.reg     [ 3:0]          cnt5        ;
40.wire                    add_cnt5    ;
41.wire                    end_cnt5    ;
42.reg     [ 3:0]          cnt6        ;
43.reg     [ 3:0]          x           ;
44.wire                    add_cnt6    ;
45.wire                    end_cnt6    ;
46.reg     [ 3:0]          cnt7        ;
47.reg     [ 1:0]          y           ;
48.wire                    add_cnt7    ;
49.wire                    end_cnt7    ;
50.
51.
52.
53.always  @(posedge clk or negedge rst_n)begin
54.    if(rst_n==1'b0)begin
55.        key1_func<=1'b0;
56.    end
57.    else if(key_vld[0]==1'b1)begin
58.        key1_func<=~key1_func;
59.    end
60.    else begin
61.        key1_func<=key1_func;
62.    end
63.end
64.
65.
66.
67.always @(posedge clk or negedge rst_n) begin
68.    if (rst_n==0) begin
69.        cnt0 <= 0;
70.    end
71.    else if(add_cnt0) begin
72.        if(end_cnt0)
73.            cnt0 <= 0;
74.        else
75.            cnt0 <= cnt0+1 ;
76.   end
77.end
78.assign add_cnt0 = key_vld[1];
79.assign end_cnt0 = add_cnt0  && cnt0 == 6-1 ;
80.
81.
82.always  @(posedge clk or negedge rst_n)begin
83.    if(rst_n==1'b0)begin
84.        key3_func<=1'b0;
85.    end
86.    else if(key1_func==1'b1 && key_vld[2]==1'b1)begin
87.        key3_func<=1'b1;
88.    end
89.    else begin
90.        key3_func<=1'b0;
91.    end
92.end
93.
94.
95.always @(posedge clk or negedge rst_n) begin
96.    if (rst_n==0) begin
97.        cnt1 <= 0;
98.    end
99.    else if(add_cnt1) begin
100.        if(end_cnt1)
101.            cnt1 <= 0;
102.        else
103.            cnt1 <= cnt1+1 ;
104.   end
105.   else begin
106.       cnt1 <= 0;
107.   end
108.end
109.assign add_cnt1 = key1_func==0;
110.assign end_cnt1 = add_cnt1  && cnt1 == 500_0000-1 ;
111.
112.
113.
114.
115.always @(posedge clk or negedge rst_n) begin
116.    if (rst_n==0) begin
117.        cnt2 <= 0;
118.    end
119.    else if(add_cnt2) begin
120.        if(end_cnt2)
121.            cnt2 <= 0;
122.        else
123.            cnt2 <= cnt2+1 ;
124.   end
125.end
126.assign add_cnt2 = (key1_func && cnt0==0 && key3_func) || (key1_func==0 && end_cnt1);
127.assign end_cnt2 = add_cnt2  && cnt2 == 10-1 ;
128.
129.
130.
131.
132.always @(posedge clk or negedge rst_n) begin
133.    if (rst_n==0) begin
134.        cnt3 <= 0;
135.    end
136.    else if(add_cnt3) begin
137.        if(end_cnt3)
138.            cnt3 <= 0;
139.        else
140.            cnt3 <= cnt3+1 ;
141.   end
142.end
143.assign add_cnt3 = (key1_func && cnt0==1 && key3_func) || (key1_func==0 && end_cnt2);
144.assign end_cnt3 = add_cnt3  && cnt3 == 6-1 ;
145.
146.
147.
148.always @(posedge clk or negedge rst_n) begin
149.    if (rst_n==0) begin
150.        cnt4 <= 0;
151.    end
152.    else if(add_cnt4) begin
153.        if(end_cnt4)
154.            cnt4 <= 0;
155.        else
156.            cnt4 <= cnt4+1 ;
157.   end
158.end
159.assign add_cnt4 = (key1_func && cnt0==2 && key3_func) || (key1_func==0 && end_cnt3);
160.assign end_cnt4 = add_cnt4  && cnt4 == 10-1 ;
161.
162.
163.always @(posedge clk or negedge rst_n) begin
164.    if (rst_n==0) begin
165.        cnt5 <= 0;
166.    end
167.    else if(add_cnt5) begin
168.        if(end_cnt5)
169.            cnt5 <= 0;
170.        else
171.            cnt5 <= cnt5+1 ;
172.   end
173.end
174.assign add_cnt5 = (key1_func && cnt0==3 && key3_func) || (key1_func==0 && end_cnt4);
175.assign end_cnt5 = add_cnt5  && cnt5 == 6-1 ;
176.
177.
178.always @(posedge clk or negedge rst_n) begin
179.    if (rst_n==0) begin
180.        cnt6 <= 0;
181.    end
182.    else if(add_cnt6) begin
183.        if(end_cnt6)
184.            cnt6 <= 0;
185.        else
186.            cnt6 <= cnt6+1 ;
187.   end
188.end
189.assign add_cnt6 = (key1_func && cnt0==4 && key3_func) || (key1_func==0 && end_cnt5);
190.assign end_cnt6 = add_cnt6  && cnt6 == x-1 ;
191.
192.
193.
194.always @(posedge clk or negedge rst_n) begin
195.    if (rst_n==0) begin
196.        cnt7 <= 0;
197.    end
198.    else if(add_cnt7) begin
199.        if(end_cnt7)
200.            cnt7 <= 0;
201.        else
202.            cnt7 <= cnt7+1 ;
203.   end
204.end
205.assign add_cnt7 = (key1_func && cnt0==5 && key3_func) || (key1_func==0 && end_cnt6);
206.assign end_cnt7 = add_cnt7  && cnt7 == y-1 ;
207.
208.
209.always  @(*)begin
210.    if(cnt7==2)begin
211.        x = 4;
212.    end
213.    else begin
214.        x =10;
215.    end
216.end
217.
218.always  @(*)begin
219.    if(cnt6>=4)begin
220.        y = 2;
221.    end
222.    else begin
223.        y = 3;
224.    end
225.end
226.
227.
228.endmodule

1.5 数码管显示模块设计
1.5.1 接口信号

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

1.5.2 设计思路
在前面的案例中已经有数码管显示的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
http://fpgabbs.com/forum.php?mod=viewthread&tid=399

1.5.3 参考代码

1.module seg_disp(
2.    clk         ,
3.    rst_n       ,
4.    segment     ,
5.    segment_data,
6.    seg_sel
7.);
8.parameter   ZERO            =   8'b1100_0000;
9.parameter   ONE             =   8'b1111_1001;
10.parameter   TWO             =   8'b1010_0100;
11.parameter   THREE           =   8'b1011_0000;
12.parameter   FOUR            =   8'b1001_1001;
13.parameter   FIVE            =   8'b1001_0010;
14.parameter   SIX             =   8'b1000_0010;
15.parameter   SEVEN           =   8'b1111_1000;
16.parameter   EIGHT           =   8'b1000_0000;
17.parameter   NINE            =   8'b1001_0000;
18.
19.
20.
21.input                   clk         ;
22.input                   rst_n       ;
23.input   [23:0]          segment_data;
24.output  [ 7:0]          segment     ;
25.output  [ 5:0]          seg_sel     ;
26.
27.
28.reg     [ 7:0]          segment     ;
29.wire    [ 7:0]          segment_tmp ;
30.reg     [ 5:0]          seg_sel     ;
31.
32.
33.reg     [15:0]          cnt8        ;
34.wire                    add_cnt8    ;
35.wire                    end_cnt8    ;
36.reg     [ 2:0]          cnt9        ;
37.wire                    add_cnt9    ;
38.wire                    end_cnt9    ;
39.
40.
41.
42.
43.always @(posedge clk or negedge rst_n) begin
44.    if (rst_n==0) begin
45.        cnt8 <= 0;
46.    end
47.    else if(add_cnt8) begin
48.        if(end_cnt8)
49.            cnt8 <= 0;
50.        else
51.            cnt8 <= cnt8+1 ;
52.   end
53.end
54.assign add_cnt8 = 1;
55.assign end_cnt8 = add_cnt8  && cnt8 == 10000-1 ;
56.
57.
58.
59.
60.always @(posedge clk or negedge rst_n) begin
61.    if (rst_n==0) begin
62.        cnt9 <= 0;
63.    end
64.    else if(add_cnt9) begin
65.        if(end_cnt9)
66.            cnt9 <= 0;
67.        else
68.            cnt9 <= cnt9+1 ;
69.   end
70.end
71.assign add_cnt9 = end_cnt8;
72.assign end_cnt9 = add_cnt9  && cnt9 == 6-1 ;
73.
74.
75.
76.
77.assign segment_tmp = segment_data[(1+cnt9)*4-1 -:4];
78.
79.always@(posedge clk or negedge rst_n)begin
80.    if(rst_n==1'b0)begin
81.         segment<=ZERO;
82.    end
83.    else  begin
84.        case(segment_tmp)
85.            4'd0:segment <= ZERO;
86.            4'd1:segment <= ONE;
87.            4'd2:segment <= TWO;
88.            4'd3:segment <= THREE;
89.            4'd4:segment <= FOUR;
90.            4'd5:segment <= FIVE ;
91.            4'd6:segment <= SIX ;
92.            4'd7:segment <= SEVEN ;
93.            4'd8:segment <= EIGHT ;
94.            4'd9:segment <= NINE ;
95.            default:begin
96.                segment<=segment;
97.            end
98.        endcase
99.    end
100.end
101.
102.
103.
104.
105.always@(posedge clk or negedge rst_n)begin
106.    if(rst_n==1'b0)begin
107.        seg_sel <= 6'b11_1110;
108.    end
109.    else begin
110.        seg_sel <= ~(6'b1<<cnt9);
111.    end
112.end
113.
114.endmodule

1.6 效果和总结
下图是该工程在mp801开发板上的现象
其中按键s4控制数字时钟的暂停与开始,按键s3来选择需要设置的位,按键s2设置数值。

下图是该工程在db603开发板上的现象
其中按键s1控制数字时钟的暂停与开始,按键s2来选择需要设置的位,按键s3设置数值。

下图是该工程在ms980试验箱上的现象
其中按键s1控制数字时钟的暂停与开始,按键s2来选择需要设置的位,按键s3设置数值。

至简设计系列_按键控制数字时钟相关推荐

  1. 至简设计系列_简易计算器

    –作者:小黑同学 本文为明德扬原创及录用文章,转载请注明出处! 1.1 总体设计 1.1.1 概述 计算器是近代人发明的可以进行数字运算的机器.现代的电子计算器能进行数学运算的手持电子机器,拥有集成电 ...

  2. 至简设计系列_电子密码锁

    –作者:肖肖肖 本文为明德扬原创及录用文章,转载请注明出处! 1.1 总体设计 1.1.1 概述 随着生活质量的不断提高,加强家庭防盗安全变得非常重要,但传统机械锁的构造过于简单,很容易被打开,从而降 ...

  3. led计数电路实验报告_至简设计系列_状态机实现LED交通灯2

    --作者:肖肖肖 本文为明德扬原创及录用文章,转载请注明出处! 1.1 总体设计 1.1.1 概述 发光二极管简称为LED,是一种常用的发光器件,通过电子与空穴复合释放能量发光,可以高效的将电能转化为 ...

  4. 至简设计系列_定时转换的LED交通灯1

    –作者:肖肖肖 本文为明德扬原创及录用文章,转载请注明出处! 1.1 总体设计 1.1.1 概述 发光二极管简称为LED,是一种常用的发光器件,通过电子与空穴复合释放能量发光,它可以高效的将电能转化为 ...

  5. 基于fpga的数码管动态扫描电路设计_【至简设计案例系列】基于FPGA的密码锁设计(altera版)...

    秦红凯 明德扬FPGA科教 一.项目背景概述 随着生活质量的不断提高,加强家庭防盗安全变得非常重要,但传统机械锁的构造过于简单,很容易被打开,从而降低了安全性.数字密码锁因为它的保密性很高,安全系数也 ...

  6. 【至简设计案例系列】基于FPGA的密码锁设计(altera版)

    本文为明德扬原创及录用文章,转载请注明出处! 作者:秦红锴 一.项目背景概述 随着生活质量的不断提高,加强家庭防盗安全变得非常重要,但传统机械锁的构造过于简单,很容易被打开,从而降低了安全性.数字密码 ...

  7. 【至简设计案例系列】基于FPGA的密码锁(XILINX ISE版)

    本文为明德扬原创及录用文章,转载请注明出处! 作者:造就狂野青春 一.总体设计 1.概述 本文基于明德扬至简设计法和明德扬设计规范,设计了一个基于FPGA的数字密码锁,实现了在拨码开关条件下,按键设置 ...

  8. 【至简设计案例系列】基于FPGA的频率、电压测量仪器

    作者:造就狂野青春 1.概述 基于明德扬至简设计法和明德扬设计规范,设计一个基于FPGA的频率.电压测量仪器,实现了测量AD采集后的波形频率和电压(峰峰值),并将数值显示在数码管上.这个案例通过扩展还 ...

  9. CLOSE-UP FORMALWEAR_意大利进口_2015秋冬_男装发布会_西装图片系列_男装西装设计资料_WeArTrends时尚资讯网_国内最专业的服装设计资讯网站...

    CLOSE-UP FORMALWEAR_意大利进口_2015秋冬_男装发布会_西装图片系列_男装西装设计资料_WeArTrends时尚资讯网_国内最专业的服装设计资讯网站 CLOSE-UP FORMA ...

最新文章

  1. Cannot set property 'render' of undefined
  2. ab压力测试: apr_socket_recv: Connection timed out (110)
  3. django实现长链接
  4. 线程启动语句的顺序是否决定线程的执行次序。_详细分析 Java 中启动线程的正确和错误方式
  5. “云上企业”是企业面向未来的战略选择
  6. 《全球互联网金融商业模式:格局与发展》——第3章,第3节互联网保险公司...
  7. P3819 松江1843路
  8. Android开发——通过扫描二维码,打开或者下载Android应用
  9. Java jdk API 1.8英文版、中文版分享
  10. 酷派W711刷机教程
  11. js日历控件html,第11款插件:jquery.jCal.js显示日历插件
  12. poi 同时 冻结第一行和第一列
  13. OSChina 周日乱弹 —— 每天叫醒我的是Bug
  14. C# 支付宝小程序 ---小程序支付
  15. 伍鸣博士受邀出席徐汇区住房租赁市场研讨会
  16. Android 文字测量
  17. 三国志战略版:Daniel_S6新武锋_司马魏枪
  18. FileUploadException: Header section has more than 10240 bytes (maybe it is not properly terminated)]
  19. 计算机技术应用读书心得,《多媒体环境下的教学设计和资源的应用》读书心得体会...
  20. 基于OneData的数据仓库建设(阿里巴巴大数据之路)

热门文章

  1. R语言进行数值模拟:模拟泊松回归模型的数据
  2. 【Android】自制静音App,解决他人手机外放问题
  3. 【安全】Java(web)项目安全漏洞及解决方式【面试+工作】
  4. http协议1.x/2.x区别
  5. STL 常用容器(1)--string
  6. 【1945. 字符串转化后的各位数字之和】
  7. 金蝶K3 BOM独立控制跳层开关开发
  8. 做量化交易的第一步,Python爬取股票数据
  9. CENTOS上的网络安全工具(二)ARKIME部署安装
  10. laragon集成环境使用,跑起laravel项目