|
本篇文章属于“如何制作基于EFM8的声音合成器”系列的第1部分。该系列包含以下内容: ● 第1部分:从方波到正弦波 ● 第2部分:驱动扬声器 ● 第3部分:通过USB播放旋律
所需的硬件/软件 ● SLSTK2000A EFM8评估板 ● Simplicity Studio集成开发环境
简介 本项目的目标是设计一个可以播放标准音符的声音合成器。我们将使用低成本、超小型的EFM8微控制器以及一些外部组件和扬声器来实现这一功能。在第一篇文章中,我们将重点介绍将EFM8生成的方波转换为适合驱动扬声器的正弦波。该实现过程的基本组成部分是时钟可调的单片低通滤波器。在这个项目中,我们将使用凌力尔特公司(Linear Technology)的LTC1063五阶巴特沃兹滤波器IC,没有采用可提供具有更高阶频率响应和不同滤波器类型的器件(例如Maxim Integrated公司的八阶贝塞尔低通滤波器MAX7401)。这些IC基于开关电容滤波器设计,因此我们将首先简要介绍该技术。
替代电阻 开关电容滤波器拓扑结构的本质是将电阻器替换为根据特定模式打开和关闭的开关。
在上图的左侧是标准RC低通滤波器,右侧是等效的开关电容。如您所见,电阻已经被两个开关和一个电容器的组合替代(后者是“开关电容”)。概念如下:当SW1闭合且SW2断开时,输入电压对C1充电。接下来,当SW1打开且SW2关闭时,电荷通过SW2从C1移动到输出端。我们这里展示的是从输入到输出的电荷。每个时钟周期传输的电荷量与C1的电容以及Vin和Vout之间的差值成比例,因此每秒传输的电荷量与C1、Vin-Vout和每秒时钟周期数(即频率)成正比。如果我们还记得电流是流经导体的每单位时间的电荷量,我们可以看到开关电容产生的平均电流如下:
根据欧姆定律,我们知道元件两端的电压除以流过该元件的电流等于元件的电阻,因此开关电容电路的等效电阻如下:
这很重要:可以通过改变驱动开关的时钟频率来简单地调整该等效电阻的值,因此可以通过数字方波的频率来控制开关电容器RC滤波器的截止频率。此外,截止频率不取决于绝对电容值,而是取决于C1与C2的比率,并且IC制造工艺可以比元件值本身更有效地控制元件值之间的比率。因此,开关电容滤波器拓扑是通用的并且与IC技术高度兼容。 IC制造商可以将多个基于开关电容的滤波器级合并到单个芯片中,从而产生具有频率响应的高阶滤波器,该频率响应可以通过数字方波的频率方便且精确地控制。
从方波到正弦波 幅度为k且频率为f的方波的傅里叶变换为
换句话说,方波是无限系列正弦波的总和,其频率增加并且幅度减小了3,5,7,9等因子。这些频率分量称为奇整数谐波。本系列中的第一项具有与原始方波相同的峰值幅度和频率,因此这是我们想要的术语。如果我们可以过滤掉所有其他的,我们将有一个平滑的,音频友好的正弦波而不是笨重的方波。
这就是五阶低通滤波器的用武之地。频率响应中每个极点应该达到6 dB /倍频程(或20 dB /十倍频率)的滚降,因此我们的五极系统的理论滚降每倍频程30 dB。 LTC1063数据表中给出的频率响应曲线证实了这一点:八度音程对应于频率加倍,我们可以看到,从20 kHz到40 kHz,增益降低了30 dB。上面给出的傅里叶级数中第二项的频率是方波频率的三倍;因此,通过适当放置滤波器的截止频率,该谐波应衰减40或45 dB。衰减40 dB对应于幅度降低100倍,高次谐波衰减甚至更多(至少70 dB),因此在这一点上看起来LTC1063可以很好地转动我们的方块挥动成正弦波。
固件概述 EFM8需要产生两个不同频率的方波。第一个频率与驱动扬声器的正弦波频率相同,第二个频率驱动LTC1063的时钟引脚,从而控制滤波器的截止频率。 LTC1063的时钟截止比为100比1;换句话说,截止频率等于时钟频率除以100.但要注意,这并不意味着EFM8产生的两个信号之间的频率比应为100比1.为什么?请考虑以下图表,其中提供了有关滤波器在截止频率附近的频率响应的详细信息:
回想一下,截止频率的另一个名称是3dB频率:在截止频率,增益为-3 dB,而不是0 dB。因此,如果EFM8的时钟信号频率恰好是声音信号频率的100倍,则声音信号将衰减3 dB。相反,我们希望声音信号频率位于频率响应开始下降的位置;此位置由上图中的红色星号标识。这一点的频率约为800赫兹;该图的时钟频率为100 kHz,100 kHz除以800 Hz等于125。因此,我们将配置EFM8的时钟信号与声音信号之比为125比1。
外设、端口和中断 我们将使用EFM8的可编程计数器阵列(PCA)生成两个方波。该外设由三个通道组成,这三个通道由相同的时钟驱动,但独立工作。每个PCA通道都可以执行各种定时和波形生成任务;在这种情况下,我们将为“高速输出”模式配置PCA通道0和1:
请注意,上面显示的“输出频率”不相关,因为我们将通过手动更新每个通道的捕获/比较寄存器来控制频率: - /**************************************
- output frequency timing
- **************************************/
- /*PCA clock period was measured at 488 ns.
- For a 400 Hz output, the period is 2.5 ms.
- 2.5 ms divided by 488 ns = 5123.
- So, 5123 PCA clocks gives the proper period for a 400 Hz output.
- However, we need to add only half of this value to the capture/compare
- register because a match between the capture/compare
- register and the PCA counter causes the output pin to toggle,
- and one clock period requires two toggles.
- The frequency of the clock that drives the lowpass filter is 125 times
- higher than the frequency of the sound signal, which means that the
- period is 125 times shorter. Thus, we divide the sound-signal increment
- by 125 to obtain the filter-clock increment.
- This same procedure is used to calculate the sound-signal and
- clock-signal increments for a 600 Hz output.*/
- #define SOUND_400Hz_INCREMENT 2562
- #define FILTCLK_400Hz_INCREMENT 20
- #define SOUND_600Hz_INCREMENT 1708
- #define FILTCLK_600Hz_INCREMENT 14
复制代码
时间详细信息在代码摘录中包含的注释中给出。一般概念如下:16位PCA计数器总是递增;当它达到0xFFFF时,它会溢出并继续计数。该计数器用于所有三个PCA通道。每个通道都有自己的捕获/比较寄存器。在“高速输出”模式下,每当PCA计数器的16位值等于存储在通道捕获/比较寄存器中的16位值时,PCA通道就会切换其输出引脚。通过以预定值反复递增捕获/比较寄存器,我们可以精确控制连续捕获/比较匹配之间的时间量,这就是PCA中断的来源:
现在,每次PCA计数器和通道的捕获/比较寄存器之间匹配时,输出引脚将切换并触发中断。在中断服务程序中,我们通过将预定的增量加到先前的捕获/比较值来控制输出信号的周期: - SI_INTERRUPT (PCA0_ISR, PCA0_IRQn)
- {
- if(PCA0CN0_CCF0) //this channel generates the sound-signal frequency
- {
- PCA0CN0_CCF0 = 0; //clear the interrupt flag
- PCA0Mod0_Compare_Value = PCA0Mod0_Compare_Value + Current_SoundSignal_Increment;
- PCA0CPL0 = LOWBYTE(PCA0Mod0_Compare_Value); //must write low byte first
- PCA0CPH0 = HIGHBYTE(PCA0Mod0_Compare_Value);
- }
- if(PCA0CN0_CCF1) //this channel generates the filter-clock frequency
- {
- PCA0CN0_CCF1 = 0; //clear the interrupt flag
- PCA0Mod1_Compare_Value = PCA0Mod1_Compare_Value + Current_FilterClock_Increment;
- PCA0CPL1 = LOWBYTE(PCA0Mod1_Compare_Value); //must write low byte first
- PCA0CPH1 = HIGHBYTE(PCA0Mod1_Compare_Value);
- }
- }
复制代码
请注意,我们在这里使用无符号的16位变量,因此我们不必担心手动处理变量溢出:当增量导致PCA0Mod0 / 1_Compare_Value变量超过65535时,它们将自动溢出到正确的值,因为PCA计数器也是16位。
两个PCA通道输出信号在P0.0和P1.1上驱动输出:
当前固件所需的唯一其他外设是Timer3,它用于产生毫秒到几秒的延迟:
我们还需要启用低频振荡器,以便我们可以将它用作Timer3的时钟源:
电路 以下是该项目部分的原理图:
以下是在面包板上实现的效果:
以下是此固件的主要while循环: - while(1)
- {
- Current_SoundSignal_Increment = SOUND_400Hz_INCREMENT;
- Current_FilterClock_Increment = FILTCLK_400Hz_INCREMENT;
- //delay 3 seconds
- SFRPAGE = TIMER3_PAGE; TMR3 = 0; while(TMR3<30000);
- Current_SoundSignal_Increment = SOUND_600Hz_INCREMENT;
- Current_FilterClock_Increment = FILTCLK_600Hz_INCREMENT;
- //delay 3 seconds
- SFRPAGE = TIMER3_PAGE; TMR3 = 0; while(TMR3<30000);
- }
复制代码
在这个无限循环中,EFM8输出400Hz的正弦波的声音和滤波器时钟信号,并保持3秒钟,然后切换为600 Hz正弦波的声音和滤波器时钟信号,并再次保持3秒钟。、
在使用这些正弦波信号驱动扬声器之前,还需要一些额外的滤波和缓冲电路,我们将在下一篇文章中继续探讨。 |