基于FPGA的可调数字钟设计

这篇具有很好参考价值的文章主要介绍了基于FPGA的可调数字钟设计。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        在此特别感谢哔站up主甘第发布的FPGA企业实训课(基于FPGA的数字钟设计)教学视频,让一个FPGA小白开始了第一个FPGA设计开发流程。本设计参考了这个教学视频,在此基础上添加并修改了一些代码,完成了这个小小的不带任何功能的数字时钟。

        初次学习FPGA,初次学习发布博客,如有错误,请指正!!!   

一、设计功能

本设计主要实现可调的数字时钟。具体功能如下:

(1)首先实现的功能是:秒计时到59后,分钟加1;分钟计时到59后,小时加1;小时计时到23后,复位,秒从0开始计时。这样循环计时,完成时钟的计时功能。

(2)在(1)的基础上添加小时和分钟的校准/调整功能,实现切换式调节数字时钟。通过按键切换至小时并闪烁,此时可通过按键加减小时的数值;然后切换至分钟,调节分钟的数值,以达到实时的准确时间。

二、设计方案

        本设计包含按键消抖模块、边缘检测模块,数字钟逻辑控制模块和数码管显示模块。系统设计架构如图1所示。各个模块的具体功能如下:

        (1)按键消抖模块:通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。抖动时间的长短由按键的机械特性决定,一般为5ms~10ms,所以软件消抖的时间大于按键自身抖动时间即可。

fpga可调数字钟,课程设计,fpga开发

 

图1 系统架构

      (2)边缘检测模块:一般在项目设计的过程中,经常需要检测信号由高到低或由低到高的跳变。即:检测输入信号,如果输入信号从0~1,检测信号对应的上升沿;如果输入信号从1~0,检测信号对应的下降沿。

     (3)数码管显示模块:该模块主要是在开发板的数码管上显示小时、分钟、秒的数值。

     (4)数字钟逻辑控制模块:此模块内部比较复杂,包含七个子模块。数字钟逻辑控制模块的内部架构如图2所示。

fpga可调数字钟,课程设计,fpga开发

 

图2 数字钟逻辑控制模块的内部架构

三、设计操作

1、设计开发流程

      (1)首先新建一个文件夹,并为文件夹重新命名。本设计为数字钟,所以以digital_clock命名。然后打开digital_clock文件夹,并在该文件夹中新建4个子文件夹,分别为doc、prj、rtl、sim,其中doc用于存放本次设计的逻辑架构文档,prj为工程文件夹,sim用于保存仿真测试文件,rtl用于保存源代码。

        (2)启动Quartus ii软件,首新建工程(注意选择芯片型号为EP4CE6E22C8),然后新建文本编辑器,开始编写代码。本设计需要四个主模块以及各个主模块中的子模块,所以需要编写很多个.v文件,比如key_filter.v、 edge_check.v、 clk_logic_ctrl.v、seven_tube等.v文件。编写完成后将这些.v文件另存(Save as)到rtl文件夹中。

       ( 3)在代码编写完后,需要对其进行编译,以便检查是否存在语法错误。点击按钮或按组合键“Ctrl+K”对代码进行编译。程序代码主界面和编译界面如图3所示。

fpga可调数字钟,课程设计,fpga开发

 

图3 程序代码主界面和编译界面

        (4)编译正确后,通过Modelsim仿真软件对程序运行情况进行仿真。Modelsim仿真有两种途径,既可以直接在Quartus ii中启动Modelsim进行仿真,也可以在Modelsim仿真软件中独立仿真。本次设计我们直接在Quartus ii中启动Modelsim进行仿真。

正在上传…重新上传取消正在上传…重新上传取消         首先在仿真之前需要建立Testbench仿真文件,一般以“模块名_tb”命名,由于本次设计的模块较多,所以只需对顶层模块进行仿真即可。新建digital_clock_tb文件夹,编写仿真代码。编写完成后,按组合键“Ctrl+K”对代码进行编译,确认代码正确后,点击菜单栏Tools   →  Run Simulation Tool  → RTL Simulation,启动Modelsim进行仿真。启动成功后,点击进入Modelsim界面。先在Wave选项卡中安“Ctrl+A”  “Ctrl+G”组合键实行自动分组。然后点击restart按钮,并设置运行时间,再点击run all按钮,在Wave选项卡查看仿真效果即可。顶层模块的部分仿真波形如图4所示。

fpga可调数字钟,课程设计,fpga开发

 

图4顶层模块的部分仿真波形

       (5)下载验证

        仿真成功后,点击Assignments菜单中的Device,弹出芯片选择界面,选择相应的下载芯片,如图5所示。

fpga可调数字钟,课程设计,fpga开发 

 

图5 下载芯片选择

        然后点击Assignments菜单中的Pin Planner,进行引脚分配,如图6所示。配置完成后关闭此界面。引脚配置完成后,点击Tools菜单栏中的Programmer,弹出如图7所示的界面。点击Hardware Setup,选择USB-Blaster [USB-0],然后点击Start,开始下载运行。

fpga可调数字钟,课程设计,fpga开发

 

6 芯片引脚配置

fpga可调数字钟,课程设计,fpga开发

 

7 下载界面

       综上所述的步骤过程为本次设计的一般开发流程。下面将逐个分析每个模块的逻辑架构及每个模块之间的联系。

2、各个模块的逻辑设计

(1)按键消抖模块

        一般在项目设计的过程中,都会用到按键开关,而通常使用的按键开关为机械弹性开关。当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上被稳定地接通,在断开时也不会马上被断开,因此,在闭合及断开的瞬间均伴有一连串的抖动。

        按键抖动的时间长短由按键的机械特性决定,一般为5至10ms;按键稳定闭合时间的长短则由操作人员的按键动作决定,一般为零点几秒至数秒。按键抖动会引起一次按键被误读为多次的错误。为了确保智能单元针对按键的一次闭合仅做一次处理,必须执行按键消抖操作:在按键闭合稳定时读取按键的状态,并且在按键释放稳定后再做处理。按键的消抖操作可用硬件或软件两种方法实现。本次设计通过软件方法实现按键消抖操作。

        按键消抖模块的输入信号有系统时钟clk,系统复位rst_n和按键输入key_in,输出信号为尖峰脉冲信号flag(输出只占一个时钟周期),其时序分析如图8所示,设计流程如图9所示。

     fpga可调数字钟,课程设计,fpga开发

 

8 按键消抖时序分析

 fpga可调数字钟,课程设计,fpga开发

 

9 按键消抖设计流程

        根据设计流程分析,可以采用状态机实现这部分的代码描述。Modelsim 的测试结果如图10所示。

fpga可调数字钟,课程设计,fpga开发 

10  按键消抖的仿真波形

(2)边沿检测模块

        一般在项目设计的过程中,经常需要检测信号由高到低或由低到高的跳变。经典的边沿检测电路有1级D触发器对应的边沿检测和2级D触发器对应的边沿检测。通过该电路,可以在信号出现跳变时产生尖峰脉冲,从而驱动其他电路模块执行相关的动作。最常用的是两级寄存器,第二级寄存器锁存住某个时钟上升沿到来时的输入电平,第一级寄存器锁存住下一个时钟沿到来时的输入电平,如果这两个寄存器锁存住的电平信号不同,就说明检测到了边沿,具体是上升沿还是下降沿可以通过组合逻辑来实现。边沿检测电路的结构图如图11所示。

fpga可调数字钟,课程设计,fpga开发

 

11 边沿检测电路的结构图

        当检测到上升沿时, pos_edge信号输出一个时钟周期的高电平; 检测到下降沿时,neg_edge输出一个时钟周期的高电平。Modelsim 的测试结果如图12所示。

fpga可调数字钟,课程设计,fpga开发

 

12  边沿检测仿真波形

(3)数码管显示模块

        LED数码管(LED Segment Displays)是由8个发光二极管构成,并按照一定的图形及排列封装在一起的显示器件。 其中7个LED构成7笔字形,1个LED构成小数点(也被称为为八段数码管)。分为两大类:共阳极数码管和共阴极数码管。对于共阴极数码管来说,其8个发光二极管的阴极在数码管内部全部连接在一起,所以称为“共阴”,而阳极独立。对于共阳极数码管来说,其8个发光二极管的阳极在数码管内部全部连接在一起,所以称为“共阳”,而阴极独立。

        本次设计所用开发板上的数码管为共阳极数码管。系统输入的时钟为50MHz(周期为20ns),而人眼可分辨的时间需要几十ms以上,即数码管之间切换的频率需要1KHz,所以要进行分频,得到设计所需要的经验频率。数码管显示模块的设计架构如图13所示。Modelsim测试结果如图14所示。

fpga可调数字钟,课程设计,fpga开发

13 数码管显示模块的设计架构

 fpga可调数字钟,课程设计,fpga开发

 

                                                     图14 数码管显示的仿真波形

 (4) 数字钟逻辑控制模块

        如前面所述,数字钟逻辑控制模块比较复杂,包括逻辑控制模块、秒控制模块、分钟控制模块、小时控制模块、二进制转BCD模块、小时闪烁模块、分钟闪烁模块。其中二进制转BCD模块是一般设计中都要用到的基础模块,下面我们采用“大四加三”算法来实现二进制转BCD。

        以8位输入二进制(8’1010_1001)为例,那么BCD码部分则需要12位,分别为BCD高位(4位二进制),BCD中间位(4位二进制),BCD低位(4位二进制)。首先准备一个20位全新序列(高12位为BCD部分(初始全为0),低8位为输入的二进制数)20’b0000_0000_0000_1010_1001。然后完成以下两个步骤:

                a.BCD部分进行“大四加三”判断;

                b.全新序列整体左移一次;

        然后将a,b步骤重复8次,最后取第8次完成的数据高12位即可得到BCD码。具体过程如下表所示:

操作步骤

a步骤:大四加三判断

b步骤:全新序列整体左移一次

BCD高位

BCD中间位

BCD低位

输入的二进制

部分

说明

0000

0000

0000

1010_1001

准备20位全新序列

1a:BCD部分进行 大四加三 判断

0000

0000

0000

1010_1001

大四加三 判断

1b:整体左移

0000

0000

0001

0101_0010

左移一次

2a:BCD部分进行 大四加三 判断

0000

0000

0001

0101_0010

大四加三 判断

2b:整体左移

0000

0000

0010

1010_0100

左移一次

3a:BCD部分进行 大四加三 判断

0000

0000

0010

1010_0100

大四加三 判断

3b:整体左移

0000

0000

0101

0100_1000

左移一次

4a:BCD部分进行 大四加三 判断

0000

0000

1000

0100_1000

大四加三 判断

4b:整体左移

0000

0001

0000

1001_0000

左移一次

5a:BCD部分进行 大四加三 判断

0000

0001

0000

1001_0000

大四加三 判断

5b:整体左移

0000

0010

0001

0010_0000

左移一次

6a:BCD部分进行 大四加三 判断

0000

0010

0001

0010_0000

大四加三 判断

6b:整体左移

0000

0100

0010

0100_0000

左移一次

7a:BCD部分进行 大四加三 判断

0000

0100

0010

0100_0000

大四加三 判断

7b:整体左移

0000

1000

0100

1000_0000

左移一次

8a:BCD部分进行 大四加三 判断

0000

1011

0100

1000_0000

大四加三 判断

8b:整体左移

0001(1)

0110(6)

1001(9)

0000_0000

左移一次

        从表中可以清楚的看出二进制转BCD的设计思想,然后编写源代码和测试代码,进行仿真,得仿真结果如图15所示,注意:仿真时将bin设置为十进制(unsigned),bcd设置为十六进制。

 fpga可调数字钟,课程设计,fpga开发

 

15 二进制转BCD仿真结果

        逻辑控制模块( logic_ctrl )采用状态机(FSM)实现。设置3个状态,分别显示正常状态(s0),调节小时状态(s1),调节分钟状态(s2)。小时控制模块,分钟控制模块,秒控制模块满足数字钟逻辑即可。

        根据数字钟的逻辑编写逻辑控制模块的源代码,进行编译。最后完成数字钟逻辑控制顶层模块的连线。

3、顶层模块的搭建

       顶层模块的作用是连接每个模块,实现信号传输。

五、硬件设计效果及说明

        设计成果展示如图16所示。

 fpga可调数字钟,课程设计,fpga开发

 

16 成果展示

       本次设计只是一个简单的数字时钟,没有额外加其他功能,比如闹钟。因为是初次学习FPGA,所以在学习设计这个数字钟的过程中,也遇到了很多问题,同时也学到了很多新的知识。

六、程序附录

//顶层模块


//`define RUN_SIM

module digital_clock(
	input			clk,
	input			rst_n,
	input			key_adjust,      //按键切换
	input			key_add,         //按键加
	input			key_sub,         //按键减
	
	
	output	[5:0]		sel,
	output	[7:0]		seg

);

	wire			key_adjust_out;
	wire			key_add_out;
	wire			key_sub_out;
	
	wire			flag_adjust;
	wire			flag_add;
	wire			flag_sub;

	wire	[23:0]	display_data;
	
	
	//按键消抖:切换按键进行消抖
	key_filter	 key_filter_adjust(
		.clk(clk),
		.rst_n(rst_n),
		.key_in(key_adjust),  
	 
		.key_out(key_adjust_out)  
	);
	
	
	//按键消抖:加按键进行消抖
	key_filter	 key_filter_add(
		.clk(clk),
		.rst_n(rst_n),
		.key_in(key_add),  
	
		.key_out(key_add_out)  
	);
	
	
	//按键消抖:减按键进行消抖
	key_filter	 key_filter_sub(
		.clk(clk),
		.rst_n(rst_n),
		.key_in(key_sub),  
	
		.key_out(key_sub_out)  
	);
	
	
	
	//边沿检测:切换按键消抖后的边沿
	edge_check	edge_check_adjust(
		.clk(clk),
		.rst_n(rst_n),
		.signal(key_adjust_out),    

		.pos_edge(flag_adjust),   
		.neg_edge()    

);

	
	//边沿检测:加按键消抖后的边沿
	edge_check	edge_check_add(
		.clk(clk),
		.rst_n(rst_n),
		.signal(key_add_out),    

		.pos_edge(flag_add),   
		.neg_edge()    

);


	
	//边沿检测:减按键消抖后的边沿
	edge_check	edge_check_sub(
		.clk(clk),
		.rst_n(rst_n),
		.signal(key_sub_out),    

		.pos_edge(flag_sub),   
		.neg_edge()    

);

	//数字钟的逻辑控制模块
	clk_logic_ctrl	clk_logic_ctrl_dut	(
		 .clk(clk),
		 .rst_n(rst_n),
		 .flag_adjust(flag_adjust),      
		 .flag_add(flag_add),         
		 .flag_sub(flag_sub),         
	
 		 .display_data(display_data)    
);


	//数码管显示模块
	seven_tube	seven_tube_dut(
		.clk(clk),
		.rst_n(rst_n),
		.data_in(display_data),
	
		.seg(seg),        
		.sel(sel)          
 
);



endmodule

//按键消抖

//按键消抖
1.	module key_filter(
2.	 input   clk,
3.	 input   rst_n,
4.	 input   key_in,  //开发板的独立按键输入
5.	 
6.	 output  reg  key_out    //输出按键消抖后的动作
7.	);
8.	
9.	 reg  [31:0]  cnt;     //延时10ms的计数器
10.	 reg    state;   //状态寄存器
11.	 
12.	 parameter  S0 = 1'b0;  //按键按下状态
13.	 parameter  S1 = 1'b1;  //按键抬起状态
14.	 
15.	 parameter  T = 10_000_000/20 - 1;    //10ms所用计数次数499_999
16.	 
17.	 always @(posedge clk or negedge rst_n) begin
18.	  if(!rst_n) begin
19.	   state <= S0;
20.	   key_out  <= 1'b1;
21.	   cnt <= 32'd0;
22.	  end
23.	  else begin
24.	   case(state)
25.	    S0 : if(key_in == 1'b0)      //按键按下
26.	       if(cnt < T) begin
27.	        cnt <= cnt +1'd1;
28.	       end
29.	       else begin
30.	        cnt <= 32'd0;
31.	        key_out  <= 1'b0;
32.	        state <= S1;
33.	       end
34.	      else
35.	       state <= S0;
36.	    S1 : begin
37.	       if(key_in == 1'b1)      //按键抬起
38.	        if(cnt < T) begin
39.	         cnt <= cnt +1'd1;
40.	         key_out  <= 1'b0;
41.	        end
42.	        else begin
43.	         cnt <= 32'd0;
44.	         key_out  <= 1'b1;
45.	         state <= S0;
46.	        end
47.	       else
48.	        state <= S1;
49.	      end
50.	    default : state <= S0;     //安全行为
51.	   endcase     
52.	  end
53.	 end 
54.	endmodule

 //边沿检测

//边沿检测
1.	module edge_check(
2.	 input  clk,
3.	 input  rst_n,
4.	 input  signal,    //待检测信号
5.	 
6.	 output  pos_edge,   //检测上升沿
7.	 output  neg_edge    //检测下降沿
8.	
9.	);
10.	 reg  q1;      //寄存一级D触发器输出
11.	 reg  q2;      //寄存二级级D触发器输出
12.	 
13.	 //二级D触发器的描述
14.	 always @(posedge clk or negedge rst_n) begin
15.	  if(!rst_n) begin
16.	   q1 <= signal;
17.	   q2 <= signal;
18.	  end
19.	  else begin
20.	   q1 <= signal;
21.	   q2 <= q1;
22.	  end
23.	 end
24.	 assign pos_edge = q1 && (~q2);   //上升沿
25.	 assign neg_edge = (~q1) && q2;    //下降沿
26.	endmodule 

//二进制转BCD 

//二进制转BCD

1.	module bin_bcd(
2.	 input [7:0]  bin,     //输入二进制数
3.	  
4.	 output [11:0] bcd      //输出BCD码
5.	);
6.	
7.	 wire  [19:0] bcd_reg0,bcd_reg1,bcd_reg2,bcd_reg3,bcd_reg4;
8.	 wire  [19:0] bcd_reg5,bcd_reg6,bcd_reg7,bcd_reg8;
9.	 
10.	 assign  bcd_reg0 = {12'b0 , bin};     //20位全新序列
11.	
12.	 //移位第1次
13.	 bcd_modify bcd_modify_m1(.bcd_in(bcd_reg0),.bcd_out(bcd_reg1));
14.	 
15.	 //移位第2次
16.	 bcd_modify bcd_modify_m2(.bcd_in(bcd_reg1),.bcd_out(bcd_reg2));
17.	 
18.	 //移位第3次
19.	 bcd_modify bcd_modify_m3(.bcd_in(bcd_reg2),.bcd_out(bcd_reg3));
20.	 
21.	 //移位第4次
22.	 bcd_modify bcd_modify_m4(.bcd_in(bcd_reg3),.bcd_out(bcd_reg4));
23.	 
24.	 //移位第5次
25.	 bcd_modify bcd_modify_m5(.bcd_in(bcd_reg4),.bcd_out(bcd_reg5));
26.	 
27.	 //移位第6次
28.	 bcd_modify bcd_modify_m6(.bcd_in(bcd_reg5),.bcd_out(bcd_reg6));
29.	 
30.	 //移位第7次
31.	 bcd_modify bcd_modify_m7(.bcd_in(bcd_reg6),.bcd_out(bcd_reg7));
32.	 
33.	 //移位第8次
34.	 bcd_modify bcd_modify_m8(.bcd_in(bcd_reg7),.bcd_out(bcd_reg8));
35.	 assign bcd = {bcd_reg8[19:8]};   //取高12位作为输出结果
36.	Endmodule
37.	module bcd_modify(
38.	 input [19:0]  bcd_in,     //移位前的数据
39.	  
40.	 output [19:0] bcd_out     //移位后的数据 
41.	);
42.	 wire [3:0]   data_bcd1;
43.	 wire [3:0]   data_bcd2;
44.	 wire [3:0]   data_bcd3;
45.	
46.	 //比较BCD高位
47.	 cmp  cmp_high(.data_in(bcd_in[19:16]),  .data_out(data_bcd1));
48.	
49.	 //比较BCD中间位
50.	 cmp  cmp_mid(.data_in(bcd_in[15:12]),  .data_out(data_bcd2));
51.	 
52.	 //比较BCD低位
53.	 cmp  cmp_low(.data_in(bcd_in[11:8]),  .data_out(data_bcd3));
54.	
55.	 assign bcd_out = {data_bcd1[2:0], data_bcd2[3:0], data_bcd3[3:0],bcd_in[7:0], 1'b0};  //整体左移一次
56.	
57.	endmodule
58.	
59.	module cmp(
60.	 input [3:0]  data_in,    //BCD位待比较的数据
61.	  
62.	 output  [3:0] data_out     //比较后大四加三的数据
63.	);
64.	
65.	 assign data_out = (data_in > 4'd4) ?(data_in + 4'd3) : data_in ;
66.	endmodule

//数码管驱动模块 

        注意我用到的开发板型号是ep4ce6e22c8n,数码管的位选是低电平有效,小伙伴们要注意自己开发板的原理哦!!!

//数码管驱动模块
1.	module seven_tube(
2.	 input     clk,
3.	 input     rst_n,
4.	 input [23:0]  data_in,
5.	 
6.	 output  [7:0]    seg ,        //数码管段选信号线
7.	 output  [5:0]  sel          //数码管位选信号线
8.	 );
9.	
10.	 wire     clk_1k;   //定义 中间连线信号
11.	
12.	 //分频模块50MHz--->1kHz
13.	 freq freq_dut(
14.	     .clk(clk),
15.	     .rst_n(rst_n),
16.	  
17.	     .clk_1k(clk_1k)
18.	 );
19.	 
20.	 //数码管驱动模块
21.	 seg_ctrl seg_ctrl_dut(
22.	     .clk_1k(clk_1k),
23.	     .rst_n(rst_n),
24.	     .data_in(data_in),
25.	  
26.	     .seg(seg),
27.	     .sel(sel)
28.	 );
29.	endmodule

30.	 module freq(
31.	 		input   clk,
32.	 		input   rst_n,
33.	 
34.	 		output reg  clk_1k
35.	
36.	 );
37.	 		reg  [31:0] count;
38.	 
39.	 		parameter   cnt_num = 50_000/2 - 1;    //1kHz 是 1ms ,所以只需计数一半,然后取反即可   0.5ms
40.	 
41.	 always @(posedge clk or negedge rst_n) begin
42.	  if(!rst_n) begin
43.	   clk_1k <= 1'b0;
44.	   count <= 32'd0;
45.	  end
46.	  else if(count < cnt_num)
47.	   count <= count + 1'd1;
48.	  else begin
49.	   count <= 32'd0;
50.	   clk_1k <= ~clk_1k;    //周期1ms(1KHz)
51.	  end
52.	 end
53.	
54.	endmodule

55.	module seg_ctrl(
56.	 input    clk_1k,
57.	 input    rst_n,
58.	 input [23:0]  data_in,
59.	 
60.	 output reg [7:0] seg ,
61.	 output reg [5:0] sel
62.	
63.	 );
64.	
65.	 //******数码管切换:1KHz******//
66.	 //parameter  [5:0] IDLE = 6'b000000;
67.	 parameter  [5:0] S0 = 6'b011111;       //位选端低电平有效
68.	 parameter  [5:0] S1 = 6'b101111;
69.	 parameter  [5:0] S2 = 6'b110111;
70.	 parameter  [5:0] S3 = 6'b111011;
71.	 parameter  [5:0] S4 = 6'b111101;
72.	 parameter  [5:0] S5 = 6'b111110;
73.	 
74.	 reg [5:0]  current_state,next_state;
75.	
76.	 //****** 每一位数码管对应的四位数据******//
77.	 reg   [3:0] data_temp;    //寄存输入24位数据的某四位
78.	 
79.	 always@(posedge clk_1k or negedge rst_n) begin
80.	  if(!rst_n) 
81.	   current_state <= S0;
82.	  else
83.	   current_state <= next_state;
84.	 end
85.	
86.	 always@(posedge clk_1k or negedge rst_n) begin
87.	  if(!rst_n)
88.	   next_state <= S0;
89.	  else
90.	   case(current_state)
91.	    S0: next_state <= S1;
92.	    S1: next_state <= S2;
93.	    S2: next_state <= S3;
94.	    S3: next_state <= S4;
95.	    S4: next_state <= S5;
96.	    S5: next_state <= S0;
97.	    default:next_state <= S0;
98.	   endcase
99.	 end
100.	  
101.	 always@(posedge clk_1k or negedge rst_n) begin
102.	  if(!rst_n) begin
103.	   sel   <= 6'b111111;
104.	   data_temp <= 4'b0;
105.	  end
106.	  else begin
107.	   case(current_state)
108.	    S0: begin sel <= S0; data_temp <= data_in[23:20]; end
109.	    S1: begin sel <= S1; data_temp <= data_in[19:16]; end
110.	    S2: begin sel <= S2; data_temp <= data_in[15:12]; end
111.	    S3: begin sel <= S3; data_temp <= data_in[11:8]; end
112.	    S4: begin sel <= S4; data_temp <= data_in[7:4]; end
113.	    S5: begin sel <= S5; data_temp <= data_in[3:0]; end
114.	    default : begin sel <= S0;data_temp <= data_in[23:20]; end
115.	   endcase
116.	  end 
117.	 end
118.	 
119.	 //****** 数码管译码******//
120.	 always @(*)  begin
121.	  if(!rst_n)
122.	   seg = 8'h0;
123.	  else 
124.	   case(data_temp)
125.	    4'h0 : seg = 8'b1100_0000;
126.	    4'h1 : seg = 8'b1111_1001;
127.	    4'h2 : seg = 8'b1010_0100;
128.	    4'h3 : seg = 8'b1011_0000;
129.	    
130.	    4'h4 : seg = 8'b1001_1001;
131.	    4'h5 : seg = 8'b1001_0010;
132.	    4'h6 : seg = 8'b1000_0010;
133.	    4'h7 : seg = 8'b1111_1000;
134.	    
135.	    4'h8 : seg = 8'b1000_0000;
136.	    4'h9 : seg = 8'b1001_0000;
137.	    4'hA : seg = 8'b1000_1000;   //A
138.	    4'hb : seg = 8'b1000_0011;   //B
139.	    
140.	    4'hC : seg = 8'b1100_0110;   //C
141.	    4'hd : seg = 8'b1010_0001;   //D
142.	    4'hE : seg = 8'b1000_0110;   //E
143.	    4'hF : seg = 8'b1000_1110;   //F
144.	    default : seg = 8'b1100_0000;
145.	   endcase
146.	 end
147.	endmodule

//数字钟逻辑控制模块(顶层模块)

//数字钟逻辑控制模块
1.	module clk_logic_ctrl(
2.	 input   clk,
3.	 input   rst_n,
4.	 input   flag_adjust,      //按键切换的标志信号
5.	 input   flag_add,         //按键加的标志信号
6.	 input   flag_sub,         //按键减的标志信号
7.	 
8.	 output [23:0]  display_data      //输出显示的数据
9.	 );
10.	 /******* logic_ctrl ******/
11.	 wire   min_en;
12.	 wire   hour_en;
13.	 wire   flag_hour_add;
14.	 wire   flag_hour_sub;
15.	 wire   flag_min_add;
16.	 wire   flag_min_sub;
17.	
18.	 /******* sec_ctrl ******/
19.	 wire   flag_min;
20.	 wire  [5:0]  sec;
21.	 
22.	 /******* min_ctrl ******/
23.	 wire   flag_hour;
24.	 wire  [5:0]  min;
25.	 
26.	 /******* hour_ctrl ******/
27.	 wire  [5:0]  hour;
28.	 
29.	 /******* bin_bcd ******/
30.	 wire  [11:0]  bcd_s;
31.	 wire  [11:0]  bcd_m;
32.	 wire  [11:0]  bcd_h;
33.	 
34.	 /******* min_adjust/hour_adjust ******/
35.	 wire  [7:0]  data_h;
36.	 wire  [7:0]  data_m;
37.	 
38.	 //逻辑控制模块
39.	 logic_ctrl  logic_ctrl_dut(
40.	     .clk(clk),
41.	     .rst_n(rst_n),
42.	     .flag_adjust(flag_adjust),     
43.	     .flag_add(flag_add),        
44.	     .flag_sub(flag_sub),        
45.	 
46.	     .flag_hour_add(flag_hour_add),   
47.	     .flag_hour_sub(flag_hour_sub),   
48.	     .flag_min_add(flag_min_add),    
49.	     .flag_min_sub(flag_min_sub),    
50.	     .hour_en(hour_en),         
51.	     .min_en(min_en)          
52.	 );
53.	
54.	 //秒的控制模块:#(.T1s(4))  //用于仿真
55.	 sec_ctrl  sec_ctrl_dut(
56.	     .clk(clk),
57.	     .rst_n(rst_n),
58.	 
59.	     .flag_min(flag_min),
60.	     .sec(sec)
61.	 );
62.	 
63.	 //分钟的控制模块
64.	 min_ctrl min_ctrl_dut(
65.	     .clk(clk),
66.	     .rst_n(rst_n),
67.	     .flag_min(flag_min),      
68.	     .flag_min_add(flag_min_add),  
69.	     .flag_min_sub(flag_min_sub),  
70.	 
71.	     .flag_hour(flag_hour),     
72.	     .min(min)          
73.	 );
74.	 
75.	 //小时的控制模块
76.	 hour_ctrl hour_ctrl_dut(
77.	     .clk(clk),
78.	     .rst_n(rst_n),
79.	     .flag_hour(flag_hour),      
80.	     .flag_hour_add(flag_hour_add),  
81.	     .flag_hour_sub(flag_hour_sub),  
82.	
83.	     .hour(hour)          
84.	 );
85.	 
86.	 //秒转码
87.	 bin_bcd bin_bcd_sec(
88.	   .bin({2'b0,sec}),     
89.	 
90.	   .bcd(bcd_s)      
91.	 );
92.	 
93.	 //分钟转码
94.	 bin_bcd bin_bcd_min(
95.	   .bin({2'b0,min}),     
96.	 
97.	   .bcd(bcd_m)      
98.	 );
99.	 
100.	 //小时转码
101.	 bin_bcd bin_bcd_hour(
102.	   .bin({2'b0,hour}),     
103.	 
104.	   .bcd(bcd_h)     
105.	 );
106.	 
107.	 //小时闪烁模块
108.	 hour_adjust hour_adjust_dut (
109.	   .clk(clk),
110.	   .rst_n(rst_n),
111.	   .hour_en(hour_en),      
112.	   .bcd_h(bcd_h[7:0]),        
113.	 
114.	   .data_h(data_h)        
115.	 );
116.	 
117.	 //分钟闪烁模块
118.	 min_adjust min_adjust_dut(
119.	  .clk(clk),
120.	  .rst_n(rst_n),
121.	  .min_en(min_en),       
122.	  .bcd_m(bcd_m[7:0]),        
123.	  .data_m(data_m)       
124.	 );
125.	 assign display_data = {data_h, data_m, bcd_s[7:0]};
126.	
127.	endmodule

//数字钟逻辑控制模块的各个底层模块文章来源地址https://www.toymoban.com/news/detail-739055.html

//逻辑控制模块
1.	module logic_ctrl(
2.	 input    clk,
3.	 input    rst_n,
4.	 input    flag_adjust,     //切换按键标志
5.	 input    flag_add,        //加按键标志
6.	 input    flag_sub,        //减按键标志
7.	 
8.	 output reg   flag_hour_add,    //小时加标志
9.	 output reg   flag_hour_sub,    //小时减标志
10.	 output reg   flag_min_add,     //分钟加标志
11.	 output reg   flag_min_sub,     //分钟减标志
12.	 output reg   hour_en,          //小时的闪烁使能
13.	 output reg   min_en            //分钟的闪烁使能
14.	);
15.	
16.	 reg  [1:0]  state;            //状态变量
17.	 
18.	 parameter   s0 = 2'b00;       //正常显示状态:切换到小时状态
19.	 parameter   s1 = 2'b01;       //小时状态(可以对小时进行调节):切换到分钟状态
20.	 parameter   s2 = 2'b10;       //分钟状态(可以对分钟进行调节):切换完成
21.	 
22.	 always@(posedge clk or negedge rst_n) begin
23.	  if(!rst_n)
24.	   state <= s0;
25.	  else 
26.	   case(state)                                //状态机结构
27.	    s0  :  if(flag_adjust == 1'b1)       //第一次按下切换按键,切换到小时
28.	       state <= s1;
29.	       else
30.	       state <= s0;
31.	    s1  :  if(flag_adjust == 1'b1)       //第二次按下切换按键,切换到分钟
32.	       state <= s2;
33.	       else
34.	       state <= s1;
35.	    s2  :  if(flag_adjust == 1'b1)       //第一次按下切换按键,切换完成
36.	       state <= s0;
37.	       else
38.	       state <= s2;
39.	    default :  state <= s0;        //安全行为
40.	   endcase
41.	  
42.	 end
43.	 
44.	 
45.	 //小时加控制
46.	 always@(posedge clk or negedge rst_n) begin
47.	  if(!rst_n)
48.	   flag_hour_add <= 1'b0;
49.	  else if(state == s1 && flag_add == 1'b1)   //切换到小时状态并且按下加按键
50.	   flag_hour_add <= 1'b1;
51.	  else
52.	   flag_hour_add <= 1'b0;
53.	 end
54.	 
55.	 //小时减控制
56.	 always@(posedge clk or negedge rst_n) begin
57.	  if(!rst_n)
58.	   flag_hour_sub <= 1'b0;
59.	  else if(state == s1 && flag_sub == 1'b1)   //切换到小时状态并且按下减按键
60.	   flag_hour_sub <= 1'b1;
61.	  else
62.	   flag_hour_sub <= 1'b0;
63.	 end
64.	 
65.	 //分钟加控制
66.	 always@(posedge clk or negedge rst_n) begin
67.	  if(!rst_n)
68.	   flag_min_add <= 1'b0;
69.	  else if(state == s2 && flag_add == 1'b1)   //切换到分钟状态并且按下加按键
70.	   flag_min_add <= 1'b1;
71.	  else
72.	   flag_min_add <= 1'b0;
73.	 end
74.	 
75.	 //分钟减控制
76.	 always@(posedge clk or negedge rst_n) begin
77.	  if(!rst_n)
78.	   flag_min_sub <= 1'b0;
79.	  else if(state == s2 && flag_sub == 1'b1)   //切换到分钟状态并且按下减按键
80.	   flag_min_sub <= 1'b1;
81.	  else
82.	   flag_min_sub <= 1'b0;
83.	 end
84.	 
85.	 
86.	 //小时的闪烁
87.	 always@(posedge clk or negedge rst_n) begin
88.	  if(!rst_n)
89.	   hour_en <= 1'b0;
90.	  else if(state == s1 )   //切换到小时状态
91.	   hour_en <= 1'b1;    //小时闪烁
92.	  else
93.	   hour_en <= 1'b0;
94.	 end
95.	 
96.	 
97.	 //分钟的闪烁
98.	 always@(posedge clk or negedge rst_n) begin
99.	  if(!rst_n)
100.	   min_en <= 1'b0;
101.	  else if(state == s2 )   //切换到分钟状态
102.	   min_en <= 1'b1;    //分钟闪烁
103.	  else
104.	   min_en <= 1'b0;
105.	 end
106.	 
107.	 
108.	endmodule
1.	//秒的控制器
2.	
3.	module sec_ctrl(
4.	 input    clk,
5.	 input    rst_n,
6.	 output   flag_min,
7.	 Output   reg  [5:0] sec
8.	);
9.	 //产生1秒周期
10.	 reg   [31:0] cnt;     //计数器计数1s
11.	 
12.	 parameter   T1s = 50_000_000 - 1;      //1s
13.	 
14.	 always @(posedge clk or negedge rst_n) begin
15.	  if(!rst_n)
16.	   cnt <= 32'd0;
17.	  else if(cnt < T1s)
18.	   cnt <= cnt + 1'd1;
19.	  else
20.	   cnt <= 32'd0;
21.	 
22.	 end
23.	 
24.	 wire    flag_1s;   //1s标志信号
25.	 
26.	 assign flag_1s = (cnt == T1s) ? 1'b1 : 1'b0;   //1s标志
27.	 
28.	 
29.	 //完成秒控制
30.	 always @(posedge clk or negedge rst_n) begin
31.	  if(!rst_n)
32.	   sec <= 6'd0;
33.	  else if(flag_1s == 1'b1 && sec < 6'd59)
34.	   sec <= sec + 1'd1;
35.	  else if(flag_1s)
36.	   sec <= 6'd0;
37.	  else
38.	   sec <= sec;
39.	 
40.	 end
41.	 
42.	 //产生分钟标志
43.	 assign  flag_min = (sec == 6'd59 && flag_1s == 1'b1) ? 1'b1 : 1'b0;   //1min
44.	endmodule 
1.	//分钟的控制器
2.	
3.	module  min_ctrl(
4.	 input    clk,
5.	 input    rst_n,
6.	 input    flag_min,      //分钟标志,是由秒产生的
7.	 input    flag_min_add,  //分钟加标志
8.	 input    flag_min_sub,  //分钟减标志
9.	 
10.	 output    flag_hour,     //小时标志
11.	 output reg [5:0] min            //分钟输出
12.	);
13.	
14.	 //控制分钟加减
15.	 always@(posedge clk or negedge rst_n) begin
16.	  if(!rst_n)
17.	   min <= 6'd0;
18.	  else if(flag_min == 1'b1 || flag_min_add == 1'b1)    //分钟都可以加
19.	   begin
20.	    if(min < 6'd59)                              //分钟小于59
21.	     min <= min + 1'b1;                       //分钟加1
22.	    else                                         //分钟大于59
23.	     min <= 6'd0;               //分钟清零
24.	   end
25.	  else if(flag_min_sub == 1'b1 && min > 6'd0)          //减按键按下且分钟大于0
26.	   min <= min - 1'd1;                               //分钟减1
27.	  else if(flag_min_sub == 1'b1 && min == 6'd0)         //分钟减到0
28.	   min <= 6'd59;                                    //分钟回到最大值
29.	  else
30.	   min <= min;                                      //分钟保持当前值
31.	 
32.	 end
33.	 
34.	 //产生小时的标志
35.	 assign flag_hour = (min <= 6'd59 && flag_min == 1'b1) ? 1'b1 : 1'b0;     //1h
36.	
37.	endmodule 
1.	//小时的控制器
2.	
3.	module  hour_ctrl(
4.	 input    clk,
5.	 input    rst_n,
6.	 input    flag_hour,      //小时标志,是由分钟产生的
7.	 input    flag_hour_add,  //小时加标志
8.	 input    flag_hour_sub,  //小时减标志
9.	 
10.	 output reg [5:0] hour            //小时输出
11.	
12.	);
13.	
14.	 //控制小时加减
15.	 always@(posedge clk or negedge rst_n) begin
16.	  if(!rst_n)
17.	   hour <= 6'd0;
18.	  else if(flag_hour == 1'b1 || flag_hour_add == 1'b1)    //小时都可以加
19.	   begin
20.	    if(hour < 6'd23)                              //小时小于59
21.	     hour <= hour + 1'b1;                       //小时加1
22.	    else                                         //小时大于59
23.	     hour <= 6'd0;               //小时清零
24.	   end
25.	  else if(flag_hour_sub == 1'b1 && hour > 6'd0)          //减按键按下且小时大于0
26.	   hour <= hour - 1'd1;                               //小时减1
27.	  else if(flag_hour_sub == 1'b1 && hour == 6'd0)         //小时减到0
28.	   hour <= 6'd23;                                    //小时回到最大值
29.	  else
30.	   hour <= hour;                                      //小时保持当前值
31.	 end
32.	endmodule 
1.	//小时的闪烁
2.	module hour_adjust(
3.	 input     clk,
4.	 input    rst_n,
5.	 input    hour_en,      //小时闪烁使能
6.	 input [7:0]  bcd_h,        //转码后的数据
7.	 
8.	 output reg [7:0]  data_h        //输出数据 
9.	
10.	);
11.	 
12.	 //产生0.5秒高,0.5秒低
13.	 reg   [31:0] cnt;     //计数器计数1s
14.	 reg    flag_1s_half;   //0.5秒闪烁使能信号
15.	 
16.	 parameter   T1s_half = 50_000_000 / 2 - 1;      //0.5s
17.	 
18.	 always @(posedge clk or negedge rst_n) begin
19.	  if(!rst_n) begin
20.	   cnt <= 32'd0;
21.	   flag_1s_half <= 1'b0;
22.	  end
23.	  else if(cnt < T1s_half)
24.	   cnt <= cnt + 1'd1;
25.	  else begin
26.	   cnt <= 32'd0;
27.	   flag_1s_half <= ~flag_1s_half;     //0.5秒高,0.5秒低  
28.	  end
29.	 
30.	 end
31.	 
32.	 //0.5秒数码管亮,0.5秒数码管灭
33.	 always @(posedge clk or negedge rst_n) begin
34.	  if(!rst_n) 
35.	   data_h <= 8'h0;
36.	  else if(hour_en == 1'b1)            //切换到小时
37.	   begin
38.	    if(flag_1s_half == 1'b1)
39.	     data_h <= bcd_h;        //0.5s
40.	    else
41.	     data_h <= 8'hff;        //数码管译码中f为8'b1111_1111  
42.	   end
43.	  else
44.	   data_h <= bcd_h;               //正常显示
45.	 end 
46.	endmodule 
//分钟的闪烁

1.	module min_adjust(  
2.	 input     clk,
3.	 input    rst_n,
4.	 input    min_en,       //分钟闪烁使能
5.	 input [7:0]  bcd_m,        //转码后的数据
6.	 
7.	 output reg [7:0]  data_m        //输出数据 
8.	
9.	);
10.	 
11.	 //产生0.5秒高,0.5秒低
12.	 reg   [31:0] cnt;     //计数器计数1s
13.	 reg    flag_1s_half;   //0.5秒闪烁使能信号
14.	 
15.	 parameter   T1s_half = 50_000_000 / 2 - 1;      //0.5s
16.	 
17.	 always @(posedge clk or negedge rst_n) begin
18.	  if(!rst_n) begin
19.	   cnt <= 32'd0;
20.	   flag_1s_half <= 1'b0;
21.	  end
22.	  else if(cnt < T1s_half)
23.	   cnt <= cnt + 1'd1;
24.	  else begin
25.	   cnt <= 32'd0;
26.	   flag_1s_half <= ~flag_1s_half;     //0.5秒高,0.5秒低  
27.	  end
28.	 
29.	 end
30.	 
31.	 //0.5秒数码管亮,0.5秒数码管灭
32.	 always @(posedge clk or negedge rst_n) begin
33.	  if(!rst_n) 
34.	   data_m <= 8'h0;
35.	  else if(min_en == 1'b1)            //切换到扥中
36.	   begin
37.	    if(flag_1s_half == 1'b1)
38.	     data_m <= bcd_m;        //0.5s
39.	    else
40.	     data_m <= 8'hff;        //数码管译码中f为8'b1111_1111  
41.	   end
42.	  else
43.	   data_m <= bcd_m;                //正常显示
44.	 end 
45.	
46.	endmodule 

到了这里,关于基于FPGA的可调数字钟设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • EDA实验(Quartus Ⅱ+fpga) (五)---多功能数字钟设计

    本实验代码为初学FPGA所写,逻辑不太清晰,请跳往下面网址查看最新的模块化设计数字钟,更易看懂 模块化多功能数字钟设计 前言: 本文主要介绍了EDA原理与应用这门课程的相关实验及代码。使用的软件是Quartus Ⅱ,该实验使用fpga芯片为cycloneⅤ 5CSEMA5F31C6。 (1)了解数字钟的

    2024年02月06日
    浏览(83)
  • 年轻人的第一个数字钟!适用于FPGA的数字钟Verilog实现

    因为闲。 当然也不是很闲,初衷是因为本科时上过的数电实验课最后的大作业就是在 FPGA 上实现一个数字钟,这个作业当时困扰了我们班的诸多同学(难以置信,我们只是学材料的弱小可怜又无助{{{(_)}}}。最终,大部分同学在 拷贝一位学长的代码 一位学长的帮助下顺利通过

    2024年02月11日
    浏览(43)
  • FPGA的数字钟带校时闹钟报时功能VHDL

    名称:基于FPGA的数字钟具有校时闹钟报时功能 软件:Quartus 语言:VHDL 要求:   1、计时功能:这是数字钟设计的基本功能,每秒钟更新一次,并且能在显示屏上显示当前的时间。   2、闹钟功能:如果当前的时间与闹钟设置的时间相同,则扬声器发出闹音。   3、校时设置:用户可

    2024年02月04日
    浏览(38)
  • FPGA-DE2-115-实验二-模块化多功能数字钟

    前言: 本文主要介绍了集成电路EDA这门课程的相关实验及代码。使用的软件是Quartus Ⅱ,该实验使用fpga芯片为cyclone IV EP4CE115F29C7。 本次实验我们需要实现生活中常见的电子手表的所有功能。 我们知道: 电子手表有五个功能,包括:时间显示功能,夜光模式功能,计时功能,闹钟功能

    2024年02月04日
    浏览(67)
  • 数电课设数字钟设计(基于quartus)

            数字钟是一种利用数字电路技术实现时、分、秒计时的钟表。与机械钟相比具有更高的准确性和直观性,具有更长的使用寿命,已得到广泛的使用。数字钟的综合性较强,将数字钟作为数电实验大作业的选题不仅可以加深对数电相关理论知识如计数器、组合逻辑电

    2024年02月05日
    浏览(46)
  • 实验 7 数字钟设计

    7.1 实 验 目 的 设计一个具有时功能和校的数字钟。 7.2 实 验 仪 器 与 器 材 1. EDA 开发软件 一 套 2.微机 一 台 3.实验开发系统 一 台 4.打印机 一 台 5.其他器件与材料 若 干 7.3 实 验 说 明 用 数码管显示小时 、 分 钟 和 秒钟。 三 个 按键用于时钟校准。 K1 用 与切换

    2024年01月22日
    浏览(37)
  • 电子设计数字钟,multisim仿真·

    设计步骤(分模块叙述,并附上各模块与总体电路图) 1.计时模块,显示模块,调时模块设计 计数器模块由七片74LS160的芯片组成,两片为“秒”,两片为“分”,两片为“时”,还有一片作为“星期”,七个数码管显示器用来显示数字。‘秒’和‘分’采用60进制。通过异步

    2024年02月11日
    浏览(41)
  • 数电仿真实验-数字钟的设计

    1、掌握任意模值计数器的设计方法 2、掌握multisim仿真软件对电路进行仿真验证的方法。 3、掌握数字综合系统设计的方法,能够对整体电路进行功能测试及故障检测。

    2023年04月26日
    浏览(43)
  • 【verilog】多功能数字钟的设计

    掌握数字钟的工作原理。 掌握计数器级联构成更大模值计数器的方法。  能用verilog描述简单的时序逻辑电路。         多功能数字钟应该具有的基本功能有:显示时-分-秒、整点报时、小时和分钟可调等。首先要知道钟表的工作机理,整个钟表的工作应该是在1Hz信号的

    2024年02月04日
    浏览(55)
  • 【基于51单片机的数字钟】

    掌握单片机 C 语言判断语句、分支语句以及子程序调用等编程知识 此程序调试时间方式为先暂停再调时,故有调秒的功能。 (1) 实现正确稳定地显示小时(两位数)、分钟(两位数)、秒钟(两位数),同时数 码管应无闪烁问题 (2) 通过按键分别实现时、分、秒信息的调整,方便用户

    2024年02月11日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包