CASE_02 基于FPGA的数字钟万年历

         该系类博客序言和资源简介可浏览该博客:PREFACE FPGA经典案例序言 快速了解该系列博客的内容与可用 资源。

目录

1 简介

2 数字钟计数方案

2.1 计数方案一

2.2 计数方案二

3 数字钟的模块设计

3.1 按键消抖模块

3.1.1 按键消抖模块代码

3.1.2 按键消抖模块仿真

 3.1.3 按键模块

  3.1.4 按键模块代码

3.1.5  按键模块仿真

3.2  模式设置模块

 3.2.1 模式设置模块代码

3.2.2  模式设计模块仿真

 3.3 位置调整模块

3.3.1  位置调整模块代码

3.3.2  位置调整模块仿真

3.4  时钟运行模块

 3.5  时间调整模块

3.6 闹钟调整模块

3.7 闹钟判断模块

3.8  闰年判断模块

3.9  数码管数值选择模块

3.10 数码管译码与动态扫描模块

3.10.1  数码管译码与动态扫描模块代码

3.10.2  数码管译码与动态扫描模块仿真

4 数字钟的系统设计

 5  硬件设计(第四讲)

5.1 电源接口电路设计

5.2  电源系统设计

 5.3 时钟设计

5.4 JTAG端口和FPGA配置电路设计

5.5 FPGA芯片的电源引脚

5.6 数码管电流扩展电路

5.8 数码管电路

5.9 复位电路

5.10 指示灯显示电路

5.11 蜂鸣器电路

5.12 按键控制电路

5.13 PCB设计图

5.14  实物图

6 实物验证

6.1 管脚分配

6.2    实物验证

资料合集包传送门:


1 简介

        本数字钟实则是数字钟万年历,具备以下功能:

(1)数码管可显示:时分秒,年月日,闹钟时间;

(2)可实现时分秒、年月日、闹钟时间的任意设定;

(3)自动实现大小月与闰年的判断;

2 数字钟计数方案

2.1 计数方案一

        计数方案一,如图:

         该计数方案,比较符合人类思维对时间的认知,在FPGA逻辑也比较好实现,对年月日时分秒的逻辑都一样好实现,尤其是在月份的大小的判断计数和闰年的判断都好处理;但是存在一个致命的缺点,产生的结果不能直接使用数码管和液晶进行显示,必须要进行运算分解数各个位,才能用于显示,分解各个位就存在一个问题就是要使用除法运算,除法运算将使用大量的逻辑资源,十分不经济。

2.2 计数方案二

        计数方案二,如图:

        计数方案二,与一相比单个时间单位的个位和十位等拆开进行计数,这种计数方式虽然给年份大小的判断和闰年的判断处理带来了不小的麻烦,但是其结果可直接用于显示,十分方便,带来了更优的资源配置,所以这点牺牲是可以接受的。

3 数字钟的模块设计

        如图为数字钟设计框图,本章内容着重讲解FPAG内部各个模块的设计,外围硬件电路由硬件工程师设计完成。

3.1 按键消抖模块

        按键消抖通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。(摘自:百度百科

         为什么要进行消抖呢?

        在不同的使用场景下,原因不同,总结一句解释消除抖动带来的不利影响;例如:弱电按钮控制强电开关,若不进行消抖,强电开关在闭合时反复抖动,引起高压电拉电弧,容易引发危险。

按键抖动通常存在两种形式,第一种为高电平有效的,如图所示:

                

        按键在没有按下时,处于常低状态;

        第二种为低电平有效的,如图所示:

               

        按键在没有按下时,处于常高状态;

          如何设计一个模块同时对两种状态都可以进行消抖呢?设置一个计数器数一定长度,当发现按键状态有变化时就开始清零计数,只有当计数器的值大于设定的长度时(这时证明按键状态稳定了),才输出按键的状态,这样就可以达到消抖的目的。

3.1.1 按键消抖模块代码

        代码工程位于: clock\module_test\wipe_key_shake\project中,源码如下:

/*=====================================================
*****************************************************
design name	    :wipe_key_shake
use			    :按键消抖模块
engineer    	:比特电子工作室
version	    	:V0.1
change note 	:
****************************************************

*****************************************************
功能说明:
	
*****************************************************

*****************************************************
端口信号说明:
in  clk         :50M时钟输入
in  rst_n       :复位信号输入

in  key_in      :按键输入
out key_out     :按键输出
*****************************************************
========================================================*/

module wipe_key_shake
#(
	parameter WIPE_TIME = 1000_000
	//消抖时间参数定义,系统时钟50M,对应的20ms参数
)
(										 
	input				clk     ,		
	input				rst_n   ,  	
	input				key_in  ,
	output	reg	key_out    
);

//==========================================================
//*********************打拍定义******************************
reg key_in_q1;
reg key_in_q2;   //消除亚稳态
reg key_in_q3;   //判断信号变化
//==========================================================


//==========================================================
//********************按键状态定义****************************
reg key_state;
//==========================================================


//==========================================================
//*********************消抖计数器定义*************************
reg [23:0] wipe_cnt;
wire       add_wipe_cnt;
wire       end_wipe_cnt; 
//==========================================================


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//*********************漂亮的分割线***************************
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


//==========================================================
//*********************打拍代码******************************
always  @(posedge clk)begin
	key_in_q1 <= key_in;
	key_in_q2 <= key_in_q1;
	key_in_q3 <= key_in_q2;
end
//==========================================================

//==========================================================
//********************按键状态代码****************************
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
		key_state <= 0;
    end
    else if(key_in_q2 != key_in_q3)begin
		key_state <= 1;
    end
	 else if(wipe_cnt == WIPE_TIME - 1)begin
		key_state <= 0;
	 end
end
//==========================================================

//==========================================================
//*********************消抖计数器代码*************************
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        wipe_cnt <= 0;
    end
    else if(add_wipe_cnt)begin
        if(end_wipe_cnt)
            wipe_cnt <= 0;
        else
            wipe_cnt <= wipe_cnt + 1;
    end
end

assign add_wipe_cnt = key_state;       
assign end_wipe_cnt = add_wipe_cnt && ( (wipe_cnt == WIPE_TIME - 1) || (key_in_q2 != key_in_q3) );   

//计数条件:按键状态变化时开始计数
//清零条件:消抖时间计满 或 按键状态发生变化
//==========================================================


//==========================================================
//*************************输出代码**************************
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
		key_out <= 0;
    end
    else if(wipe_cnt == WIPE_TIME - 1)begin
		key_out <= key_in_q2;
    end
end
//只有在消抖时间计满时,才输出键值
//==========================================================

endmodule

3.1.2 按键消抖模块仿真

        仿真工程位于Clock\module_test\wipe_key_shake中,仿真文件位于Clock module_test\wipe_key_shake\project\simulation\modelsim\ wipe_key_shak.vt文件可以使用quartuse II打开查看。        

        此处使用了quartuse ii调用modelsim自动仿真,工程已经搭建好,按照视频中指导可得出仿真结果。(注:工程已经搭建好,若想学习自动仿真工程的配置,请学习quartuse ii的安装与调用modelsim的自动仿真的文档

仿真文件中模拟输入了一个常高和常低按键按下过程各一个。(注:仿真过程参见视频)

仿真结果如下图:

              

 3.1.3 按键模块

        上面的内容进行单个按键的消抖,在数值钟的设计中,一共需要使用4个按键,分别是模式按键、移位按键、数值加按键、显示切换按键;为了集中对按键进行消抖处理,单独设置一个模块,调用4个按键消抖模块,在这个模块中统一对按键进行消抖处理输出,这样可使系统设计的结构更加简明。

  3.1.4 按键模块代码

        代码工程位于: Clockmodule_test\key_module\project中,源码如下:

/*=====================================================
*****************************************************
design name	    :key_module
use			    :按键模块
engineer    	:比特电子工作室
version	    	:V0.1
change note 	:
****************************************************

*****************************************************
功能说明:
	    对输入按键消抖进行统一处理
*****************************************************

*****************************************************
端口信号说明:
in  clk               :50M时钟输入
in  rst_n             :复位信号输入
  
in  mode_key          :模式按键输入
in  move_key          :移位按键输入
in  add_key           :数值加键输入
in  switch_key        :显示选择按键输入

out filter_mode_key   :消抖模式按键输出
out filter_move_key   :消抖移位按键输出
out filter_add_key    :消抖数值加键输入
out filter_switch_key :消抖显示选择按键输出
*****************************************************
========================================================*/
module key_module
#(
	parameter WIPE_TIME = 1000_000
	//消抖时间参数定义,系统时钟50M,对应的20ms参数
)
(
	input  clk                ,               
	input  rst_n              ,             
	
	input  mode_key           ,         
	input  move_key           ,         
	input  add_key            ,           
	input  switch_key         ,        
	
	output filter_mode_key    ,  
	output filter_move_key    ,  
	output filter_add_key     ,   
	output filter_switch_key 
);

//filter_mode_key
wipe_key_shake 
#(
	.WIPE_TIME  (WIPE_TIME       )  //20ms/50Mhz
)
key_low_1
(								 		 
	.clk        (clk             ),		
	.rst_n      (rst_n           ),  	
	.key_in     (mode_key        ),
	.key_out    (filter_mode_key )   
);

//filter_move_key
wipe_key_shake 
#(
	.WIPE_TIME  (WIPE_TIME       )  //20ms/50Mhz
)
key_low_2
(								 		 
	.clk        (clk             ),		
	.rst_n      (rst_n           ),  	
	.key_in     (move_key        ),
	.key_out    (filter_move_key )   
);

//filter_add_key
wipe_key_shake 
#(
	.WIPE_TIME  (WIPE_TIME       )  //20ms/50Mhz
)
key_low_3
(								 		 
	.clk        (clk             ),		
	.rst_n      (rst_n           ),  	
	.key_in     (add_key         ),
	.key_out    (filter_add_key  )   
);

//filter_mode_key
wipe_key_shake 
#(
	.WIPE_TIME  (WIPE_TIME        )  //20ms/50Mhz
)
key_low_4
(								 		 
	.clk        (clk              ),		
	.rst_n      (rst_n            ),  	
	.key_in     (switch_key       ),
	.key_out    (filter_switch_key)   
);

endmodule

3.1.5  按键模块仿真

        仿真工程位于Clock\module_test\key_module\project中,仿真文件位于Clock \module_test\ key_module \project\simulation\modelsim\ key_module.vt文件可以使用quartuse II打开查看。

        此处使用了quartuse ii调用modelsim自动仿真,工程已经搭建好,按照视频中指导可得出仿真结果。(注:工程已经搭建好,若想学习自动仿真工程的配置,请学习quartuse ii的安装与调用modelsim的自动仿真的文档

        仿真文件中模拟输入了4个抖动的按键输入。(注:仿真过程参见视频)

仿真结果如下图:

3.2  模式设置模块

        在数字钟逻辑中需要一个控制当前模块的模块,该模块可以控制当前处于什么状态,通过模式值可以轻松控制逻辑流程,实现对应功能,模式分别如下:

  1. 正常运行模式:数字钟按照正常节拍运行;
  2. 时间调整模式:调整时分秒;
  3. 年月日调整模式:调整年月日;
  4. 闹钟调整模式:调整闹钟时间;

 3.2.1 模式设置模块代码

/*=====================================================
*****************************************************
design name	    :mode_module
use			    :模式设置模块
engineer    	:比特电子工作室
version	    	:V0.1
change note 	:
****************************************************

*****************************************************
功能说明:
        根据模式按键,控制电子钟模式
*****************************************************

*****************************************************
端口信号说明:
in  clk         :50M时钟输入
in  rst_n       :复位信号输入

in  mode_key    :模式按键
out mode        :模式值 0:正常显示模式 1:调整时间模式 2:调整年月日模式 3:调整闹钟模式

out mode_led1   :正常显示模式指示灯
out mode_led2   :调整时间模式指示灯
out mode_led3   :调整年月日模式指示灯
out mode_led4   :调整闹钟模式指示灯
*****************************************************
========================================================*/

module mode_module
(
	input             clk      ,
	input             rst_n    ,
	
	input             mode_key ,
	output      [1:0] mode     ,
	
	output reg        mode_led1,
	output reg        mode_led2,
	output reg        mode_led3,
	output reg        mode_led4
);

//========================================================
//********************打拍定义*****************************
reg mode_key_q1;
reg mode_key_q2;  //用于判断上升沿
//========================================================

//========================================================
//*********************模式计数器定义***********************
reg  [1:0] mode_cnt;
wire       add_mode_cnt;
wire       end_mode_cnt;
//========================================================

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//*********************漂亮的分割线*************************
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++


//========================================================
//********************打拍代码*****************************
always  @(posedge clk)begin
	mode_key_q1 <= mode_key;
	mode_key_q2 <= mode_key_q1;
end
//========================================================

//========================================================
//*********************模式计数器代码***********************
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        mode_cnt <= 0;
    end
    else if(add_mode_cnt)begin
        if(end_mode_cnt)
            mode_cnt <= 0;
        else
            mode_cnt <= mode_cnt + 1;
    end
end

assign add_mode_cnt = ( mode_key_q1 == 1 )&& ( mode_key_q2 == 0 );       
assign end_mode_cnt = add_mode_cnt && mode_cnt == 4 - 1;  

//计数条件:在按键脉冲的上升沿计数
//结束条件:计满
//========================================================


//========================================================
//*********************模式输出代码*************************
assign mode = mode_cnt;
//========================================================

//========================================================
//*********************模式灯代码**************************
//mode_led1
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
		mode_led1 <= 0;
    end
    else if(mode == 0)begin
		mode_led1 <= 1;
    end
    else begin
		mode_led1 <= 0;
    end
end

//mode_led2
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
		mode_led2 <= 0;
    end
    else if(mode == 1)begin
		mode_led2 <= 1;
    end
    else begin
		mode_led2 <= 0;
    end
end

//mode_led3
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
		mode_led3 <= 0;
    end
    else if(mode == 2)begin
		mode_led3 <= 1;
    end
    else begin
		mode_led3 <= 0;
    end
end

//mode_led4
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
		mode_led4 <= 0;
    end
    else if(mode == 3)begin
		mode_led4 <= 1;
    end
    else begin
		mode_led4 <= 0;
    end
end
//========================================================


endmodule

3.2.2  模式设计模块仿真

   仿真工程位于Clock\module_test\mode_module中,仿真文件位于Clock \module_test\mode_module\project\simulation\modelsim\ mode_module.vt文件可以使用quartuse II打开查看。

        此处使用了quartuse ii调用modelsim自动仿真,工程已经搭建好,按照视频中指导可得出仿真结果。(注:工程已经搭建好,若想学习自动仿真工程的配置,请学习quartuse ii的安装与调用modelsim的自动仿真的文档

        仿真结果如下图:

                

 3.3 位置调整模块

        位置调整模块用于接入移位按键,产生一个位置值,位置用于确定在各个调整模式中,对应当前的调整位,例如在日期调整模式中,如果位置值为0,表示正在调整的是秒钟的个位,此时按下数值加键,秒钟个位就会进行加1,以此类推。

3.3.1  位置调整模块代码

/*=====================================================
*****************************************************
design name	    :move_site_module
use			    :位置调整模块
engineer    	:比特电子工作室
version	        :V0.1
change note 	:
****************************************************

*****************************************************
功能说明:
        根据MOVE按键,确定调整时间的位数位置
*****************************************************

*****************************************************
端口信号说明:
in  clk         :50M时钟输入
in  rst_n       :复位信号输入

in  mode        :模式值 0:正常显示模式 1:调整时间模式 2:调整年月日模式 3:调整闹钟模式

in  move_key    :位置调整按键
out move_site   :位置值       0~7代表数码管位置1~8
*****************************************************
========================================================*/

module move_site_module
(
	input        clk      ,
	input        rst_n    ,
	
	input  [1:0] mode     ,
	input        move_key ,
	
	output [2:0] move_site
);

//========================================================
//********************打拍定义*****************************
reg       move_key_q1;
reg       move_key_q2;  //用于判断上升沿

reg [1:0] mode_q1;
reg [1:0] mode_q2;      //用于判断模式值是否发生变化
//========================================================

//========================================================
//*********************位置计数器定义***********************
reg  [2:0] move_site_cnt;
wire       add_move_site_cnt;
wire       end_move_site_cnt;
//========================================================


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//*********************漂亮的分割线*************************
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++


//========================================================
//********************打拍代码*****************************
always  @(posedge clk)begin
	move_key_q1 <= move_key;
	move_key_q2 <= move_key_q1;
end

always  @(posedge clk)begin
	mode_q1 <= mode;
	mode_q2 <= mode_q1;
end
//========================================================


//========================================================
//*********************模式计数器代码***********************
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        move_site_cnt <= 0;
    end
	 else if(mode_q1 != mode_q2)begin  //模式值变化就将位置值清零
		  move_site_cnt <= 0;
	 end	 
    else if(add_move_site_cnt)begin
        if(end_move_site_cnt)
            move_site_cnt <= 0;
        else
            move_site_cnt <= move_site_cnt + 1;
    end
end

assign add_move_site_cnt = ( move_key_q1 == 1 )&& ( move_key_q2 == 0 ) && (mode != 0);       
assign end_move_site_cnt = add_move_site_cnt && move_site_cnt == 8 - 1;  

//计数条件:在按键脉冲的上升沿计数 且 此时模式不处于正常显示模式
//结束条件:计满8个清零
//========================================================

//========================================================
//********************位置值输出代码************************
assign move_site = move_site_cnt;
//========================================================

endmodule

3.3.2  位置调整模块仿真

        仿真工程位于Clock\module_test\ move_site_module中,仿真文件位于Clock \module_test\ move_site_module\project\simulation\modelsim\ move_site_module.vt文件可以使用quartuse II打开查看。

        此处使用了quartuse ii调用modelsim自动仿真,工程已经搭建好,按照视频中指导可得出仿真结果。(注:工程已经搭建好,若想学习自动仿真工程的配置,请学习quartuse ii的安装与调用modelsim的自动仿真的文档

        仿真文件中模拟了一个移位按键调整位置值的过程。(注:仿真过程参见视频 )

        仿真结果如下图:

        

3.4  时钟运行模块

        时钟运行模块的逻辑就是按照大家所认知的时间逻辑来进行设计的,包含了时分秒、年月日的运行进位逻辑,同时可实现大小月,平润年的识别,同时可在对应调整模式下引入时钟调整模块的值,达到调整时间的功能,实则这个设计功能是一个电子钟万年历的功能,比普通的数字钟功能更为强大。

        仿真工程位于Clock\module_test\clock_run中,仿真文件位于Clock \module_test\ clock_run\project\simulation\modelsim\ clock_run.vt文件可以使用quartuse II打开查看。

        此处使用了quartuse ii调用modelsim自动仿真,工程已经搭建好,按照视频中指导可得出仿真结果。(注:工程已经搭建好,若想学习自动仿真工程的配置,请学习quartuse ii的安装与调用modelsim的自动仿真的文档

整个工程模拟了一个数字钟正常运行的过程。(注:仿真过程参见视频)

        仿真结果如下图:

 3.5  时间调整模块

        时间调整模块的逻辑与时钟运行模块的逻辑很相似,不同的地方在于使用了按键去调整值,其中工包含了时分秒、年月日的调整逻辑,同样可实现大小月,平润年的识别,同时可将正常运行模式下时钟运行模块的时间引入,作为调整时间的基准,以提高调整时间的便捷程度。

        仿真工程位于Clock\module_test\clock_adjust中,仿真文件位于Clock \module_test\ lock_adjust\project\simulation\modelsim\ lock_adjust.vt文件可以使用quartuse II打开查看。

        此处使用了quartuse ii调用modelsim自动仿真,工程已经搭建好,按照视频中指导可得出仿真结果。(注:工程已经搭建好,若想学习自动仿真工程的配置,请学习quartuse ii的安装与调用modelsim的自动仿真的文档

整个工程模拟了一个使用按键进行时间调整的过程。(注:仿真过程参见视频)

        仿真结果如下图:

3.6 闹钟调整模块

        闹钟调整模块的逻辑和时间调整模块的逻辑是一样的,只是用于设置闹钟的时间,这里不做过多解释。

   仿真工程位于Clock\module_test\clock_adjust中,仿真文件位于Clock \module_test\clock_adjust\project\simulation\modelsim\ clock_adjust.vt文件可以使用quartuse II打开查看。

        此处使用了quartuse ii调用modelsim自动仿真,工程已经搭建好,按照视频中指导可得出仿真结果。(注:工程已经搭建好,若想学习自动仿真工程的配置,请学习quartuse ii的安装与调用modelsim的自动仿真的文档

仿真文件中模拟了一个调整闹钟时间的过程。(注:仿真过程参见视频)

        仿真结果如下图:

3.7 闹钟判断模块

        闹钟判断模块用于判断闹钟设定时间与当前的时间是否相同,若相同则让喇叭响5秒钟后关闭掉,到达一个闹钟的功能,源码如下:

/*=====================================================
*****************************************************
design name	    :alarm_judge
use			    :闹钟判断模块
engineer    	:比特电子工作室
version	    	:V0.1
change note 	:
****************************************************

*****************************************************
功能说明:
			通过判定闹钟设定时间与实时时间,进行闹铃控制
*****************************************************

*****************************************************
端口信号说明:
in   clk              :50M时钟输入
in   rst_n            :复位信号输入

in   second_u         :实时秒钟个位实时数值
in   second_d         :实时秒钟十位实时数值
in   minute_u         :实时分钟个位实时数值
in   minute_d         :实时分钟十位实时数值
in   hour_u           :实时小时个位实时数值
in   hour_d           :实时小时十位实时数值 

in   alarm_second_u   :闹钟秒钟个位实时数值
in   alarm_second_d   :闹钟秒钟十位实时数值
in   alarm_minute_u   :闹钟分钟个位实时数值
in   alarm_minute_d   :闹钟分钟十位实时数值
in   alarm_hour_u     :闹钟小时个位实时数值
in   alarm_hour_d     :闹钟小时十位实时数值

out  speaker          :喇叭控制
*****************************************************
========================================================*/

module alarm_judge
(
	input clk                        ,
	input rst_n                      ,
	
	input      [3:0] second_u        ,
	input      [3:0] second_d        ,
	input      [3:0] minute_u        ,
	input      [3:0] minute_d        ,
	input      [3:0] hour_u          ,
	input      [3:0] hour_d          ,
	     
	input      [3:0] alarm_second_u  ,
	input      [3:0] alarm_second_d  ,
	input      [3:0] alarm_minute_u  ,
	input      [3:0] alarm_minute_d  ,
	input      [3:0] alarm_hour_u    ,
	input      [3:0] alarm_hour_d    ,
	
	output reg       speaker 

);


always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
		speaker <= 0;
    end
    else if( (alarm_hour_d == hour_d) && (alarm_hour_u == hour_u) && (alarm_minute_d == minute_d) &&
	 
	          (alarm_minute_u == minute_u) && (alarm_second_d == second_d) && (alarm_second_u == second_u) )begin
				 
		speaker <= 1;
    end
    else if( second_u - alarm_second_u == 5 )begin  //简易逻辑(响5秒即可关闭)
		speaker <= 0;
    end
end


endmodule

3.8  闰年判断模块

        闰年判断是万年历中一个不可或缺功能,因为是否是闰年将决定着2月的天数,是时钟调整模块和时钟运行模块的正确执行的重要条件,闰年判定的条件如下:

  1. 能直接被400整除;
  2. 能被4整除但是不能被100整除;

满足以上两个条件的任意一个则被判定为闰年。

     可见闰年的判断需要关注的点则是除法的余数,所以在这里必须要使用除法运算了,同时考虑FPGA中不能使用过多除法IP核的做法,采用分时复用的方法来执行除法运算流程,得出结果,流程如下:

  1. 使用运行模式来控制进入闰年判断的年份数值是来自于时钟运行模块,还是时间调整模块,因为这两个模块都需要判断是否是闰年。
  2. 将分离的年份数值通过算法计算为一个数 ,公式如下:year = year_k * 1000 + year_h * 100 + year_d*10 + year_u;
  3. 除法第一轮:计算year/400 ,并取出余数;
  4. 除法第一轮:计算year/4 ,并取出余数;
  5. 除法第一轮:计算year/100 ,并取出余数;
  6. 通过对三个余数的判断年份是否为闰年;

        仿真工程位于Clock\module_test\ leap_year_judge中,仿真文件位于Clock \module_test\ leap_year_judge\project\simulation\modelsim\ leap_year_judge.vt文件可以使用quartuse II打开查看。

        此处使用了quartuse ii调用modelsim自动仿真,工程已经搭建好,按照视频中指导可得出仿真结果。(注:工程已经搭建好,若想学习自动仿真工程的配置,请学习quartuse ii的安装与调用modelsim的自动仿真的文档

        仿真文件中输入了不同的年份,进行了闰年的判断。(注:仿真过程参见视频)

仿真结果如下图:

3.9  数码管数值选择模块

        外围电路设计了8个数码管用于显示,但是设计中却有很多数要显示,比如:正常运行的时分秒、年月日,调整模式下的时分秒、年月日,以及闹钟时间;于是需要在不同模式或则不同按键模式下,选择不同的数值输出,提供给数码管驱动,这样在数码管上就可以需要的数值,方案如下:

  1. 在正常运行模式下:输出正常运行的时分秒值;
  2. 在正常运行模式下,switch_key被按下:输出正常运行时的年月日值;
  3. 在时间调整模式下:输出被调整的时分秒值,并根据位置值对相应位进行秒闪烁,提示目前调整的是该位的值;
  4. 在年月日调整模式下:输出被调整的年月日值,并根据位置值对相应位进行秒闪烁,提示目前调整的是该位的值;
  5. 在闹钟调整模式下:输出被调整的闹钟时分秒值,并根据位置值对相应位进行秒闪烁,提示目前调整的是该位的值;

      仿真工程位于Clock\module_test\ tube_num_selecte中,仿真文件位于Clock \module_test\ tube_num_select\project\simulation\modelsim\ tube_num_select.vt文件可以使用quartuse II打开查看。

        此处使用了quartuse ii调用modelsim自动仿真,工程已经搭建好,按照视频中指导可得出仿真结果。(注:工程已经搭建好,若想学习自动仿真工程的配置,请学习quartuse ii的安装与调用modelsim的自动仿真的文档

        仿真文件中实现了不同模式和不同按键值,仿真模块是否能根据不同的模式去完成数值输出的选择。(注:仿真过程参见视频)

仿真结果如下图:

3.10 数码管译码与动态扫描模块

        这里指的数码管就是我们最常见的七段显示数码管,如图:

        可以看出它是由7段小灯组成的,通过点亮小灯的组合不同可以组成不同的字符。管脚图如下:

        

       每个管脚控制着一段灯管,数码管还分为共阴和共阳两种,共阴是说每段小灯的负极是连接在一起的(一个共地管脚),每段小灯的正级是分开的(对应着每个管脚),共阳数码管的则刚好相反,本文就以共阴的数码管为例来讲解。

       数码管显示0~9这10个数字,按照管脚状态的组合,我们可以得到如下的数字管脚组合表,称它为译码表(小数点忽略,该设计不涉及小数点的使用):

       图中用‘1’表示该管脚状态为高,点亮该管脚对应的段码;‘0’则相反;

数字   管脚

a

b

c

d

e

f

g

0

1

1

1

1

1

1

0

1

0

1

1

0

0

0

0

2

1

1

0

1

1

0

1

3

1

1

1

1

0

0

1

4

0

1

1

0

0

1

1

5

1

0

1

1

0

1

1

6

1

0

1

1

1

1

1

7

1

1

1

0

0

0

0

8

1

1

1

1

1

1

1

9

1

1

1

1

0

1

1

       了解数码管的基本原理以后,还需要解决一个问题,频率计中一共使用了8个数码管,如果每个数码管都需要单独驱动的话,那么就需要56个管脚,管脚使用太多的话不论是对FPGA IO资源占用还是PCB布线都是不合理的,所以需要使用数码管的动态扫描来解决这个问题。

       动态扫描的原理(百度百科):数码管动态显示接口是嵌入式系统中应用最为广泛的一种显示方式之一,动态驱动是将所有数码管的8个显示笔划"a,b,c,d,e,f,g,dp"的同名端连在一起,另外为每个数码管的公共极COM增加位选通控制电路,位选通由各自独立的I/O线控制,当单片机输出字形码时,所有数码管都接收到相同的字形码,但究竟是哪个数码管会显示出字形,取决于单片机对位选通COM端电路的控制,所以我们只要将需要显示的数码管的选通控制打开,该位就显示出字形,没有选通的数码管就不会亮。通过分时轮流控制各个数码管的的COM端,就使各个数码管轮流受控显示,这就是动态驱动。在轮流显示过程中,每位数码管的点亮时间为12ms,由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位数码管并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感,动态显示的效果和静态显示是一样的,能够节省大量的I/O端口,而且功耗更低。

3.10.1  数码管译码与动态扫描模块代码

/*=====================================================
*****************************************************
design name	    :nixie_tube
use			    :数码管译码与动态扫描模块
engineer    	:比特电子工作室
version	    	:V0.2
change note     :1,增加全灭显示; 2,增加“-”显示
****************************************************

*****************************************************
功能说明:
	按照 3.3	数码管译码与动态扫描模块 的原理完成代码设计
*****************************************************

*****************************************************
端口信号说明:
in   clk                :50M时钟输入
in   rst_n              :复位信号输入

in   num8               :分解数字的第8位
in   num7               :分解数字的第7位
in   num6               :分解数字的第6位
in   num5               :分解数字的第5位
in   num4               :分解数字的第4位
in   num3               :分解数字的第3位
in   num2               :分解数字的第2位
in   num1               :分解数字的第1位

out  seg_en             :数码管使能端
out  seg_data           :数码管数据端
*****************************************************
========================================================*/
module nixie_tube
#(
	parameter 	TIME_2_5MS = 250000  //2.5ms计时
)
(
   input            clk      ,
   input            rst_n    ,
	
	input      [3:0] num8     ,
	input      [3:0] num7     , 
	input      [3:0] num6     ,
	input      [3:0] num5     ,
	input      [3:0] num4     ,
	input      [3:0] num3     , 
	input      [3:0] num2     ,
	input      [3:0] num1     ,
	
	output reg [7:0] seg_en   ,
	output reg [6:0] seg_data   //从高到低位abcdefg
);

//=========================================
//*************动态扫描计数器定义*************
reg  [19:0] single_cnt;     //单个数码管点亮时间计数器
wire        add_single_cnt;
wire        end_single_cnt;

parameter   SEG_NUM =  8;  //扫描数码管的个数

reg  [3:0]  seg_cnt;       //数码管扫描个数计数器
wire        add_seg_cnt;
wire        end_seg_cnt;
//=========================================


//=========================================
//*************中间变量定义******************
reg [3:0] num;
//=========================================


//=========================================
//*************动态扫描计数器代码*************

/******************************************
人眼分辨频率大约为30Hz,为提高观感,将数码管动态
扫描频率提高至50Hz,意味着一轮数码管的扫描时间为
20ms,总共8个数码管需要点亮,那么在一轮扫描中,单个
数码管点亮的时间为2.5ms
*******************************************/
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        single_cnt <= 0;
    end
    else if(add_single_cnt)begin
        if(end_single_cnt)
            single_cnt <= 0;
        else
            single_cnt <= single_cnt + 1;
    end
end

assign add_single_cnt = 1;       
assign end_single_cnt = add_single_cnt && single_cnt == TIME_2_5MS - 1;  
//点亮时间计数器计数条件:  始终计数
//点亮计数器计数器结束条件:计满2.5ms就停止计数


always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        seg_cnt <= 0;
    end
    else if(add_seg_cnt)begin
        if(end_seg_cnt)
            seg_cnt <= 0;
        else
            seg_cnt <= seg_cnt + 1;
    end
end

assign add_seg_cnt = end_single_cnt;       
assign end_seg_cnt = add_seg_cnt && seg_cnt == SEG_NUM - 1;  
//数码管扫描个数计数器:一个2.5ms计数完成
//数码管扫描个数计数器:所有数码管扫描完毕
//=========================================

//=========================================
//*************中间变量代码******************
always  @(*)begin
	case(seg_cnt)
		0: num = num8;
		1: num = num7;
		2: num = num6;
		3: num = num5;
		4: num = num4;
		5: num = num3;
		6: num = num2;
		7: num = num1;
      default: num = 4'hf;
	endcase
end
//=========================================


//=========================================
//****************输出代码******************
//seg_data
always  @(posedge clk or negedge rst_n)begin
	if(rst_n==1'b0)begin
		seg_data <= 7'b0000000;
	end
	else begin
		case(num)
			4'h0   : seg_data <= 7'b1111110;
			4'h1   : seg_data <= 7'b0110000;
			4'h2   : seg_data <= 7'b1101101;
			4'h3   : seg_data <= 7'b1111001;
			4'h4   : seg_data <= 7'b0110011;
			4'h5   : seg_data <= 7'b1011011;
			4'h6   : seg_data <= 7'b1011111;
			4'h7   : seg_data <= 7'b1110000;
			4'h8   : seg_data <= 7'b1111111;
			4'h9   : seg_data <= 7'b1111011;
			4'he   : seg_data <= 7'b0000001;  //"-"
			4'hf   : seg_data <= 7'b0000000;  //全灭
			default: seg_data <= 7'b0000000;    
		endcase	
	end
end

//seg_en
always  @(posedge clk or negedge rst_n)begin
	if(rst_n==1'b0)begin
		seg_en <= 8'b1111_1111;
	end
	else begin
		case(seg_cnt)
			4'h0   : seg_en <= 8'b0111_1111;  //点亮第8个数码管
			4'h1   : seg_en <= 8'b1011_1111;  //点亮第7个数码管
			4'h2   : seg_en <= 8'b1101_1111;  //点亮第6个数码管
			4'h3   : seg_en <= 8'b1110_1111;  //点亮第5个数码管
			4'h4   : seg_en <= 8'b1111_0111;  //点亮第4个数码管
			4'h5   : seg_en <= 8'b1111_1011;  //点亮第3个数码管
			4'h6   : seg_en <= 8'b1111_1101;  //点亮第2个数码管
			4'h7   : seg_en <= 8'b1111_1110;  //点亮第1个数码管
			default: seg_en <= 8'b1111_1111;    
		endcase	
	end
end
//=========================================

endmodule

3.10.2  数码管译码与动态扫描模块仿真

        仿真工程位于clock\module_test\ nixie_tube \project中,仿真文件位于clock\\module_test\ nixie_tube \project\simulation\modelsim\ nixie_tube.vt文件可以使用quartuse II打开查看。

        此处使用了quartuse ii调用modelsim自动仿真,工程已经搭建好,按以下步骤即可获得仿真结果。(注:工程已经搭建好,若想学习自动仿真工程的配置,请学习quartuse ii的安装与调用modelsim的自动仿真的文档

        仿真文件中在数码管译码与动态扫描的数字输入端口num8~num1上分别输入了数字1、2、3、4、5、6、7、8,查看功能是否正确。(注:仿真过程参见视频)

仿真结果如下图:

                

       结论:一共出现8端波形,从左至右分别是:第8位数码管显示1、第7位数码管显示2、第6位数码管显示3、第5位数码管显示4、第4位数码管显示5、第3位数码管显示6、第2位数码管显示7、第1位数码管显示8,证明显示结果正确。

4 数字钟的系统设计

        上一章已经完成了数字钟的各个模块的仿真验证工作,这一章将上一章节的模块有机的结合起来就构成了一个完整的数字钟了,这里要完成的工作即是编写数字钟的顶层设计代码,将各个模块连接起来。

        用quartuse ii对系统工程进行综合以后,点击RTL_VIWER系统可以自动生成设计的系统框图(RTL视图),也就是大家误以为的连线图和原理图,如图:

 5  硬件设计(第四讲)

        前面我们的硬件描述语言已经把整个设计的做完,功能仿真也进行完毕了。但是我们的硬件设计的核心器件还没有定下来。采用器件(EP4CE6E22C8)对工程进行综合编译,可以看到资源占用报告,如图:

         可以看出设计占用了该器件22%的资源,这样我们还有78%的资源余量,那这样看来该器件是可以完成该设计的。

       下面我们以FPGA为例选用EP4CE6E22C8来完成该设计,下面分模块对硬件原理图进行讲解。

5.1 电源接口电路设计

        电源接口电路采用了两种供电的方式,满足不同的情况下使用。

1Micro Usb供电,取电方便,用智能手机的充电线或者充电宝均可取电;(2)排针供电,但是必须要实验室直流电源通过排线接入5V电源。

电路中使用D1用来防反接,防止电源接反;R1使用的是自恢复保险丝,当后面电路意外短路的时,电阻急剧增大,形成高阻状态,防止烧坏后面电路的器件,当短路情况消失后,电阻减少,形成低阻状态,从而不需要人工干预,有效地保护电路。LED作为一个电源指示灯,当电源接好并且K1开关打开时,电源指示灯常亮。

5.2  电源系统设计

        FPGA芯片需要用到3种电源供电,所以电路中设计3种电源转换电路。

(1)5V电源转3.3V,E5、E6用来滤波储能,C3、C6用来滤出高频电源噪声。

(2)5V电源转2.5V,外部加的滤波电容功能同上。

(3)3V电源转1.2V,外部的电容功能同上。

                

 5.3 时钟设计

        全局主时钟采用50M有源晶振设计,频率精度高、稳定性好,C7、C8用来滤除有源晶振的电源噪声。

5.4 JTAG端口和FPGA配置电路设计

        程序的下载,采用通用的JTAG方式下载程序,由于FPGA掉电易失,所以需要挂一个存储器,电路中选用的是EPCS16S18N,设计PCB板的时候需要注意的地方,就是有些脚需要上拉电阻。

5.5 FPGA芯片的电源引脚

   

          FPGA的电源引脚分布情况,VCC3_3,VCC1_2,以及VCC2_5。

5.6 数码管电流扩展电路

        此次设计的数码管电路采用动态扫描的方式来显示。由于电路中有8个数码管,如果不采用动态扫描电路,那么将会用到60多个IO口,不利用PCB布线且也浪费了FPGA芯片的IO口。由于闪烁的频率在大于40HZ后,人眼就无法识别闪烁了,这样,在程序中设置每个数码管的点亮时间大于40HZ,就会实现眼睛被欺骗的结果,数码管常亮。在使用数码管动态扫描电路的时候,一定要注意驱动电流的选择,如果不增加电流扩展电路,那么最终的结果就是数码管的显示很暗又或者显示不稳定,电路中每个公共信号都采用一个NPN三极管来扩展电流,增加驱动能力。

5.8 数码管电路

        电路里面的使用了8个上图所示的数码管,采用共阴极的接法,程序中按照8个数码管从高到地位分时选择seg_en_x的管脚,点亮选择的数码管,由于分时选择的速度很快,远远大于人眼能够识别的频率,从而看上去数码管都是亮的.

5.9 复位电路

       复位电路,用于程序的复位,本电路中使用的是低电平复位。

5.10 指示灯显示电路

        电路设计上留了4个不同颜色的灯,均通过FPGA芯片的IO口直接供电,高电平,点亮对应的灯,这4种灯具有以下的功能:LED3(闹钟设置模式指示灯)、LED4(日期设置模式指示灯)、LED5(时间调整模式指示灯)、LED6(正常显示模式下指示灯)。

5.11 蜂鸣器电路

        蜂鸣器用来配合着闹钟使用,当定时的时间到了设定的值后,蜂鸣器会鸣叫,提示闹钟设置时间到。电路中用的蜂鸣器是有源蜂鸣器,只需通电,便会鸣叫,方便易于控制,高电平导通,蜂鸣器鸣叫。

5.12 按键控制电路

        根据程序的功能,设计了4个按键,分别是数码管显示切换按键,调整增加按键,移位按键以及模式调整按键;高电平有效。

5.13 PCB设计图

5.14  实物图

                        

6 实物验证

        在这里就已经到达我们设计的最后一个阶段了,我们需要把整个设计在我们的实物上进行验证。

6.1 管脚分配

Step1:打开clock\formal_code\clock中的工程

       (注:formal_code正式程序中的在系统仿真的工程中加入了锁相环,以后可以方便的通过锁相环将设计应用于任何开发板)。

Step2:选择Pin Planner

Step3:根据《管脚分配手册》,对管脚进行分配

              分配完成以后保存并从新编译工程。(注:在正式程序的工程中,管脚已经分配好,所以使用时无需再进行分配,这里只是带大家学习这一过程。

6.2    实物验证

        最后一步任务就是把程序下载进入我们设计的开发板中,然后就可以验证功能是否正确了。

Step1:将实物行如下连接:

          ​​​​​​​        

Step2: 打开clock\formal_code\clock中的工程

  Step3:打开programmer

  Step4:设置硬件链接

​​​​​​​

 Step4:点击start等待下载完成,下载完成后重新上电

                                        

 ​​​​​​​        运行结果:(工程中的下载配置文件已固化好,下载即可,若想学习如何进行下载文件的生成,请自行搜索相关方法

资料合集包传送门:

          至此,基于FPGA的交通灯的全部设计流程已经讲解完毕,博客中讲解了所有的设计原理和设计重点,同时介于篇幅的限制,部分源码没办法全部给出,需要进一步学习的可下载全套资料合集包,本案例资料合集包较大所以分卷压缩成了两卷,请下载两卷后放在同一文件夹下,这样才能解压成功:

        第一卷传送门: Clock_Verilog.part1.rar

        第二卷传送门:Clock_Verilog.part2.rar​​​​​​​

资料包含内容如下:

文件夹名

功能描述

备注

bom

元器件清单

module_test

模块级的仿真工程(可能采用不同的仿真形式和仿真工具,具体以对应的博客为准)

需要安装Quartuse II 13.1

system_test

系统级的仿真工程(可能采用不同的仿真形式和仿真工具,具体以对应的文档为准)

需要安装Quartuse II 13.1

formal_code

程序工程的实物运行版本版本,用于下载到板卡上运行的(与仿真版本只有参数上的差异,并且生成了配置文件)

需要安装Quartuse II 13.1

sch&pcb

原理图和PCB设计图

打开文档需要安装Adobe Acrobat

video

产品设计讲解课程,与document的文件使用方法相同,也需要用你的机器码来获取播放密码。并且需要“比特电子视频专用播放器”打开,否则会报文件损坏错误。

使用“比特电子视频专用播放器” 去打开视频

软件提取网址

工作室提供了文件使用所需要的软件下载地址和对应的软件安装使用教程提取网址

  • 9
    点赞
  • 117
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
1、设计要求基于小脚丫FPGA开发板和四位数码管实现数字时钟的设计,要求: 1)采用FPGA+按键+四位数码管实现数字时钟功能; 2)时间显示格式:XX:XX:XX (时:分:秒),采用24小时制; 3)四位数码管显示时分秒,可以通过按键控制选择显示时分界面还是分秒界面。 4)通过按键设定初始时间。 5)设置在整点12点时,通过蜂鸣器响示意整点报时。蜂鸣器响维持大概5S; 2、硬件连接FPGA的系统时钟来自于小脚丫FPGA开发板配置的24MHz时钟晶振,连接FPGA的C1引脚。 本设计用到五个个按键K1~K5,硬件设计如图1所示,五个按键分别连接到FPGA的B8、C8、A10、A11和A12引脚。 图1. 按键硬件设计设计用到一个蜂鸣器来示意整点报时,硬件设计如图1所示,蜂鸣器连接到FPGA的B2引脚。 图2. 蜂鸣器硬件设计设计用到四位数码管来显示时间,四位数码管用两个74HC595驱动,硬件设计如图1所示,74HC595的串行时钟SCK、并行时钟RCK和串行数据DIN分别连接到FPGA的N2、M1和K1引脚上。 图3. 数码管驱动74HC595硬件设计 3、工作原理1)使用计数器做分频处理,得到周期为1秒的脉冲信号; 2)使用三个8bit的BCD码表示时钟、分钟、秒钟的值,其中高4bit表示值的十位,低4bit表示值的个位; 3)正常运行时,每来一个1S脉冲信号个位加1,个位满10清零同时十位加1,当秒钟满60清零同时分钟个位加1,依次进行...直到23:59:59的下一刻全部清零; 4)按键K5,模式调节,设计共分4中模式(分秒显示、分秒调节、时分显示、时分调节),按动K5依次切换模式; 5)按键K2,时间调节,当数字时钟在时针调节、分针调节或秒针调节模式时,按动K2调节对应时间位; 6)在调时分和调分秒两个状态,可以通过K4和K2键分别左移右移要调整的位,要调整的位会通过对应位的闪烁来示意。通过K4和 K2左右移动选择好要调整的位以后,就可以通过K1和K3来增大或调小对应的位; 4、代码设计为了实现所需要的功能,我们将整个设计划分不同的模块,如图4所示。 图4. 数字时钟程序设计框架 4.1五位按键消抖模块 图5. 五位按键消抖模块 Ø输入:五位的按键电平信息输入 Ø输出:五位消抖后的脉冲输出 Ø功能:将按键按下一次的电平信号,经过消抖后变成一个维持一个时钟周期的脉冲信号; Ø原理: 图6. 按键抖动特性 FPGA过20ms检测按键是否按下,存储检测到的值,并且按位取反与前一个20ms检测的值相与,得到一个值,如果为1,则判断按键按下,否则则无按下。 图7. FPGA按键的理解示意图 4.2电子表显示控制模块。 图8. 电子表显示控制模块 Ø输入:五位的按键脉冲 Ø输出:十六位的BCD码输出,每四个代表一个十进制数; Ø原理:四位的位闪烁控制信号。某一位为一代表这位对应的数码管的一位进行闪烁显示。(在调整状态下,会让当前调整的哪一位进行闪烁。正常显示状态下seg_flash_data全为零); Ø功能:主要就是一个状态机,通过检查输入的按键信息,进行显示状态切换,时间调整。四个状态分别为:显示分秒,调分秒,时分显示,调时分;当K5按键按下(key_pulse[4])时依次跳转,如图9所示。 图 9. 数字时钟状态控制设计 4.3数码管译码模块 图10. 数码管译码模块 Ø输入:四位的BCD码数据 Ø输出:八位的七段数码数据 Ø功能:一个case语句,将输入的四位BCD码转化为七段数码数据; Ø原理:数码管分为共阳极数码管和共阴极数码管。共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)的数码管,共阳极(COM)需接+5V才能使其工作。共阴数码管是指将所有发光二极管的阴极接到一起形成公共阴极(COM)的数码,共阴极(COM)需接GND才能使其工作。小脚丫拓展板上的数码管如下图所示: 图11. 数码管内部电路 共阴极数码管: 位选为低电平(即0)选中数码管;各段选为高电平(即接+5V时)选中各数码段;由0到f的编码为: 4.4四位数码管显示控制模块。 图12. 数码管显示控制模块 Ø输入:四个八位的七段数码管数据和位闪烁控制信号seg_flash_data. Ø输出:需要串行输出给74HC595的十六位数据; Ø功能:模块就是循环的将四位七段数码数据,组合一个十六位的输出数据; 4.5 74HC595驱动功能模块 图13. 74HC595驱动功能模块 Ø输入:十六位的位选段选数据。 1)duan_wei_data[13:0]分别对应: 2)[ X,X,H+,H-,DIG4,DIG3,DIG2,DIG1,DP,G,F,E,D,C,B,A] ; Ø输出:SPI接口输出,串行输出十六位位选段选信号; Ø
基于FPGA设计万年历数字时钟课程设计Quartus工程源码+设计报告文档资料,可以做为你的学习设计参考。 系统总体设计 2.1.1 系统功能介绍 本次万年历实现的功能有: (1)年月日、时分秒的走时、设置及其显示。 (2)闹钟功能、闹钟设置及其闹钟设置显示。 2.1.2 系统硬件介绍 本次万年历设计使用的硬件资源有:6个数码管、三个微动开关、两个拨码开关和两个LED灯。 其中,6个数码管选择显示年月日、时分秒及其闹钟设置的时分秒。三个按键分别为翻页按键(选择数码管显示内容)、选择按键(选择需调整的内容)、加一按键(调整的内容加一),用于翻页和时间及其闹钟的设置。两个拨码开关分别为闹钟使能开关和复位开关,用于闹钟的打开及其关闭和将整个系统复位。两个LED灯分别为闹钟开关状态和闹钟标志(代替蜂鸣器)。 2.1.3 系统总体框图介绍 整个系统采用自顶向下的设计方法,从系统的总体功能出发,将整个系统划分为多个模块,然后再将各个模块划分为多个功能部分,编程完成后将各个部分联系起来组成整个系统。 module calendar(system_clk,reset,key_turn,key_switch,key_add,switch_alarm,led,beep,hex0,hex1,hex2,hex3,hex4,hex5); input system_clk,reset,key_turn,key_switch,key_add,switch_alarm;//系统时钟、复位、三个按键和一个拨码开关 output led,beep; //LED灯和蜂鸣器 output [6:0] hex0; //输出:数码管0 output [6:0] hex1; //输出:数码管1 output [6:0] hex2; //输出:数码管2 output [6:0] hex3; //输出:数码管3 output [6:0] hex4; //输出:数码管4 output [6:0] hex5; //输出:数码管5 wire second_add,minute_add,hour_add,day_add,month_add,year_add,alarm_second_add,alarm_minute_add,alarm_hour_add; //专属增一信号 wire [1:0] flag_turn; //翻页状态信号 wire [1:0] flag_switch; //闪烁数码管选择信号 wire select_sign; //选择状态 wire [5:0] second; //秒 wire [5:0] minute; //分 wire [4:0] hour; //小时 wire [4:0] day; //日 wire [3:0] month; //月 wire [6:0] year; //年 wire [5:0] alarm_second; //闹钟秒 wire [5:0] alarm_minute; //闹钟分 wire [4:0] alarm_hour; //闹钟小时 //例化按键模块 key_drive_module use_key_drive_module( .system_clk (system_clk), .reset (reset), .key_turn (key_turn), .key_switch (key_switch), .key_add (key_add), .flag_switch (flag_switch), .flag_turn (flag_turn), .second_add (second_add), .minute_add (minute_add), .hour_add (hour_add), .day_add (day_add), .month_add (month_add), .year_add (year_add), .alarm_second_add (alarm_second_add), .alarm_minute_add (alarm_minute_add), .alarm_hour_add (alarm_hour_add), .select_sign (select_sign) ); //例化时钟模块 clock

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

比特FPGA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值