基于FPGA的电子钢琴设计
完成了电子钢琴要求的实现,可以演奏出美丽的音乐,还可以实现蜂鸣器与扬声器的切换
标签
FPGA
数字逻辑
显示
USB
fpga钢琴
voncg
更新2022-09-06
安徽师范大学
478

任务要求:

  1. 基于我们提供的套件和工具,自己组装电子琴
  2. 自己编程基于FPGA实现:
    1. 存储一段音乐,并可以进行音乐播放,
    2. 可以自己通过板上的按键进行弹奏,支持两个按键同时按下(和弦)并且声音不能失真,板上的按键只有13个,可以通过有上方的“上“、”下”两个按键对音程进行扩展
    3. 使用扬声器进行播放时,输出的音调信号除了对应于该音调的单频正弦波外,还必须包含至少一个谐波分量
    4. 音乐的播放支持两种方式,这两种方式可以通过开关进行切换: 
      1. 当开关切换到蜂鸣器端,可以通过蜂鸣器来进行音乐播放
      2. 当开关切换到扬声器端,可以通过模拟扬声器来进行音乐播放,每个音符都必须包含基频 + 至少一个谐波分量

一:环境配置:

1、Diamond编程工具:

         官方推荐的开发软件

2、电子森林设计的简易电子钢琴

二.程序实现:

顶层文件设计,端口调用

module top(
		input clk,
		input rst,  
		input [14:0] key,
		input [1:0] switch,
		input sw,
		output  pwmout,
		output  speak,
		output	[7:0] led
		);

 

按键消抖

module debounce (clk,rst,key,key_out,key_pulse);
 
	parameter       N  =  1;       
	input             clk;
	input             rst;
	input 	[N-1:0]   key;       
	output  [N-1:0]   key_out;
	output  [N-1:0]   key_pulse;    
 
	reg     [N-1:0]   key_rst_pre;  
	reg     [N-1:0]   key_rst;       
	wire    [N-1:0]   key_edge;      
 
	
	always @(posedge clk  or  negedge rst)
		begin
			if (!rst) begin
				key_rst <= {N{1'b1}};       
				key_rst_pre <= {N{1'b1}};
				end
			else begin
				key_rst <= key;             
				key_rst_pre <= key_rst;   
				end    
		end
 
	assign  key_edge = key_rst_pre & (~key_rst);
	
	reg	[17:0]	  cnt;                            
 
		always @(posedge clk or negedge rst)
		begin
			if(!rst)
				cnt <= 18'h0;
			else if(key_edge)
				cnt <= 18'h0;
			else
				cnt <= cnt + 1'h1;
			end  
 
	reg     [N-1:0]   key_sec_pre;                
	reg     [N-1:0]   key_sec;                    
 
 
	always @(posedge clk  or  negedge rst)
		begin
			if (!rst) 
				key_sec <= {N{1'b1}};                
			else if (cnt==18'h3ffff)
				key_sec <= key;  
		end
	always @(posedge clk  or  negedge rst)
		begin
			if (!rst)
				key_sec_pre <= {N{1'b1}};
			else                   
				key_sec_pre <= key_sec;             
			end      
	assign  key_pulse = key_sec_pre & (~key_sec);   
	assign	key_out = key_sec;
 
endmodule

波形频率

module synthesizer(
			input clk,
			input rst,
			input [12:0] key,
			input up,
			input down,
			output [10:0] wavecnt
			);

wire  [9:0] waveldo,wavelre,wavelmi,wavelfa,wavelso,wavella,wavelsi,
			 wavemdo,wavemre,wavemmi,wavemfa,wavemso,wavemla,wavemsi,
			 wavehdo,wavehre,wavehmi,wavehfa,wavehso,wavehla,wavesi;


always @(posedge clk or negedge rst) begin

	if (!rst) begin

		wave #(366) ldo(.clk(clk),.rst(rst),.enable(key[0]),.waveout(waveldo));
		wave #(411) lre(.clk(clk),.rst(rst),.enable(key[1]),.waveout(wavelre));
		wave #(461) lmi(.clk(clk),.rst(rst),.enable(key[2]),.waveout(wavelmi));
		wave #(488) lfa(.clk(clk),.rst(rst),.enable(key[3]),.waveout(wavelfa));
		wave #(548) lso(.clk(clk),.rst(rst),.enable(key[4]),.waveout(wavelso));
		wave #(615) lla(.clk(clk),.rst(rst),.enable(key[5]),.waveout(wavella));
		wave #(691) lsi(.clk(clk),.rst(rst),.enable(key[6]),.waveout(wavelsi));

		wave #(732) mdo(.clk(clk),.rst(rst),.enable(key[7]),.waveout(wavemdo));
		wave #(821) mre(.clk(clk),.rst(rst),.enable(key[8]),.waveout(wavemre));
		wave #(922) mmi(.clk(clk),.rst(rst),.enable(key[9]),.waveout(wavemmi));
		wave #(977) mfa(.clk(clk),.rst(rst),.enable(key[10]),.waveout(wavemfa));
		wave #(1096) mso(.clk(clk),.rst(rst),.enable(key[11]),.waveout(wavemso));
		wave #(1230) mla(.clk(clk),.rst(rst),.enable(key[12]),.waveout(wavemla));


		assign wavecnt = waveldo+wavelre+wavelmi+wavelfa+wavelso+wavella+wavelsi+wavemdo+wavemre+wavemmi+wavemfa+wavemso+wavemla;
				 
	end

	else
		if (~down) begin 
               
			wave #(366) ldo(.clk(clk),.rst(rst),.enable(key[0]),.waveout(waveldo));
			wave #(411) lre(.clk(clk),.rst(rst),.enable(key[1]),.waveout(wavelre));
			wave #(461) lmi(.clk(clk),.rst(rst),.enable(key[2]),.waveout(wavelmi));
			wave #(488) lfa(.clk(clk),.rst(rst),.enable(key[3]),.waveout(wavelfa));
			wave #(548) lso(.clk(clk),.rst(rst),.enable(key[4]),.waveout(wavelso));
			wave #(615) lla(.clk(clk),.rst(rst),.enable(key[5]),.waveout(wavella));
			wave #(691) lsi(.clk(clk),.rst(rst),.enable(key[6]),.waveout(wavelsi));

			wave #(732) mdo(.clk(clk),.rst(rst),.enable(key[7]),.waveout(wavemdo));
			wave #(821) mre(.clk(clk),.rst(rst),.enable(key[8]),.waveout(wavemre));
			wave #(922) mmi(.clk(clk),.rst(rst),.enable(key[9]),.waveout(wavemmi));
			wave #(977) mfa(.clk(clk),.rst(rst),.enable(key[10]),.waveout(wavemfa));
			wave #(1096) mso(.clk(clk),.rst(rst),.enable(key[11]),.waveout(wavemso));
			wave #(1230) mla(.clk(clk),.rst(rst),.enable(key[12]),.waveout(wavemla));


			assign wavecnt = waveldo+wavelre+wavelmi+wavelfa+wavelso+wavella+wavelsi+wavemdo+wavemre+wavemmi+wavemfa+wavemso+wavemla;
        end
		
	    else (~up) begin 
        	  
	  

			wave #(821) mre(.clk(clk),.rst(rst),.enable(key[8]),.waveout(wavemre));
			wave #(922) mmi(.clk(clk),.rst(rst),.enable(key[9]),.waveout(wavemmi));
			wave #(977) mfa(.clk(clk),.rst(rst),.enable(key[10]),.waveout(wavemfa));
			wave #(1096) mso(.clk(clk),.rst(rst),.enable(key[11]),.waveout(wavemso));
			wave #(1230) mla(.clk(clk),.rst(rst),.enable(key[12]),.waveout(wavemla));
			wave #(1381) msi(.clk(clk),.rst(rst),.enable(key[0]),.waveout(wavemsi));

			wave #(1474) hdo(.clk(clk),.rst(rst),.enable(key[1]),.waveout(wavehdo));
			wave #(1642) hre(.clk(clk),.rst(rst),.enable(key[2]),.waveout(wavehre));
			wave #(1843) hmi(.clk(clk),.rst(rst),.enable(key[3]),.waveout(wavehmi));
			wave #(1953) hfa(.clk(clk),.rst(rst),.enable(key[4]),.waveout(wavehfa));
			wave #(2192) hso(.clk(clk),.rst(rst),.enable(key[5]),.waveout(wavehso));
			wave #(2461) hla(.clk(clk),.rst(rst),.enable(key[6]),.waveout(wavehla));
			wave #(2762) hsi(.clk(clk),.rst(rst),.enable(key[7]),.waveout(wavehsi));

			assign wavecnt = wavemre+wavemmi+wavemfa+wavemso+wavemla+wavemsi+wavehdo+wavehre+wavehmi+wavehfa+wavehso+wavehla+wavehsi;	
				 
		end

end



endmodule

四分之一分频

module clk_quarter(
	input wire clk_in,rst_n_in,
	output reg clk_out
);

	localparam counter_max = 32'd3000000;   // 25MHz*0.25s = 6250000
	reg [31:0]counter;
	
	always@(posedge clk_in or negedge rst_n_in)begin
		if(!rst_n_in)begin
			counter <= 0;
			clk_out <= 1'b0;
		end
		else begin
			if(counter >= counter_max)begin
				counter <= 0;
				clk_out <= 1'b1;
			end
			else begin
				counter <= counter + 1'b1;
				clk_out <= 1'b0;
			end
		end
	end
	
endmodule

自动播放

initial begin
			freq[0]=19'h7FFFF;
			freq[1]=19'h7FFFF;
			freq[2]=19'h7FFFF;
			freq[3]=19'h7FFFE;
			freq[4]=19'h7FFFD;
			freq[5]=19'h7FFFB;
			freq[6]=19'h7FFF7;
			freq[7]=19'h7FFEF;
			freq[8]=19'h7FFDE;
			freq[9]=19'h7FFBE;
			freq[10]=19'h7FF7F;
			freq[11]=19'h7FEFF;
			freq[12]=19'h7FDFF;
			freq[13]=19'h7FBFF;
			freq[14]=19'h7F7FF;
			freq[15]=19'h7EFFF;
			freq[16]=19'h7DFFF;
			freq[17]=19'h7BFFF;
			freq[18]=19'h77FFF;
			freq[19]=19'h6FFFF;
			freq[20]=19'h5FFFF;
		end
	
	
		initial
		
		begin	                                               
		Music[0]  =10;  Music[01] =10; Music[02] =10; Music[03] =10;  Music[04] =11;  Music[05] =11;  Music[06] =12;  Music[07] =12;
		Music[08] =12;  Music[09] =12; Music[10] =11; Music[11] =11;  Music[12] =10;  Music[13] =10;  Music[14] =9;   Music[15] =9;  
		Music[16] =8;   Music[17] =8;  Music[18] =8;  Music[19] =8;   Music[20] =9;   Music[21] =9;   Music[22] =10;  Music[23] =10;
		Music[24] =10;  Music[25] =10; Music[26] =10; Music[27] =9;   Music[28] =9;   Music[29] =9;   Music[30] =9;   Music[31] =9;
		Music[32] =10;  Music[33] =10; Music[34] =10; Music[35] =10;  Music[36] =11;  Music[37] =11;  Music[38] =12;  Music[39] =12; 
		Music[40] =12;  Music[41] =12; Music[42] =11; Music[43] =11;  Music[44] =10;  Music[45] =10;  Music[46] =9;   Music[47] =9;
		Music[48] =8;   Music[49] =8;  Music[50] =8;  Music[51] =8;   Music[52] =9;   Music[53] =9;   Music[54] =10;  Music[55] =10;
		Music[56] =9;   Music[57] =9;  Music[58] =9;  Music[59] =8;   Music[60] =8;   Music[61] =8;   Music[62] =8;   Music[63] =8;  
		Music[64] =9;   Music[65] =9;  Music[66] =9;  Music[67] =9;   Music[68] =10;  Music[69] =10;  Music[70] =8;   Music[71] =8;
		Music[72] =9;   Music[73] =9;  Music[74] =10; Music[75] =11;  Music[76] =10;  Music[77] =10;  Music[78] =8;   Music[79] =8;
		Music[80] =9;   Music[81] =9;  Music[82] =10; Music[83] =11;  Music[84] =10;  Music[85] =10;  Music[86] =8;   Music[87] =8;  
		Music[88] =8;   Music[89] =8;  Music[90] =9;  Music[91] =9;   Music[92] =5;   Music[93] =5;   Music[94] =10;  Music[95] =10;
		Music[96] =10;  Music[97] =10; Music[98] =10; Music[99] =10;  Music[100]=11;  Music[101]=11;  Music[102]=12;  Music[103]=12;
		Music[104]=12;  Music[105]=12; Music[106]=11; Music[107]=11;  Music[108]=10;  Music[109]=10;  Music[110]=9;   Music[111]=9;  
		Music[112]=8;   Music[113]=8;  Music[114]=8;  Music[115]=8;   Music[116]=9;   Music[117]=9;   Music[118]=10;  Music[119]=10;
		Music[120]=9;   Music[121]=9;  Music[122]=9;  Music[124]=8;   Music[124]=8;   Music[125]=8;   Music[126]=8;   Music[127]=8;
		end 

三.效果展示

实现了题目要求的功能,具体效果演示可以看视频演示,完成了简单钢琴的设计

四.总结与感悟

电子琴的工作原理

检测到键盘输入,fpga查波表,生成波值,和弦、音频叠加,以PWM调制的形式发出去,再经过音频放大电路提高输出能力,之后转化为电信号,就成为了我们可以听到的声音。

蜂鸣器和模拟喇叭的差别

蜂鸣器一般分为自带震荡源或不带震荡源两种。驱动自带震荡源的蜂鸣器时直接使用直流电即可;而驱动不带震荡源的蜂鸣器则需要使用交流信号。

喇叭,也称扬声器、音箱、扩音器,是将电子信号转换成为声音的换能器、电子组件,可以由一个或多个组成音响组。扬声器是由磁铁、线圈、喇叭振膜组成。

用蜂鸣器和模拟喇叭的实现方法差别以及音效差别分析

蜂鸣器实现方法和音效

驱动不带震荡源的无源蜂鸣器需要使用交流信号,可以采用方波信号。由于方波的信号特点,发声中带有丰富的杂波信号,音效较差,音色单一。

模拟喇叭实现方法和音效

在fpga钢琴中是由fpga的IO口产生PWM信号,经过RC低通滤波器,再经过调幅变阻器、AC耦合电容到放大电路,转换为较为平滑的低频信号来驱动扬声器。

喇叭有数模转换过程,而且理论与实测都发现喇叭比蜂鸣器的声音层次感厚很多,听起来也更舒服,更饱满。

主要难题

谐波真的很难添加,请教大佬可以使用matlab进行模拟,于是又自学了matlab,之前没接触过,很多新东西

仿真部分因为自己理论知识不足,经过一番挣扎,还是很垃圾,所以。。。

感悟

       首先很感谢硬禾学堂,能够提供一个平台,能够在空余时间提高自己开发的能力。我作为一名准大三学生,第一次接触到verilog,可以说有很大的新鲜感,同时,也学习到了更多的知识。同时我也期待未来还能够通过电子森林的寒暑假练习等活动学习更多的内容。感谢电子森林能够提供这个平台。希望未来电子森林能够有更多好玩有趣的项目。

      在进行项目的过程中,首先遇到的问题肯定就是没有接触过verilog。但是很感谢电子森林提供了直播视频讲解,还有相关文档。并且还有相关在线仿真工具,以下是我借鉴过文档链接

entry_verilog [电子森林] (eetree.cn)

stepfpga

还有课堂上学习的fpga书《 EDA 技术实用教程 VERILOG HDL 版》

可以说本次暑假练确实十分艰难,但是最后在朋友、群友交流帮助下,最终还是实现了任务目标,还是十分开心的,真的十分感谢电子森林!

附件下载
软件代码.zip
团队介绍
韩威、老学长
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号