基于FPGA的电子琴设计
使用小脚丫核心板加电子琴底板设计的一套电子琴,能播放音乐,弹奏,支持蜂鸣器、喇叭两种输出方式
标签
FPGA
小脚丫
电子琴
2022暑假在家练
综合设计
冷月烟
更新2022-09-06
760

1.任务目的

1.基于我们提供的套件和工具,自己组装电子琴

2.自己编程基于FPGA实现:

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

 

2.电子琴的工作原理和框图

电子琴有13个按键作为电子琴的琴键,2个按键用于调节音程,小脚丫上有4个按键,其中一个被我拿来做音乐播放按键,一个拿来做整体的复位按键。有个切换开关,用于切换蜂鸣器或者喇叭。滑动变阻器,用于调节声音大小。

按键接到FPGA上直接处理,接出两路输出,一路经三极管到蜂鸣器,用于控制蜂鸣器的音调。一路经RC滤波到滑动变阻器再到功率放大器,最后接到喇叭,用于控制喇叭的发声。其中滑动变阻器是通过改变输入信号分压的方式,来硬件调节喇叭输出声音大小的。

 

3.蜂鸣器和模拟喇叭的差别

蜂鸣器:分有源蜂鸣器跟无源蜂鸣器两种,有源蜂鸣器是通电后产生一个固定音调的声音,不可调节。无源蜂鸣器,也是这次电子琴使用的蜂鸣器,可以通过改变驱动PWM的频率使其发出不同音调的声音。

喇叭:按结构和电-声换能的方式不同可分为电动式扬声器、电磁式扬声器、静电式扬声器和压电式扬声器。按扬声器的形状可分为纸盆式、号筒式和球顶式。按频率范围可分为高音扬声器、中音扬声器和低音扬声器。电动式扬声器(又被称为动圈式扬声器)主要由永久磁铁、线圈(又称音圈)和纸盆(与线圈连在一起)构成。当电信号通过引出线流进线圈时,线圈产生磁场。由于流进线圈的电流是变化的,线圈产生的磁场也是变化的,线圈变化的磁场与磁铁的磁场相互作用,线圈和磁铁不断排斥和吸引,使重量轻的线圈产生运动(时而远离磁铁,时而靠近磁铁),线圈的运动带动与它相连的纸盆振动,纸盆就发出声音,就使扬声器产生随音频变化的声音,从而实现了电-声转换。

对于我们控制来说,两者最大的区别是蜂鸣器没法精细控制,只能是pwm信号,导通不导通两个状态,没有中间态,而喇叭的话可以有很多个状态,可以控制纸盆的位置。

 

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

实现方法:

模拟喇叭:FPGA将声音信号(波形)转换为高速的PWM波形,占空比的多少对应着波形每一个点的位置,将此信号输出,外部接一个RC滤波,作为低通滤波器使用,将输出的PWM平滑重新还原为波形,这样就跟常规的DAC驱动是一样的效果了。

FuH390WbXbuFZhabtWuV2h30JnIh

之后信号经过滑动变阻器分压(可用于控制输入波形幅值进而控制输出声音大小),然后接音频功率放大器,最后输出到喇叭。

Fs-DDZ0b3fxJAhwbngu9c32TGux3

蜂鸣器:只能通过改变输入信号频率进而改变音调,这里我将原始的声音波形信号(虽然叠加谐波单整体接近正弦波),取其接近中点的位置作为输出信号PWM的判断,高于则为高电平,低于则为低电平,这样就能得到一个与原始声音信号频率相同的PWM信号。此PWM信号输出。外部电路用三极管做控制,接收PWM信号使喇叭发声。

FoFXzZGrjQUL5TMbuGPnAoMjGw8b

音效差别:

模拟喇叭的音色是可以通过控制输入波形的形状来控制,换种说法就是输入波形叠加不同数量大小的谐波,输入的波形不同(频率相同),音色也就不同。而蜂鸣器不能这样控制,音色比较单一。而且喇叭本身的设计就是为了发出不同的声音,本身的频率特性就好,高频低频都支持,而且比较柔和,蜂鸣器的设计则不是,而是更好的去警醒人们,方便作为电路报警提醒,那是怎么刺耳怎么设计,怎么便宜怎么设计,音效就比较刺耳。

5.模拟放大电路的仿真及分析

电子琴音频放大部分是一个典型的反向放大器,将其电路抽象,就是类似下图的电路:

FhpV7WiaiV86wefHJqWrYzSM8jDS

对其分析,运放的同向端接地=0V,反向端和同向端虚短,所以也是0V,反向输入端输入电阻很高,虚断,几乎没有电流注入和流出,那么R1和R2相当于是串联 的,流过一个串联电路中的每一只组件的电流是相同的,即流过R1的电流和流过R2的电流是相同的。流过R1的电流I1 = (Vi - V-)/R1 ……a 流过R2的电流I2 = (V- - Vout)/R2 ……b V- = V+ = 0 ……c I1 = I2 ……d 求解上面的初中代数方程得Vout = (-R2/R1)*Vi 这就是反向放大器的输入输出关系式了。在这里其放大倍数正是47k/10k=4.7倍,与下图仿真结果相同(黄色为运放输入,蓝色为运放输出)。

FqC_itW2QEnDCubbm6PxLWjOPlQC

 

6.主要代码片段及说明

整体控制

module top 
(
input         clk,      //clk = 12mhz
input         rst,    //rst_n, active low
input  [14:0] key,
input         sw,
input         auto,
output        pwm_out,
output        speak_out
);
wire key_up,key_down,key_auto;
reg [25:0] key_out;

wire [10:0] wavecnt;
wire        waveout;

reg  [1:0]mode;

wire [13:0]music_out;

assign pwm_out = sw?wavecnt[8]:1'b0;
assign speak_out = sw?1'b0:waveout;

debounce key_up_debounce
(
.clk        (clk),
.rst_n      (rst),
.key_in     (key[14]),
.key_flag   (key_up)
);

debounce key_down_debounce
(
.clk        (clk),
.rst_n      (rst),
.key_in     (key[13]),
.key_flag   (key_down)
);

debounce key_auto_debounce
(
.clk        (clk),
.rst_n      (rst),
.key_in     (auto),
.key_flag   (key_auto)
);

music music_u0(
	.clk(clk),
	.rst(rst),
	.enable((mode==2)?1'b1:1'b0),
	.music_out(music_out)
	);

pwm pwm_u0(
		.clk(clk),
		.rst(rst),
		.duty(wavecnt),
		.pwm_out(waveout)
		);

synthesizer synthesizer_u0(
		.clk(clk),
		.rst(rst),
		.key(key_out[25:0]),
		.wavecnt(wavecnt)
		);

always @(posedge clk or negedge rst) begin
	if(!rst)
		mode <= 0;
	else if(key_down)
		mode <= 0;
	else if(key_up)
		mode <= 1;
	else if(key_auto)
		mode <= 2;
end
always @(*) begin
	if(!rst) begin
		key_out[25:13] = 13'b0;
		key_out[12:0]  = ~key[12:0];
	end
	else if(mode == 0) begin
		key_out[25:13] = 13'b0;
		key_out[12:0]  = ~key[12:0];
	end
	else if(mode == 1) begin
		key_out[25:13] = ~key[12:0];
		key_out[12:0]  = 13'b0;
	end
	else if(mode == 2) begin
		key_out[25:13] = 13'b0;
		key_out[12:0]  = music_out;
	end
end
endmodule

 

按键消抖

module debounce(
    input           clk,//12Mhz
    input           rst_n,
    input           key_in,
    output          key_flag
);
parameter   JITTER  =   240;//12Mhz / (1/20ms)
reg   [1:0]     key_r;
wire             change;
reg   [15:0]    delay_cnt;
always @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        key_r <= 0;
    end
    else begin
        key_r <= {key_r[0],key_in};
    end
end
assign  change = (~key_r[1] & key_r[0]) | (key_r[1] & ~key_r[0]);
always @(posedge clk or negedge rst_n)begin
    if(rst_n == 1'b0)begin
        delay_cnt <= 0;
    end
    else if(change == 1'b1)
        delay_cnt <= 0;
    else if(delay_cnt == JITTER)
        delay_cnt <= delay_cnt;
    else 
        delay_cnt <= delay_cnt + 16'd1;
end
assign  key_flag = ((delay_cnt == JITTER - 1) && (key_in == 1'b1))? 1'b1: 
1'b0;
endmodule

 

1/4部分基波与谐波存储

module sin15_table(
		input  [5:0] address,  
		output reg [8:0] sin  
		);
  
always @(address)
	begin
		  case(address)	
			  6'h0: sin=9'h0;
			6'h1: sin=9'hD;
			6'h2: sin=9'h19;
			6'h3: sin=9'h26;
			6'h4: sin=9'h32;
			6'h5: sin=9'h3D;
			6'h6: sin=9'h48;
			6'h7: sin=9'h53;
			6'h8: sin=9'h5D;
			6'h9: sin=9'h66;
			6'hA: sin=9'h6E;
			6'hB: sin=9'h75;
			6'hC: sin=9'h7C;
			6'hD: sin=9'h81;
			6'hE: sin=9'h86;
			6'hF: sin=9'h89;
			6'h10: sin=9'h8C;
			6'h11: sin=9'h8E;
			6'h12: sin=9'h8F;
			6'h13: sin=9'h8F;
			6'h14: sin=9'h8F;
			6'h15: sin=9'h8E;
			6'h16: sin=9'h8C;
			6'h17: sin=9'h8B;
			6'h18: sin=9'h88;
			6'h19: sin=9'h86;
			6'h1A: sin=9'h83;
			6'h1B: sin=9'h81;
			6'h1C: sin=9'h7F;
			6'h1D: sin=9'h7C;
			6'h1E: sin=9'h7A;
			6'h1F: sin=9'h79;
			6'h20: sin=9'h78;
			6'h21: sin=9'h78;
			6'h22: sin=9'h78;
			6'h23: sin=9'h79;
			6'h24: sin=9'h7A;
			6'h25: sin=9'h7D;
			6'h26: sin=9'h80;
			6'h27: sin=9'h84;
			6'h28: sin=9'h88;
			6'h29: sin=9'h8D;
			6'h2A: sin=9'h94;
			6'h2B: sin=9'h9A;
			6'h2C: sin=9'hA1;
			6'h2D: sin=9'hA9;
			6'h2E: sin=9'hB1;
			6'h2F: sin=9'hBA;
			6'h30: sin=9'hC3;
			6'h31: sin=9'hCC;
			6'h32: sin=9'hD4;
			6'h33: sin=9'hDD;
			6'h34: sin=9'hE6;
			6'h35: sin=9'hEE;
			6'h36: sin=9'hF6;
			6'h37: sin=9'hFD;
			6'h38: sin=9'h104;
			6'h39: sin=9'h10A;
			6'h3A: sin=9'h10F;
			6'h3B: sin=9'h114;
			6'h3C: sin=9'h117;
			6'h3D: sin=9'h11A;
			6'h3E: sin=9'h11B;
			6'h3F: sin=9'h11C;

		   endcase
	  end
endmodule

 

计算补充完整波形

通过高两位判断当前是波形的那一段,通过不同的访问顺序来实现完整波形

module look_table(
			input  	[7:0] phase,
			output signed	[10:0] sin_out
			);
 
reg   	[5:0] 	address;
wire   	[1:0] 	sel;
wire   	[8:0] 	sine_table_out; 
reg signed     [10:0]   sine_onecycle_amp;

assign sin_out = sine_onecycle_amp[10:0];
 
assign sel = phase[7:6];
 
sin15_table u_sin_table(address, sine_table_out);
 
always @(sel or sine_table_out)
begin
	case(sel)
	2'b00: 	begin
			sine_onecycle_amp = 9'h11C+sine_table_out; 
			address = phase[5:0];
	     	end
  	2'b01: 	begin
			sine_onecycle_amp = 9'h11C+sine_table_out[8:0];
			address = ~phase[5:0];
	     	end
  	2'b10: 	begin
			sine_onecycle_amp = 9'h11C-sine_table_out;
			address = phase[5:0];
     		end
  	2'b11: 	begin
			sine_onecycle_amp = 9'h11C-sine_table_out[8:0];
			address = ~phase[5:0];
     		end
	endcase
end
 
endmodule

 

产生不同频率波形

通过控制累加器每一次增加的大小来控制频率

module wave(
	input clk,
	input rst,
	input enable,
	output reg signed [10:0] waveout
	);
	
	parameter FREQ = 24'd336;  //C1 261.63Hz ʱ��12MHz =(261.63*2**24) /12000000
	
	reg [23:0] phase_acc;
	
	wire signed [10:0] sinout;
	
	always @(posedge clk or negedge rst)
		if(!rst)
			phase_acc <= 0;
		else
			phase_acc <= phase_acc + FREQ;
			
	always @(posedge clk or negedge rst)
		if(!rst)
			waveout <= 0;
		else if(enable)
			waveout <= sinout;
		else
			waveout <= 0;
			
	look_table u0_table(
			.phase(phase_acc[23:16]),
			.sin_out(sinout)
			);

endmodule

 

生成具体按键对应关系

最后将所有按键输出相加作为最终输出

module synthesizer(
			input clk,
			input rst,
			input [25:0] key,
			output [10:0] wavecnt
			);
			
wire signed [10:0] wave_262, wave_227, wave_294, wave_311, wave_330, wave_349, wave_370, wave_392, wave_415, wave_440, wave_446, wave_494, wave_523
, wave_740, wave_784, wave_831, wave_880, wave_932, wave_988, wave_1046, wave_1109, wave_1175, wave_1245, wave_1318, wave_1397, wave_1480;

wave #(366) d1 (.clk(clk), .rst(rst), .enable(key[0]), .waveout(wave_262));
wave #(388) d_1(.clk(clk), .rst(rst), .enable(key[1]), .waveout(wave_227));
wave #(411) d2 (.clk(clk), .rst(rst), .enable(key[2]), .waveout(wave_294));
wave #(435) d_2(.clk(clk), .rst(rst), .enable(key[3]), .waveout(wave_311));
wave #(461) d3 (.clk(clk), .rst(rst), .enable(key[4]), .waveout(wave_330));
wave #(488) d4 (.clk(clk), .rst(rst), .enable(key[5]), .waveout(wave_349));
wave #(517) d_4(.clk(clk), .rst(rst), .enable(key[6]), .waveout(wave_370));
wave #(548) d5 (.clk(clk), .rst(rst), .enable(key[7]), .waveout(wave_392));
wave #(581) d_5(.clk(clk), .rst(rst), .enable(key[8]), .waveout(wave_415));
wave #(615) d6 (.clk(clk), .rst(rst), .enable(key[9]), .waveout(wave_440));
wave #(652) d_6(.clk(clk), .rst(rst), .enable(key[10]), .waveout(wave_446));
wave #(690) d7 (.clk(clk), .rst(rst), .enable(key[11]), .waveout(wave_494));
wave #(732) z1 (.clk(clk), .rst(rst), .enable(key[12]), .waveout(wave_523));

wave #(1034) z_4(.clk(clk), .rst(rst), .enable(key[13]), .waveout(wave_740));
wave #(1096) z5 (.clk(clk), .rst(rst), .enable(key[14]), .waveout(wave_784));
wave #(1162) z_5(.clk(clk), .rst(rst), .enable(key[15]), .waveout(wave_831));
wave #(1230) z6 (.clk(clk), .rst(rst), .enable(key[16]), .waveout(wave_880));
wave #(1303) z_6(.clk(clk), .rst(rst), .enable(key[17]), .waveout(wave_932));
wave #(1381) z7 (.clk(clk), .rst(rst), .enable(key[18]), .waveout(wave_988));
wave #(1462) g1 (.clk(clk), .rst(rst), .enable(key[19]), .waveout(wave_1046));
wave #(1550) g_1(.clk(clk), .rst(rst), .enable(key[20]), .waveout(wave_1109));
wave #(1643) g2 (.clk(clk), .rst(rst), .enable(key[21]), .waveout(wave_1175));
wave #(1741) g_2(.clk(clk), .rst(rst), .enable(key[22]), .waveout(wave_1245));
wave #(1843) g3 (.clk(clk), .rst(rst), .enable(key[23]), .waveout(wave_1318));
wave #(1953) g4 (.clk(clk), .rst(rst), .enable(key[24]), .waveout(wave_1397));
wave #(2069) g_4(.clk(clk), .rst(rst), .enable(key[25]), .waveout(wave_1480));
assign wavecnt = wave_262 + wave_227 + wave_294 + wave_311 + wave_330 + wave_349 + wave_370 + wave_392 + wave_415 + wave_440 + wave_446 + wave_494 + wave_523
				+ wave_740 + wave_784 + wave_831 + wave_880 + wave_932 + wave_988 + wave_1046 + wave_1109 + wave_1175 + wave_1245 + wave_1318 + wave_1397 + wave_1480;

endmodule

 

7.遇到的主要难题及解决方法

无法实现和弦功能,两个键一按下就会破音

输出信号的幅度太大了,两个按键同时按下,相加后就会超范围,通过减小原始存储波形与输出幅度的比值,减小了输出幅度,让多键输出相加后也不会超出硬件输出能力。

 

8.改进建议

电子琴可用于测试的点太少,而且使用不方便。

建议在板边可以加一个可供示波器夹子夹持的地孔,或者留一个边缘的开窗。关键信号可以将测试孔留到偏板边的位置(主要是顶板的干涉比较大)。测试点可以使用下面这种器件。

Fqzr2VYdUC-2LH7_Vp8gE3I08HR6

板子空白较多,可以多加点丝印标注。

比如将小脚丫核心板的IO连接的内部FPGA引脚标注出来,方便查看。

 

资源使用:

   Number of registers:   1020 out of  4635 (22%)
      PFU registers:         1020 out of  4320 (24%)
      PIO registers:            0 out of   315 (0%)
   Number of SLICEs:      1544 out of  2160 (71%)
      SLICEs as Logic/ROM:   1544 out of  2160 (71%)
      SLICEs as RAM:            0 out of  1620 (0%)
      SLICEs as Carry:        679 out of  2160 (31%)
   Number of LUT4s:        3087 out of  4320 (71%)
      Number used as logic LUTs:        1729
      Number used as distributed RAM:     0
      Number used as ripple logic:      1358
      Number used as shift registers:     0
   Number of PIO sites used: 21 + 4(JTAG) out of 105 (24%)
   Number of block RAMs:  0 out of 10 (0%)
   Number of GSRs:  1 out of 1 (100%)
   EFB used :       No
   JTAG used :      No
   Readback used :  No
   Oscillator used :  No
   Startup used :   No
   POR :            On
   Bandgap :        On
   Number of Power Controller:  0 out of 1 (0%)
   Number of Dynamic Bank Controller (BCINRD):  0 out of 6 (0%)
   Number of Dynamic Bank Controller (BCLVDSO):  0 out of 1 (0%)
   Number of DCCA:  0 out of 8 (0%)
   Number of DCMA:  0 out of 2 (0%)
   Number of PLLs:  0 out of 2 (0%)
   Number of DQSDLLs:  0 out of 2 (0%)
   Number of CLKDIVC:  0 out of 4 (0%)
   Number of ECLKSYNCA:  0 out of 4 (0%)
   Number of ECLKBRIDGECS:  0 out of 2 (0%)

 

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