基于小脚丫fpga的电子琴设计
摘要:利用小脚丫fpga核心板(Lattice MXO2-C)和Piano Kit拓展套件,组装并通过编程驱动模拟扬声器实现音乐播放、电子琴的功能。电子琴支持和弦、使用扬声器进行播放时,输出的音调信号除了对应于该音调的单频正弦波外,还包含一个谐波分量。
任务要求
1、基于我们提供的套件和工具,自己组装电子琴
2、自己编程基于FPGA实现:
1、存储一段音乐,并可以进行音乐播放,
2、可以自己通过板上的按键进行弹奏,支持两个按键同时按下(和弦)并且声音不能失真,板上的按键只有13个,可以通过有上方的“上“、”下”两个按键对音程进行扩展
3、使用扬声器进行播放时,输出的音调信号除了对应于该音调的单频正弦波外,还必须包含至少一个谐波分量
4、音乐的播放支持两种方式,这两种方式可以通过开关进行切换:
当开关切换到蜂鸣器端,可以通过蜂鸣器来进行音乐播放
当开关切换到扬声器端,可以通过模拟扬声器来进行音乐播放,每个音符都必须包含基频 + 至少一个谐波分量
一、电子琴的工作原理和框图
现代电子琴一般采用PCM采样音源。所谓采用就是录制乐器的声音,将其数字化后存入ROM或FLASH里,然后按下键时,CPU或DSP芯片回放该音。现代波形记忆式电子琴可以靠包络线路来控制和制造和编辑音色。
而本次fpga电子琴与原理大致与现代电子琴相同,通过制作波表存储相应信号波形,通过频率控制字控制信号输出频率,再通过pwm使其数字化输出使得蜂鸣器和喇叭发声。
如下为本次电子琴的设计框图:
二、 分析蜂鸣器和模拟喇叭的差别
1、喇叭
喇叭其实是一种电能转换成声音的一种转换设备,当不同的电子能量传至线圈时,线圈产生一种能量与磁铁的磁场互动,这种互动造成纸盘振动,因为电子能量随时变化,喇叭的线圈会往前或往后运动,因此喇叭的纸盘就会跟着运动,这此动作使空气的疏密程度产生变化而产生声音。
2、蜂鸣器
蜂鸣器的发声原理由振动装置和谐振装置组成,而蜂鸣器又分为无源激型与有源自激型。无源激型蜂鸣器的工作发声原理是:方波信号输入谐振装置转换为声音信号输出。有源自激型蜂鸣器的工作发声原理是:直流电源输入经过振荡系统的放大取样电路在谐振装置作用下产生声音信号。
三、用蜂鸣器和模拟喇叭的实现方法差别以及音效差别分析
在本项目中使用的蜂鸣器为无源蜂鸣器,可以使用简单的PWM实现,而扬声器需要用先得到模拟信号,再用PWM数字化才能输出。
音效差别:喇叭的频率响应比蜂鸣器要好得多,蜂鸣器只在一个很窄的频率范围内电声功率转换比较高。因此喇叭的音色特别是和弦时比蜂鸣器好很多。
四、 模拟放大电路的仿真及分析
使用LTspice软件,按照官网所给原理图进行搭建仿真。
信源首先使用440hz正弦波,得到仿真结果:
之后再将信源改为方波信号,得到仿真结果:
可以看到,经过放大电路的信号已经出现弯曲。此时进行一下频域仿真,结果如下:
可以看出,此电路为一低通滤波器,截止频率约为21KHz,而spwm信号进过低通滤波器即可变为正弦信号,因此可以驱动喇叭输出。
五、主要代码片段及说明
模块一:按键模块
keys key_u(
.clk(clk),
.KEYs_in(keys),
.keys_pluse_in(keys_pluse_in),
.keys_out(keys_out),
.keys_pluse(keys_pluse)
);
主要实现按键消抖功能,keys_out为琴键按键,keys_pluse输出一个高脉冲,为音乐盒和音程切换按键。
模块二:音乐盒选择模块
music_change u_music_change(
.clk(clk),
.rst_n(rst_n_in),
.mode_n(mode_n),
.mode(mode)
);
Mode为1选择音乐盒模式,为0选择弹奏模式,按下一次mode_n键则mode取一次反。
模块三:音乐盒模块
music_box musicbox_u(
.clk_in (clk ),
.rst_n_in (rst_n_in ),
.pwm_en (mode ),
.music_box (music_box )
);
输出音乐各音符所对应的频率,里面分出4hz时钟来表示4分音符,共存储了100个音符。
模块四:按键控制波表输出模块
beeper beeper_u(
.clk_in (clk ),
.rst_n_in (rst_n_in ),
.music_box (music_box ),
.keys_pluse (keys_pluse ),
.mode (mode ),
.keys_out (~keys_out ),
.dac_data_out (dac_data )
);
1、按键控制信号频率部分。
使用24位相位累加器和按键来控制读表速率以控制输出信号的频率,输入给内部的读表模块从而输出不同频率的正弦信号以及谐波信号。
if(keys_out[0])
begin
phase_acc[0] <= 24'd1462;
end
else
phase_acc[0] <= 24'd0;
2、和弦部分
assign dac_data_out_xiebo = (dac_xiebo1+dac_xiebo2+dac_xiebo3+dac_xiebo4+dac_xiebo5+dac_xiebo6+dac_xiebo7+dac_xiebo8+dac_xiebo9+dac_xiebo10+dac_xiebo11+dac_xiebo12+dac_xiebo13);
dds_main_xiebo u_dds_main_xiebo1(.clk_in(clk_in),
.rst_n(rst_n_in),
.en(keys_out[0]),
.phase(phase_acc[0]),
.dac_data_out(dac_xiebo1));
通过实例化多个读表模块,并将其加输出波形信号得到和弦信号。由于代码未完全优化,资源利用过多, 最多实现5个按键同时按下。
3、谐波部分
module lookup_tables_xiebo(phase, xiebo_out);
module xiebo(address,xiebo);
将谐波比例定义为[1:0.618],使用matlab中的simulink功能进行仿真,取一个周期256点,从而得到谐波波表,再通过读表模块输出给扬声器。
模块五、蜂鸣器频率信号选择模块
unpwm_beep2 beep2_u(
.clk(clk),
.keys_pluse(keys_pluse[1:0]),
.sel_speaker(sel_speaker),
.Keys_in(~keys_out),
.music_box(music_box),
.mode(mode),
.preset_note(preset_note)
);
此模块主要实现了按键改变蜂鸣器的输出频率。
模块六、pwm模块
pwm u_pwm(
.clk_in(clk),
.reset_n(rst_n_in),
.sel_speaker(sel_speaker),
.en(!(&keys_out[12:0]) | mode),
.dac_data(dac_data),
.preset_note(preset_note),
.piano_out(piano_out),
.beep(beepout)
);
此模块完成以下功能:1:模拟信号数字化。2:选择蜂鸣器或扬声器输出。
功能1:主要采用pwm累加器来实现,采用最高位输出。
always @(posedge clk_in) begin
if(!reset_n)begin
pwm_acc <= 0;
end
else if (en) begin
pwm_acc <= pwm_acc[12:0]+dac_data;
end
else begin
pwm_acc <= 0;
end
end
assign pwm_out = pwm_acc[13];
功能2:利用sel_speaker选择输出。
if(sel_speaker)//喇叭
begin
piano_out <= pwm_out;
beep <= 0;
end
else begin//选择蜂鸣器
piano_out <= 0;
if(cnt0 == preset_duty)begin
beep <= ~beep;//蜂鸣器低电平有效
end
else begin
beep <= beep;
end
end
六 遇到的主要难题及解决方法
问题1:如何添加谐波分量。这部分问题主要还是不了解谐波,在查询了资料后,发现谐波和基波的关系,以及在真的钢琴声中其比例关系,于是利用matlab对其进行了合成并创建了波表。波形图如下
问题2:模拟放大电路如何仿真。这部分问题主要是没有理解对那一部分进行仿真,以及如何仿真,怎么样展现仿真结果。后来发现,电子森林网站上有仿真工具的介绍,于是选择了LTspice软件,在网友的提示下对喇叭放大电路进行了仿真。
七 改进建议
1、代码资源使用率较高,需要优化,才能支持更多按键的和弦。下图为资源使用报告。
2、还可添加3次、4次谐波,产生更好的音色。
3、在音乐盒部分,本次代码只写入了一首歌曲,实际上应该还能进行乐曲切换,播放更多歌曲,以及实现八分、二分音符,使得更符合一个音乐盒的要求。
八、可编译下载的代码(放在项目的附件,用于验证)