简易电子琴 —— PWM音频合成与蜂鸣器/喇叭驱动

文章目录


每日一句正能量

读懂他人是一种智慧,看清自己才是通透,战胜别人是一种能力,超越自己才是强大。

观察力、共情力,是聪明人的技能。但看清自己更难------需要勇气面对自己的阴暗、局限、真实欲望。看得清自己,才不会被他人的评价轻易带跑。超越自己是无限的游戏------今天比昨天多一分从容,今年比去年少一分内耗,这才是真正由内而外的强大。

前言

在嵌入式系统开发中,音频输出往往是一个既基础又充满挑战的领域。从简单的蜂鸣器提示音到复杂的音乐播放,PWM(脉冲宽度调制)技术提供了一种低成本、高效率的音频合成方案。本文将深入探讨如何利用PWM在嵌入式平台上实现一个简易电子琴,涵盖音阶频率计算、多音和弦合成、硬件驱动电路设计以及完整的代码实现。


一、PWM音频合成的物理原理

1.1 声音的本质与PWM调制

声音的本质是空气振动,人耳能感知的频率范围约为 20Hz ~ 20kHz。在嵌入式系统中,我们没有昂贵的DAC(数模转换器),但可以利用PWM的"等效电压"特性来模拟音频信号。

PWM通过快速切换高低电平,利用占空比(Duty Cycle)来控制平均输出电压。当PWM频率远高于音频频率时,经过简单的低通滤波,就能还原出平滑的模拟音频信号。

上图展示了不同占空比PWM信号的频谱特性。可以看到,50%占空比的方波主要包含基频和奇次谐波,这正是蜂鸣器发声的物理基础------蜂鸣器振膜对基频分量响应最强,谐波分量则决定了音色。

1.2 蜂鸣器与喇叭的驱动差异

无源蜂鸣器本质是一个电磁式扬声器单元,内部不含振荡源,必须依赖外部输入的交变信号才能发声。其优势在于音调完全可控,通过改变输入频率可精确生成Do、Re、Mi等标准音阶。

有源蜂鸣器则内置振荡电路,通电即发出固定频率声音(通常为2.7kHz或4kHz),无法控制音调,仅适合简单提示音。

对于音质要求更高的场景,可以使用**小喇叭(扬声器)**配合音频功放芯片(如LM386),能获得更宽的频响范围和更丰富的音色表现。


二、音阶频率的数学基础

2.1 十二平均律与频率计算

现代音乐采用十二平均律(Equal Temperament),将八度音程均分为12个半音,相邻半音的频率比为:

f n + 1 = f n × 2 1 / 12 ≈ f n × 1.05946 f_{n+1} = f_n \times 2^{1/12} \approx f_n \times 1.05946 fn+1=fn×21/12≈fn×1.05946

以国际标准音 A4 = 440Hz 为基准,各音名的频率可按公式计算:

f = 440 × 2 ( n − 69 ) / 12 f = 440 \times 2^{(n-69)/12} f=440×2(n−69)/12

其中 n n n 为MIDI音符编号(A4对应69)。

2.2 C大调音阶频率表

下表列出了嵌入式电子琴常用的C大调音阶频率(中音区):

音名 唱名 频率(Hz) 周期(μs) 半周期(μs)
C4 Do 262 3817 1908
D4 Re 294 3401 1700
E4 Mi 330 3030 1515
F4 Fa 349 2865 1433
G4 Sol 392 2551 1276
A4 La 440 2273 1136
B4 Si 494 2024 1012
C5 Do' 523 1912 956

计算示例 :以STM32系统时钟72MHz为例,TIM2经72分频后得到1MHz计数频率。要生成262Hz的Do音,需要设置自动重装载值:

A R R = 1 , 000 , 000 262 − 1 = 3816 ARR = \frac{1,000,000}{262} - 1 = 3816 ARR=2621,000,000−1=3816

占空比50%时,比较值为 C C R = 3816 / 2 = 1908 CCR = 3816 / 2 = 1908 CCR=3816/2=1908。


三、硬件电路设计

3.1 蜂鸣器驱动电路

蜂鸣器属于感性负载,驱动电路需要考虑以下关键点:

  1. 电流放大:MCU GPIO引脚驱动能力有限(通常<25mA),需通过三极管(如S8550 PNP)进行电流放大
  2. 续流保护:蜂鸣器线圈在电流突变时产生反向电动势,必须并联续流二极管(如1N4148)保护三极管
  3. 阻抗匹配:蜂鸣器常见阻抗为8Ω或16Ω,需确保驱动电路能提供足够电流

3.2 喇叭驱动与低通滤波

当使用喇叭作为输出时,建议增加以下电路:

推挽放大电路:使用NPN+PNP三极管组成互补推挽结构,提升驱动能力。

RC低通滤波器:PWM载波频率通常在10kHz以上,而音频信号最高约4kHz。通过一阶RC低通滤波器(如R=1kΩ, C=100nF,截止频率约1.6kHz)可有效滤除高频载波成分,还原平滑音频。

f c = 1 2 π R C f_c = \frac{1}{2\pi RC} fc=2πRC1


四、单音PWM合成实现

4.1 STM32 HAL库实现

以下是基于STM32F103的完整PWM音频驱动代码:

c 复制代码
/**
 * @file  pwm_audio.c
 * @brief 基于PWM的简易电子琴驱动
 * @note  使用TIM2_CH4输出PWM至PA3引脚
 */

#include "pwm_audio.h"
#include "stm32f1xx_hal.h"

TIM_HandleTypeDef htim2;

/*=================== 音阶频率表 ===================*/
// C大调中音区 (基于1MHz计数频率计算ARR值)
#define NOTE_C4   3816   // Do   262Hz
#define NOTE_D4   3401   // Re   294Hz
#define NOTE_E4   3030   // Mi   330Hz
#define NOTE_F4   2865   // Fa   349Hz
#define NOTE_G4   2551   // Sol  392Hz
#define NOTE_A4   2273   // La   440Hz
#define NOTE_B4   2024   // Si   494Hz
#define NOTE_C5   1912   // Do'  523Hz

// 休止符
#define NOTE_REST 0

/* 音阶数组 */
const uint16_t scale[] = {
    NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4,
    NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5
};

/*=================== 定时器初始化 ===================*/
void PWM_Audio_Init(void)
{
    __HAL_RCC_TIM2_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // TIM2配置: 72MHz / 72 = 1MHz计数频率
    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 71;        // 72分频
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = 3816;         // 默认262Hz
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_PWM_Init(&htim2);
    
    TIM_OC_InitTypeDef sConfigOC = {0};
    sConfigOC.OCMode = TIM_OCMODE_PWM1;
    sConfigOC.Pulse = 1908;           // 50%占空比
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
    sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
    HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_4);
    
    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);
}

/*=================== 播放指定音调 ===================*/
void PWM_PlayTone(uint16_t arr_value, uint16_t duration_ms)
{
    if (arr_value == NOTE_REST) {
        HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_4);
        HAL_Delay(duration_ms);
        return;
    }
    
    // 安全切换:先停止再更新寄存器
    HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_4);
    
    __HAL_TIM_SET_AUTORELOAD(&htim2, arr_value);
    __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, arr_value / 2);
    
    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4);
    HAL_Delay(duration_ms);
    
    HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_4);
    HAL_Delay(50);  // 音符间隔
}

/*=================== 播放音阶 ===================*/
void PWM_PlayScale(void)
{
    for (int i = 0; i < 8; i++) {
        PWM_PlayTone(scale[i], 500);
    }
}

/*=================== 播放《小星星》 ===================*/
typedef struct {
    uint16_t note;
    uint16_t duration;
} Note_t;

const Note_t little_star[] = {
    {NOTE_C4, 500}, {NOTE_C4, 500}, {NOTE_G4, 500}, {NOTE_G4, 500},
    {NOTE_A4, 500}, {NOTE_A4, 500}, {NOTE_G4, 1000},
    {NOTE_F4, 500}, {NOTE_F4, 500}, {NOTE_E4, 500}, {NOTE_E4, 500},
    {NOTE_D4, 500}, {NOTE_D4, 500}, {NOTE_C4, 1000},
    {NOTE_G4, 500}, {NOTE_G4, 500}, {NOTE_F4, 500}, {NOTE_F4, 500},
    {NOTE_E4, 500}, {NOTE_E4, 500}, {NOTE_D4, 1000},
    {NOTE_G4, 500}, {NOTE_G4, 500}, {NOTE_F4, 500}, {NOTE_F4, 500},
    {NOTE_E4, 500}, {NOTE_E4, 500}, {NOTE_D4, 1000},
    {NOTE_C4, 500}, {NOTE_C4, 500}, {NOTE_G4, 500}, {NOTE_G4, 500},
    {NOTE_A4, 500}, {NOTE_A4, 500}, {NOTE_G4, 1000},
    {NOTE_F4, 500}, {NOTE_F4, 500}, {NOTE_E4, 500}, {NOTE_E4, 500},
    {NOTE_D4, 500}, {NOTE_D4, 500}, {NOTE_C4, 1000},
};

void PWM_PlaySong(const Note_t *song, uint16_t length)
{
    for (uint16_t i = 0; i < length; i++) {
        PWM_PlayTone(song[i].note, song[i].duration);
    }
}

4.2 51单片机实现(定时器中断方式)

对于资源受限的51单片机,可以使用定时器中断方式生成方波:

c 复制代码
#include <reg52.h>

sbit BUZZER = P1^0;

// 音阶频率定义
#define NOTE_C4  262
#define NOTE_D4  294
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_G4  392
#define NOTE_A4  440
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_REST 0

// 12MHz晶振,机器周期1μs
#define TIMER_VAL(f)  ((f) ? (65536 - (500000L / (f))) : 0)

code unsigned int melody[] = {
    NOTE_C4, NOTE_C4, NOTE_G4, NOTE_G4, NOTE_A4, NOTE_A4, NOTE_G4,
    NOTE_F4, NOTE_F4, NOTE_E4, NOTE_E4, NOTE_D4, NOTE_D4, NOTE_C4
};

unsigned char note_index = 0;
bit music_playing = 0;

void Timer0_Init(void)
{
    TMOD |= 0x01;   // 定时器0模式1(16位定时)
    ET0 = 1;        // 使能定时器0中断
    EA = 1;         // 开总中断
}

void set_tone(unsigned int freq)
{
    if (freq == 0) {
        TR0 = 0;
        BUZZER = 0;
    } else {
        unsigned int val = TIMER_VAL(freq);
        TH0 = val >> 8;
        TL0 = val & 0xFF;
        TR0 = 1;
    }
}

void Timer0_ISR(void) interrupt 1
{
    BUZZER = ~BUZZER;  // 翻转IO产生方波
    
    // 重装初值
    unsigned int val = TIMER_VAL(melody[note_index]);
    TH0 = val >> 8;
    TL0 = val & 0xFF;
}

void play_music(void)
{
    music_playing = 1;
    note_index = 0;
    set_tone(melody[0]);
    
    while (music_playing) {
        // 主循环可处理其他任务
        // 音符切换由另一个定时器或延时控制
    }
}

五、多音和弦合成技术

5.1 和弦的物理本质

和弦是多个不同频率正弦波的叠加。例如C大三和弦由C(262Hz)、E(330Hz)、G(392Hz)三个音组成,其合成波形为:

V c h o r d ( t ) = A 1 sin ⁡ ( 2 π ⋅ 262 ⋅ t ) + A 2 sin ⁡ ( 2 π ⋅ 330 ⋅ t ) + A 3 sin ⁡ ( 2 π ⋅ 392 ⋅ t ) V_{chord}(t) = A_1\sin(2\pi \cdot 262 \cdot t) + A_2\sin(2\pi \cdot 330 \cdot t) + A_3\sin(2\pi \cdot 392 \cdot t) Vchord(t)=A1sin(2π⋅262⋅t)+A2sin(2π⋅330⋅t)+A3sin(2π⋅392⋅t)

在数字系统中,我们需要将多个音源混合后通过单一PWM通道输出。

5.2 多音PWM合成算法

由于大多数MCU只有一个PWM输出通道,实现多音合成需要采用时间片复用占空比调制技术。

方案一:快速PWM混音(软件合成)

c 复制代码
/**
 * @brief 多音PWM合成 - 基于查找表和相位累加器
 * @note  适用于高主频MCU(如STM32F4系列)
 */

#include <math.h>

#define SAMPLE_RATE     44100   // PWM载波频率
#define MAX_VOICES      4       // 最大同时发音数
#define SINE_TABLE_SIZE 256

// 正弦波查找表(8位精度)
uint8_t sine_table[SINE_TABLE_SIZE];

// 音源结构体
typedef struct {
    uint32_t phase_acc;      // 相位累加器
    uint32_t phase_inc;      // 相位增量
    uint8_t  amplitude;      // 音量 (0-255)
    uint8_t  active;           // 是否激活
} Voice_t;

Voice_t voices[MAX_VOICES];

/*=================== 初始化正弦表 ===================*/
void SineTable_Init(void)
{
    for (int i = 0; i < SINE_TABLE_SIZE; i++) {
        sine_table[i] = (uint8_t)(127 + 127 * sin(2 * M_PI * i / SINE_TABLE_SIZE));
    }
}

/*=================== 设置音源频率 ===================*/
void Voice_SetFrequency(uint8_t voice_id, uint16_t freq_hz, uint8_t amplitude)
{
    if (voice_id >= MAX_VOICES) return;
    
    // 相位增量 = (频率 * 查找表大小) / 采样率
    voices[voice_id].phase_inc = ((uint32_t)freq_hz * SINE_TABLE_SIZE) / SAMPLE_RATE;
    voices[voice_id].amplitude = amplitude;
    voices[voice_id].active = 1;
    voices[voice_id].phase_acc = 0;
}

/*=================== 混合采样值 ===================*/
uint8_t Audio_MixSamples(void)
{
    int16_t mixed = 0;
    uint8_t active_count = 0;
    
    for (int i = 0; i < MAX_VOICES; i++) {
        if (voices[i].active) {
            // 更新相位累加器
            voices[i].phase_acc += voices[i].phase_inc;
            voices[i].phase_acc &= 0xFF;  // 取低8位作为索引
            
            // 查表获取采样值并叠加
            uint8_t sample = sine_table[voices[i].phase_acc];
            mixed += ((int16_t)sample - 127) * voices[i].amplitude / 255;
            active_count++;
        }
    }
    
    // 防止溢出并归一化
    if (active_count > 0) {
        mixed = mixed / active_count + 127;
    } else {
        mixed = 127;
    }
    
    // 限幅
    if (mixed > 255) mixed = 255;
    if (mixed < 0) mixed = 0;
    
    return (uint8_t)mixed;
}

/*=================== 定时器中断 - 音频采样更新 ===================*/
void TIM_AUDIO_IRQHandler(void)
{
    uint8_t sample = Audio_MixSamples();
    
    // 将采样值转换为PWM占空比
    // 假设TIM3为PWM载波,频率SAMPLE_RATE
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, sample);
}

方案二:双定时器和弦模拟(资源受限方案)

对于只有单个定时器PWM的MCU,可以通过快速切换两个频率来模拟双音效果:

c 复制代码
/**
 * @brief 快速频率切换模拟双音效果
 * @note  利用人耳的"融合效应",快速交替播放两个频率
 */

#define CHORD_SWITCH_US  100   // 切换周期100μs

void PWM_PlayChord(uint16_t freq1, uint16_t freq2, uint16_t duration_ms)
{
    uint16_t arr1 = (1000000 / freq1) - 1;
    uint16_t arr2 = (1000000 / freq2) - 1;
    uint16_t ccr1 = arr1 / 2;
    uint16_t ccr2 = arr2 / 2;
    
    uint32_t start = HAL_GetTick();
    
    while (HAL_GetTick() - start < duration_ms) {
        // 播放频率1
        __HAL_TIM_SET_AUTORELOAD(&htim2, arr1);
        __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, ccr1);
        delay_us(CHORD_SWITCH_US);
        
        // 播放频率2
        __HAL_TIM_SET_AUTORELOAD(&htim2, arr2);
        __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, ccr2);
        delay_us(CHORD_SWITCH_US);
    }
    
    HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_4);
}

注意:此方法利用了人耳的"时间融合"效应,当切换频率高于约100Hz时,人耳会感知为两个音的混合。但音质较方案一差,适合资源极度受限的场景。


六、ADSR音量包络控制

6.1 包络的概念

真实乐器的声音不是简单的方波,而是具有**起音(Attack)、衰减(Decay)、延音(Sustain)、释音(Release)**四个阶段的动态音量变化。ADSR包络能显著提升电子音色的真实感。

6.2 软件实现

c 复制代码
/**
 * @brief ADSR包络生成器
 */

typedef enum {
    ADSR_IDLE,
    ADSR_ATTACK,
    ADSR_DECAY,
    ADSR_SUSTAIN,
    ADSR_RELEASE
} ADSR_State_t;

typedef struct {
    ADSR_State_t state;
    uint16_t attack_time;    // 起音时间(ms)
    uint16_t decay_time;     // 衰减时间(ms)
    uint8_t  sustain_level;  // 延音音量(0-255)
    uint16_t release_time;   // 释音时间(ms)
    uint32_t state_timer;    // 状态计时器
    uint8_t  current_level;  // 当前音量
} ADSR_Env_t;

ADSR_Env_t adsr_env;

void ADSR_Init(ADSR_Env_t *env, uint16_t a, uint16_t d, uint8_t s, uint16_t r)
{
    env->attack_time = a;
    env->decay_time = d;
    env->sustain_level = s;
    env->release_time = r;
    env->state = ADSR_IDLE;
    env->current_level = 0;
}

void ADSR_NoteOn(ADSR_Env_t *env)
{
    env->state = ADSR_ATTACK;
    env->state_timer = 0;
}

void ADSR_NoteOff(ADSR_Env_t *env)
{
    env->state = ADSR_RELEASE;
    env->state_timer = 0;
}

uint8_t ADSR_Update(ADSR_Env_t *env, uint16_t delta_ms)
{
    env->state_timer += delta_ms;
    
    switch (env->state) {
        case ADSR_ATTACK:
            if (env->state_timer >= env->attack_time) {
                env->current_level = 255;
                env->state = ADSR_DECAY;
                env->state_timer = 0;
            } else {
                env->current_level = 255 * env->state_timer / env->attack_time;
            }
            break;
            
        case ADSR_DECAY:
            if (env->state_timer >= env->decay_time) {
                env->current_level = env->sustain_level;
                env->state = ADSR_SUSTAIN;
            } else {
                env->current_level = 255 - (255 - env->sustain_level) 
                                     * env->state_timer / env->decay_time;
            }
            break;
            
        case ADSR_SUSTAIN:
            env->current_level = env->sustain_level;
            break;
            
        case ADSR_RELEASE:
            if (env->state_timer >= env->release_time) {
                env->current_level = 0;
                env->state = ADSR_IDLE;
            } else {
                env->current_level = env->sustain_level 
                                   * (env->release_time - env->state_timer) 
                                   / env->release_time;
            }
            break;
            
        default:
            env->current_level = 0;
    }
    
    return env->current_level;
}

七、完整电子琴系统实现

7.1 系统架构

完整的简易电子琴系统包含以下层次:

应用层 :按键扫描、乐曲解析、和弦识别

中间层 :音阶频率表、ADSR包络、多音混合

驱动层 :定时器配置、PWM输出、GPIO控制

硬件层:蜂鸣器/喇叭、驱动电路、按键矩阵

7.2 主程序框架

c 复制代码
/**
 * @file  main.c
 * @brief 简易电子琴主程序
 */

#include "pwm_audio.h"
#include "adsr.h"
#include "keypad.h"

// 按键映射表
const uint16_t key_freq_map[] = {
    NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4,   // 按键0-3
    NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5    // 按键4-7
};

ADSR_Env_t env;

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    
    PWM_Audio_Init();
    Keypad_Init();
    ADSR_Init(&env, 50, 100, 180, 200);  // A=50ms, D=100ms, S=180, R=200ms
    
    while (1) {
        uint8_t key = Keypad_Scan();
        
        if (key != KEY_NONE) {
            // 按键按下 - 起音
            ADSR_NoteOn(&env);
            PWM_PlayTone(key_freq_map[key], 0);  // 持续播放
            
            // 等待释放
            while (Keypad_Scan() == key) {
                uint8_t vol = ADSR_Update(&env, 10);
                PWM_SetVolume(vol);  // 通过占空比调节音量
                HAL_Delay(10);
            }
            
            // 按键释放 - 释音
            ADSR_NoteOff(&env);
            while (env.state != ADSR_IDLE) {
                uint8_t vol = ADSR_Update(&env, 10);
                PWM_SetVolume(vol);
                HAL_Delay(10);
            }
            
            HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_4);
        }
        
        HAL_Delay(10);
    }
}

八、性能优化与常见问题

8.1 PWM载波频率选择

载波频率 音质 CPU负载 适用场景
10kHz 一般 蜂鸣器、简单提示音
20kHz 良好 小喇叭、音乐播放
40kHz 优秀 高保真音频、专业应用

载波频率应至少为最高音频频率的5倍以上,以确保低通滤波器能有效分离载波和音频。

8.2 常见问题排查

现象 可能原因 解决方案
音调不准 晶振精度不足、定时器计算误差 使用高精度晶振,校准频率表
音量太小 驱动电流不足、占空比设置不当 检查三极管选型,调整占空比至50%
杂音/噪声 PWM载波泄漏、电源纹波 增加RC低通滤波,优化电源退耦
和弦失真 多音叠加溢出、切换频率过低 使用硬件混音或提高切换频率
高频音发不出 蜂鸣器谐振频率限制 选用宽频响喇叭替代蜂鸣器

九、进阶扩展

9.1 波形表合成(Wavetable Synthesis)

通过预存不同乐器的波形样本(如钢琴、小提琴、吉他),在运行时通过改变播放速度(改变频率)和幅度来实现更丰富的音色。这需要更大的存储空间,但音质显著提升。

9.2 与RTOS结合

将音频处理作为独立的RTOS任务,使用消息队列接收音符指令,可实现更复杂的交互逻辑,如:

  • 多轨道同时播放
  • 实时音效处理(混响、合唱)
  • MIDI文件解析与播放

9.3 低功耗优化

在电池供电场景下,可采用:

  • 动态调整PWM频率(空闲时降低载波频率)
  • 使用DMA传输减少CPU中断
  • 按键唤醒机制,无操作时进入低功耗模式

十、总结

本文从PWM音频合成的物理原理出发,系统性地介绍了简易电子琴的完整实现方案:

  1. 理论基础:十二平均律频率计算、PWM频谱特性
  2. 硬件设计:蜂鸣器/喇叭驱动电路、低通滤波器
  3. 单音实现:基于定时器的精确频率控制
  4. 多音合成:相位累加器算法与快速切换方案
  5. 音色增强:ADSR包络控制提升真实感
  6. 系统整合:分层架构与完整代码框架

PWM音频合成技术虽然简单,但蕴含了数字信号处理的核心理念。通过本文的实践,读者不仅能实现一个可用的电子琴,更能深入理解嵌入式音频系统的底层原理,为后续更复杂的音频项目(如MP3播放器、语音合成)奠定坚实基础。


转载自:https://blog.csdn.net/u014727709/article/details/162457682

欢迎 👍点赞✍评论⭐收藏,欢迎指正