欢迎各位朋友关注“郝旭帅电子设计团队”,本篇为各位朋友介绍基于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的音频信号,阻抗匹配器推动压电蜂鸣片发声。
设计思想如下:
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://pan.baidu.com/s/1VGbPqU9O_k2UBtOGkMJjIQ
提取码:dzye
本篇内容中有部分资源来源于网络,如有侵权,请联系作者。
如果您觉得本公众号还不错的话,可以推给身边的朋友们,感谢并祝好!