基于FPGA的电子琴设计
利用 小脚丫FPGA核心板(Lattice MXO2-C)和Piano kit拓展套件,实现一个简单的电子琴,其能够实现弹奏、自动播放音乐、音程和音量调节等功能。
标签
FPGA
DDS
PWM
2022暑假在家练
USAMI
更新2022-09-06
北京理工大学
1024

基于FPGA的电子琴设计——项目总结报告

一、项目概况及需求

利用小脚丫FPGA核心板(Lattice MXO2-C)和Piano Kit拓展套件,组装并编程基于FPGA实现以下功能:存储一段音乐,并可以进行音乐播放;可以自己通过板上的按键进行弹奏,支持两个按键同时按下(和弦)并且声音不能失真,板上的按键只有13个,可以通过有上方的“上“、”下”两个按键对音程进行扩展;使用扬声器进行播放时,输出的音调信号除了对应于该音调的单频正弦波外,还必须包含至少一个谐波分量;音乐的播放支持两种方式,这两种方式可以通过开关进行切换。

二、项目理论分析

1、电子琴工作原理

根据项目要求,设计的电子琴应有手动弹奏和自动播放功能,则电子琴工作的大致框图如下:

FgeSGwHSqlzjfHU9v2ZJ2vOQyEuR

本电子琴电路系统可大致分为控制模块、FPGA模块和音频模块三大部分。控制模块主要反映了用户对电子琴的功能选择操作,通过琴键上的控制按钮完成。播放模式选择按钮高电平时为自动播放模式,读取预设的音符信息并通过不同频率输出,实现自动播放,当按钮低电平时为手动弹奏模式,用户可按键进行弹奏。电子琴发音原理于后代码分析中展示。FPGA模块是核心模块,存有编写好的代码内容,控制电子琴所有功能。音频模块中,本次项目有扬声器和蜂鸣器两个发音装置,都能够将电信号转化为声音信号。

2、蜂鸣器、扬声器原理和区别

本项目中的蜂鸣器采用的是无源蜂鸣器,通过不同频率的脉冲驱动,能够发出不同频率的声音信号。蜂鸣器分压电式蜂鸣器和电磁式蜂鸣器,前者在通电后,振动膜片在电磁线圈和磁铁的相互作用下周期性地振动发电,后者通过多谐振荡器起振,输出1.5~2.5kHZ的音频信号,阻抗匹配器推动压电蜂鸣片发声。

而扬声器是一种把电信号转变为声信号的换能器件,在音响设备中是一个最薄弱的器件,而对于音响效果而言,它又是一个最重要的部件。其音频电能通过电磁、压电或静电效应,使其纸盆或膜片振动并与周围的空气产生共振而发出声音。

蜂鸣器和扬声器最大的区别是喇叭的频率响应要比蜂鸣器好得多,蜂鸣器只在一个很窄的频率范围内电声功率转换比较高。播放音乐时,最直观的感受是扬声器的音色要比蜂鸣器好很多。

在本项目中,通过beeper模块产生弹奏的各个音符频率控制字,输入到dds信号发生器中,实现弹奏、播放以及谐波对应频率的波形,再将数据输入值pwm模块中,通过控制pwm占空比产生不同的pwm波形,最后选择输出到蜂鸣器或喇叭。

三、实现过程说明和主要代码展示

1、顶层模块top

例化clk_wiz, debounce, btn_coding, beeper, pwm, music_box模块,将所有功能整合,各个模块功能稍后说明,

2、btn_coding模块

always@(posedge clk)
2.	    if(!rst_n)
3.	        begin
4.	            sel_speaker <= 0;
5.	            mode <= 0;
6.	        end
7.	    else 
8.	        begin
9.	            if(sel_speaker_n)
10.	                sel_speaker <= ~sel_speaker;
11.	            if(mode_n)
12.	                mode <= ~mode;
13.	        end

对输入的模式切换按键即声音输出设备选择按键进行处理(经测试需按两次才可切换),同时控制自动播放和弹奏模式的选择。

3、music_box模块

播放音乐控制模块,产生要播放的音乐的各个节拍的频率控制字,输出给dds模块。选用曲目《梁祝》,设置寄存器存放其简谱

1.	reg  [31:0]  freq [0:21]; //do re mi fa so la si do 低音、中音、高音,共21个
2.	 reg  [7:0]  PUA  [0:136];//存放曲1的简谱,一个寄存器放半拍
3.	 initial begin     
4.	   freq[0]=0;
5.	   freq[1]= 366; 
6.	   freq[2]= 411;
7.	   freq[3]= 461; 
8.	   freq[4]= 488; 
9.	   freq[5]= 548; 
10.	   freq[6]= 615; 
11.	   freq[7]= 691; 
12.	   freq[8]= 731; 
13.	   freq[9]= 821; 
14.	   freq[10]=921; 
15.	   freq[11]=976; 
16.	   freq[12]=1096; 
17.	   freq[13]=1230; 
18.	   freq[14]=1381; 
19.	   freq[15]=1464; 
20.	   freq[16]=1643; 
21.	   freq[17]=1844; 
22.	   freq[18]=1953; 
23.	   freq[19]=2192; 
24.	   freq[20]=2461; 
25.	   freq[21]=2750;
26.	 end
27.	 initial begin  //梁祝化蝶部分的简谱,一个寄存器存半拍
28.	  PUA[0]  = 3;  PUA[01] = 3;  PUA[02] = 3;  PUA[03] =3;  PUA[04] =5;  PUA[05] =5;  PUA[06] =5;  PUA[07] =6;
29.	  PUA[08] = 8;  PUA[09] = 8;  PUA[10] = 8;  PUA[11] =9;  PUA[12] =6;  PUA[13] =8;  PUA[14] =5;  PUA[15] =5;
30.	  PUA[16] = 12; PUA[17] = 12; PUA[18] = 15; PUA[19] =15; PUA[20] =13; PUA[21] =12; PUA[22] =10; PUA[23] =12;
31.	  PUA[24] = 9;  PUA[25] = 9;  PUA[26] = 9;  PUA[27] =9;  PUA[28] =9;  PUA[29] =9;  PUA[30] =9;  PUA[31] =0;
32.	  PUA[32] = 9;  PUA[33] = 9;  PUA[34] = 9;  PUA[35] =10; PUA[36] =7;  PUA[37] =7;  PUA[38] =6;  PUA[39] =6;
33.	  PUA[40] = 5;  PUA[41] = 5;  PUA[42] = 5;  PUA[43] =6;  PUA[44] =8;  PUA[45] =8;  PUA[46] =9;  PUA[47] =9;
34.	  PUA[48] = 3;  PUA[49] = 3;  PUA[50] = 8;  PUA[51] =8;  PUA[52] =6;  PUA[53] =5;  PUA[54] =6;  PUA[55] =8;
35.	  PUA[56] = 5;  PUA[57] = 5;  PUA[58] = 5;  PUA[59] =5;  PUA[60] =5;  PUA[61] =5;  PUA[62] =5;  PUA[63] =0;
36.	  PUA[64] = 10; PUA[65] = 10; PUA[66] = 10; PUA[67] =12; PUA[68] =7;  PUA[69] =7;  PUA[70] =9;  PUA[71] =9;
37.	  PUA[72] = 6;  PUA[73] = 8;  PUA[74] = 5;  PUA[75] =5;  PUA[76] =5;  PUA[77] =5;  PUA[78] =5;  PUA[79] =5;
38.	  PUA[80] = 3;  PUA[81] = 5;  PUA[82] = 3;  PUA[83] =3;  PUA[84] =5;  PUA[85] =6;  PUA[86] =7;  PUA[87] =9;
39.	  PUA[88] = 6;  PUA[89] = 6;  PUA[90] = 6;  PUA[91] =6;  PUA[92] =6;  PUA[93] =6;  PUA[94] =5;  PUA[95] =6;
40.	  PUA[96] = 8;  PUA[97] = 8;  PUA[98] = 8;  PUA[99] =9;  PUA[100]=12; PUA[101]=12; PUA[102]=12; PUA[103]=10;
41.	  PUA[104]= 9;  PUA[105]= 9;  PUA[106]= 10; PUA[107]=9;  PUA[108]=8;  PUA[109]=8;  PUA[110]=6;  PUA[111]=5;
42.	  PUA[112]= 3;  PUA[113]= 3;  PUA[114]= 3;  PUA[115]=3;  PUA[116]=8;  PUA[117]=8;  PUA[118]=8;  PUA[119]=8;
43.	  PUA[120]= 6;  PUA[121]= 8;  PUA[122]= 6;  PUA[123]=5;  PUA[124]=3;  PUA[125]=5;  PUA[126]=6;  PUA[127]=8;
44.	  PUA[128]= 5;  PUA[129]= 5;  PUA[130]= 5;  PUA[131]=5;  PUA[132]=5;  PUA[133]=5;  PUA[134]=5;  PUA[135]=5;
45.	 end

4、dds_main模块

作为dds模块的顶层,控制dds产生波形的频率

1.	module dds_main(clk_in,rst_n,en, FTwoRD,dac_data_out);
2.	input clk_in;                 // 小脚丫FPGA的外部时钟频率位           12MHz,内部可以通过PLL产生
3.	                                      //12MHz整数倍的高速时钟 ,比如96MHz、108MHz、120MHz...216MHz
4.	input rst_n;
5.	input en;
6.	input [23:0]FTwoRD;     //频率控制字
7.	output [9:0] dac_data_out;        // 10位数据输出送给外部
8.	reg [23:0] cnt=0;               // 自由运行的计数器,共计24位,最低频率为cnt[23]的12MHz/2^24= 0.7Hz
9.	always@(posedge clk_in)  cnt <= cnt + 1'b1;
10.	 wire [9:0]dac_data;
11.	 reg [23:0]  phase_acc = 0;
12.	 always @(posedge clk_in, negedge rst_n) 
13.	  if(!rst_n)
14.	   phase_acc <= 0;
15.	  else
16.	   phase_acc <= phase_acc + FTwoRD;  //主时钟为12MHz,产生20KHz的正弦波信号
17.	 lookup_tables u_lookup_tables(.phase(phase_acc[23:16]), .sin_out(dac_data));
18.	 assign dac_data_out = en ? dac_data : 0;
19.	endmodule

5、beeper模块

这一模块包括了多个功能。首先是采用键盘弹奏时,各个音符频率控制字的产生;

1.	always@(posedge clk_in or negedge rst_n_in) begin
2.	 if(!rst_n_in) 
3.	  begin
4.	   FTwoRD_temp[0] <= 0; 
5.	   FTwoRD_temp[1] <= 0; 
6.	   FTwoRD_temp[2] <= 0; 
7.	   FTwoRD_temp[3] <= 0; 
8.	   FTwoRD_temp[4] <= 0; 
9.	   FTwoRD_temp[5] <= 0; 
10.	   FTwoRD_temp[6] <= 0; 
11.	   FTwoRD_temp[7] <= 0; 
12.	   FTwoRD_temp[8] <= 0; 
13.	   FTwoRD_temp[9] <= 0; 
14.	   FTwoRD_temp[10] <= 0; 
15.	   FTwoRD_temp[11] <= 0; 
16.	   FTwoRD_temp[12] <= 0; 
17.	  end 
18.	 else begin
19.	   if(PB_state[0])
20.	    begin
21.	     FTwoRD_temp[0] <= 10'd365; //M1,C
22.	    end
23.	   else
24.	    FTwoRD_temp[0] <= 10'd0; //M1,C
25.	    
26.	   if(PB_state[1])
27.	    begin
28.	     FTwoRD_temp[1] <= 10'd387; //H1,#C 
29.	    end
30.	   else
31.	    FTwoRD_temp[1] <= 10'd0; //H1,#C 
32.	    
33.	   if(PB_state[2])                    
34.	    begin
35.	     FTwoRD_temp[2] <= 10'd410; //M2,D
36.	    end
37.	    
38.	   else
39.	    FTwoRD_temp[2] <= 10'd0; //M2,D
40.	   
41.	   if(PB_state[3])
42.	    begin
43.	     FTwoRD_temp[3] <= 10'd434; //H2,#D 
44.	    end
45.	   else
46.	    FTwoRD_temp[3] <= 10'd0; //H2,#D 
47.	    
48.	   if(PB_state[4])                    
49.	    begin
50.	     FTwoRD_temp[4] <= 10'd460; //M3,E
51.	    end
52.	   else
53.	    FTwoRD_temp[4] <= 10'd0; //M3,E
54.	    
55.	   if(PB_state[5])                    
56.	    begin
57.	     FTwoRD_temp[5] <= 10'd488; //M4,F
58.	    end
59.	   else
60.	    FTwoRD_temp[5] <= 10'd0; //M4,F
61.	   
62.	   if(PB_state[6])
63.	    begin
64.	     FTwoRD_temp[6] <= 10'd517; //H4,#F
65.	    end
66.	   else
67.	    FTwoRD_temp[6] <= 10'd0; //H4,#F
68.	    
69.	   if(PB_state[7])                    
70.	    begin
71.	     FTwoRD_temp[7] <= 10'd548; //M5,G
72.	    end
73.	   else
74.	    FTwoRD_temp[7] <= 10'd0; //M5,G
75.	    
76.	   if(PB_state[8])
77.	    begin
78.	     FTwoRD_temp[8] <= 10'd580; //H5,#G
79.	    end
80.	   else
81.	    FTwoRD_temp[8] <= 10'd0; //H5,#G
82.	       
83.	   if(PB_state[9])
84.	    begin
85.	     FTwoRD_temp[9] <= 10'd615; //M6,A
86.	    end
87.	   else 
88.	    FTwoRD_temp[9] <= 10'd0; //M6,A
89.	   
90.	   if(PB_state[10])
91.	    begin
92.	     FTwoRD_temp[10] <= 10'd651; //H6,#A
93.	    end
94.	   else
95.	    FTwoRD_temp[10] <= 10'd0; //H6,#A
96.	    
97.	   if(PB_state[11])                
98.	    begin
99.	     FTwoRD_temp[11] <= 10'd690; //M7,B
100.	    end 
101.	   else
102.	    FTwoRD_temp[11] <= 10'd0; //M7,B
103.	   if(PB_state[12])                   
104.	    begin
105.	     FTwoRD_temp[12] <= 10'd730; //HIGH_C
106.	    end
107.	   else
108.	    FTwoRD_temp[12] <= 10'd0; //HIGH_C
109.	 end
110.	end

其次是和弦的算法,本人乐理知识较为一般,此处采用了取频率平均值的方法解决按多个按键产生和弦的问题,经测试同时按下二、三个按键不会出现失真现象

1.	assign FTwoRD_temp_1 = (FTwoRD_temp[0]+FTwoRD_temp[1]+FTwoRD_temp[2]+FTwoRD_temp[3]+FTwoRD_temp[4]+FTwoRD_temp[5]+FTwoRD_temp[6]+FTwoRD_temp[7]+FTwoRD_temp[8]+FTwoRD_temp[9]+FTwoRD_temp[10]+FTwoRD_temp[11]+FTwoRD_temp[12])/(PB_state[0]+PB_state[1]+PB_state[2]+PB_state[3]+PB_state[4]+PB_state[5]+PB_state[6]+PB_state[7]+PB_state[8]+PB_state[9]+PB_state[10]+PB_state[11]+PB_state[12]);

此为测试和弦功能时的仿真效果

FpjMiXtoz-P0m6uXLdbqdTrnXD4F

还有音程调节部分,此处设定为按住调节键时才会升或降低八度

1.	always@(posedge clk_in or negedge rst_n_in) begin
2.	 if(!rst_n_in) begin
3.	  FTwoRD <= 24'hffffff; 
4.	 end else begin
5.	  case(PB_state[14:13])  
6.	   2'b01: FTwoRD <= FTwoRD_temp_1 >> 1; //升八度
7.	   2'b10: FTwoRD <= FTwoRD_temp_1 << 1; //降八度
8.	   
9.	   default:FTwoRD <= FTwoRD_temp_1; 
10.	  endcase
11.	 end
12.	end

最后,dds模块的例化也包含在beeper里,

dds_main u_dds_main(
2.	   .clk_in(clk_in),
3.	   .rst_n(rst_n_in),
4.	   .en(1),
5.	   .FTwoRD(FTwoRD),//输入频率控制字
6.	   .dac_data_out(dac_data)
7.	  );
8.	 dds_main bbox_dac(
9.	   .clk_in(clk_in),
10.	   .rst_n(rst_n_in),
11.	   .en(1),
12.	   .FTwoRD(box_word),//输入频率控制字
13.	   .dac_data_out(bbox_dac_data)
14.	  ); 
15.	 dds_main bbox_dac_h(
16.	   .clk_in(clk_in),
17.	   .rst_n(rst_n_in),
18.	   .en(1),
19.	   .FTwoRD(box_word <<1),//输入频率控制字
20.	   .dac_data_out(bbox_dac_data_h)
21.	  );  
22.	 assign dac_data_out = mode ?  sel_speaker ? (bbox_dac_data_h + bbox_dac_data) :  (bbox_dac_data) : dac_data;

需要特别注意的是后两个例化,实现了在音乐自动播放时,音符产生谐波的功能,第二个例化中的dds模组用以产生基波,第三个对应谐波,此处设置谐波为两倍的基波。

此外,还有debounce模块,实现按键消抖功能;sin_table模块,储存正弦波1/4波表;lookup_table模块,基于正弦波1/4波表,用对称性实现其他3个1/4周期波形,此处不再展示,具体代码见附件。

5、总体布局

各模块布线情况见schematic.pdf文件

Fp-cshDPBjsYsRfriFjQscal-oJ9

四、后记

1、遇到的难题及解决方法

此次项目个人认为工作量较大,从编写代码的数量就可以看出。由于本人是FPGA新手,若是没有任何资料进行参考,直接看项目的话必然是一头雾水。好在电子森林有大量的参考案例可供参考,帮助我理解并写出了电子琴项目的整体框架。

我认为本项目中最难的部分是对声音的处理。在进行学习之前,我以为只需对各个按键绑定发出特定频率的声音而不用加以处理,就可以完成功能。而想要完成发声,还需要学习DDS信号发生器和PWM相关知识,而这方面知识没有信号与系统等相关课程的基础是难以理解的。

2、改进方向

最大的改进方向在于和弦功能的实现。上文提及本人采用的方法是取频率的平均值,相当于将多个按键发出的声音合成为一个,本以为没有什么问题,但直到我听了真正乐器的和弦,我才觉得貌似我理解的“乐理知识”应该与实际有一定偏差。碍于时间关系,留作个人的后续改进项目。

在自动播放音乐方面,实代码写出了不止播放预设的单一歌曲的功能,实际上应该能够导入并切换其他乐曲,但没有进行测试,若成功,则可以实现能够切换歌曲的音乐盒,使功能更丰富。

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