官方论坛
官方淘宝
官方博客
微信公众号
点击联系吴工 点击联系周老师

【案例】按键控制数字时钟设计

发布时间:2021-06-25   作者:admin 浏览量:

案例编号:001600000062

至简设计系列_按键控制数字时钟

--作者:小黑同学

本文为明德扬原创及录用文章,转载请注明出处!

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 系统结构框图

系统结构框图如下所示:

「每周FPGA案例」按键控制数字时钟设计
「每周FPGA案例」按键控制数字时钟设计

结构图共分两个,如果使用的开发板上是普通按键的时候,对应的结构图是图一。如果使用的开

发板上是矩阵键盘的时候,对应的结构图是图二。

1.1.4 模块功能

➢ 按键检测模块实现功能

将外来异步信号打两拍处理,将异步信号同步化;

实现 20ms 按键消抖功能,并输出有效按键信号。

➢ 矩阵键盘模块实现功能

将外来异步信号打两拍处理,将异步信号同步化;

实现 20ms 按键消抖功能;

实现矩阵键盘的按键检测功能,并输出有效按键信号。

➢ 时间产生模块实现功能

产生时间数据;

根据接收到的不同的按键信号,产生暂停、开启、设置时间的功能。

➢ 数码管显示模块实现功能

对接收到的时间数据进行译码。

1.1.5 顶层信号

「每周FPGA案例」按键控制数字时钟设计

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 接口信号

「每周FPGA案例」按键控制数字时钟设计

1.2.2 设计思路

➢ 硬件电路

「每周FPGA案例」按键控制数字时钟设计

独立式按键工作原理如上图所示,4 条输入线连接到 FPGA 的 IO 口上,当按键 S1 按下时,3.

3V 的电源通过电阻 R53 再通过按键 S1 最终进入 GND 形成一条通路,这条线路的全部电压都加在 R

53 上,则 KS0 是低电平。当松开按键后,线路断开,就不会有电流通过,KS0 应该是 3.3V,为高电

平。我们可以通过 KS0 这个 IO 口的高低电平状态来判断是否有按键按下。其他按键原理与 S1 一致。

从图上可以看出,如果我们按下按键,那么按键就会接通并连接到低电平 GND,如果我们没有

按下,那么按键就会断开并接到 3.3V,因此按键为低电平有效。通常的按键所用开关为机械弹性开

关,当机械触点断开或者闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地

接通,在断开时也不会一下子断开。因而机械式按键在闭合及断开的瞬间均伴随有一连串的抖动,如

果不进行处理,会使系统识别到抖动信号而进行不必要的反应,导致模块功能不正常,为了避免这种

现象的产生,需要进行按键消抖的操作

➢ 按键消抖

按键消抖主要分为硬件消抖和软件消抖。两个"与非"门构成一个 RS 触发器为常用的硬件消抖。

软件方法消抖,即检测出键闭合后执行一个延时程序,抖动时间的长短由按键的机械特性决定,一般

为 5ms~20ms,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认按下按

键操作有效。当检测到按键释放后,也要给 5ms~20ms 的延时,待后沿抖动消失后才能转入该键的

处理程序。经过按键消抖的行人优先按键,判断按键有效后,按键信号传递给控制系统,控制系统再

进入相应的处理程序。

「每周FPGA案例」按键控制数字时钟设计

由于按键按下去的时间一般都会大于 20ms,为了达到不管按键按下多久,都视为按下一次的效

果,提出以下计数器架构,如下图所示:

「每周FPGA案例」按键控制数字时钟设计

消抖计数器 cnt:用于计算 20ms 的时间,加一条件为 flag==0 &&(&key_in_ff1==0),表示当

某个按键按下就开始计数;结束条件为 100000,表示数到 20ms 就结束

按键:表示被按下的按键,没被按下时为高电平,按下后为低电平。

Flag: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 接口信号

「每周FPGA案例」按键控制数字时钟设计

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 接口信号

「每周FPGA案例」按键控制数字时钟设计

1.4.2 设计思路

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

「每周FPGA案例」按键控制数字时钟设计

该架构由 7 个计数器组成:时钟计数器 cnt1、秒低位计数器 cnt2、秒高位计数器 cnt3、分低位

计数器 cnt4、分高位计数器 cnt5、时低位计数器 cnt6、时高位计数器 cnt7。

时钟计数器 cnt1:用于计算 0.1 秒的时钟个数,加一条件为 key1_func==0,表示刚上电时开始

计数,key1 按下之后停止计数,再按下又重新开始计数;结束条件为 5000000,表示数到 0.1 秒就

清零。

秒低位计数器 cnt2:用于对"1 秒"(实际为 0.1 秒)进行计数,加一条件为(key1_func &&cn

t0==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_fun

c)||(key1_func==0 &&end_cnt5),表示在设置状态下可通过按键 key3 来控制加一,或者在正常状态

时数到 1 小时就加 1;结束条件为 x,表示数到 x 小时就清零。

时高位计数器 cnt7:用于对 10 小时进行计数,加一条件为(key1_func &&cnt0==5 &&key3_fu

nc)||(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 接口信号

「每周FPGA案例」按键控制数字时钟设计

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 设置数值。

「每周FPGA案例」按键控制数字时钟设计

➢ 下图是该工程在 db603 开发板上的现象

其中按键 s1 控制数字时钟的暂停与开始,按键 s2 来选择需要设置的位,按键 s3 设置数值。

「每周FPGA案例」按键控制数字时钟设计

➢ 下图是该工程在 ms980 试验箱上的现象

「每周FPGA案例」按键控制数字时钟设计

其中按键 s1 控制数字时钟的暂停与开始,按键 s2 来选择需要设置的位,按键 s3 设置数值。

由于该项目的上板现象是动态的,开始、暂停、时间设置等

现象无法通过图片表现出来,想观看

完整现象的朋友可以看一下现象演示的视频。

设计视频教程、工程源文件请到明德扬论坛观看或下载!

感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行 FPGA 相关工程设计学习。

往期推荐:

《基于 FPGA 的密码锁设计》

《波形相位频率可调 DDS 信号发生器》

《基于 FPGA 的曼彻斯特编码解码设计》

《基于 FPGA 的出租车计费系统》

《数电基础与 Verilog 设计》

《基于 FPGA 的频率、电压测量》

《基于 FPGA 的汉明码编码解码设计》

《关于锁存器问题的讨论》

《阻塞赋值与非阻塞赋值》

《参数例化时自动计算位宽的解决办法》


明德扬是一家专注于 FPGA 领域的专业性公司,公司主要业务包括开发板、教育培训、项目承

接、人才服务等多个方向。

点拨开发板——学习 FPGA 的入门之选。

MP801 开发板——千兆网、ADDA、大容量 SDRAM 等,学习和项目需求一步到位。

网络培训班——不管时间和空间,明德扬随时在你身边,助你快速学习 FPGA。

周末培训班——明天的你会感激现在的努力进取,升职加薪明德扬来助你。

就业培训班——七大企业级项目实训,获得丰富的项目经验,高薪就业。

专题课程——高手修炼课:提升设计能力;实用调试技巧课:提升定位和解决问题能力;FIFO 架构

设计课:助你快速成为架构设计师;时序约束、数字信号处理、PCIE、综合项目实践课等你来选。

项目承接——承接企业 FPGA 研发项目。

人才服务——提供人才推荐、人才代培、人才派遣等服务。

   拓展阅读