国外课栈 - 国外电子信息技术

FPGA微型板Verilog定时蜂鸣

 二维码 30
文章附图

您将基于通用的display_7_seg,decoder_7_seg和debouncer模块构建一个倒数计时器,其功能实际上类似于真实产品。 即使是表面上看似简单的设备(如计时器)也可能难以设计。 为了简化设计,通常使用称为状态机的表示。 该技术不是FPGA独有的,它是一种以图表形式进行系统行为建模的好方法。 使用发声模块来驱动蜂鸣器,一旦倒计时结束,蜂鸣器就会响起。

状态机

状态机扎根于数学,因此,就像数学中的许多事物一样,听起来确实很聪明,但是归结起来却很简单。 您还将找到状态机和状态机图,它们被称为有限状态机(FSM)和状态转换图(STD)。

基本概念是,系统会保持稳定状态,直到某个触发器导致其转换到另一状态为止。 此过渡可能会导致在过渡期间发生某些动作。 状态在图中以方框或气泡的形式绘制,并以带有箭头的线的过渡形式出现,箭头从一个方框到另一个方框或从一个方框回到自身。 在过渡线上绘制了两部分的标签。 标签的顶部是过渡发生的条件(例如,按下了一个按钮),然后有一条水平线,在该线下显示了过渡期间需要执行的任何操作(例如,转弯 LED亮)。

状态机设计

为了使该示例在所有三个示例板上都能使用,它将使用三个七段数字。 两位显示秒,一位显示分钟。 “向上/向下”按钮将设置分钟,“开始/停止”按钮将开始倒数计时,“取消”按钮会将计时器重置为上次使用的分钟数。 当倒数到零时,蜂鸣器将连接到板上以发出声音。 该项目的状态机如下图所示。

初始状态为SETTING,在此状态下,“ Up”和“ Down”按钮将从分钟数显示的数字中增加或减少1。 按下“开始/停止”按钮后,项目将进入“运行”状态。 按“开始/停止”将立即使您回到“设置”状态,如按“取消”一样。 但是,按“取消”也会重设时间。 在SETTING状态下,每秒显示的时间将减少1秒,直到倒数到000,此时进入BEEPING状态。

Beeping声响起,直到按下“取消”按钮,此时系统返回到设置状态,并且时间已重置,可以再次使用计时器。

硬件

连接

模块结构

使用了很多按键,需要使用很多消抖动模块。

用户约束文件

接下来列出Mojo和IO Shield的用户约束文件(UCF)。 主要区别仅涉及引脚分配以及开关输入的上拉/下拉差异。

即使项目仅使用三位数字,所有四位数字都在UCF中定义,因此未使用的数字可以被清空。

定义了一个新的网络蜂鸣器,为连接到GPIO引脚P97的压电蜂鸣器产生声音。

Verilog模块 - 定时器

计时器模块的声明将输入和输出与UCF中的网络链接起来:

首先,为每个按钮定义连线,然后将防抖器分配给与防抖器的trans_dn(向下转换)输出相连的每根导线:

寄存器(alarm_on)用于打开和关闭蜂鸣器。 这链接到警报实例的启用输入。 此实例还提供了CLK输入和GPIO引脚以打开蜂鸣器:

为了跟踪时间,分别为秒(secs),数十秒(ten_secs)和分钟(mins)定义了单独的4位寄存器。 另一个寄存器(mins_stored)用于记录计时器启动之前最初设置的分钟数的副本,以便在计时器返回其SETTING状态时记住上一次使用的时间。

这可以通过将该数字的值设置为10来完成。display_7_seg和coder_7_seg不会显示超过9的数字,因此将使该数字保持空白。

26位寄存器预分频器用于在需要时通过计数每个FPGA时钟周期直至达到某个值来计数CLK滴答信号,直到达到某个值为止:

display_7_seg模块在相应的数字位置链接到前面所述的寄存器。 一旦将它们链接起来,我们只需要更改寄存器值secs,ten_secs和mins,显示就会刷新:

状态机的Verilog代码非常紧密地遵循上述状态机图。 2位寄存器(状态)用于跟踪当前状态。 为了您不必使用数字来引用状态,localparam命令允许您定义三个值,例如传统编程语言中称为SETTING,RUNNING和BEEPING的常量,以对应于项目的三个状态:

在Verilog中编写好的,干净的状态机实现的关键是,通过使用任务将在过渡期间发生的动作移出always块的主体,从而使always块尽可能短。 否则,它将变得巨大。

case语句用于分隔每个状态的代码。 如果您有许多条件依赖相同的值(在这种情况下为state),那么case语句可以代替使用许多if命令。 看一下case语句中的第一个子句,我们有

这将执行任务handle_settings中包含的一些逻辑,您很快就会看到。 然后,它将处理从SETTING状态到RUNNING状态的唯一可能的转换,如果按下“开始/停止”按钮将发生这种转换。

第一个动作是将mins存入mins_stored,然后再设置state的新值。 RUNNING状态的代码更加复杂,因为它有3种转换要从该状态进行处理。 首先调用一个任务decrement_time,该任务将递减1秒,然后处理其他过渡。 如果按下“开始/停止”按钮,它会直接跳回到设置状态。 如果按下“取消”按钮,也会发生同样的情况,但是首先调用reset_time任务。

最后,如果secs,ten_secs和mins均为零,则倒数计时已完成,并且启用了警报模块以启动蜂鸣器鸣响,并且状态设置为BEEPING:

BEEPING状态仅需要处理在按下“取消”按钮时发生的过渡:

timer.v中的其余代码实现了always块中使用的任务。 在Verilog中,出于与在编程语言中使用函数相同的原因,您使用任务或其相对于函数的功能。 它们允许您通过将功能分成更易于管理的块来构造代码并使代码更具可读性。 函数类似于任务,除了它返回值(即具有输出)。

您可能已经注意到,在always块中的多个位置调用了reset_time任务。 通过将其分成一个任务,不仅使代码更易于理解,而且还消除了在多个地方重复同一代码的潜在陷阱,这可能会导致代码改进的问题。 一个地方,但并非到处都在使用。

任务以关键字task开头,后跟任务名称。 这些任务中的第一个是handle_settings:

此任务涉及按下“向上”和“向下”按钮以增加和减少分钟寄存器。 如果您将分钟数增加到9或1以下,它也可以处理环绕情况。

接下来列出的decrement_time任务负责将时间每秒减少1秒。 它使用预分频器寄存器在CLK的每五千万分之一周期执行一次操作。 值49999999将与您的FPGA的时钟频率(–1)相匹配,或者您的计时器将运行得太快或太慢,尽管您不太可能注意到五十分之一秒。

达到第50万个–1刻度后,必须将预分频器设置回0。寄存器秒数递减,并且if语句的级联集确保当前一位达到0时,其他位数也递减:

最后一个任务(reset_time)是最简单的:它只是将secs和ten_secs寄存器设置回0,并将mins设置为在启动计时器时在mins_store中记录的mins的最后使用值:

测试

源码

阅读完整文档

文章分类: 嵌入式VerilogFPGA
分享到: