该系类博客序言和资源简介可浏览该博客: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)beginkey_in_q1 <= key_in;key_in_q2 <= key_in_q1;key_in_q3 <= key_in_q2;
end
//==========================================================//==========================================================
//********************按键状态代码****************************
always  @(posedge clk or negedge rst_n)beginif(rst_n==1'b0)beginkey_state <= 0;endelse if(key_in_q2 != key_in_q3)beginkey_state <= 1;endelse if(wipe_cnt == WIPE_TIME - 1)beginkey_state <= 0;end
end
//==========================================================//==========================================================
//*********************消抖计数器代码*************************
always @(posedge clk or negedge rst_n)beginif(!rst_n)beginwipe_cnt <= 0;endelse if(add_wipe_cnt)beginif(end_wipe_cnt)wipe_cnt <= 0;elsewipe_cnt <= wipe_cnt + 1;end
endassign 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)beginif(rst_n==1'b0)beginkey_out <= 0;endelse if(wipe_cnt == WIPE_TIME - 1)beginkey_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)beginmode_key_q1 <= mode_key;mode_key_q2 <= mode_key_q1;
end
//========================================================//========================================================
//*********************模式计数器代码***********************
always @(posedge clk or negedge rst_n)beginif(!rst_n)beginmode_cnt <= 0;endelse if(add_mode_cnt)beginif(end_mode_cnt)mode_cnt <= 0;elsemode_cnt <= mode_cnt + 1;end
endassign 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)beginif(rst_n==1'b0)beginmode_led1 <= 0;endelse if(mode == 0)beginmode_led1 <= 1;endelse beginmode_led1 <= 0;end
end//mode_led2
always  @(posedge clk or negedge rst_n)beginif(rst_n==1'b0)beginmode_led2 <= 0;endelse if(mode == 1)beginmode_led2 <= 1;endelse beginmode_led2 <= 0;end
end//mode_led3
always  @(posedge clk or negedge rst_n)beginif(rst_n==1'b0)beginmode_led3 <= 0;endelse if(mode == 2)beginmode_led3 <= 1;endelse beginmode_led3 <= 0;end
end//mode_led4
always  @(posedge clk or negedge rst_n)beginif(rst_n==1'b0)beginmode_led4 <= 0;endelse if(mode == 3)beginmode_led4 <= 1;endelse beginmode_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)beginmove_key_q1 <= move_key;move_key_q2 <= move_key_q1;
endalways  @(posedge clk)beginmode_q1 <= mode;mode_q2 <= mode_q1;
end
//========================================================//========================================================
//*********************模式计数器代码***********************
always @(posedge clk or negedge rst_n)beginif(!rst_n)beginmove_site_cnt <= 0;endelse if(mode_q1 != mode_q2)begin  //模式值变化就将位置值清零move_site_cnt <= 0;end     else if(add_move_site_cnt)beginif(end_move_site_cnt)move_site_cnt <= 0;elsemove_site_cnt <= move_site_cnt + 1;end
endassign 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)beginif(rst_n==1'b0)beginspeaker <= 0;endelse 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) )beginspeaker <= 1;endelse if( second_u - alarm_second_u == 5 )begin  //简易逻辑(响5秒即可关闭)speaker <= 0;end
endendmodule

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端,就使各个数码管轮流受控显示,这就是动态驱动。在轮流显示过程中,每位数码管的点亮时间为1~2ms,由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位数码管并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感,动态显示的效果和静态显示是一样的,能够节省大量的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)beginif(!rst_n)beginsingle_cnt <= 0;endelse if(add_single_cnt)beginif(end_single_cnt)single_cnt <= 0;elsesingle_cnt <= single_cnt + 1;end
endassign 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)beginif(!rst_n)beginseg_cnt <= 0;endelse if(add_seg_cnt)beginif(end_seg_cnt)seg_cnt <= 0;elseseg_cnt <= seg_cnt + 1;end
endassign add_seg_cnt = end_single_cnt;
assign end_seg_cnt = add_seg_cnt && seg_cnt == SEG_NUM - 1;
//数码管扫描个数计数器:一个2.5ms计数完成
//数码管扫描个数计数器:所有数码管扫描完毕
//=========================================//=========================================
//*************中间变量代码******************
always  @(*)begincase(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)beginif(rst_n==1'b0)beginseg_data <= 7'b0000000;endelse begincase(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)beginif(rst_n==1'b0)beginseg_en <= 8'b1111_1111;endelse begincase(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 电源接口电路设计

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

(1)Micro 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的文件使用方法相同,也需要用你的机器码来获取播放密码。并且需要“比特电子视频专用播放器”打开,否则会报文件损坏错误。

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

软件提取网址

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

CASE_02 基于FPGA的数字钟万年历相关推荐

  1. 基于FPGA的数字钟(四)——时钟控制模块

    一.需求分析 时钟控制模块,需要生成秒,分,时三路信号,需要有外部信号能对其进行控制. 按照正常时钟逻辑: 秒满60归零: 分在秒满60时进1,分满60归零: 时在分满60时进1,时满24归零. 外部 ...

  2. 基于FPGA的数字钟——(三)时钟显示模块(数码管)

    基于FPGA的数字钟--(三)数码管显示模块 一.硬件原理 本设计中使用 6 个共阳数码管,可以显示 6 个数字(包含小数点) .电路用 PNP管来反向驱动并且控制列扫描信号来选择哪个数码管.而且所有 ...

  3. [SystemVerilog] 基于 FPGA 的数字钟设计

    本文使用 SystemVerilog 语言在 Basys3 开发板上实现了带时钟.倒计时.闹钟.秒表.日期.日期/时间设定功能的数字钟. 整体思路上,为避免除法器的使用,每个数位设置一个寄存器.有五套 ...

  4. 基于FPGA的数字钟设计实验报告

    一.引言 E D A技术在当今社会为现代电子设计领域提供了可能性,这也是E D A 技术飞速发展的理由.现如今,E D A技术不再是某一门课程的分支,也不是某种新的技术,而是一门综合性学科.让计算机软 ...

  5. 基于FPGA的数字钟 ——最终实现

    我手里只有一块黑金的AX4010板子,板载的是Altera公司的Cyclone IV E系列 EP4CE10F17C8芯片.亲测成功,目前没发现bug 自己敲代码也不容易啊,虽说工程很简单,但所有代码 ...

  6. 基于FPGA的数字时钟设计

    基于FPGA的数字时钟设计 芯片与开发板 技术指标 1.具有正常的日时分秒技术显示功能,用七个数码管分别显示日,时,分,秒. 2.有按键校日,校时,校分,校秒. 3.利用led模拟整点报时功能. 4. ...

  7. 基于 FPGA 的数字抢答器设计

    下面是基于 FPGA 的数字抢答器设计,在quartusII下,modelsim下仿真,编程语言VHDL.   1.抢答器的工作原理 主持人宣布可以抢答后,在 20s 的定时时间内若有人按键抢答,则这 ...

  8. [FPGA]基于FPGA的数字跑表

    基于FPGA的数字跑表的设计与实现 一.设计要求 用FPGA设计并实现一个数字跑表,范围为0~59分59.99秒.可以实现数字跑表进行启动.停止计时和显示读数三个操作,可以在数码管上显示读数. 二.设 ...

  9. 基于FPGA的数字识别实现

    前言 数字识别在我们生活中很常见,比如车牌识别.本篇博客就将介绍数字识别的方法,由于只是研究数字识别的方法,我们就不用硬件平台,而是用Modelsim和Matlab来仿真验证. 具体方法如下: 我们用 ...

最新文章

  1. Oracle监听配置
  2. Java 一组温度从摄氏到华氏的转换
  3. 医学工作者如何进行医学科研设计?
  4. WordPress窗体化侧边栏
  5. String path = request.getContextPath(....拼装当前网页的相对路径
  6. 用一个中介对象来封装一系列的对象交互
  7. WPD 从便携设备拷贝文件到PC文件不完整的解决办法
  8. 【重点!DP】LeetCode 639. Decode Ways II
  9. 构建可扩展的思科互联网络---多区域OSPF
  10. oseasy还原卡_学校机房管理批量更新软件的问题还原卡、网刻、噢易OSS对比区别...
  11. 数据库MySQL学习——内含34道MySQL练习题及答案
  12. 【报告分享】 2020-2021年数字内容产业趋势报告-企鹅智库 (附下载)
  13. 悼念侯耀文:慢慢消失的笑声
  14. js重力感应小球游戏
  15. 动词ing形式的5种用法_动词ing形式的5种用法
  16. Activiti会签
  17. Laravel框架发送邮件 阿星小栈
  18. 8086/88系统中CLK引脚需要的8284时钟发生器
  19. [VCam和VSC]-VCam虚拟摄像头和 VSC虚拟声卡的使用方法
  20. python 图片中物体识别_使用TensorFlow识别照片中的物体

热门文章

  1. 什么是前端开发中的viewport
  2. Angular页面调试一个有用的小技巧 - normalizeDebugBindingName和normalizeDebugBindingValue - [object Object]
  3. Angular应用内路由(In App Route)的最佳实践
  4. why we see different http status code like 404, 500. where are they handled
  5. 一段成功通过SAP云平台IDP SAML验证的响应字段分析
  6. 如何解决error message Data cannot be maintained for set type COM_TA_R3_ID
  7. 使用action framework 实现order change时自动发送邮件
  8. Involved Parties Object ID generation logic
  9. Java Spring MVC model学习
  10. Error message Exception raised without specific error