基于FPGA的电子琴设计(按键和蜂鸣器)----第一版

fpga · 浏览次数 : 0

小编点评

**设计思想:** * 监听按键输入并检测按键稳定时间。 * 对消抖后的按键信号进行边沿检测。 * 计算半周期数。 * 根据半周期数,控制蜂鸣器输出方波。 * 在按键按下后,计时20ms,如果超过,则蜂鸣器输出取反。 **关键部分:** * **KeyWave检测:**用于检测按键按下和释放的时间。 * **计数器:**用于记录半周期数。 * **蜂鸣器控制:**用于输出方波。 **设计步骤:** 1. 监听按键输入。 2. 检查按键稳定时间。 3. 对消抖后的按键信号进行边沿检测。 4. 计算半周期数。 5. 根据半周期数,控制蜂鸣器输出方波。 6. 在按键按下后,计时20ms,如果超过,则蜂鸣器输出取反。 **仿真设置:** * 消抖模块中的20ms进行调小。 * 测试时,将不同按键输入,观察蜂鸣器的响应。 **资源链接:** * 设计代码: * 博客:pan.baidu.com/s/1VGbPqU9O_k2UBtOGkMJjIQ * 设计工程: * 视频:bilibili.com/video/BV1Gk4y1X7XT/?spm_id_from=333.999.0.0&vd_source=b5405faeab8632f02533bcbfc5e52e55 * 资源来源:网络

正文

欢迎各位朋友关注“郝旭帅电子设计团队”,本篇为各位朋友介绍基于FPGA的电子琴设计(按键和蜂鸣器)----第一版。

功能说明:

  外部输入七个按键,分别对应音符的“1、2、3、4、5、6、7”,唱作do、re、mi、fa、sol、la、si。当某个按键按下时,蜂鸣器发出对应的声音----1. 默认发出0.2秒(可以调整)。2. 蜂鸣器发出对应的中音。 

使用平台:本次设计应用Altera的平台设计(芯片:EP4CE10F17C8N)。

仿真平台:Modelsim。

作者QQ:746833924

说明:本篇设计中不涉及到IP和原语,代码在其他平台依然可以适用;当其他板卡电路不同时,会导致不同的现象出现,如有需要修改代码请联系作者;如需作者使用的板卡,请联系作者;

 

蜂鸣器是一种一体化结构的电子讯响器,采用直流电压供电,广泛应用于计算机、打印机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件

 

 

蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。

 

压电式蜂鸣器 压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器及共鸣箱、外壳等组成。有的压电式蜂鸣器外壳上还装有发光二极管。多谐振荡器由晶体管或集成电路构成。当接通电源后(1.5~15V直流工作电压),多谐振荡器起振,输出1.5~2.5kHZ的音频信号,阻抗匹配器推动压电蜂鸣片发声。
电磁式蜂鸣器电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。接通电源后,振荡器产生的音频信号电流通过电磁线圈,使电磁线圈产生磁场。振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。
按照内部有无震荡源可以分为有源蜂鸣器和无源蜂鸣器。有源蜂鸣器内部带震荡源,所以只要一通电就会发出声音;而无源内部不带震荡源,所以如果用直流信号无法令其鸣叫。必须用一定频率的方波去驱动它。
蜂鸣器给予不同的频率是可以发出近似1、2、3、4、5、6、7这七个基本音符。

设计思想如下:

 

        key_ctrl模块负责将外部的按键信号进行消抖,并且产生对应边沿变化时的脉冲;piano_beep_ctrl模块负责根据脉冲信号产生输出控制脉冲(一个周期)和应该输出音符对应波形的半周期数;beep_ctrl模块根据piano_beep_ctrl产生脉冲时,接收半周期数,然后产生对应的方波持续0.2秒。

 

key_ctrl模块设计思想为:按键信号是由外部机械式按键产生,每次按下或者抬起时,会产生一定的抖动。如果直接对其进行边沿检测就会导致多次触发。故而需要设计按键消抖,进而对消抖之后的波形进行边沿检测。消抖原理为:外部按键信号发生改变后,如果能够持续20ms,没有新的改变,就认为此次改变不是抖动,而是真正的按下,然后进行采样即可。

 

// 记录任意边沿之后没有遇到新的边沿的时间长度是否达到20Ms 

//--------------------------------------------------------------------------------------  

always@(posedgeclk)begin

   if(rst_n ==1'b0)

     cnt_20ms <=20'd0;

   else

     if(pulse_key_negedge ==1'b1||pulse_key_posedge ==1'b1)

       cnt_20ms <=20'd1;

     else

       if(cnt_20ms >20'd0&&cnt_20ms <T_20ms)

         cnt_20ms <=cnt_20ms +1'b1;

       else

           cnt_20ms <=20'd0;

 end

// --------------------------------------------------------------------------------------- 

// 任意边沿之后没有遇到新的边沿的时间长度达到20Ms,认为按键稳定,此时采样 

//--------------------------------------------------------------------------------------    

initial key_wave = 1'b1;


always@(posedgeclk)begin

   if(rst_n ==1'b0)

     key_wave <=1'b1;

   else

     if(cnt_20ms ==T_20ms)

       key_wave <=key_rr;

     else

       key_wave <=key_wave;

 end

//--------------------------------------------------------------------------------------

// 对消抖之后的按键信号进行边沿检测 

//---------------------------------------------------------------------------------------------

initialkey_wave_r =1'b1;

always@(posedgeclk)key_wave_r <=key_wave;

assignflag_neg =(key_wave_r ==1'b1&&key_wave ==1'b0)?1'b1:1'b0;

assignflag_pos =(key_wave_r ==1'b0&&key_wave ==1'b1)?1'b1:1'b0;

//--------------------------------------------------------------------------------------------  

 

piano_beep_crtl模块中,计算半周期数的方法如下:

 

假如要发出中音“1”,就需要输出频率为523.3hz的方波。此方波对应的周期为1910949.742021785 ns,我们取1910950ns。因为我们使用的基本时钟为50MHz,周期为20ns,以及我们预计计数一半取反来生成方波,所以只需要计数一般,即:1910950/2/20 = 47774;

 

将其他的全部计算出来:

 

// 1    :     523.3Hz       num : 47774

// 2    :     587.3Hz       num : 42568

// 3    :     659.3Hz       num : 37919

// 4    :     698.5Hz       num : 35791

// 5    :     784Hz          num : 31888

// 6    :     880Hz          num : 28409

// 7    :     987.8Hz       num : 25309



// 产生输出脉冲:无论外部那个脉冲有效,都对外产生一个脉冲

 always @ (posedge clk) begin

   if (rst_n == 1'b0)

     flag <= 1'b0;

   else

     flag <= |key_flag;

 end

  

// 如果外部多个脉冲同时有效,那么输出num为0

 always @ (posedge clk) begin

   if (rst_n == 14'b0)

     num <= 32'd0;

   else

     case (key_flag)

       7'b100_0000    :   num <= 32'd47774;

       7'b010_0000    :   num <= 32'd42568;

       7'b001_0000    :   num <= 32'd37919;

       7'b000_1000    :   num <= 32'd35791;

       7'b000_0100    :   num <= 32'd31888;

       7'b000_0010    :   num <= 32'd28409;

       7'b000_0001    :   num <= 32'd25309;

       default        :   num <= 32'd0;

     endcase

 end

 

以上即为piano_beep_ctrl模块的设计思想;

 

在beep_ctrl中,首先设计一个当输入脉冲时,就让一个计数器cnt_200ms记录0.2秒。

 

// 外部输入一个脉冲,cnt_200ms计数器记录200ms的时间 

 // 如果第一个脉冲输入后,没有计时到200ms,第二个脉冲没有任何作用

 always @ (posedge clk) begin

   if (rst_n == 1'b0)

     cnt_200ms <= 32'd0;

   else

     if (flag == 1'b1 && cnt_200ms == 32'd0)

       cnt_200ms <= cnt_200ms + 1'b1;

     else

       if (cnt_200ms > 32'd0 && cnt_200ms < T_200ms - 1'b1)

         cnt_200ms <= cnt_200ms + 1'b1;

       else

         cnt_200ms <= 32'd0;

 end

 

由于外部给予半周期数时,只有一个周期有效,所以需要寄存一下。

 

//外部输入脉冲时,寄存外部输入的半周期的时钟数

 always @ (posedge clk) begin

   if (rst_n == 1'b0)

     num_r <= 32'd0;

   else

     if (flag == 1'b1 && cnt_200ms == 32'd0)

       num_r <= num;

     else

       num_r <= num_r;

 end

 

当200ms计数器开始计数后,我们启动另外一个计数器ocnt,来记录需要输出方波的半周期数。然后当到半周期数的时候,将输出值取反。

 

// 当200ms开始计时后,输出计数器开始计数,

// 只需要计数到外部输入的半周期时钟数即可

   always @ (posedge clk) begin

   if (rst_n == 1'b0)

     ocnt <= 32'd0;

   else

     if (cnt_200ms > 32'd0)

       if (ocnt < num_r)

         ocnt <= ocnt + 1'b1;

       else

         ocnt <= 32'd0;

     else

       ocnt <= 32'd0;

 end



// 当外部输入的半周期时钟数为0时,蜂鸣器不动作

// 当不为0时,到半周期时钟数,将输出取反

 always @ (posedge clk) begin

   if (rst_n == 1'b0)

     beep <= 1'b0;

   else

     if (num_r == 32'd0)

       beep <= 1'b0;

     else

       if (ocnt == num_r)

         beep <= ~beep;

       else

         beep <= beep;

 end

 

以上即为所有的设计说明。

 

仿真时,需要将消抖模块中的20ms进行调小,否则仿真的时长将会格外的长。

 

 

    下板后,经过按下不同的按键,蜂鸣器就可以产生对应的声音。

  下板后,演示视频(链接)如下:
https://www.bilibili.com/video/BV1Gk4y1X7XT/?spm_id_from=333.999.0.0&vd_source=b5405faeab8632f02533bcbfc5e52e55
     本设计所有内容(设计代码、设计工程)链接为:

 

链接:https://pan.baidu.com/s/1VGbPqU9O_k2UBtOGkMJjIQ 

提取码:dzye

  本篇内容中有部分资源来源于网络,如有侵权,请联系作者。

 

  如果您觉得本公众号还不错的话,可以推给身边的朋友们,感谢并祝好!

与基于FPGA的电子琴设计(按键和蜂鸣器)----第一版相似的内容:

基于FPGA的电子琴设计(按键和蜂鸣器)----第一版

欢迎各位朋友关注“郝旭帅电子设计团队”,本篇为各位朋友介绍基于FPGA的电子琴设计(按键和蜂鸣器) 第一版。 功能说明: 外部输入七个按键,分别对应音符的“1、2、3、4、5、6、7”,唱作do、re、mi、fa、sol、la、si。当某个按键按下时,蜂鸣器发出对应的声音 1. 默认发出0.2秒(可

基于FPGA的4x4矩阵键盘驱动设计---第一版

欢迎各位朋友关注“郝旭帅电子设计团队”,本篇为各位朋友介绍基于FPGA的4x4矩阵键盘驱动设计 第一版 功能说明: 1. 驱动4x4矩阵键盘:按下任意一个按键,解析出对应按键信息,并给出标志 使用平台:纯代码形式 使用语言:Verilog HDL 作者QQ:746833924 说明:本篇设计中不涉及

基于FPGA的数字钟设计---第三版

欢迎各位朋友关注“郝旭帅电子设计团队”,本篇为各位朋友介绍基于FPGA的数字钟设计 第三版。 功能说明: 1. 在数码管上面显示时分秒(共计六个数码管,前两个显示小时;中间两个显示分钟;最后两个显示秒)。 2. 利用按键可以切换24/12小时制(默认24小时制)。 3. led1的亮灭表示24小时制

基于FPGA的计算器设计---第一版

欢迎各位朋友关注“郝旭帅电子设计团队”,本篇为各位朋友介绍基于FPGA的计算器设计 第一版。 功能说明: 1. 计算器的显示屏幕为数码管。 2. 4x4矩阵键盘作为计算器的输入设备。 3. 计算任意两位正整数的加减乘除。 4. 当减法结果出现负数时(一个小的数字减去一个大的数字),数码管需要显示负数

基于FPGA的贪吃蛇游戏 之代码解析

基于FPGA的贪吃蛇游戏 之代码解析 1. 代码结构 代码结构包含7格.v文件。 下面依次解析。 2. 代码解析 (1) seg_display.v 数码管的译码模块是最熟悉,最简单的模块了。这里是共阳极的数码管,用case语句编码即可。从上图可以看到,这个模块被例化了3次,分别驱动3个数码管显示,

ZynqMP PL固件通过U-BOOT从指定位置加载FPGA BIT

原因 PL固件可能经常修改,而BOOT.BIN和文件系统、内核实际上基本不会变,在一个平台上可以用同一份。如果每次修改都要重新打包PL 固件到BOOT.BIN,操作起来非常麻烦。所以希望PL 的固件可以直接从指定位置加载。典型的可以从SD卡的FAT32分区加载。 https://xilinx-wik

基于 Three.js 的 3D 模型加载优化

作为一个3D的项目,从用户打开页面到最终模型的渲染加载的时间也会比普通的H5项目要更长一些,从而造成大量的用户流失。为了提升首屏加载的转化率,需要尽可能的降低loading的时间。这里就分享一些我们在模型加载优化方面的心得。

基于MindSpore实现BERT对话情绪识别

本文分享自华为云社区《【昇思25天学习打卡营打卡指南-第二十四天】基于 MindSpore 实现 BERT 对话情绪识别》,作者:JeffDing。 模型简介 BERT全称是来自变换器的双向编码器表征量(Bidirectional Encoder Representations from Trans

基于 Vagrant 手动部署多个 Redis Server

环境准备 宿主机环境:Windows 10 虚拟机环境:Vagrant + VirtualBox Vagrantfile 配置 首先,我们需要编写一个 Vagrantfile 来定义我们的虚拟机配置。假设已经在 D:\Vagrant\redis 目录下创建了一个 Vagrantfile,其内容如下:

基于EF Core存储的Serilog持久化服务

前言 Serilog是 .NET 上的一个原生结构化高性能日志库,这个库能实现一些比内置库更高度的定制。日志持久化是其中一个非常重要的功能,生产环境通常很难挂接调试器或者某些bug的触发条件很奇怪。为了在脱离调试环境的情况下尽可能保留更多线索来辅助解决生产问题,持久化的日志就显得很重要了。目前Ser