任务要求:
- 基于我们提供的套件和工具,自己组装电子琴
- 自己编程基于FPGA实现:
- 存储一段音乐,并可以进行音乐播放,
- 可以自己通过板上的按键进行弹奏,支持两个按键同时按下(和弦)并且声音不能失真,板上的按键只有13个,可以通过有上方的“上“、”下”两个按键对音程进行扩展
- 使用扬声器进行播放时,输出的音调信号除了对应于该音调的单频正弦波外,还必须包含至少一个谐波分量
- 音乐的播放支持两种方式,这两种方式可以通过开关进行切换:
- 当开关切换到蜂鸣器端,可以通过蜂鸣器来进行音乐播放
- 当开关切换到扬声器端,可以通过模拟扬声器来进行音乐播放,每个音符都必须包含基频 + 至少一个谐波分量
一:环境配置:
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)
还有课堂上学习的fpga书《 EDA 技术实用教程 VERILOG HDL 版》
可以说本次暑假练确实十分艰难,但是最后在朋友、群友交流帮助下,最终还是实现了任务目标,还是十分开心的,真的十分感谢电子森林!