
第一章绪论
1.1.选题意义与研究现状
在这个时间就是金钱的年代里,数字电子钟已成为人们生活中的必需品。目前应用的数字钟不仅可以实现对年、月、日、时、分、秒的数字显示,还能实现对电子钟所在地点的温度显示和智能闹钟功能,广泛应用于车站、医院、机场、码头、厕所等公共场所的时间显示。
随着现场可编程门阵列( field program-mable gate array ,FPGA) 的出现,电子系统向集成化、大规模和高速度等方向发展的趋势更加明显, 作为可编程的集成度较高的ASIC,可在芯片级实现任意数字逻辑电路,从而可以简化硬件电路,提高系统工作速度,缩短产品研发周期。故利用 FPGA这一新的技术手段来研究电子钟有重要的现实意义。设计采用FPGA现场可编程技术,运用自顶向下的设计思想设计电子钟。避免了硬件电路的焊接与调试,而且由于FPGA的 I /O端口丰富,内部逻辑可随意更改,使得数字电子钟的实现较为方便。
本课题使用Cyclone EP1C6Q240的FPGA器件,完成实现一个可以计时的数字时钟。该系统具有显示时、分、秒,智能闹钟,按键实现校准时钟,整点报时等功能。满足人们得到精确时间以及时间提醒的需求,方便人们生活。
1.2.国内外研究及趋势
随着人们生活水平的提高和生活节奏的加快,对时间的要求越来越高,精准数字计时的消费需求也是越来越多。
二十一世纪的今天,最具代表性的计时产品就是电子时钟,它是近代世界钟表业界的第三次。第一次是摆和摆轮游丝的发明,相对稳定的机械振荡频率源使钟表的走时差从分级缩小到秒级,代表性的产品就是带有摆或摆轮游丝的机械钟或表。第二次是石英晶体振荡器的应用,发明了走时精度更高的石英电子钟表,使钟表的走时月差从分级缩小到秒级。第三次就是单片机数码计时技术的应用,使计时产品的走时日差从分级缩小到1/600万秒,从原有传统指针计时的方式发展为人们日常更为熟悉的夜光数字显示方式,直观明了,并增加了全自动日期、星期的显示功能,它更符合消费者的生活需求!因此,电子时钟的出现带来了钟表计时业界跨跃性的进步。
我国生产的电子时钟有很多种,总体上来说以研究多功能电子时钟为主,使电子时钟除了原有的显示时间基本功能外,还具有闹铃,报警等功能。商家生产的电子时钟更从质量,价格,实用上考虑,不断的改进电子时钟的设计,使其更加的具有市场。
1.3.论文结构
第一章详细论述了近些年来,数字化时钟系统研究领域的动态及整个数字化时钟系统的发展状况,同时分析了所面临的问题与解决方案,从而提出了本论文的研究任务。
第二章从研究任务着手,选择符合设计要求的常用芯片及其它元器件,详细论述了各接口电路的设计与连接,以模块化的形式,整合数字化时钟硬件的设计从小到大,从局部到整体,循序渐进,最终实现一个功能齐全的数字化时钟系统。
第三章根据系统设计要求,着手对数字化时钟系统软件进行功能的实现,将各功能模块有机结合,实现时钟走时,实现闹铃、整点报时附加功能。
第四章按照设计思路,在联机调试过程中,对时钟系统的不足和缺点进行分析,将调试过程作重点的记录。
第五章对全文的总结,对本系统功能实现以及制作过程中需要注意的方面,及整个系统软件编写中所吸取的经验教训进行论述,同时,也对整个研究应用进行展望。
第二章
编程软件及语言介绍
2.1Quarters II编程环境介绍
运行环境设计采用quartus II软件实现,因此针对软件需要用到的一些功能在这里进行描述.
Quartus II软件界面简单易操作,如下图2.1:
图2.1Quartus II软件界面图
2.1.1菜单栏
1)【File】菜单
Quartus II的【 File】菜单除具有文件管理的功能外,还有许多其他选项
图2.2Quartus II菜单栏图
(1)【New 】选项:新建工程或文件,其下还有子菜单
【New Quartus II Project】选项:新建工程。
【Design File】选项:新建设计文件,常用的有:AHDL文本文件、VHDL文本文件、Verilog HDL文本文件、原理图文件等。
【Vector Waveform Five】选项:矢量波形文件。
(2)【Open】选项:打开一个文件。
(3)【N】选项:创建新工程。点击后弹出对话框。单击对话框最上第一栏右侧的“…”按钮,找到文件夹已存盘的文件,再单击打开按钮,既出现如图所示的设置情况。对话框中第一行表示工程所在的工作库文件夹,第二行表示此项工程的工程名,第三行表示顶层文件的实体名,一般与工程名相同。
图2.3Quartus II新建工程图
(4)【creat /update】选项:生成元件符号。可以将设计的电路封装成一个元件符号,供以后在原理图编辑器下进行层次设计时调用。
2)【 View】菜单:进行全屏显示或对窗口进行切换,包括层次窗口、状态窗口、消息窗口等。
图2.4Quartus II菜单栏全屏切换图
3)【Assignments】菜单
(1)【Device】选项:为当前设计选择器件。
(2)【Pin】选项:为当前层次树的一个或多个逻辑功能块分配芯片引脚或芯片内的位置。
(3)【Timing Ananlysis Setting】选项:为当前设计的 tpd、tco、tsu、fmax等时间参数设定时序要求。
(4)【EDA tool setting】选项:EDA 设置工具。使用此工具可以对工程进行综合、仿真、时序分析,等等。EDA 设置工具属于第三方工具。
(5)【Setting】选项:设置控制。可以使用它对工程、文件、参数等进行修改,还可以设置编译器、仿真器、时序分析、功耗分析等。
(6)【assignment editor】选项:任务编辑器。
(7)【pin planner 】选项:可以使用它将所设计电路的 I/O 引脚合理的分配到已设定器件的引脚上。
图2.5Quartus II菜单栏设定引脚下拉图
4)【processing】菜单
【processing】菜单的功能是对所设计的电路进行编译和检查设计的正确性。
(1)【Stop process】选项:停止编译设计项目。
(2)【Start Compilation】选项:开始完全编译过程,这里包括分析与综合、适
配、装配文件、定时分析、网表文件提取等过程。
(3)【analyze current file】选项:分析当前的设计文件,主要是对当前设计文
件的语法、语序进行检查。
(4)【compilation report】选项:适配信息报告,通过它可以查看详细的适配信
息,包括设置和适配结果等。
(5)【start simulation】选项:开始功能仿真。
(6)【simulation report】选项:生成功能仿真报告。
(7)【compiler tool】选项:它是一个编译工具,可以有选择对项目中的各个文
件进行分别编译。
(8)【simulation tool】选项:对编译过电路进行功能仿真和时序仿真。
(9)【classic timing analyzer tool】选项:classic时序仿真工具。
(10)【powerplay power analyzer tool】选项:PowerPlay 功耗分析工具。
图2.6Quartus II菜单栏运行下拉图
5)【tools】菜单
【tools 】菜单的功能是
(1)【run EDA simulation tool 】选项:运行EDA仿真工具,EDA是第三方仿真工具。
(2)【run EDA timing analyzer tool 】选项:运行EDA时序分析工具,EDA是第三方仿真工具。
(3)【Programmer 】选项:打开编程器窗口,以便对 Altera 的器件进行下载编程。
图2.7Quartus II仿真菜单下拉图
2.1.2工具栏
工具栏紧邻菜单栏下方,它其实是各菜单功能的快捷按钮组合区。
2.8Quartus II菜单栏图
图2.9Quartus II菜单栏按键功能图
2.1.3功能仿真流程
1、新建仿真文件
图2.10Quartus II菜单栏新建文件夹图
2、功能方正操作
在菜单上点processing在下拉菜单中,如下图:
图2.11Quartus II菜单栏processing下拉图
2.2Verilog HDL语言介
2.2.1什么是verilog HDL语言
Verilog HDL是一种硬件描述语言,用于从算法级、门级到开关级的多种抽象设计层次的数字系统建模。被建模的数字系统对象的复杂性可以介于简单的门和完整的电子数字系统之间。数字系统能够按层次描述,并可在相同描述中显式地进行时序建模。
Verilog HDL 语言具有下述描述能力:设计的行为特性、设计的数据流特性、设计的结构
组成以及包含响应监控和设计验证方面的时延和波形产生机制。所有这些都使用同一种建模语言。此外,Verilog HDL语言提供了编程语言接口,通过该接口可以在模拟、验证期间从设计外部访问设计,包括模拟的具体控制和运行。
Verilog HDL语言不仅定义了语法,而且对每个语法结构都定义了清晰的模拟、仿真语义。因此,用这种语言编写的模型能够使用Ve rilog仿真器进行验证。语言从C编程语言中继承了多种操作符和结构。
Verilog HDL提供了扩展的建模能力,其中许多扩展最初很难理解。但是,Verilog HDL语言的核心子集非常易于学习和使用,这对大多数建模应用来说已经足够。当然,完整的硬件描述语言足以对从最复杂的芯片到完整的电子系统进行描述。
2.2.2主要功能
下面列出的是Verilog硬件描述语言的主要能力:
●基本逻辑门,例如and、or和nan d等都内置在语言中。
●用户定义原语(UP)创建的灵活性。用户定义的原语既可以是组合逻辑原语,也可以是时序逻辑原语。
●开关级基本结构模型,例如pmos和nmos等也被内置在语言中。
●提供显式语言结构指定设计中的端口到端口的时延及路径时延和设计的时序检查。
●可采用三种不同方式或混合方式对设计建模。这些方式包括:行为描述方式—使用过程化结构建模;数据流方式—使用连续赋值语句方式建模;结构化方式—使用门和模块实例语句描述建模。
●Verilog HDL中有两类数据类型:线网数据类型和寄存器数据类型。线网类型表示构件间的物理连线,而寄存器类型表示抽象的数据存储元件。
●能够描述层次设计,可使用模块实例结构描述任何层次。
●设计的规模可以是任意的;语言不对设计的规模(大小)施加任何。
●Verilog HDL不再是某些公司的专有语言而是IEEE标准。
●人和机器都可阅读Verilog语言,因此它可作为EDA的工具和设计者之间的交互语言。
●Verilog HDL语言的描述能力能够通过使用编程语言接口(PLI)机制进一步扩展。PLI是允许外部函数访问Verilog模块内信息、允许设计者与模拟器交互的例程集合。
●设计能够在多个层次上加以描述,从开关级、门级、寄存器传送级(RTL)到算法级,包括进程和队列级。
●能够使用内置开关级原语在开关级对设计完整建模。
●同一语言可用于生成模拟激励和指定测试的验证约束条件,例如输入值的指定。
●Verilog HDL能够监控模拟验证的执行,即模拟验证执行过程中设计的值能够被监控和显示。这些值也能够用于与期望值比较,在不匹配的情况下,打印报告消息。
●在行为级描述中,Verilog HDL不仅能够在RTL级上进行设计描述,而且能够在体系结构级描述及其算法级行为上进行设计描述。
●能够使用门和模块实例化语句在结构级进行结构描述。
●如图显示了Verilog HDL的混合方式建模能力,即在一个设计中每个模块均可以在不同设计层次上建模。
●Verilog HDL还具有内置逻辑函数,例如&(按位与)和|(按位或)。
●对高级编程语言结构,例如条件语句、情况语句和循环语句,语言中都可以使用。
●可以显式地对并发和定时进行建模。
●提供强有力的文件读写能力。
●语言在特定情况下是非确定性的,即在不同的模拟器上模型可以产生不同的结果;例如,事件队列上的事件顺序在标准中没有定义。
图2.12混合设计层次图
第三章
数字化时钟系统硬件设计
3.1系统核心板电路分析
本系统采用的开发平台标配的核心板是QuickSOPC,可以实现EDA、SOP和DSP的实验及研发。本系统采用QuickSOPC标准配置为Altera公司的EP1C6Q240C8芯片。
(1)核心板的硬件资源
核心板采用4层板精心设计,采用120针接口。QuickSOPC核心板的硬件原理框图如图2-1所示:
图3.1 QuickSOPC硬件方块图
(2)FPGA电路
核心板QuickSOPC上所用的FPGA为Altera公司Cyclone系列的EP1C6Q240。EP1C6Q240包含有5980个逻辑单元和92Kbit的片上RAM。EP1C6Q240有185个用户I/O口,封装为240-Pin PQFP。核心板EP1C6Q240器件特性如表2-1。
表3-1 核心EP1C6Q240器件特性:Hb7838电子-技术资料-电子元件-电路图-技术应用网站-基本知识-原理-维修-作用-参数-电子元器件符号-各种图纸
| 特性 | 核心板EP1C6Q240器件 |
| 逻辑单元(LE) | 5980 |
| M4K RAM 块 | 20 |
| RAM总量(bit) | 92160 |
| PLL(个) | 2 |
| 最大用户I/O数(个) | 185 |
| 配置二进制文件(.rbf)大小(bit) | 1167216 |
| 可选串行主动配置器件 | EPCS1/ EPCS4/ EPCS16 |
Cyclone FPGA的配置方式包括:主动配置模式、被动配置模式以及JTAG配置模式。本系统采用的是JTAG配置模式下载配置数据到FPGA。
通过JTAG结果,利用Quartus II软件可以直接对FPGA进行单独的硬件重新配置。Quartus II软件在编译时会自动生成用于JTAG配置的.sof文件。Cyclone FPGA设计成的JTAG指令比其他任何器件操作模式的优先级都高,因此JTAG配置可随时进行而不用等待其他配置模式完成。JTAG模式使用4个专门的信号引脚:TDI、TDO、TMS以及TCK。JTAG的3个输入脚TDI、TMS和TCK具有内部弱上拉,上拉电阻大约为25kΩ。在JGTA进行配置的时候,所有用户I/O扣都为高阻态。
(4)时钟电路
FPGA内部没振荡电路,使用有源晶振是比较理想的选择。EP1C6Q240C8的输入的时钟频率范围为15.625~387MHz,经过内部的PLL电路后可输出15.625~275MHz的系统时钟。当输入时钟频率较低时,可以使用FPGA的内部PLL调整FPGA所需的系统时钟,使系统运行速度更快。
核心板包含一个48MHz的有源晶振作为系统的时钟源。如图2-2所示。为了得到一个稳定、精确的时钟频率,有源晶振的供电电源经过了LC滤波。
本系统硬件整体设计框图如图2-3所示:
图3.2数字时钟系统硬件电路总体框图
3.2系统主板电路分析
3.2.1时钟模块电路
FPGA内部没振荡电路,使用有源晶振是比较理想的选择。EP1C6Q240C8的输入的时钟频率范围为15.625~387MHz,经过内部的PLL电路后可输出15.625~275MHz的系统时钟。当输入时钟频率较低时,可以使用FPGA的内部PLL调整FPGA所需的系统时钟,使系统运行速度更快。
核心板包含一个50MHz的有源晶振作为系统的时钟源。为了得到一个稳定、精确的时钟频率,有源晶振的供电电源经过了LC滤波。
图3.3 系统时钟电路图
3.2.2显示电路
由于本设计需要显示时间信息包括:时、分、秒,显所以采用主板上七段数码管显示电路与系统连接实现显示模块的功能。
主板上七段数码管显示电路如图2-4 所示,RP4和 RP6 是段码上的限流电阻,位码由于电流较大,采用了三极管驱动。
图3.4七段数码管显示电路图
数码管 LED显示是工程项目中使用较广的一种输出显示器件。常见的数管有共阴和 共阳 2 种。共阴数码管是将 8 个发光二极管的阴极连接在一起作为公共端,而共阳数码管是将 8 个发光二极管的阳极连接在一起作为公共端。公共端常被称作位码,而将其他的 8 位称作段码。如图 2-5所示为共阳数码管及其电路,数码管有 8 个段分别为:h、g、f、e、d、c、b 和a(h 为小数点) ,只要公共端为高电平“1” ,某个段输出低电平“0”则相应的段就亮。
图3.5七段数码管显示电路图
从电路可以看出,数码管是共阳的,当位码驱动信号为 0时,对应的数码管才能操作;当段码驱动信号为 0 时,对应的段码点亮。
3.2.3键盘控制电路
键盘控制电路要实现时钟系统调时的功能和闹铃开关的功能。本设计采用主板上的键盘来实现这两个功能。当键盘被按下是为“0”,未被按下是为“1”。电路连接图如图2-6所示。电路中为了防止FPGA的I/O设为输出且为高电平在按键下直接对地短路,电阻RP9、RP10对此都能起到保护作用。
图3.6 键盘电路图
3.2.4蜂鸣电路设计
如图2-7所示,蜂鸣器使用 PNP三极管进行驱动控制,蜂鸣器使用的是交流蜂鸣器。当在 BEEP输入一定频率的脉冲时,蜂鸣器蜂鸣,改变输入频率可以改变蜂鸣器的响声。因此可以利用一个 PWM 来控制 BEEP,通过改变 PWM 的频率来得到不同的声响,也可以用来播放音乐。若把 JP7断开,Q4 截止,蜂鸣器停止蜂鸣。
图3.7蜂鸣电路图
第四章
数字化时钟系统软件设计
4.1整体方案介绍
4.1.1整体设计描述
设计中的数字时钟,带有按键校准,定点报时,数码管显示等功能。因此数字时钟所包含的模块可分为,分频模块,按键模块,计时校准模块,闹钟模块,LED显示模块,模块之间的关系下图:
图4.1整体模块框图
针对框图流程,设定出各个模块的需求:
1、分频电路:
针对计时器模块与闹钟设定模块的需求,可以知道分频模块需要生成一个1Hz的频率信号,确保计时模块可以正常计数。
2、计时器模块:
计数模块的作用是收到分频模块1Hz频率的信号线,能进行正确计时,并且可以通过按键进行时间的修改,且当整点时,给蜂鸣器产生使能信号,进行整点报时,播放音乐。
3、闹钟设定模块:
可根据按键的设定闹钟的时间,当计时模块的时间与闹钟设定模块的时间相等的时候,给蜂鸣器一个使能信号,蜂鸣器闹铃。
4、蜂鸣器模块:
根据计时模块,闹钟模块给出的使能信号,判定蜂鸣器是整点报时,还是闹钟响铃。整点报时会播放音乐,闹钟时嘀嘀嘀报警。
5、LED显示模块:
根据实际的需求显示计时模块的时间,还是闹钟设定模块的时间,8个七段码LED数码管,进行扫描方式显示数据。
4.1.2整体信号定义
对整个模块进行信号定义。
接口及寄存器定义
module clock(clk,key,dig,seg,beep);// 模块名 clock
input clk; 输入时钟
i 输入按键 ,key[3:0]分别为秒,分钟,小时的增加按键。Key[4]为闹钟设置按键,key[5]为校准设置按键。
output [7:0] dig; 数码管选择输出引脚 a
output [7:0] seg; 数码管段输出引脚
output beep; 蜂鸣器输出端
r 定义数码管输出寄存器
r 定义数码管选择输出寄存器
reg [3:0] disp_dat; 定义显示数据寄存器
r 定义计数寄存器
r 定义计数中间寄存器
r 定义现在时刻寄存器
reg [23:0] clktime = 24'h000000; 定义设定闹钟
r 定义标志位
reg [4:0] dout1 = 5'b11111;
reg [4:0] dout2 = 5'b11111;
reg [4:0] dout3 = 5'b11111; 寄存器
wire [4:0] key_done; 按键消抖输出
reg [15:0] beep_count = 16'h0; 蜂鸣器寄存器
reg [15:0] beep_count_end = 16'hffff; //蜂鸣器截止寄存器
reg clktime_en = 1'b1; 闹钟使能寄存器
reg sec ; 秒时钟
reg clk1; 时钟
reg beep_r; 寄存器
wire beepen; 闹钟使能信号
4.1.3模块框图
通过quartus II的creat symble for current file功能生成框图如下:
图4.2生成的符号图
分频模块实现,计数电路所需时钟信号为1HZ,而系统时钟为48MHZ,所以要对系统时钟进行分频以来满足电路的需要。
4.2分频模块实现
4.2.1分频模块描述
对于分频模块,关键是生成个1Hz的时钟信号。考虑到仿真的需要,模块中间生成1个1kHz的时钟信号。1Hz的信号的产生用来产生时钟的秒脉冲,框图如下图4.2:
图4.3分频模块图
4.2.2分频模块设计
本系统程序设计时钟的准确与否主要取决于秒脉冲的精确度。为了保证计时准确,我们对系统时钟48MHz进行了48000分频生成1kHz信号clk1,在通过1kHz信号,生成1Hz信号clk。
//1ms信号产生部分
a 定义 clock 上升沿触发
begin
到了吗?
计数器清零
置位秒标志
end
//秒信号产生部分
a 定义 clock 上升沿触发
begin
到了吗?
计数器清零
置位秒标志
End
4.2.3分频模块仿真
通过设置功能仿真,检查代码的正确性
1、仿真结果
图4.4 分频模块波形仿真图
右上图可以知道,计数寄存器count累加到23999时,重新变为0,共计数了24000个值。触发clk1跳变,使得count1加一,count1累加到499的时候,下一个数据为0,共技术500个值。所以,sec信号的频率为1Hz,满足设计要求。
4.3计时模块实现
4.3.1计时模块描述与实现
计时模块是采用16进制来实现的,将hour[23,0]定义为其时分秒,其中hour[3,0]为其秒钟上的个位数值,hour[4,7]为其秒钟上的十位数值,以此类推分钟、时钟的个位和十位。当clk脉冲过来时,秒个位hour[3,0]便开始加1,当加到9时,秒十位加1,与此同时秒个位清零,继续加1。当秒十位hour[7,4]为5秒个位为9时(即59秒),分个位hour[11,8]加1,与此同时秒个位和秒十位都清零。以此类推,当分十位hour[15,12]为5和分个位为9时(即59分),时个位加1,与此同时分个位hour[19,16]和分十位都清零。当时分十位[23,20]为2和分个位为4,全部清零,开始重新计时。从功能上讲分别为模60计数器,模60计数器和模24计数器。
//时间计算及校准部分
always @(negedge sec)//计时处理
秒加 1
if(hour[3:0] >= 4'ha) 加到10,复位
begin
hour[3:0] = 4'h0;
hour[7:4] = hour[7:4] + 1'b1; 秒的十位加一
if(hour[7:4] >= 4'h6) 加到6,复位
begin
hour[7:4] = 4'h0;
hour[11:8] = hour[11:8] + 1'b1; 分个位加一
if(hour[11:8] >= 4'ha) 加到10,复位
begin
hour[11:8] = 4'h0;
hour[15:12] = hour[15:12] + 1'b1; 分十位加一
if(hour[15:12] >= 4'h6) 加到6,复位
begin
hour[15:12] = 4'h0;
hour[19:16] = hour[19:16] + 1'b1; 时个位加一
if(hour[19:16] >= 4'ha) 加到10,复位
begin
hour[19:16] = 4'h0;
hour[23:20] = hour[23:20] + 1'b1; 时十位加一
end
if(hour[23:16] >= 8'h24) 加到24,复位
hour[23:16] = 8'h0;
end
end
end
end
end
4.3.2计时模块仿真
对计时模块进行仿真,记录仿真波形
图4.5 计时模块仿真图
由上图可见,当sec信号下降沿跳变时,hour寄出去会加1,也就相当于跳了一秒钟时间。当hour的时间为235959是,下一个计数器的值为000000,hour寄存器归零,相当于半夜0点的时刻。仿真的结果达到预期,通过。
4.4按键处理模块实现
4.4.1按键处理模块描述
框图如下图4.4:
图4.6按键控制功能图
模块讲计时部分和时间调整部分整合到一起,正常态的时候,时间正常运行,当key[5]被按下时,进入时间校准,可以通过key[2:0]三个键,分别对秒,分,时进行加1操作,从而进行时间校准。当key[3]被按下时,进入闹钟设定,可以通过key[2:0]三个键,分别对秒,分,时进行加1操作,从而进行闹钟的设定。
图4.7 按键模块仿真图
通过按键key进行仿真控制,可以发现clktime会随着按键的按下,分别有时钟,分钟秒钟加1,仿真结果满足设计要求。
4.4.2按键去抖处理模块设计
按键模块实现去抖处理,及乒乓按键设计,确保后面的计时模块与闹钟模块的功能实现。
assign key_done = key|dout3; 按键消抖输出
always @(posedge count1[5]) 按键去噪声
begin
dout1 <= key;
dout2 <= dout1;
dout3 <= dout2; 连续赋值
end
always @(negedge key_done[4])
begin
校准按键转换乒乓按键
end
always @(negedge key_done[3])
begin
定时按键转换乒乓按键
End
4.4.3按键模块去抖仿真
对于按键去抖动仿真,同样才用功能仿真方式,这里不再重复设置与操作,如同上面的分频模块进行设置并进行仿真。
Key寄存器为输入按键,初始化电路为高电平,当有按键按下去的时候,变为低电平。因此改变key的值,观察仿真结果是否正确。
功能仿真,记录仿真结果,如下图:
图4.8 按键模块仿真图
通过上图可以知道,key_done会随着key的变化而发生相应的变化,并有消除噪声的作用,功能仿真正确,达到设计目的。
4.5闹钟模块实现
4.5.1闹钟模块设计
本设计中,判断闹铃时间到,是通过判定时钟系统实时时间的时钟与分钟是否分别等于设定的闹铃时间的时钟、分钟、秒钟。当时间(hour[23:0])等于设定的闹钟时间(clktime[23:0])时,闹钟触发时,播放嘀嘀嘀报警声,闹钟会响10秒的时间(clktime[23:0]+10 >=hour[23:0])。正常情况下,闹铃时间到会进行为时1分钟的蜂鸣报时,可以通过按下闹钟按键key[3]使其停止。当闹铃设置为整点是,会先进行整点报时,然后进入闹铃。
图4.9 闹钟控制键功能图
4.5.2闹钟设定模块仿真
图4.10 闹钟模块仿真图
通过按键key进行仿真控制,可以发现clktime会随着按键的按下,分别有时钟,分钟秒钟加1,仿真结果满足设计要求。
4.6蜂鸣器模块实现
4.6.1蜂鸣器模块描述
蜂鸣器模块负责整点报时,和闹铃的时候进行出声的作用。整点报时的时候,播放音乐,10秒音乐播报完后停止整点报时。闹钟触发时,播放嘀嘀嘀报警声。当闹铃设置为整点是,会先进行整点报时,然后进入闹铃。当闹钟设定键被按下,响起的蜂鸣声会被屏蔽。模块框图如下图4.9:
4.6.2蜂鸣器模块实现
//蜂鸣器的计数定时器
always@(posedge clk)
begin
b 计数器加 1
if((beep_count == beep_count_end)&&(!(beep_count_end == 16'hffff)))
计数器清零
取反输出信号
end
always @(posedge clk)
begin
if (!beepen)
case(hour[3:0]) 整点报时音乐内容
中音 6 的分频系数值
中音 4 的分频系数值
中音 5 的分频系数值
中音 1 的分频系数值
中音 1 的分频系数值
中音 5 的分频系数值
中音 6 的分频系数值
中音 4 的分频系数值
其他情况无声
endcase
else if (!clktime_en)
begin
闹钟嘀嘀嘀声内容
高音 7 的分频系数值
其他情况不出声
end
else
end
//闹铃使能控制
always @(posedge clk)
begin
判断闹铃是否有取消
闹铃响起后,需要手动关闭闹铃
闹铃过一点时间,自动关闭。
end
4.6.3蜂鸣器模块仿真
功能仿真,记录波形图:
图4.11蜂鸣器模块仿真图
通过上图可以看出来,当hour与clktime相等时,闹铃被触发,经过一段时间后,闹铃停止工作,设计满足要求。
4.7显示模块实现
4.7.1显示模块描述
此设计中的LED七段数码管显示模块主要显示时间的时、分、秒信息,数码管为共阳的。在此设计中占非常重要的地位,它是确保时间能直观呈现的桥梁。在设计过程中,首先进行程序编写和调试的应该是显示模块。
下面输入的端口为闹钟设定键被按下,七段数码管会显示闹钟设定情况下数码管所对应的数字。正常时间情况、闹钟设定以及查看闹钟所设定好的时间都是同样的原理,当他们被按下数码管会显示对应的模式相应的数字。
图4.12显示模块图
4.7.2显示模块实现
//数码管显示内容
always @(posedge clk)
begin
选择扫描显示数据
秒个位
秒十位
显示"-"
分个位
分十位
显示"-"
时个位
时十位
秒个位
秒十位
显示"-"
分个位
分十位
显示"-"
时个位
时十位
显示"-"
//数码管选择
选择数码管显示位
选择第一个数码管显示
选择第二个数码管显示
选择第三个数码管显示
选择第四个数码管显示
选择第五个数码管显示
选择第六个数码管显示
选择第七个数码管显示
选择第八个数码管显示
end
//数码管显示
always @(posedge clk)
begin
case(disp_dat)
4'h0:seg_r = 8'hc0; 显示 0
4'h1:seg_r = 8'hf9; 显示 1
4'h2:seg_r = 8'ha4; 显示 2
4'h3:seg_r = 8'hb0; 显示 3
4'h4:seg_r = 8'h99; 显示 4
4'h5:seg_r = 8'h92; 显示 5
4'h6:seg_r = 8'h82; 显示 6
4'h7:seg_r = 8'hf8; 显示 7
4'h8:seg_r = 8'h80; 显示 8
4'h9:seg_r = 8'h90; 显示 9
4'ha:seg_r = 8'hbf; 显示 -
不显示
endcase
if((count1[3:1]== 3'd2)&sec)
seg_r = 8'hff;
End
4.7.3显示模块仿真
编译程序,进行功能仿真,记录仿真图形:
图4.13显示模块仿真图
通过上面的图可以知道,LED数码管是通过扫描的方式实现数据更新,通过dig,seg寄存器的数据可以知道,数据能正常显示,满足设计要求。
第五章
系统调试及运行结果分析
5.1硬件调试
在软件联机调试之前,首先要确定硬件是否完全正确。检查方面主要包括:
(1)PC机的接口和核心板上的JTAG下载口是否连接正确;
(2)蜂鸣器的电路是否为通路;
(3)检查接地、电源线是否连接正确;
(4)用示波器检测核心板的各个引脚是否有信号输出;
(5)LED七段数码管显示正常。
引脚分布如下表5-1:
表5-1 引脚分布图:
| 信号 | 引脚 | 说明 | 信号 | 引脚 | 说明 |
| seg[0] | 169 | 数码管断信号选择 | dig[0] | 160 | 数码管片选信号控制 |
| seg[1] | 170 | dig[1] | 159 | ||
| seg[2] | 167 | dig[2] | 162 | ||
| seg[3] | 168 | dig[3] | 161 | ||
| seg[4] | 165 | dig[4] | 215 | ||
| seg[5] | 166 | dig[5] | 216 | ||
| seg[6] | 163 | dig[6] | 213 | ||
| seg[7] | 1 | dig[7] | 214 | ||
| key[0] | 121 | 秒加1控制 | clk | 28 | 时钟信号 |
| key[1] | 122 | 分加1控制 | Beep | 175 | 蜂鸣器信号 |
| key[2] | 123 | 时加1控制 | |||
| key[3] | 124 | 闹钟控制 | |||
| key[4] | 143 | 时间校准控制 |
在确定好硬件系统正确之后,我对本设计进行分模块的软件调试。功能模块先后调试顺序为:显示模块→时间模块→键盘模块→时间设定及其显示模块→闹铃、整点报时设定及其显示模块。每完成一个模块就与前一个已完成的模块结合起来调试,直至实现相应功能,再编写下一模块程序。在与主程序衔接时,主程序和各子程序也需作相应的改动,以便与子程序更好的衔接,特别是显示子程序需作较大改动,以便对不同内容进行显示。
软件编译后的结果:
图5.1 软件编译结果图
通过上图5.1可以知道,整个设计值用了383个逻辑单元,占用很少的资源。
5.3调试过程及结果
调试过程按照:显示模块→时间模块→键盘模块→时间设定及其显示模块→闹铃、整点报时设定及其显示模块的顺序进行调试。
(1)显示模块调试
本系统功能完整运行离不开正确显示,所以显示模块的实现非常重要,在联机调试第一步就要确定系统的显示是否能够正常运行。将需要显示的内容编写在程序内,单独得进行仿真。此模块调试结果如图4-1所示:
图5.2显示模块调试图
(2)时间系统模块调试
确定系统显示成功后,进行时间系统的调试。时间系统需要实现时、分、秒的正确走时。显示结果为时、分、秒,其中秒钟可以正常走动。此模块调试结果如图4-2所示:
图5.3 时间系统模块调试图
(3)按键模块调试
key1按键被按下进入校时状态,key2按键被按下进入调闹钟状态(在闹铃响时按此键能屏蔽闹铃音),在两个不同状态下,key3、key4、key5三个按键的功能是相同的,当被按下时,时、分、秒的数值加1。(由于图片校时状态下和闹钟设定类似不予以截图。)
图5.4时间调整/闹钟设定模块调试图
5.4调试注意事项
软件设计时,需要确定好软件设计的思路,即先确定各个功能需要实现的先后,再将各功能模块单独进行编写调试,待各模块功能完善后,再进行总体联合调试。调试过程中出现了一些问题,并一一解决:
(1)在进行系统联机调试时,要注意电源是否接通,PC机的接口和核心板上的JTAG下载口是否连接正确。确定将未使用的引脚设置为三态输入,否则可能会损坏芯片。
(2)显示模块正确调试之后,进行时钟系统的调试过程中,出现时间信息并没有显示在屏幕上,而在对应时间信息的位置上显示的是“-”。出现此问题时候,首先对硬件进行再次检查,用示波器检查引脚两端是否有信号的输入输出。结果发现硬件电路连接正确,问题出现在引脚设置的方面。在设定引脚时,没有选择正确的输出引脚。通过查找书本,修改输出引脚,解决了这个问题。
(3)时钟模块时,出现秒钟走的过快的现象。最终发现是分频程序中没有正确的对时钟脉冲信号进行正确的分频。通过在网络资源和书籍的学习正确的改善了这个问题。
(4) 键盘调整时间程序中,出现了无法正常调时情况,按键盘后,时间信息没有改变或者改变过多。经过检查,找到原因是没有给按键进行按键去抖动。这说明在编写程序时候要充分了解各芯片内容资料,减少编写小错误引起对整个程序造成的影响。
(5)闹钟报时和整点报时蜂鸣时间问题。闹钟报时中,如果闹钟时间到就会闹铃1分钟,考虑到实际生活中,闹钟都是可以手动关闭的,于是设定了一个闹钟开关键,为了减少系统硬件的繁杂,当系统时间到达闹铃时间和整点报时的时间,蜂鸣器才会响起。
第六章
总结和展望
5.5总结
在FPGA上设计和调试都需要耐心,时钟设计在生活中无处不在,设计的过程要考虑到应用的习惯,设计更人性化的体验,才会是一个好的设计。在Verilog HDL语言的学习上还存在一些问题,没有深入的学习,对于有些语法错误,还需要仔细的查找。如果将这个数字时钟应用于现实生活中,还存在些许的问题。例如按键太多,操作起来没那么的方便等等。
5.6展望
针对数字时钟的设计,通过以上的验证,我们可以知道,对数字时钟来说,其实占用很少逻辑资源,也就意味着如果一点制作成SOC的话,成本会非常的低,所以基于这点,整个模块完全可以依附在其他功能产品(如CPU内部)上,给整个产品增加的优势。
参考文献
[1].刘君,常明,秦娟,基于硬件描述语言(VHDL)的数字时钟设计,天津理工大学学报,2007,第23卷 第4期,40-41
[2].廖日坤,CPLD/FPGA嵌入式应用开发技术白金手册,中国电力出版社,2003,212-218。
[3].王开军,姜宇柏,面向CPLD/FPGA的VHDL设计,机械工业出版社,2006,28-65。
[4].赵保经,中国集成电路大全,国防工业出版社,1985。
[5].高吉祥,电子技术基础实验与课程设计,电子工业出版社,2002。
[6].吕思忠,数子电路实验与课程设计,哈尔滨工业大学出版社,2001。
[7].谢自美,电子线路设计、实验、测试,华中理工大学出版社,2003。
[8].赵志杰,集成电路应用识图方法,机械工业出版社,2003,35-40。
[9].张庆双,电子元器件的选用与检测,机械工业出版社,2003。
[10].谭会生,张昌凡,EDA技术及应用,西安电子科技大学出版社,2002。
[11].李可,数字钟电路及应用[M],北京:电子工业出版社,1996。
[12].康华先,电子技术基础(数字部分)第四版[M].北京:高等教育出版社, 2000. 213-224。
[13].集成电路手册分编委会编,中外集成电路简明速查手册,TTL、CMOS电路[M],北京:电子工业出版社,1997。
[14].康华光,电子技术基础,高等教育出版社,2002
[15].宋春荣,通用集成电路速查手册,山东科学技术出版社,1995。
[16].阎石,数字电子技术基础(第四版)[M],北京:高等教育出版社,1998。
[17].Kawasaki Hiroaki,Sakurada Hiroshi,Narushima Shinichi, etal Double-faced vacuum fluorescent display [P].U S Patent:5463276,1995
致 谢
在我进行此毕业论文撰写的过程中,我的导师给了我极大的帮助。在这里,我要感谢在这些在我毕业设计过程中指导和帮助过我的人。
首先,我要感谢我的指导严格要求。本论文的选题和撰写都是在她的指导下完成的。从开始对verilog HDL语言不懂的情况下,慢慢学习,经过一段时间的摸索,开始学着在实验板上进行练习操作,才有了长足的进步,最终调试成功;老师对于问题思路的指导,都将成为今后工作、学习的宝贵精神财富。在我走向社会,走向工作岗位的过程中,老师给予我的教诲、帮助和殷切期望,促使我更加积极追求新的人生目标。
感谢父母一直默默地给予我关心和支持,父母的悉心关怀,永远是鼓励我积极向上的不竭动力。
最后,还要感谢和我一样即将毕业的同班同学;感谢大学四年的同寝室室友;感谢那些没有提及姓名,但也同样给予我帮助的老师、同学和朋友!
附 录
module clock(clk,key,dig,seg,beep); // 模块名 clock
input clk; // 输入时钟
input [4:0] key; //输入按键
output [7:0] dig; // 数码管选择输出引脚 a
output [7:0] seg; // 数码管段输出引脚
output beep; //蜂鸣器输出端
reg [7:0] seg_r = 8'h0; //定义数码管输出寄存器
reg [7:0] dig_r; //定义数码管选择输出寄存器
reg [3:0] disp_dat; // 定义显示数据寄存器
reg [8:0] count1; //定义计数寄存器
reg [14:0] count; //定义计数中间寄存器
reg [23:0] hour = 24'h235956; // 定义现在时刻寄存器
reg [23:0] clktime = 24'h000000; //定义设定闹钟
reg [1:0] keyen = 2'b11; // 定义标志位
reg [4:0] dout1 = 5'b11111;
reg [4:0] dout2 = 5'b11111;
reg [4:0] dout3 = 5'b11111; // 寄存器
wire [4:0] key_done; // 按键消抖输出
reg [15:0] beep_count = 16'h0; //蜂鸣器寄存器
reg [15:0] beep_count_end = 16'hffff; //蜂鸣器截止寄存器
reg clktime_en = 1'b1; //闹钟使能寄存器
reg sec ; //1秒时钟
reg clk1; //1ms时钟
reg beep_r; //寄存器
wire beepen;
assign beep = beep_r; //输出音乐
assign dig = dig_r; //输出数码管选择
assign seg = seg_r; //输出数码管译码结果
assign beepen = |hour[15:4];
assign key_done = key|dout3; // 按键消抖输出
//1ms信号产生部分
always @(posedge clk) // 定义 clock 上升沿触发
begin
count = count + 1'b1;
if(count == 15'd24000) //0.5mS到了吗?
begin
count = 15'd0; //计数器清零
clk1 = ~clk1; //置位秒标志
end
end
//秒信号产生部分
always @(posedge clk1) // 定义 clock 上升沿触发
begin
count1 = count1 + 1'b1;
if(count1 == 9'd500) //0.5S到了吗?
begin
count1 = 9'd0; //计数器清零
sec = ~sec; //置位秒标志
end
end
always @(posedge count1[5]) //按键去噪声
begin
dout1 <= key;
dout2 <= dout1;
dout3 <= dout2;
end
always @(negedge key_done[4])
begin
keyen[1] = ~keyen[1]; //校准按键转换
end
always @(negedge key_done[3])
begin
keyen[0] = ~keyen[0]; //定时按键转换
end
//闹钟定时部分
always @(negedge sec)
begin
if (!keyen[0]) //如果有闹钟设置键按下
begin
case(key_done[2:0])
3'b110:
begin
clktime[19:16] = clktime[19:16] + 1'b1; //时个位加一
if(clktime[19:16] == 4'ha)
begin
clktime[19:16] = 4'h0;
clktime[23:20] = clktime[23:20] + 1'b1; //时十位加一
end
if(clktime[23:16] == 8'h24)
clktime[23:16] = 8'h0;
end
3'b101:
begin
clktime[11:8] = clktime[11:8] + 1'b1; //分个位加一
if(clktime[11:8] == 4'ha)
begin
clktime[11:8] = 4'h0;
clktime[15:12] = clktime[15:12] + 1'b1; //分十位加一
end
if(clktime[15:8] == 8'h60)
clktime[15:8] = 8'h0;
end
3'b011:
begin
clktime[3:0] = clktime[3:0] + 1'b1; //秒个位加一
if(clktime[3:0] == 4'ha)
begin
clktime[3:0] = 4'h0;
clktime[7:4] = clktime[15:12] + 1'b1; //秒十位加一
end
if(clktime[7:0] == 8'h60)
clktime[7:0] = 8'h0;
end
default:
clktime = clktime;
endcase
end
end
//数码管显示内容
always @(posedge clk)
begin
case({keyen[0],count1[3:1]}) //选择扫描显示数据
4'd0:disp_dat = clktime[3:0]; //秒个位
4'd1:disp_dat = clktime[7:4]; //秒十位
4'd2:disp_dat = 4'ha; //显示"-"
4'd3:disp_dat = clktime[11:8]; //分个位
4'd4:disp_dat = clktime[15:12]; //分十位
4'd5:disp_dat = 4'ha; //显示"-"
4'd6:disp_dat = clktime[19:16]; //时个位
4'd7:disp_dat = clktime[23:20]; //时十位
4'd8:disp_dat = hour[3:0]; //秒个位
4'd9:disp_dat = hour[7:4]; //秒十位
4'd10:disp_dat = 4'ha; //显示"-"
4'd11:disp_dat = hour[11:8]; //分个位
4'd12:disp_dat = hour[15:12]; //分十位
4'd13:disp_dat = 4'ha; //显示"-"
4'd14:disp_dat = hour[19:16]; //时个位
4'd15:disp_dat = hour[23:20]; //时十位
default:disp_dat = 4'ha; //显示"-"
endcase
//数码管选择
case(count1[3:1]) //选择数码管显示位
3'd0:dig_r = 8'b11111110; //选择第一个数码管显示
3'd1:dig_r = 8'b11111101; //选择第二个数码管显示
3'd2:dig_r = 8'b11111011; //选择第三个数码管显示
3'd3:dig_r = 8'b11110111; //选择第四个数码管显示
3'd4:dig_r = 8'b11101111; //选择第五个数码管显示
3'd5:dig_r = 8'b11011111; //选择第六个数码管显示
3'd6:dig_r = 8'b10111111; //选择第七个数码管显示
3'd7:dig_r = 8'b01111111; //选择第八个数码管显示
endcase
end
//数码管显示
always @(posedge clk)
begin
case(disp_dat)
4'h0:seg_r = 8'hc0; //显示 0
4'h1:seg_r = 8'hf9; //显示 1
4'h2:seg_r = 8'ha4; //显示 2
4'h3:seg_r = 8'hb0; //显示 3
4'h4:seg_r = 8'h99; //显示 4
4'h5:seg_r = 8'h92; //显示 5
4'h6:seg_r = 8'h82; //显示 6
4'h7:seg_r = 8'hf8; //显示 7
4'h8:seg_r = 8'h80; //显示 8
4'h9:seg_r = 8'h90; //显示 9
4'ha:seg_r = 8'hbf; //显示 -
default:seg_r = 8'hff; //不显示
endcase
if((count1[3:1]== 3'd2)&sec)
seg_r = 8'hff;
end
//时间计算及校准部分
always @(negedge sec)//计时处理
begin
if(!keyen[1]) //校准键是否有按下
begin
case(key_done[2:0])
3'b110:
begin
hour[19:16] = hour[19:16] + 1'b1; //时个位加一
if(hour[19:16] >= 4'ha)
begin
hour[19:16] = 4'h0;
hour[23:20] = hour[23:20] + 1'b1; //时十位加一
end
if(hour[23:16] >= 8'h24)
hour[23:16] = 8'h0;
end
3'b101:
begin
hour[11:8] = hour[11:8] + 1'b1; //分个位加一
if(hour[11:8] >= 4'ha)
begin
hour[11:8] = 4'h0;
hour[15:12] = hour[15:12] + 1'b1; //分十位加一
end
if(hour[15:12] >= 4'h6)
hour[15:8] = 8'h0;
end
3'b011:
begin
hour[3:0] = hour[3:0] + 1'b1; //秒个位加一
if(hour[3:0] >= 4'ha)
begin
hour[3:0] = 4'h0;
hour[7:4] = hour[15:12] + 1'b1; //秒十位加一
end
if(hour[7:4] >= 4'h6)
hour[7:0] = 8'h0;
end
default:
hour = hour;
endcase
end
else
begin
hour[3:0] = hour[3:0] + 1'b1; //秒加 1
if(hour[3:0] >= 4'ha)
begin
hour[3:0] = 4'h0;
hour[7:4] = hour[7:4] + 1'b1; // 秒的十位加一
if(hour[7:4] >= 4'h6)
begin
hour[7:4] = 4'h0;
hour[11:8] = hour[11:8] + 1'b1; //分个位加一
if(hour[11:8] >= 4'ha)
begin
hour[11:8] = 4'h0;
hour[15:12] = hour[15:12] + 1'b1; //分十位加一
if(hour[15:12] >= 4'h6)
begin
hour[15:12] = 4'h0;
hour[19:16] = hour[19:16] + 1'b1; //时个位加一
if(hour[19:16] >= 4'ha)
begin
hour[19:16] = 4'h0;
hour[23:20] = hour[23:20] + 1'b1; //时十位加一
end
if(hour[23:16] >= 8'h24)
hour[23:16] = 8'h0;
end
end
end
end
end
end
//蜂鸣器的计数定时器
always@(posedge clk)
begin
beep_count = beep_count + 1'b1; //计数器加 1
if((beep_count == beep_count_end)&&(!(beep_count_end == 16'hffff)))
begin
beep_count = 16'h0; //计数器清零
beep_r = ~beep_r; //取反输出信号
end
end
always @(posedge clk)
begin
if (!beepen)
case(hour[3:0])
4'h0:beep_count_end = 16'h6a88; //中音 6 的分频系数值
4'h1:beep_count_end = 16'h8637; //中音 4 的分频系数值
4'h2:beep_count_end = 16'h7794; //中音 5 的分频系数值
4'h3:beep_count_end = 16'hb327; //中音 1 的分频系数值
4'h5:beep_count_end = 16'hb327; //中音 1 的分频系数值
4'h6:beep_count_end = 16'h7794; //中音 5 的分频系数值
4'h7:beep_count_end = 16'h6a88; //中音 6 的分频系数值
4'h8:beep_count_end = 16'h8637; //中音 4 的分频系数值
default:beep_count_end = 16'hffff;
endcase
else if (!clktime_en)
begin
case(count1[8:5])
4'h0,4'h2,4'h6,4'h8:beep_count_end = 16'h2f74; //高音 7 的分频系数值
default:beep_count_end = 16'hffff;
endcase
end
else
beep_count_end = 16'hffff;
end
//闹铃使能控制
always @(posedge clk)
begin
if (!keyen[0]) //判断闹铃是否有取消
clktime_en = 1'b1;
else if ((clktime[23:0] <= hour[23:0])&(clktime[23:0]+10 >=hour[23:0]))
clktime_en = 1'b0;
end
endmodule
