目录

  • 一、知识了解
  • 二、模块设计
  • 三、程序实现
  • 四、管脚配置及结果展示
  • 五、写给小白看的

上一篇博文:【入门学习一】基于 FPGA 使用 Verilog 实现按键点灯代码及原理讲解

功能描述:通过前面一篇学习的按键使用,本篇文章进一步使用按键,通过点击按键后,可以让蜂鸣器播放不同的曲子

一、知识了解

PWM 技术控制蜂鸣器不同声响

  • 所谓 PWM 就是脉冲宽度调制,本文通过变化输出的脉冲频率来使得蜂鸣器发出不同的音符声音。
  • 下表是每个音符所对应的频率及半周期。
音符 对应频率(Hz) 时钟周期数
1 523 95600
2 587 85150
3 659 75850
4 698 71600
5 784 63750
6 880 56800
7 988 50600
  • 音符对应的时钟周期数越多,那么它的一个周期就越长,从下图可以看出来,音符从 1 到 7,它的一个周期逐次递减。
  • 所以说,如何让蜂鸣器发声,只需要往蜂鸣器的管脚输出连续的高低变化电平即可。
  • 就拿我这个板子的蜂鸣器来说,图中有个 PNP 型三极管,当基极为高电平时截止,也就是高电平的发射极 Q7 无法导通到集电极使蜂鸣器发声,低电平时导通,此时高电平的发射极 Q7 可以导通到集电极使蜂鸣器发声。
  • 要让蜂鸣器发出不同的声音,采用 PWM 原理改变基极的脉冲宽度即可,也就是前面提到的,不同音符的一个周期所对应不同宽度脉冲。

设置每个音符持续时长

  • 当输出一个周期的脉冲给蜂鸣器,它肯定会响,但是有一点,一个周期的脉冲时长只有几百毫秒,我们能听到吗?当然听不到,所以需要不断地重复一个周期的脉冲,使它连续输出波形长达 1 s 或者 0.5 s,也就是一个音符的持续时长,那么我们就肯定能听到了。
  • 这里我以音符 1 为基准,让它的一个周期重复 250 次,那么它总时长为 95600×250=23,900,00095600×250=23,900,00095600×250=23,900,000 个时序周期,其它的音符总时长也为 23,900,00023,900,00023,900,000 个时序周期,所以不难得出
    每个音符的重复次数=23,900,000该音符一个周期时序数每个音符的重复次数=\frac{23,900,000}{该音符一个周期时序数}\\ 每个音符的重复次数=该音符一个周期时序数23,900,000​
  • 音符1 =23,900,00095600≈250(次)= \frac{23,900,000}{95600}≈250(次)=9560023,900,000​≈250(次)
    音符2 =23,900,00085150≈281(次)= \frac{23,900,000}{85150}≈281(次)=8515023,900,000​≈281(次)
    音符3 =23,900,00075850≈315(次)= \frac{23,900,000}{75850}≈315(次)=7585023,900,000​≈315(次)
    音符4 =23,900,00071600≈334(次)= \frac{23,900,000}{71600}≈334(次)=7160023,900,000​≈334(次)
    音符5 =23,900,00063750≈375(次)= \frac{23,900,000}{63750}≈375(次)=6375023,900,000​≈375(次)
    音符6 =23,900,00056800≈421(次)= \frac{23,900,000}{56800}≈421(次)=5680023,900,000​≈421(次)
    音符7 =23,900,00050600≈472(次)= \frac{23,900,000}{50600}≈472(次)=5060023,900,000​≈472(次)

歌谱

  • 歌谱如下:

二、模块设计

  • 由于程序比较简单,所以就只需要一个蜂鸣器 .v 文件即可,它也是顶层模块,再在其中引用按键模块。
  • 其中按键模块 key_debounce.v 在前一篇博文中已经贴出了,这里就不再重复贴出代码以及讲解了。

三、程序实现

蜂鸣器模块 pwm_buzzer.v

module pwm_buzzer(input              clk     ,       //时钟输入input             rst_n   ,       //复位按键输入input               key_in  ,       //按键输入output    reg         buzzer          //驱动蜂鸣器);wire               press   ;       //线,连接按键标志信号//引用按键模块key_debounce u_key_debounce(.clk         (clk    ),.rst_n            (rst_n  ),.key          (key_in ),.press            (press  ));//定义音符时序周期数localparam            M0  = 98800,M1 = 95600,M2 = 85150,M3 = 75850,M4 = 71600,M5  = 63750,M6    = 56800,M7 = 50600;//信号定义reg      [16:0]      cnt0        ;   //计数每个音符对应的时序周期reg      [10:0]      cnt1        ;   //计数每个音符重复次数reg     [5 :0]      cnt2        ;   //计数曲谱中音符个数reg      [16:0]      pre_set     ;   //预装载值wire  [16:0]      pre_div     ;   //占空比reg        [10:0]      cishu       ;   //定义不同音符重复不同次数wire  [10:0]      cishu_div   ;   //音符重复次数占空比reg              flag        ;   //歌曲种类标志:0小星星,1两只老虎reg        [5 :0]      YINFU       ;   //定义曲谱中音符个数//歌曲种类标志位always @(posedge clk or negedge rst_n) beginif(!rst_n) beginflag <= 1'b0;endelse if(press) beginflag <= ~flag;endend//重设音符的个数always @(posedge clk or negedge rst_n) beginif(!rst_n)YINFU <= 48;else if(flag == 1'b1)YINFU <= 36;elseYINFU <= 48;end//计数每个音符的周期,也就是表示音符的一个周期always @(posedge clk or negedge rst_n) beginif(!rst_n) begincnt0 <= 0;endelse if(press)cnt0 <= 0;else beginif(cnt0 == pre_set - 1)cnt0 <= 0;elsecnt0 <= cnt0 + 1;endend//计数每个音符重复次数,也就是表示一个音符的响鸣持续时长always @(posedge clk or negedge rst_n) beginif(!rst_n) begincnt1 <= 0;endelse if(press)cnt1 <= 0;else beginif(cnt0 == pre_set - 1)beginif(cnt1 == cishu)cnt1 <= 0;elsecnt1 <= cnt1 + 1;endendend//计数有多少个音符,也就是曲谱中有共多少个音符always @(posedge clk or negedge rst_n) beginif(!rst_n) begincnt2 <= 0;endelse if(press)cnt2 <= 0;else beginif(cnt1 == cishu && cnt0 == pre_set - 1) beginif(cnt2 == YINFU - 1) begincnt2 <= 0;endelsecnt2 <= cnt2 + 1;endendend//定义音符重复次数always @(*) begincase(pre_set)M0:cishu = 242;M1:cishu = 250;M2:cishu = 281;M3:cishu = 315;M4:cishu = 334;M5:cishu = 375;M6:cishu = 421;M7:cishu = 472;endcaseend//曲谱定义always @(*) beginif(flag == 1'b0) begincase(cnt2)   //小星星歌谱0 : pre_set = M1;1 : pre_set = M1;2 : pre_set = M5;3 : pre_set = M5;4 : pre_set = M6;5 : pre_set = M6;6 : pre_set = M5;7 : pre_set = M0;8 : pre_set = M4;9 : pre_set = M4;10: pre_set = M3;11: pre_set = M3;12: pre_set = M2;13: pre_set = M2;14: pre_set = M1;15: pre_set = M0;16: pre_set = M5;17: pre_set = M5;18: pre_set = M4;19: pre_set = M4;20: pre_set = M3;21: pre_set = M3;22: pre_set = M2;23: pre_set = M0;24: pre_set = M5;25: pre_set = M5;26: pre_set = M4;27: pre_set = M4;28: pre_set = M3;29: pre_set = M3;30: pre_set = M2;31: pre_set = M0;32: pre_set = M1;33: pre_set = M1;34: pre_set = M5;35: pre_set = M5;36: pre_set = M6;37: pre_set = M6;38: pre_set = M5;39: pre_set = M0;40: pre_set = M4;41: pre_set = M4;42: pre_set = M3;43: pre_set = M3;44: pre_set = M2;45: pre_set = M2;46: pre_set = M1;47: pre_set = M0;endcaseendelse begincase(cnt2)   //两只老虎歌谱0 : pre_set = M1;1 : pre_set = M2;2 : pre_set = M3;3 : pre_set = M1;4 : pre_set = M1;5 : pre_set = M2;6 : pre_set = M3;7 : pre_set = M1;8 : pre_set = M3;9 : pre_set = M4;10: pre_set = M5;11: pre_set = M0;12: pre_set = M3;13: pre_set = M4;14: pre_set = M5;15: pre_set = M0;16: pre_set = M5;17: pre_set = M6;18: pre_set = M5;19: pre_set = M4;20: pre_set = M3;21: pre_set = M1;22: pre_set = M5;23: pre_set = M6;24: pre_set = M5;25: pre_set = M4;26: pre_set = M3;27: pre_set = M1;28: pre_set = M2;29: pre_set = M5;30: pre_set = M1;31: pre_set = M0;32: pre_set = M2;33: pre_set = M5;34: pre_set = M1;35: pre_set = M0;endcaseendendassign pre_div = pre_set >> 1;  //除以2assign cishu_div = cishu * 4 / 5;//向蜂鸣器输出脉冲always @(posedge clk or negedge rst_n) beginif(!rst_n) beginbuzzer <= 1'b1;endelse if(pre_set != M0) beginif(cnt1 < cishu_div) beginif(cnt0 < pre_div) beginbuzzer <= 1'b1;endelse beginbuzzer <= 1'b0;endendelse beginbuzzer <= 1'b1;endendelsebuzzer <= 1'b1;endendmodule
  • 其中要说明的一点是,曲谱中有一个 “-” 的符号,我将它定位 M0,可以在向蜂鸣器输出波形的时序逻辑中,可以看到当音符为 M0 时,它输出高电平,也就是让它此时不发声。
  • 还有一点时,我设置了一个时间占空比为 4/5,也就是前 4/5 发声,后 1/5 的时间不发声,如果不设置时间占空比也可以,只不过它会连续不断的播放曲谱,感觉就不好听了,所以这里设置了个时间占空比。

代码执行过程:

  • 首先设定 flag 的默认值为 0,那么它最开始会自动开始循环播放小星星。
  • 其中,最开始会计数一个音符的周期,直至计满不同音符所对应的周期数,它就会归零,重新开始计数。
  • 计满一个周期后,计数周期重复次数的计数器就会 +1,直至计满不同音符所对应的重复次数,它就会归零,重新开始计数。
  • 然后开始该歌曲的下一个音符。
  • 随着 cnt2 的值不断累加,其所代表的音符类别也在不断改变,这里使用一个 case 语句来罗列一整首歌的音符顺序。
  • 当按下按键后,press 脉冲信号传递过来,改变 flag 的值,以及重设 cnt0、cnt1、cnt2 的值归零。
  • 当 flag 的值变为 1’b1,那么 if 条件语句中,就会选择第二个 case ,也就是第二首歌。
  • 其实我这样讲解代码执行过程并不正确,这样讲只是便于理解,应该配置时钟去看语句的执行情况,说明一点,assign 语句是随时钟变化的,也就是说,时钟信号变化一次,它就执行一次,品一品这个意思。

四、管脚配置及结果展示

管脚配置

  • 要按照自己开发板的管脚进行配置。

结果展示

  • 最后结果是指唱歌发生,所以没办法展示出来,这个代码是我自己手写的,亲测可以成功,按键按下后,也可以切歌。
  • 如果你烧录程序后还是无法发声,那么就是管脚的配置问题,具体查一下你自己开发板的管脚原理图,并配置正确。
  • 当然,如果想实现发一个声,LED 灯闪亮一下,也是很简单可以做到的,如果有兴趣可以做一下。

下一篇博文:【入门学习三】基于 FPGA 使用 Verilog 实现按键状态机代码及原理详解

五、写给小白看的

Verilog 语言如何执行的?????

  • Verilog 语言其实和 C 语言差不多,只不过多一个时钟信号而已
  • 在 FPGA 中,有一个时钟模块会一直产生一个时钟信号,就像下面这样
  • 信号由低电平变为高电平时,为上升沿;由高电平变低电平时,为下降沿

时序逻辑

always @(posedge clk or negedge rst_n) begin
  • 这表示时序逻辑,什么意思呢?
  • posedge 表示检测上升沿
    clk 是文件首部定义的时钟信号接入,也就是说此时的 clk 就是上图所示的时钟信号
    posedge clk 就是检测时钟信号 clk 的上升沿,也就是说每遇到一个时钟信号的上升沿,这个时序逻辑中的代码就执行一遍
  • negedge 表示检测下降沿
    rst_n 是文件首部定义的按键信号接入,也就是说此时的 rst_n 就是按键信号,一个按键在没有按下的情况一直都是高电平,按下后就变为低电平,抬起后又变为高电平
    negedge rst_n 就是检测按键信号 rst_n 的下降沿,也就是说每遇到一个按键信号的下降沿,这个时序逻辑中的代码就执行一遍
  • 是不是很像 C 语言的函数,括号中的内容其实就是参数,只不过传递的参数是时钟模块产生的连续不断的时钟信号以及按键信号
  • 总结一下它什么时候执行:①每一个周期的时钟信号的上升沿到了,时序逻辑中的代码都会执行一次,或许一秒内,它执行了几千上万次了②按下 rst_n 按键后,它会执行一次

组合逻辑

always @(*)
  • 什么是组合逻辑?看代码来得更快
  • 任凭时钟信号再多,这个组合逻辑中的代码都不会执行,除非!!!!cnt2 这个变量的值改变了,always 中的代码才会从头开始执行,比如 cnt2 从 0 变为 1 ,那么组合逻辑就执行一次,从 if 条件开始执行
  • 那个 * 啥意思呢?就是声明我组合逻辑中的所有变量来自组合逻辑块的外面
  • 总结一下它什么时候执行:只有当组合逻辑中的变量的值改变了,组合逻辑才会执行

计数器

  • FPGA 中最基础的便是计数器,计数有啥用呢?怎么计数呢?
  • 计数有啥用?计数的应用领域十分广泛,比如设计一个时钟肯定要计数吧,VGA显示肯定要计数吧(涉及到行同步和列同步),蜂鸣器要计数吧等等等,基本上可以说,计数完全是个基础,灵活的计数可以产生不同的效果
  • 怎么计数呢?很简单啊,那个时序逻辑的特点是什么?就是每一个周期的时钟信号的上升沿有了,它就执行一次,那么完全可以这样做。定义一个变量,在时序逻辑中不断地累加+1,就可以对时钟周期进行计数了,当然不可能永无止境的计数下去,可以设定一个阈值啊,比如我让它计数到 200 就归零,这样不断地计数到 200 个时钟周期,它就归零,200 它就归零,200 它就归零······
  • 这样就可以产生一个条件,什么条件呢?比如说 cnt 是个计数变量,cnt == 200 -1(减 1 是因为它是从 0 开始计数的),这就是一个计数器计满 200 个时钟周期的条件
  • 有这个条件能干啥呢?我可以对 200 计数啊!我可以再定义个计数变量,计数有多少个 200 ,1 个 200,2 个 200······
  • 如此反复套娃,就是计数器的基本使用了,接下来我们就可以通过计数器计满的条件去改变其它自己想改变的值
  • 比如本文中的歌曲,也是套娃,一层一层的套:首先计数一个音符对应频率的时钟周期数(比如音符 1 的时钟周期数是 95600),然后计数一个音符持续时长(也就是 N 个 95600,差不多也就 0.8 秒左右时间),然后再计数一首歌有多少个音符,最后再计数歌曲的种类
  • 是不是很像一层层的套娃,计数器的总结就到这里了,希望各位小白能够理解透彻~

【入门学习二】基于 FPGA 使用 Verilog 实现蜂鸣器响动的代码及原理讲解相关推荐

  1. 【入门学习四】基于 FPGA 使用 Verilog 实现串口回传通信代码及原理讲解

    目录 一.相关知识 二.模块设计 三.代码设计 3.1 串口接收模块 3.2 控制模块 3.3 串口发送模块 四.FIFO 核引用 五.管脚定义及结果展示 上一篇博文:[入门学习三]基于 FPGA 使 ...

  2. FPGA零基础学习:基于FPGA的多路选择器设计(附代码)

    FPGA零基础学习:基于FPGA的多路选择器设计(附代码) 大侠好,欢迎来到FPGA技术江湖.本系列将带来FPGA的系统性学习,从最基本的数字电路基础开始,最详细操作步骤,最直白的言语描述,手把手的& ...

  3. FPGA零基础学习:基于FPGA的二进制转BCD设计(附代码)

    FPGA零基础学习:基于FPGA的二进制转BCD设计(附代码) 本系列将带来FPGA的系统性学习,从最基本的数字电路基础开始,最详细操作步骤,最直白的言语描述,手把手的"傻瓜式"讲 ...

  4. hadoop hive hbase 入门学习 (二)

    hadoop 自学系列                hadoop hive hbase 入门学习 (一) hadoop安装.hdfs学习及mapreduce学习 hadoop 软件下载 (hadoo ...

  5. JBox2d入门学习二 -----我的小鸟

    入门学习一当中我学会了如何定义并且创建一个世界,在世界当中定义并且创建一个刚体,并尝试给刚体一个力.最近比较忙..现在抽空实现了一个类似于愤怒小鸟的例子,先看看图吧.   贴代码,注解写的比较详细了, ...

  6. OpenGL入门学习[二] 绘制简单的几何图形

    OpenGL入门学习[二] 本次课程所要讲的是绘制简单的几何图形,在实际绘制之前,让我们先熟悉一些概念. 一.点.直线和多边形 我们知道数学(具体的说,是几何学)中有点.直线和多边形的概念,但这些概念 ...

  7. 实验二 基于FPGA的分频器的设计(基本任务:设计一个分频器,输入信号50MHz,输出信号频率分别为1KHz、500Hz及1Hz。拓展任务1:用按键或开关控制蜂鸣器的响与不响。拓展任务2:用按键或开)

    实验二 基于FPGA的分频器的设计 1. 实验目的: (1) 掌握QuartusⅡ软件的层次型设计方法: (2) 掌握元件封装及调用方法: (3) 熟悉FPGA实验平台,掌握引脚锁定及下载. 2. 实 ...

  8. 计算机组成原理课程设计:基于FPGA的Verilog模型机设计。

    理解简单模型机的工作原理,理解程序计数器,算数逻辑运算单元,控制单元,的工作原理.学会设计以及使用指令完成一定的功能,并将程序写入FPGA开发板并结合led灯数码管予以显示. 通过模型机设计可以掌握用 ...

  9. 知识图谱论文阅读(八)【转】推荐系统遇上深度学习(二十六)--知识图谱与推荐系统结合之DKN模型原理及实现

    学习的博客: 推荐系统遇上深度学习(二十六)–知识图谱与推荐系统结合之DKN模型原理及实现 知识图谱特征学习的模型分类汇总 知识图谱嵌入(KGE):方法和应用的综述 论文: Knowledge Gra ...

最新文章

  1. pythonshell画图_Python Shell下使用matplotlib
  2. js截屏代码_JavaScript网页截屏方法,你get到了嘛?
  3. 网站哪些细节做不好会导致网站的跳出率过高?
  4. swift中使用core data
  5. 地理数据库 (geodatabase) 的架构
  6. Tableau研学小课堂(part5)--参数
  7. 安徽大学计算机教学平台c语言作业,安徽大学计算机教学部练习题与答案1.pdf
  8. rtsp摘要认证协议(Response计算方法)
  9. java开源服务框架_Java框架服务
  10. Redis基础1(定义及基础)
  11. C++ 的Tool工具收集
  12. 程序员未来前景如何?大龄程序员出路在哪里?
  13. c语言位运算知乎,07-C语言运算符-指趣学院
  14. protobuf编码原理及其在schema格式转换的应用
  15. SQL:PostgreSQL设置自增序列
  16. Android应用签名方法
  17. 影视搜索播放PHP源码_可对接资源网
  18. windows电脑系统自带的画图工具如何实现自由拼图
  19. 【笔记:Spring】
  20. 所有学习资源都给你你汇总好啦!

热门文章

  1. 飞鱼:比python更好用的生信神器!
  2. 【科技百咖】人大金仓:先定一个小目标,比如做中国No.1的数据库
  3. 写一首关于教育的诗,500字
  4. python快递分拣小程
  5. 6-2 增加区块容量:扩容与隔离见证
  6. C# CAD 二次开发(一) -前期搜集资料篇
  7. 职高计算机专业理论知识,浅论职高计算机专业教学
  8. 关于Description: Parameter 0 of method customRouteLocator in com.powernode.config.RouteConfig require
  9. labVIEW while循环中的移位寄存器的用法及作用
  10. UCML异常提示:无效URI