项目创建者:Jayson Johnston和Bjorn Nelson
在当今的音乐产业中,最常用的“乐器”之一就是数字合成器。从嘻哈到流行音乐,甚至是乡村音乐,每种类型的音乐都使用工作室中的数字合成器来创造他们需要的音乐和节奏。在本教程中,我们将使用Basys 3 FPGA电路板创建一个非常简单的合成器。
合成器将能够以每分钟不变的次数播放四个选定的四分音符。用户将使用开关将每个四分音符分配到一个音高。对于这个项目,我们使用一个4位数模转换器(DAC)来从电路板获取输出并将其转换为模拟信号。然后DAC的输出将被馈送到标准的电脑扬声器,创造我们的音乐。十六个分立的音高是可能的。我们将把我们的合成器限制在12个音符的一个八度音阶之间,这个音符位于中间C(261.6 Hz)和B4(493.9 Hz)之间。用户还可以选择同时分配多个音符,也可以在没有音高开关向上移动的情况下按下分配来分配一个音符。当每个音符被选中并被播放时,字母音符显示在7段显示器上。
一旦用户对其音符的选择感到满意,并且在按下播放按钮之后,合成器将连续播放每个音符,直到用户按下暂停或选择。
以下是所需设备的清单:
- Vivado(或任何VHDL工作空间)
- Basys 3或类似的FPGA板
- 数模转换器(最小4位)
- 扬声器与耳机插孔
- 导线
第1步:数字音序器的用户操作
以下步骤是操作数字音序器。数字音序器支持从261.6 Hz到493.9 Hz的12个不同音高(C,Db,D,Eb,E,F,Gb,G,Ab,A,Bb,B)的播放。
1.按下左键将电路板置于选择模式。在此模式下,最左边的4个开关(开关13至16)将分别用于存储不同的音高值。
2.要进行选择,请打开其中一个左侧开关,然后使用最右侧的4个开关(开关1至4)选择所需的音高。与右开关的特定组合相关联的音调将显示在七段显示器上,并且只要右开关被移位到新的组合,显示器就会更新到新的相关音调。可以通过从不给一个左开关分配一个音高,或者通过在显示器上给音符指定一个显示为0的音高来指定一个静止。一旦找到所需的音高并显示在显示屏上,按底部的分配按钮将特定音高分配给音符。
3.对剩下的三个音符重复步骤2,分别打开每个剩余的左侧开关,用右侧开关选择各自的音高,然后按底部按钮将音高分配到音符。通过同时向上移动多个左侧开关,可以为多个音符指定相同的音高。
4.现在已经分配了所有的音符音调,数字音序器就准备好播放了。要在扬声器上播放音符,只需按右侧的播放/暂停按钮即可开始播放音乐。播放顺序的顺序从左到右镜像与左侧开关关联的音高。按照1,2,3,4,1,2 ...的顺序,音符将以每分钟的设定次数播放。显示屏将显示扬声器播放音乐时正在播放的音符。要暂停音乐播放,只需按右键,然后音乐将停止播放,并在显示屏上显示一个暂停符号。再按一次右键将恢复播放。
第2步:技术细节
我们的合成器使用许多不同的数字组件。包括有限状态机,寄存器,多路复用器,时钟分频器等等。要构建我们的合成器,我们使用了10个独特的模块化文件。我们不是把每个模块都作为一个组件,而是按功能分解模块化文件。因此,大多数模块不止一个组件。请注意,上面的图片显示了我们顶级设计中的每个块连在一起。
我们将通过描述输入和输出来讨论每个模块,分解它的组件,并在整体设计中解释它的用途。可指导的底部包含一个ZIP文件,其中包含项目中使用的每个VHDL代码文件。
输入
- Clk(本地时钟信号)
- PP(播放/暂停)
- Sel(将合成器置于选择模式)
- 分配(分配一个音高)
- 步骤(位置注释)
- 频率(开关创建所需的音高)
输出
- 阳极(7段阳极)
- 阴极(7段阴极)
- DAC(4位驱动DAC)
第3步:技术细节
第4步:7段时钟分频器
我们的合成器使用三个时钟分频器,所有这些都产生了在我们的项目中用于不同目的的信号。时钟分频器产生一个本地时钟信号并产生一个频率比原始时钟信号小的改变信号。Basys 3的本地时钟是100 MHz。这是我们时钟分频器使用的频率。如果您使用不同的本地时钟频率的FPGA板,则可能需要更改代码。
7段时钟分频器产生一个驱动seg_display文件的信号。我们将在解释该文件时详细解释这个文件是如何工作的。本质上,这个时钟分频器产生一个240赫兹的信号,将被用来在显示器上的阳极和阴极之间切换。该信号是240Hz,因为人眼不能识别不存在光的频率是60Hz。我们使用两位数字,所以通过加倍这个频率,每个数字将以60Hz振荡。然后,我们加倍得到240赫兹,因为系统只在信号变高时变化,而不是变低。
为了实现这一点,分频器采用本机的100 MHz信号,并在每个上升沿计数。当计数器达到416667时,输出将从低到高,反之亦然。
输入
- Clk(本地时钟信号)
输出
- clk_7seg(对seg_display)
组件
第5步:每分钟跳动分频器
BPM时钟分频器以类似的方式工作。这个分频器产生的时钟频率驱动四个阶段之间的切换时,在播放状态输出音调。我们决定在100 BPM之间切换音符。在100BPM时,每个音符将播放3/5秒。结果信号的频率为1.67 Hz。
为了产生这个频率的信号,我们再次使用了一个计数系统,但这次计数是6000万。每当计数器达到6000万时,输出信号就会高或低。
输入
- Clk(本地时钟频率)
输出
- Clk_BPM(至output_FSM)
组件
- D寄存器
- MUX
- 逆变器
- 加法器
步骤6:投球时钟分频器
时钟分频器是我们时钟分频器中最大的。这个分频器输出12个不同的信号,对应于我们的合成器可以播放的12个不同的音符。利用音乐理论的基本知识,我们推断一个位或总线可能以与音符的频率相对应的速率摆动。我们用了第四个八度的音高。
这里使用相同的计数系统。对于我们计算的特定值,请参阅标记为Clk_div_pitches的文件。
输入
- Clk(本地时钟频率)
输出
- C,Db,D,Eb,E,F,Gb,G,Ab,A,Bb,B(至output_select)
组件
- D寄存器
- MUX
- 逆变器
- 加法器
第7步:播放/暂停/选择状态机

在我们的项目中有两个有限状态机(FSM)。FSM是一种逻辑器件,只能在有限的状态中以一种状态存在。使用FSM,数字电路可以基于输入的组合移动到新的状态。使用输入逻辑时,FSM的状态将在时钟的上升沿发生改变。从状态和输入到电路中,您可以创建输出逻辑,只有当FSM处于特定状态时,输出才会存在。
PPS状态机是我们电路中的第一个FSM。这个FSM有三个状态; 播放,暂停和选择模式。为了移动不同的状态,我们使用了PP和选择按钮。查看上面的状态图,了解状态之间的转换是如何发生的。我们在原生100 MHz时钟的上升沿进行了FSM转换,因此即使在很短的时间内按下其中一个按钮,机器也不可能转换。当前状态(P_state)是该模块的唯一输出。
输入
- Clk(本地时钟频率)
- Sel(左键)
- PP(右键)
输出
- P_state(当前状态,输出_FSM,note_assign,seg_dsiplay,final_select)
组件
- MUX
- D寄存器
第八步:播放/暂停/选择状态机
第9步:输出FSM
这是上一节中引用的第二个FSM。这个FSM的功能与其他功能不同,但是这个功能的基础是相同的。
只有当来自第一FSM的当前状态是“01”(播放状态)时,输出FSM才操作。实质上,这是模块的启用。如果状态为“01”,则FSM将在BPM时钟信号的上升沿之间切换状态。我们这样做是因为output_FSM正在控制将所选音高的哪个二进制数发送到output_select和seg_display模块。FSM具有来自音符分配模块的16位输入,这将在下面介绍。在output_FSM的“00”状态下,模块将为分配的第一个音符输出“xxxx”。然后在“01”中,输出“yyyy”作为第二个音符,依此类推,然后回到第一个音符。看到上面的状态图。
这种FSM与第一种不同,因为没有输入逻辑来控制状态之间的切换。相反,FSM只在第一个FSM的状态为“01”时才开始工作,然后这个FSM只在时钟信号的上升沿之间转换。另一个区别是,这个模块有输出逻辑,这意味着它不输出当前状态,它输出在该状态下音调的二进制数。
输入
- Clk_BPM(来自时钟分频器的BPM时钟信号)
- FSM1_state(来自PPS FSM的PS)
- Pitch_in(来自note_assign的音高)
输出
- Pitch_out(一次一个音高,到output_select和seg_display)
组件
- MUX
- D寄存器
第10步:输出FSM
步骤11:音符分配
音符赋值模块负责实际为位置音符或步骤分配音高。这个模块其实很简单。它首先检查电路是否处于“选择”状态,以及步进开关(最左)是否高。如果这是真的,按下分配按钮,模块的输出将等于由频率开关表示的二进制数(最右边)。
最初,我们曾试图制作一个模块,可以将一个音高时钟信号保存到输出端,但是我们遇到了输出信号跟随输入时钟信号的问题。这是最终设计中唯一使用的模块。每一步都有一个与之关联的note_assign模块,因此,模块的每个实例都会获得Step总线的一个位。
输入
- P_state(来自PPS FSM的当前状态)
- Sel(左键)
- 开关(一步开关)
- Freq(音高最右边的开关)
- 指定(底部按钮,指定一个音符)
输出
- 间距(二进制数,输出_FSM)
组件
- MUX
- D resgister
第12步:输出选择
输出选择负责取一个音调的二进制数并将其连接到相应的时钟信号。尽管它的大小,这也是一个相对简单的模块。Output_select本质上是一个二进制解码器,将二进制数字解码为一个特定的时钟信号。实际上,将输出分配给一个时钟频率与note_assign模块相比效果更好,因为这个模块所要做的就是用时钟信号MUX来代表控制输入的二进制数字。
我们为奇怪的路由道歉,Vivado按字母顺序为clk_div_pitches文件组织了音高信号,但是对于这个文件,它通过升序二进制数字来组织它们,导致音高顺序不同。还要注意,如果output_FSM的二进制数是“0000”或者大于“1100”,那么MUX通过平坦的“0”信号发送。
输入
- Pitch(来自output_FSM);
- C,Db,D,Eb,E,F,Gb,G,Ab,A,Bb,B(音高时钟信号)
产量
- 音(与选定的时钟信号匹配的单个位,与square_wave)
组件
- MUX
步骤13:方波一代
模块square_wave是从电路板输出到DAC的方波发生器。使用来自前一个文件的音调信号,这个square_wave在音调的上升沿反转“0000”和“1111”之间的4位数字。Tone是特定的音调频率,所以当output_FSM转换到另一个状态时,square_wave会产生不同频率的波形。这个模块的4位输出送到fin_sel模块,在那里逻辑决定了这个总线是否将根据PPS FSM的状态输出。
这个方波发生器的另一种选择是产生一个正弦波。虽然这最有可能产生更好的最终音调,但实现起来要困难得多,所以我们选择了产生方波。
输入
- 音(来自output_select的振荡位)
输出
- DAC_input(以相同频率变化的振荡4位总线)
组件
- 逆变器
- D寄存器
步骤14:7段显示
seg_display模块控制我们的basys板上的7段显示。在模块内,发生两个过程。第一个进程在“选择”状态时解码Freq,在“播放”模式时解码。在“暂停”模式下,模块解码显示暂停符号。查看VHDL代码,可以看到二进制解码器实际上将输入解码为两个不同的信号,即阴极1和阴极2。阴极1代表与要显示的音高相对应的字母,而阴极2代表平面符号(b)(如果有的话)。其原因与seg_display模块完成的第二个过程有关。
在basys3板上,分段显示器具有相同的阴极。当阳极控制哪个数字打开时,阴极控制哪个分段打开。由于显示屏具有相同的阴极,这意味着您一次只能显示一组分段。这对这个项目提出了一个问题,因为我们希望在第一位数字和平面符号(如果需要的话)同时显示一个字母。现在请记住7seg时钟信号?为了解决这个问题,我们在七段时钟信号上来回改变阳极和阴极。由于时钟信号是240赫兹,我们使用两位数字,每个数字将以60赫兹振荡。对于人眼来说,它看起来像数字根本没有振荡。
另外请注意,basys3板显示使用负逻辑。这意味着如果阳极或阴极设置为“0”,那么该数字或段将打开,反之亦然。
输入
- 音高(音符的二进制数,用于播放状态)
- 频率(频率开关,在选择状态时使用)
- P_state(来自PPS FSM的当前状态)
- Clk_240Hz(来自Clk_div_7seg的时钟信号,双120因为我们只使用上升沿)
输出
- 阴极(在显示器上控制分段的总线,最终输出)
- 阳极(控制显示器上的数字的总线,最终输出)
组件
- 闩
- MUX
- D寄存器
步骤15:最终选择
最终选择是在这个项目中使用的最后一个模块。另一个简单的模块,这个模块控制将要去DAC的最终输出。当处于“选择”或“暂停”状态时,模块将输出一个静态“0000”,以便扬声器不会播放音乐。在“播放”状态下,模块将输出由square_wave确定的振荡4位。
输入
- P_state(来自PPS FSM的当前状态)
- DAC_input(来自square_wave的振荡4位)
输出
- DAC(等于DAC_input处于播放状态,最终输出)
组件
- MUX
第16步:外部设备:DAC
数模转换器(DAC)接收离散信号并将其转换为连续信号。我们的DAC有4位,由加法放大器构成。通过使用电源和反馈环路中的电阻比率,我们可以创建一个系统,以16个不同的电平输出,并通过每个分支的“求和”创建。最高分支Bit0承载的权重最小,当分支的阻力较高时,贡献最小的潜力。你走下树枝时体重会增加。如果用二进制数来计数,然后用位输入进行计数,则输出电压看起来像是一个正弦波。DAC的输入连接到电路板上的PMOD之一来传输4位信号。
DAC最初是为一个电气工程类组装的,由我们设计和焊接,而不是从商店购买。以上是用于创建印刷电路板的设计文件的图像。
第17步:外部设备:扬声器
对于这个项目,你不会想要购买一对超好的扬声器。如你所知,声音是非常基本的。我们去买了一套来自百思买的8台电脑音箱。任何与耳机插孔工作正常。单调也很好。你甚至可以使用耳机,但是你可能会把它吹掉!
要将DAC的输出连接到扬声器,我们使用跨接电缆,然后将输出电缆固定到耳机插孔的顶端,将电缆连接到底座。我们尝试使用电工胶带来固定电缆,但是它造成了很多干扰。尝试不同风格的磁带可以解决这个问题。
对于我们的演讲者,我们把他们调到最高的位置,并得到了一个相当响亮的噪音。
这是从FPGA电路板创建数字音序器的最后一步!转到下面的两个部分来下载我们所有的VHDL代码,并看到序列发生器正在运行。
第18步:视频演示
https://youtu.be/dsz4KljJ0Wc本视频展示了工作项目的最终版本,包括将开关分配到4个不同音高的过程,以及演奏相应音符的扬声器。
第19步:VHDL代码
附件是整个项目的代码,包括构建顺控程序时使用的约束和sim文件。