FPGA学习笔记(七)——定时器设计、蜂鸣器驱动、变音蜂鸣器

这篇文章主要讲解两个知识点:驱动蜂鸣器(定时器设计)、变音蜂鸣器(层次化系统设计)。

定时器与计数器的区别不大。

定时器:核心单元本质上是个计数器,设置一个定时值,启动定时器后,计数器开始计数,计数满后产生计数满标志信号,提示设定的定时时间到达。

计数器:对脉冲信号进行计数,统计一确定时间段内该脉冲信号出现的次数,或者等待指定次数的脉冲信号出现后,产生相应标志。

定时时间可修改的定时器需要满足以下要求:

(1)定时器在不同的运行时段,可能需要的定时时间不同;

(2)定时器可以循环定时,也可以单次定时;

(3)定时时间到达时,需要产生标志信号。

(4)必要时,需要对外提供实时计数值。

一、定时器设计

本节设计一个定时器,能够支持以下功能:

  1. 该定时器的定时时间参数可以通过该模块的一个端口输入,通过调节端口上输入数据的值,就能修改其定时时间;

  1. 设置一个计数模式控制信号,当该信号为1时,设置为循环定时模式,当该信号为0时,设置为单次定时模式;

  1. 设置一个计数启动信号,在循环定时模式下,该信号为高电平使能计时,为低电平则停止计时。在单次计数模式下,该信号的一个单基准时钟周期的脉冲使能一次定时。

  1. 输出计时器实时计数值,该值将用于产生特定占空比的方波。

我们结合AC620开发板上的蜂鸣器,完成定时器的验证实验。

蜂鸣器按照驱动电路得不同可以分为有源蜂鸣器与无源蜂鸣器。有源蜂鸣器内部带震荡源,所以只要通电就会鸣叫;而无源蜂鸣器内部不带震荡源,因此如果用直流信号无法令其鸣叫,这就需要用200-5K的方波(声音频率)去驱动。

创建工程

编写verilog文件

我们要先想清楚要设计哪几个module。由于时序逻辑电路,所以要有clk和Rst_n。根据设计要求,我们要设置时间参数输入端CNT_ACC,计数模式控制端MODE,计数启动端Cnt_GO,输出实时计数端CNT_NOW,计时完成标志端Full_Flag。

设置好输入和输出

接下来就是根据要求设计敏感事件了,

复位端为0时置0

复位端为1且计数模式控制端为1时:循环计数。如果计数启动端为1且当前计数小于设定值时,计数+1,否则计数置0

复位端为1且计数模式控制端为0时:单次计数。而对于计数启动端,在单次计数模式下,该信号的一个单基准时钟周期的脉冲使能一次定时。这里就产生一个问题,单基准时间周期的脉冲怎么表示?

定义一个oneshot信号

再引入Full_Flag,整体代码如下

注意逗号,分号,下划线都要用英文格式,如果错用中文格式就会报错。

使用了66个逻辑单元,33个寄存器,69个引脚

编写testbench

先写timescale和module

拷贝并粘贴beep_test中的module部分,并起个新名字beep_test0

在这段代码上面拷贝粘贴输入输出部分,并将input替换成reg,将output替换成wire

按Ctrl+H可以进行查找和替换

例化

设置仿真流程

仿真与调试

RTL仿真出现了如下错误

我们双击错误提示,找到错误出现的位置

原来是因为出现了对oneshot先使用后定义的情况,但是在Quartus里这不会产生错误,因为Quartus是并行执行,ModelSim是顺序执行,这就出现问题了。所以我们把oneshot的定义挪到上面

改好了记得保存,编辑好的testbench文件别忘记导入,之前的博客中有介绍,否则不会出波形的。

转成十进制

我们发现了一个重要问题:输出计数端CNT_NOW没有计数,一直保持0。这说明我们的代码逻辑在某些地方是存在问题的,那么根据现象我们去回头找错误在哪。

目前cnt一直为0,最有可能的就是定时器一直处于置0状态,那么就要去看tb文件中与Rst_n有关的语句,看看是不是使它一直保持0。

这里是有问题的,跳出复位后,Rst_n应该等于1,因为复位端是低电平有效,保持低电平会使输出一直是0,继续检查,oneshot信号没有被设置为必须仅在MODE等于0时才变化。所以这样改

再跑一次波形就正常了,但是出现一个问题,单次计数时,多计了一个

这是因为Full_flag占了两个时钟周期,所以我们要让Full_flag提前一个时钟周期触发高电平

这回就没问题了。如果我们想让Full_Flag只占一个时钟周期,那就将>=改成==

现在的情况属于超前进位,如果我们想在此基础上将Full_Flag向后移位一个时钟周期呢?也就是从599挪到600。如果只是单纯的把-1去掉,那就会出现跟之前一样的问题(多计一位)

所以我们需要更加复杂的手段,那就是串联一个D触发器,使Full_Flag延迟一拍。

把原来的Full_Flag设为Full_Flag_r,把Full_Flag定义为寄存器类型,令Full_Flag等于Full_Flag_r,刚好可以占一个时钟周期,达到延迟的效果。

至此,定时器设计完成。接下来我们进行蜂鸣器测试

二、蜂鸣器驱动

设计顶层文件

设计一个顶层文件,命名为beep_top

保存到rtl文件夹中,进行分析和综合

接着,切换工程顶层文件为beep_top

再次进行分析和综合

我们发现逻辑单元为0,这是因为top文件中,我们只例化,而没调用,只有输入没有输出。重新编写一下beep_top,再进行分析和综合

引脚分配

先把I/O 标准都换成3.3V。

然后去查用户手册,分配个端口的引脚

clk是时钟,我们用的是50MHz的,对应的引脚是E1

Cnt_GO是计数启动端,对应的是按键,我们选择S0,也就是M16引脚。Rst_n连S2,E16引脚

蜂鸣器连L16

接着进行全编译,无问题

最后连上板子,进行板机验证

板机验证

点击start之后,板子的蜂鸣器会发出声音,按住S0会停止发声,松开就会继续发声。

这是因为按键默认高电平,按下S0就会使Cnt_GO低电平,使CNT_NOW=cnt=0,蜂鸣器低电平。

那么,这个简单的蜂鸣器驱动实验就完成了。接下来可以做一个比较复杂的实验

三、变音蜂鸣器(电子琴)

设计要求

设计一个能够变频率驱动蜂鸣器的系统,该系统包括蜂鸣器定频驱动(定时器)和蜂鸣器频率设定电路。

每个音调的的频率如下表所示

定时器的定时设定参数输入端口,一般称作预重装值(𝑐𝑜𝑢𝑛𝑡𝑒𝑟_𝑎𝑟𝑟)。

预重装值与对应频率(计数周期的倒数)的关系为:

𝑐𝑜𝑢𝑛𝑡𝑒𝑟_𝑎𝑟𝑟=𝑓_𝑐𝑙𝑘/𝑓_𝑝𝑤𝑚 −1=50000000/5000−1=9999

调整蜂鸣器的驱动频率,实质就是调整该参数的值,下表为当计数基准时钟为50MHz时,期望频率值与预重装值的关系。

预重装值就是我们设的CNT_ACC,之后我们将其都修正为CNT_ARR (Ctrl+F)

编写verilog文件

我们写一个查找表(Look-Up-Table)的代码,使用了case语句。ARR作为被赋值的对象,一定要定义成reg类型。

分析和综合没问题,然后我们在beep_top里调用它,我们是用层次化系统设计,在顶层文件调用多个模块。设计了两个定时器,第一个用于驱动蜂鸣器,第二个用于音调切换。

先分析和综合,然后来看一下RTL视图

结构清晰,通过beep_test1定时器驱动sound_lut以生成相应的预重装值(CNT_ARR),传输到beep_test0定时器来改变蜂鸣器的音调。

板机验证

首先进行全编译

直接双击Assembler也可以,因为最后两步我们用不到,可以节约时间

接下来运行,确实可以听到蜂鸣器在发出哆来咪发梭拉西哆,实验成功。

  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值