所有关于电路
项目

EFM8声音合成器:通过USB播放旋律

2015年9月17日经过罗伯特·凯

第3部分“如何制作基于EFM8的声音合成器”系列。

第3部分“如何制作基于EFM8的声音合成器”系列。

本系列的前几篇文章

本文是一个完整的项目的一部分,以efm8为基础的合成器。请从以下内容开始:

必需的硬件/软件

描述 数量 Digi-key p / n
面包板 1 377-2094-ND
插座跳线电线 4. 1471 - 1231
5v AC/DC壁挂式电源 1 1470 - 2771
0.1µF电容器 5. 399-4266-nd.
五阶开关电容低通滤波器 1 LTC1063CN8 # PBF-ND
通用运放 2 LT1638CN8#PBF-ND
模拟电力缓冲器 1 lt1010cn8#pbf-nd
2µF电容器 1 490 - 8835
10 kΩ电阻 2 10kqbk-nd.
100Ω电阻 1 100年qbk-nd
8 Ω, 1瓦特扬声器 1 gf0771-nd.

项目概述

在上一篇文章结束时,我们的固件和音频电路可以一起产生滤波,放大的音乐音符频率正弦波,能够驾驶扬声器。我们甚至发现这种相对简单的系统产生的音调令人惊讶的是令人惊叹的。但是,如果所有它可以确实可以通过C重大比例无休止,我们的声音合成器将逐渐失去吸引力,因此我们需要一种方便的方式来打开和关闭音频,更重要的是发挥舒缓的旋律。

我们将通过在EFM8和Scilab之间建立一个虚拟COM端口(VCP) USB连接来实现这一点(参见本文有关Scilab的介绍性信息这一个介绍Scilab串口库)。本项目中使用的Scilab脚本提供了一个简单的命令行界面,用户可以通过该界面启动和停止音频回放、设置节奏并为一个简单的旋律输入音符信息。

scilab.

这是脚本:

efm8_soundsynthesizer_scilab_script.zip.

首先,Scilab配置并打开VCP。如果由于某种原因串行端口库无法与EFM8建立连接,Scilab控制台将说明无法打开COM端口,然后“中止”,这是Scilab Word用于“停止脚本执行并返回正常操作”。“如果连接已成功建立,则脚本进入无限命​​令行输入循环,其中scilab控制台窗口提示用户输入要发送到EFM8的字符串。要发送命令,只需键入相应的字符,然后按Enter键。要打破循环并退出脚本,请在不键入任何其他字符的情况下按Enter键(请注意,脚本终止之前“卸载”VCP连接,串行库“卸载”)。

EFM8固件识别出四种类型的命令:播放音频(“p”),停止音频(“s”),设置速度(“t ###”),并创建旋律(下面讨论的命令格式)。播放和停止音频命令打开或关闭声音信号和滤波器时钟方波,而不会干扰固件的正常操作。SET TEMPO命令用于控制播放速度;速度在每分钟节拍(BPM)中输入。固件当前将TEMPO限制为60到180bpm之间的节奏,因此该范围高于或低于此范围的数字将分别由EFM8更改为180或60。

创建旋律的方法可能看起来有点晦涩,但它很容易实现,如果你在输入字符的同时查看一段音乐,实际上可以非常快速和直观。旋律是由一个字母(代表音符)和一个数字(代表音符演奏的时间)组成的一系列字符组合输入的。固件目前支持全音符、半音符、四分音符和八分音符。

在进入旋律时,您将使用“1”整个注释,半张音符“2”,四分之一的音符“4”,并为第八次注释“8”。对于您自己,您使用相应的信件。命令行界面支持以C开头的两个八度高,并以B结尾(无锐白或单位)。下八度名中的音符由小写字母表示,更高octav中的字母由大写字母表示。因此,如果您想要播放接口支持的所有音符,按升序顺序且每次注释为季度注释,则输入“C4D4E4F4G4A4B4C4D4E4F4G4A4B4”。笔记:当前的固件只支持音符高达E在第二个八度;更高的音符不需要演示旋律,这些音符将逐步更衰减RC低通滤波器,如在前一篇文章中讨论。此外,在某个点,滤波器时钟信号将变得不可靠,因为PCA中断将如此频繁地发生,以至于EFM8的处理器将无法持续更新捕获/比较寄存器。你当然可以扩展固件包括这些更高的音符,但你需要关注滤波器时钟方波,你可能需要修改RC低通滤波器更高的截止频率。

关于命令行界面的其他细节:

  • 对于速度命令,速度值的ASCII表示转换为单个字节编号,以便EFM8不需要执行此转换。另一个命令直接传递给EFM8。
  • 发送Melodod命令时,会自动启用播放。
  • 所有命令都通过EFM8回馈,如下图所示。
  • 您可以使用“r”指定休息(即,没有播放的期限)“1”,“2,”“4,”或“8”;该数字指定其余的持续时间,作为整体,一半,季度或第八章。
  • 您可以在任何时候设置节奏,但播放速度不会改变,直到您输入一个新的旋律(或重新输入当前的旋律)。默认的节拍是120 BPM。
  • 在命令提示符下按向上方向键可以循环使用之前输入的字符串;这是非常有用的,当你发送长旋律命令,只是想改变一个音符,或如果你需要重新输入一个前一个旋律。
  • 为简单起见,所有命令目前都限于一个64字节USB数据包。每个音符需要两个字节,因此最大旋律长度为32个音符。

下面是显示命令行界面正确使用的示例。执行脚本,并且EFM8被编程为使用所有季度注释播放升高的C重大比例,然后是所有八分音符的降序C重大比例。接下来,速度降至60bpm,然后增加到180bpm。最后,在脚本终止之前再次启用播放。您会注意到速度命令的回音字符看起来不正确,但实际上它们是scilab显示与EFM8接收的单字节速度编号对应的ASCII字符。

USB接口配置

这篇文章提供使用Silabs VCPxpress库的USB通信概述,以及将VCP功能添加到Simplicity Studio项目的重要方面。在这里,我们将简要介绍将VCP通信合并到我们固件中的所有步骤:

1.将“vcpxpress.h,”vcpxpress.lib,“descriptor.c,”和“descriptor.h”复制到项目目录中:

2.在项目属性中添加适当的include路径:

3.在项目属性中添加VCPXpress库:

4.插入一个源文件,其中包含与vcpxpress库的交互和处理USB数据包的代码:

5.插入新函数原型的代码,全局变量和预处理器定义(构建错误将帮助您找到任何可能错过的任何内容),并在任何要求它们的源文件中包含“VCPXPRESS.H”和“DESSITERR.H”:

6.呼叫硬件初始化功能后,将调用添加到usb_init()和api_callback_enable():

7.要抑制链接器警告,复制“叠加(?PR?_USB_WRITEFIFO?efm8_usbdep!*,?PR?_usbd_read?efm8_usbd!*,?pr?_usbd_write?efm8_usbd!*,?pr?_vcpxcore_write?vcpxpress!*)”进入““链接器设置”中的其他标志:

中断优先级

EFM8的外设/中断配置的唯一变化是一个微妙但重要的更改:PCA中断已设置为高优先级。

最初,滤波器时钟方波输出非常不可靠,可能是因为与VCPXpress库相关的高优先级中断抢占了PCA中断。对优先级设置的修改确保及时处理PCA中断。

功能细节

固件通过将命令字符串转换为一系列音符和相应的Timer3计数序列来产生旋律,每个序列存储在一个单独的数组中:

void StoreNewMelody() {unsigned char x,y;y = 0;为(x = 0;x < USBBytesReceived;x += 2) {NotesSequence[y] = USBRxPacket[x];switch(USBRxPacket[x+1]) {case '1': Timer3Counts_Sequence[y] = WholeNoteCounts;打破;case '2': Timer3Counts_Sequence[y] = HalfNoteCounts;打破;case '4': Timer3Counts_Sequence[y] = QuarterNoteCounts; break; case '8': Timer3Counts_Sequence[y] = EighthNoteCounts; break; } y++; } NumberofNotes = y; CurrentNoteIndex = NumberofNotes - 1; //start playback at the beginning of the melody PLAYorSTOP = PLAY; }

在收到SET TEMPO命令时计算整个整体,一半,季度或第八次注释的计时器3的数量:

void SetTempo(unsigned char TempoBPM) {if(TempoBPM > 180) TempoBPM = 180;else if(TempoBPM < 60) TempoBPM = 60;QuarterNoteCounts = ((float)60/TempoBPM) * 10000;WholeNoteCounts = QuarterNoteCounts * 4;HalfNoteCounts = QuarterNoteCounts * 2;EighthNoteCounts = QuarterNoteCounts / 2;}

这些计算假设四分之一章节的持续时间是一个节拍,因此整个音符是4个节拍,半张音符2节拍,第八次注释一半的节拍。因此,对于60 bpm的速度,笔记将具有以下持续时间:

在主while循环中,程序循环遍历音符数组中的每个元素,并相应地设置声音信号和滤波器时钟增量,然后使用Timer3-counts数组中的相应值来实现适当的延迟。

while(1) {for(CurrentNoteIndex = 0;CurrentNoteIndex < NumberofNotes;CurrentNoteIndex++) {REPEATED_NOTE = FALSE;if(CurrentNoteIndex < (NumberofNotes - 1)) {if(NotesSequence[CurrentNoteIndex] == NotesSequence[CurrentNoteIndex + 1]) REPEATED_NOTE = TRUE;} switch(NotesSequence[CurrentNoteIndex]) {case 'c': Current_SoundSignal_Increment = SOUND_C5_INCREMENT;Current_FilterClock_Increment = FILTCLK_C5_INCREMENT;打破;case 'd': Current_SoundSignal_Increment = soundd_d5_increment;Current_FilterClock_Increment = FILTCLK_D5_INCREMENT;打破; case 'e': Current_SoundSignal_Increment = SOUND_E5_INCREMENT; Current_FilterClock_Increment = FILTCLK_E5_INCREMENT; break; case 'f': Current_SoundSignal_Increment = SOUND_F5_INCREMENT; Current_FilterClock_Increment = FILTCLK_F5_INCREMENT; break; case 'g': Current_SoundSignal_Increment = SOUND_G5_INCREMENT; Current_FilterClock_Increment = FILTCLK_G5_INCREMENT; break; case 'a': Current_SoundSignal_Increment = SOUND_A5_INCREMENT; Current_FilterClock_Increment = FILTCLK_A5_INCREMENT; break; case 'b': Current_SoundSignal_Increment = SOUND_B5_INCREMENT; Current_FilterClock_Increment = FILTCLK_B5_INCREMENT; break; case 'C': Current_SoundSignal_Increment = SOUND_C6_INCREMENT; Current_FilterClock_Increment = FILTCLK_C6_INCREMENT; break; case 'D': Current_SoundSignal_Increment = SOUND_D6_INCREMENT; Current_FilterClock_Increment = FILTCLK_D6_INCREMENT; break; case 'E': Current_SoundSignal_Increment = SOUND_E6_INCREMENT; Current_FilterClock_Increment = FILTCLK_E6_INCREMENT; break; case 'r': SFRPAGE = PCA0_PAGE; PCA0CN0_CR = STOP; break; } //delay for the length of time corresponding to the current note SFRPAGE = TIMER3_PAGE; TMR3 = 0; while(TMR3 < Timer3Counts_Sequence[CurrentNoteIndex]);

这种方法的一个问题是,两个相同音调的短音符听起来像一个更长的音符。要纠正此缺陷,代码检查下一个注释是否与当前注释相同,如果是,则当前注释后跟一个短延迟,在此期间禁用声音信号和滤波器时钟波形波:

重复= n = false;if(CurrentNoteIndex < (NumberofNotes - 1)) {if(NotesSequence[CurrentNoteIndex] == NotesSequence[CurrentNoteIndex + 1]) REPEATED_NOTE = TRUE;} // --------------------------------------------//then after the note has been played... //----------------------------------------------------- if(REPEATED_NOTE == TRUE) { SFRPAGE = PCA0_PAGE; PCA0CN0_CR = STOP; SFRPAGE = TIMER3_PAGE; TMR3 = 0; while(TMR3 < 200); } SFRPAGE = PCA0_PAGE; PCA0CN0_CR = PLAYorSTOP;

SoundSynthesizer_Part3.zip

这种技术让我们能够从“咔嚓-噗”声中获益,这通常是音频电路中一个棘手的方面:当方波被重新激活时,快速的咔嚓声或噗声便能够在重复的音符之间提供额外的定义。然而,我们不希望有太多的“点击-pop”现象,因此我们将电路中的dc阻塞电容从2µF减少到0.1µF,以努力缓解这些人为干扰,这些人为干扰部分是由于充电滤波电容产生的瞬态电流。

下面的视频提供了两个样本旋律;相应的命令行条目在标题中给出。这个系统产生的声音似乎隐约让人想起中世纪的长笛,旋律也相应地被选择。

旋律命令:E4a4a2E4a4a4a8b8C4C4b4C4D4D8D8C4D4E4E8E8D4D4C4C4b2a4C4b4g4a2a2;节奏命令:T180

旋律命令:d2a2e2f8e4d2a2C4D2C4a4b4g4a2r2a4D2D4C2a2g4f4e2c4d2a4g2f4e4d4c4d1;节奏命令:T120。其中的一些音符是模糊的,因为用一个更长的音符替换了多个相同音高的音符,使得将整个旋律放入一个64字节的数据包成为可能。

为自己提供这个项目!得到BOM.