基于FPGA的电子琴制作
基于小脚丫核心板及外围电路,本设计实现了包含弹奏和音乐播放器两种功能的电子琴。电子琴可以通过开关在蜂鸣器和扬声器中选择其一输出音频,后者支持音量调节和和弦输出,且两者均支持音程扩展(包含三个八度)。核心板的LED等部件可显示当前工作状态。
标签
FPGA
数字逻辑
DDS
DAC
2022暑假在家练
Parteiadler
更新2022-09-02
浙江大学
1052

一、任务要求:

    基于提供的套件和工具,自己组装电子琴。

    利用FPGA和Verilog编程实现:

        存储一段音乐,并可以进行音乐播放。

        可以通过板上的按键进行弹奏,支持多个按键同时按下(和弦)并且声音不能失真,并可以通过右上方的“上”“下”两个按键对音程进行扩展。

        使用扬声器进行播放时,输出的音调信号除了对应于该音调的单频正弦波外,还必须包含至少一个谐波分量。

        音乐的播放支持两种方式,这两种方式可以通过开关进行切换:

            当开关切换到蜂鸣器端,可以通过蜂鸣器播放音乐。

            当开关切换到扬声器端,可以通过模拟扬声器播放音乐,每个音符都必须包含基频+至少一个谐波分量。

二、作品实现的功能:

本电子琴基于硬禾学堂提供的的小脚丫FPGA核心板(Lattice MXO2-C)及外围电路实现了以下功能:

电子琴可以通过蜂鸣器或模拟扬声器发声,两者之间通过板子上方的切换开关切换,开关拨到“P”端使用蜂鸣器,拨到“A”端使用扬声器。使用扬声器时,可以通过板子上方的旋钮调节输出音量,逆时针音量减小,顺时针音量增大。

电子琴有两种工作模式:音乐播放器模式和弹奏模式,两种模式均可以自由选择蜂鸣器或扬声器发声,可以通过mode按键(开发板上的K2按键)切换工作模式。切换工作模式时,原模式的状态会被保存,当工作模式切换回来时,可以从之前的状态继续工作。

音乐播放器模式下可以播放存储的四段音乐,通过两个功能按键控制。play按键(开发板上的K4按键)可以使播放器在播放和暂停之间切换,next按键(开发板上的K3按键)可以使播放器切换到下一首乐曲,并自动开始播放。当前乐曲播放结束后,可以按下play键重新播放,也可按下next键播放下一首歌曲。

弹奏模式下可以通过板上的琴键进行弹奏。其中,使用扬声器发声时支持最多4个按键同时按下(和弦,或其他任意组合)。板上只有13个按键,可以通过右上角两个按钮(“上”“下”)切换音程(升高或降低一个八度),共支持三组音程(C3~C4、C4~C5、C5~C6)。

开发板上的两位数码管、两个RGB LED灯和三个红色LED灯可以显示当前电子琴的工作状态。

两位数码管:电路处于音乐播放器模式时显示当前歌曲编号(01、02、03、04),处于弹奏模式时显示“P”“L”两个字符,表示“play”状态。

两个RGB LED灯:左侧、右侧的RGB灯分别表示音乐播放器模式和弹奏模式的工作状态。音乐播放器模式下,若当前正在播放音乐,左侧RGB灯亮绿色;音乐播放暂停,或音乐已播放结束,左侧RGB灯亮蓝色;右侧RGB灯始终亮红色,表示弹奏模式不可用。弹奏模式下,若当前没有任何琴键被按下,右侧RGB灯亮蓝色;有一个或以上琴键被按下,右侧RGB灯亮绿色;左侧RGB灯始终亮红色,表示音乐播放器模式不可用。

三个红色LED灯:用于显示当前电子琴所处的音程范围。弹奏模式下,当电路处于最低的音程时,最下方1个LED灯亮起;电路处于第二个音程时,最下方2个LED灯亮起;电路处于最高的音程时,最下方3个LED灯亮起。音乐播放器模式下,下方3个LED灯也会根据上述规则,显示正在播放的音符所处的音程范围。

使用者随时可以通过按下rst按键(开发板上的K1按键)使电路复位到初始状态(弹奏模式,上电默认状态)。

三、电子琴的工作原理与电路框图:

电子琴整体电路框图如下:

FgHPxQvxhtK97EiWyrdh5A_0pszt

该电路可以分为按键处理模式控制音乐播放器音频输出状态显示5个部分。

按键处理部分:

本部分包括若干按键处理模块(button_process_unit)和音程控制模块(range_ctrl)。

按键处理模块,用于同步化按键异步输入信号、消除按键抖动和信号脉冲变换。其电路框图如下。因电路特殊性,所处理的按键信号需为高电平有效,与我们的按键输入恰好相反(低电平有效),因此在模块输入端添加了一个非门。

Fgg5dWxKNUdgDsccjdHYVLZr7NNA

按键输入信号对于内部时序电路来说是异步信号,为避免异步信号将错误的数据(此处为按键状态)送入后续时序电路,需要增加同步器。

FmdsKsDwdU1o4qrJXvm6ChQDo7Fq

按键被按下或释放时,会有几毫秒的不稳定状态(抖动),这种抖动极可能使电路进入错误的状态。debounce模块对应消抖电路,内部包括分频器、计时器和控制器。

FhiczS6FKyBRfDzJcd8FEMFpuXEQ

分频器counter将12MHz的系统时钟转换为频率为1kHz、宽度为一个时钟周期的脉冲co。将该脉冲送至计时器模块的使能输入端,后者负责完成10ms的计时,按键抖动的时间一般都小于这个值。控制器则用以控制计时器开关和稳定信号输出。经控制器的调节,消抖电路的输出会从按键刚刚按下、开始抖动时变为高电平,直到按键完全松开、结束抖动时变回低电平,消除了抖动。

后续电路处理按键信号时,通常会要求信号是宽度恰好为一个时钟周期的脉冲,消抖电路的输出往往不满足这个要求。我们用一个D触发器和一个与门将消抖电路输出的长高电平转换为宽度为一个时钟周期的短脉冲。

这样,按键处理模块就完成了将按键输入转换为宽度为一个时钟周期的脉冲的功能。

音程控制模块,负责处理弹奏模式下右上角两个音程切换按键(该按键信号是经过按键处理模块处理后的短脉冲),输出当前琴键所处音程range(三个有效值,对应三组音程),实质上是一个0~3的加减计数器。

模式控制部分:

本部分包括一个D触发器和若干数据选择器,电路相对简单,但涉及到电路中绝大部分信号的选择,相当于整个电子琴的控制器。

mode按键信号经按键处理模块转换后,被送至D触发器的使能端。D触发器的输出信号mode(电路内部保存的模式编号)在经非门翻转后送入输入端D,这样我们每按一次mode按键,电子琴的工作模式就会在音乐播放器模式和弹奏模式之间切换。

电路中的数据选择器则负责不同的功能。

切换音程的“上”“下”按键只能在弹奏模式下工作,音乐播放器模式下必须使其无效化;play和next两按键则恰好相反。数据选择器根据当前mode值,对需要无效化的按键处理模块输入持续的高电平(等同于按键未按下)。

为节省FPGA资源,音乐播放器和按键弹奏共用同一组音频输出电路(蜂鸣器和扬声器的输出),两种工作模式下控制该部分电路的按键状态信号(key_in_n,n意为低电平有效)和音程范围信号(range)来源不同。数据选择器会根据当前mode值,从实际按键控制输入信号和音乐播放器输出的“模拟”控制信号之间选择其一,送至音频输出部分电路。

最后,电子琴需要根据使用者设置(切换开关),从蜂鸣器和扬声器中选择其一输出音频。因为蜂鸣器和扬声器是硬连接至开发板的,所以我们需要增加数据选择器电路,根据开关状态从两音频输出电路中选择其一来工作。

上述数据选择器可以简单地使用条件语句实现,以“上”按键的处理模块为例,其输入会在mode=1(音乐播放器模式)时设为高电平(“1”),请见button_in信号。

button_process_unit range_up(

    .clk(clk),

    .rst_n(rst_n),

    .button_in(mode?1'b1:range_button[1]),

    .button_out(pulse_range_up));

音乐播放器部分:

本部分包括主控制器、乐曲读取和音符播放三个核心模块,另外还有一个将1kHz脉冲转换为1Hz脉冲的分频器。

Foa7zDk1FWbYQ40VCOCAUFQY5ZQN

主控制器模块接收按键信息,通知乐曲读取模块是否要播放(play)及播放哪首乐曲(song)。每当控制器检测到next按键信号,就会将counter的计数值加1,即song的值加1,播放下一首歌曲。

FpsmJUiYmWNsHU02v7p3MpzPLuPx

乐曲读取模块随之逐个取出音符信息{note,duration}(音高、时长,从C3~C6每个音都对应一个note编号,duration则以1/48s为单位)送到音符播放模块。当前乐曲播放完毕时,回复主控制器模块一个乐曲播放结束脉冲(song_done)。

song_rom是一个27×12bits的ROM,其中存储了四首音乐的音符信息(note和duration,均为6位),每首音乐占用25×12bits空间,即每首乐曲最多能存储32个音符(包括空拍,note=0)。因此,ROM查找地址高2位为song信号,低5位则由计数器累加得到。

FmD-_Ee860XQmtzrSZIW3NqgIBOr

音符播放模块在当前音符播放时间(duration)内输出该音符对应的按键状态(key_in_n)和音程范围(range)送至音频输出部分,“模拟”了弹奏模式下的琴键输入。当音符播放结束时,向乐曲读取模块发送一个note_done脉冲,以索取新的音符。音符时长duration的计时通过一个特殊的计时器实现,该计数器相比于普通计时器增加了一个duration数据输入作为最大计数值。

FgCftdVO04DWwJNN3DvMEXSjtR5Y

音频输出部分:

本部分包括蜂鸣器和扬声器的驱动电路,是本次设计的重点内容。

本电子琴使用无源蜂鸣器,它需要在供电端加上高低不断变化的电信号才可以发出声音。驱动蜂鸣器所需电流较大,FPGA的IO口无法满足,因此借用一个NPN三极管驱动,FPGA的IO口连接至基极作为控制信号。

Fv9qvzhRC3XieJPK_dseA-jpsfd5

(图源电子森林百科知识库)

蜂鸣器发出的音高与输入高低电平的振荡频率有关,在本设计中我们使用占空比为50%的不同频率方波实现蜂鸣器的音高控制,该方波由一个PWM波产生模块生成。

PWM波产生模块实质上是一个计数器,最大计数值为cycle-1,当计数值小于duty时输出高电平,否则输出低电平。实现代码如下:

always@(posedge clk or negedge rst_n)

    if(!rst_n)

        cnt <= 0;

    else if(cnt >= cycle-1)

        cnt <= 0;

    else

        cnt <= cnt+1;

assign pwm_out = (cnt < duty)? 1 : 0;

这样,我们根据当前琴键状态(key_in_n)和音程范围(range),从一个ROM模块中取出相应频率的计数值(cycle)送入PWM波产生模块中,就可以控制输出方波的频率了。cycle值等于系统时钟频率和输出方波频率的分频比,例如我们想要利用12MHz的系统时钟产生440Hz(对应音高A3)的方波,则cycle=12×106/440≈27273。因为占空比始终为50%,将cycle值右移一位即可得到占空比计数值(duty)。系统中cycle的最大可能值为45802,使用16位计数器已足够。下图为蜂鸣器驱动电路框图。

FsBWtMQ85SB10_wkztuHSn7DMikm

不同于利用数字信号驱动的蜂鸣器,扬声器信号需要由模拟信号驱动。我们的FPGA IO口只能输出数字信号(高、低两种电平),因此扬声器驱动模块需要对输出信号做一定的处理。其具体工作原理如下。

首先我们将想要输出的模拟信号波形以一定频率采样后存入一个查找表中,通过控制查找表寻址地址及其跳变步长,我们就可以输出不同频率的模拟信号值了,这就是DDS模块的工作原理。本设计DDS模块内部的查找表为212×16 bits,相关参数的计算及确定请参考“项目进度”中的“DDS”部分。

需要指出,模拟波形中的谐波分量是直接写入查找表内的。我猜测设计任务中的谐波是要求我们使用多个相位增量输出不同谐波,再在FPGA电路中将这些采样值相加。这一目标只需要微调电路,给DDS增加几个分时输出就可以了,但谐波的幅值控制起来不够灵活,故我在利用C语言代码生成查找表时直接将谐波分量写入查找表内了。

得到模拟信号采样值后,我们需要对其做一定处理,以便于FPGA输出。此处我们使用了一个简单的σ-Δ调制器,其输出为占空比与输入信号值正相关的PWM波。核心代码如下:

reg [16:0] acc;

always@(posedge clk or negedge rst_n)

    if(~rst_n) acc <= 0;

    else acc <= acc[15:0] + sample_offset;

assign pwm_out = acc[16];

sample_offset为输入的16位模拟信号采样值,acc为模块内部的17位累加寄存器。我们取acc的低16位与sample_offset相加,并输出acc的最高位,这样当累加器的低16位有进位时,模块就会输出高电平。sample_offset的值越大,输出高电平的宽度就越宽。下图为扬声器驱动电路框图。

FnRq0qQDRafF7OHoZN5FHSTMT-h_

该PWM波经开发板外接的一个RC低通滤波器后,就会变为与DDS模块查找表内部波形基本一致的模拟信号,该信号经运算放大器放大后送入扬声器,使其发声。可以想到,只要我们改变查找表中的波形数据,就可以让扬声器发出任意波形的声音了。

状态显示部分:

本部分只由state_display一个模块组成。该模块根据电路内部各状态变量控制数码管、RGB灯和普通LED灯的输出,方便使用者直观地看到电子琴的工作状态。本部分使用纯组合电路实现,根据功能要求简单编写即可。

四、蜂鸣器和模拟扬声器的音效差别:

从视频里可以明显看出,蜂鸣器发出的声音明显要比扬声器的声音“硬”一些。这一对比在扬声器使用单频率正弦波时更为明显。这是由人耳对于高频信号更加敏感这一现象导致的。蜂鸣器输出的方波中包含丰富的高频分量,所以听起来会比较刺耳;单频率正弦波只包含一个频率分量,因此听起来很“柔”。我们挂断电话时的“嘟嘟”声就是450Hz的正弦波音频,这一频率和A3音高(440Hz)相近,弹奏时听起来也确实几乎一模一样。

此外,扬声器的灵活性也自然不必多提了,这一点是蜂鸣器很难甚至无法实现的。

五、低通滤波电路仿真

此处内容是完成项目后补充的,仅实现了单频率正弦波的simulink仿真。仿真电路由PWM产生模块和低通滤波电路级联组成,整体仿真电路如下:

FsJIHvMRYMk0Qh5GXQpOpNK51kDd

左侧的字电路即PWM产生电路,其电路框图如下:

FkA6B96hdtNH2rmK7pw9Sqg7viU1

可以看到本模块是完全按照Verilog设计实现的。

改变输入的相位增量的值,分别仿真了262Hz、440Hz和2093Hz的正弦波,三个取值分别对应电子琴最低音、标准音和最高音。仿真波形如下:

FgfpvQGUpwoqGVl9Y5ce8o1O4DDcFvTQiB9CfSn65mCQeoRo3XRVXOjgFnNQWrEqCzhAhDPNwGj043qmIkyg

每次仿真输出波形从上至下依次为PWM、DDS输出和LPF输出波形。可以看到,输出波形存在一定波纹,但失真仍在可接受的范围内。

六、遇到的难题:

按键消抖模块中因为加入了同步化电路,若直接使用低电平有效的按键输入信号,即便修改后续状态机电路,在系统复位时仍会进入错误状态,不能正常工作(也可能是状态机的编写不太正确吧)。目前我是在模块前加一个非门翻转输入信号解决这个问题的,引入了不必要的时延(不过对于电子琴这种低速电路基本没有影响)。

蜂鸣器如何同时输出多个音符?这一问题是否可解我还不清楚。本设计在蜂鸣器模式下是只支持单音输出的,若同时按下多个按键,蜂鸣器就会被关闭,以避免错误的音频输出。

DDS参数的确定。开发过程中做出过位宽比较小的DDS模块,输出的音色不是很理想(波形不够“光滑”),且最高音程的最后几个音输出时有明显失真(听觉上是音高不准,用示波器观察波形失真比较严重,可能是接近采样定理极限的缘故)。最后的成品索性在确定DDS参数时将其理论频率范围设置为20Hz~20kHz(人耳的听觉范围),占用空间大大增加了,不过音色也确实好听许多。

刚刚成功驱动扬声器发声时,音量调至最大时会失真。通过示波器观察发现运放饱和了(见“项目进度”的“驱动扬声器(单音)”部分),通过减小波形查找表中的信号幅值避免了这个问题。

最开始设计扬声器多音(和弦)输出时,直接对每个琴键分别例化了一个DDS模块,导致FPGA资源耗尽了。最后借(照)鉴(搬)了群友分时复用的方案,将DDS的例化个数减少到1个,成功实现了。此外,还遇到输出波形不正确的现象(见“项目进度”的“驱动扬声器(多音)”部分),经检查代码逻辑并无问题,后来发现是不少组合电路达不到时序约束要求,在综合过程中被优化掉了。将这些电路改为由时钟驱动的时序电路后避免了这个问题。设计复杂电路时时序分析很重要,有时需要跑一跑后仿真。

设计状态显示模块时发现板内ROM已经被用光了,模块内若使用always块实现,电路就无法正常工作(因为此时电路被综合成了ROM块,资源不够用就出问题了)。只好用assign语句将该模块用纯组合电路实现。

七、未来优化方向:

优化DDS模块:在对音色影响不大的前提下尽量减少DDS模块占用的资源,可以通过减小采样值位宽和降低采样频率实现。此外,若使用硬件电路计算谐波的方案,则可以利用正弦信号的对称性,将波形查找表缩小为原有大小的1/4。实现信号ROM的分时复用时,25个循环周期中只使用了10个,剩余周期可以用于电路的其他工作,比如将各谐波分量求和。

设计不同音色:扬声器具有很高的灵活性,我们可以存储不同的模拟波形来实现不同的输出音色,使用者可以通过按键切换。

音乐播放器模式:目前播放器的实现方式是不支持输出和弦的,可以考虑改进结构后加入和声;可以为播放器添加更多交互,比如切换至上一首、弹奏跟练功能等。

弹奏模式:可以考虑利用开发板的外设,向使用者展示当前按下音符的音高、按出的和弦等信息。

考虑到FPGA资源,上述方向可能无法同时实现,不过还是很值得尝试的。

八、改进建议:

琴键的布局可以改进一下,模仿真实的琴键排布。目前的盖板黑键和白键之间没有区别,弹奏时很容易误判。

板上可以开设一个接地用的测试孔(或者说我没有找到?)。使用示波器时接地端探针是将开发板翘起后挂在GND引脚上的,不是很方便。

九、工程文件说明:

项目附件中包含README文件、可以直接用于烧录的成品jed文件、成品工程文件包和开发过程中的部分工程文件包。每个工程文件包下有三个文件夹。diamond文件夹存放Lattice Diamond工程文件;sim文件夹存放ModelSim仿真工程文件,若该工程文件包包括多个仿真,这些仿真工程会存放在不同文件夹内,文件夹名称均以tb结尾;src文件夹存放代码文件(.v),包括各模块代码和仿真用到的顶层代码,后者文件名称以tb结尾。

十、参考资料:

电子森林百科知识库

数字系统设计实验教程

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