【问题标题】:Sound synthesis with PWM output带 PWM 输出的声音合成
【发布时间】:2023-01-18 03:44:24
【问题描述】:

我正在尝试在 Arduboy 上合成声音,Arduboy 是一款手持游戏设备,带有 AVR ATMega32u4 微控制器和连接在其引脚 C6 和 C7 之间的扬声器。

我的方案是用定时器4在C7上产生高频PWM信号,然后用定时器3改变定时器4的占空比。对于“hello world”级别的程序,我尝试每秒从 PROGMEM 读取 3906 个 8 位样本。

首先,为了确保我的示例文件确实是我认为的格式,我已经使用SoX在电脑上播放它:

$ play -e unsigned-integer -r 3906 -b 8 sample2.raw 

这是我的代码的相关部分:

pub fn setup() {
    without_interrupts(|| {
        PLLFRQ::unset(PLLFRQ::PLLTM1);
        PLLFRQ::set(PLLFRQ::PLLTM0);
        TCCR4A::write(TCCR4A::COM4A1 | TCCR4A::PWM4A); // Set output C7 to high between 0x00 and OCR4A
        TCCR4B::write(TCCR4B::CS40); // Enable with clock divider of 1
        TCCR4C::write(0x00);
        TCCR4D::write(0x00);
        TC4H::write(0x00);
        OCR4C::write(0xff); // One full period = 256 cycles
        OCR4A::write(0x00); // Duty cycle = OCR4A / 256

        TCCR3B::write(TCCR3B::CS32 | TCCR3B::CS30); // Divide by 1024
        OCR3A::write(3u16); // 4 cycles per period => 3906 samples per second
        TCCR3A::write(0);
        TCCR3B::set(TCCR3B::WGM30); // count up to OCR3A
        TIMSK3::set(TIMSK3::OCIE3A); // Interrupt on OCR3A match

        // Speaker
        port::C6::set_output();
        port::C6::set_low();
        port::C7::set_output();
    });
}

progmem_file_bytes!{
    static progmem SAMPLE = "sample2.raw"
}

// TIMER3_COMPA
#[no_mangle]
pub unsafe extern "avr-interrupt" fn __vector_32() {
    static mut PTR: usize = 0;

    // This runs at 3906 Hz, so at each tick we just replace the duty cycle of the PWM
    let sample: u8 = SAMPLE.load_at(PTR);
    OCR4A::write(sample);
    PTR += 1;
    if PTR == SAMPLE.len() {
        PTR = 0;
    }
}

基本问题是它根本不起作用:我没有听到音频样本,而是听到扬声器发出的杂音。

请注意,这不是“完全错误”,有一些预期操作的外观。例如,我可以听到噪声具有长度合适的重复结构。如果我在PTR < SAMPLE.len() / 2时将占空比sample设置为0,那么我可以清楚地听到我的样本长度的一半没有声音。所以我认为定时器 3 和它的中断处理程序确实按预期工作。

所以这让我想要么我错误地配置了计时器 4,要么我误解了 OCR4A 的作用以及需要如何设置占空比,或者我可能只是对基于 PWM 的音频合成的方式有根本错误的理解应该工作。

【问题讨论】:

  • “高频”定时器 4 设置的频率是多少? (如果它太低(太接近可听范围),那就可以解释问题了。)

标签: rust timer avr pwm sound-synthesis


【解决方案1】:

无论您上面编写的代码如何,您的设计似乎都存在几个缺陷:

a) 扬声器需要交流电通过它;它是通过改变正电压和负电压之间的张力来实现的。在您的设计中,PWM 的输出可能在 0 到 5V 之间。如果您的 PWM 占空比为 50%,它会变成一个 2.5V 的平均偏移电压,持续施加到您的扬声器上。这对它来说可能是致命的(拯救恩典可能是下面的缺陷 3,即电流不足)。解决此问题的常用方法是通过电容器连接扬声器来切断直流电流,以滤除任何直流电流。必须调整电容器的值,使其不会滤除您希望在低频下使用的信号。

b) 出于以下几个原因,您不应该将扬声器直接连接到您的微控制器 PIN:

  1. 您的微控制器可能是电路板上昂贵的设备,应该避免可能导致其损坏的设备故障(如快捷方式)。

  2. 您的微控制器可以驱动每个 io PIN 最大 40mA,as described in datasheet, 29.1 Absolute Maximum Ratings 。一个典型的扬声器的电阻在 8 欧姆和 32 欧姆之间变化,如果我们考虑 50% 的占空比,您平均通过它的电流为 20mA。为了有效地驱动您的 HP,您需要更多的电流流过。这就是扬声器通常由放大器级驱动的原因。

  3. 扬声器是内部带有 coils 的设备。因此,它们存储磁能,当电压下降时它们会返回到电路,注入反向电流。如果处理不当,这些反向电流很容易损坏晶体管栅极。在你的情况下,幸运的是,反向电流量很低,因为你没有足够地驱动 HP。但是您应该始终避免这种情况。

    c) 使用 PWM 来产生声音似乎很尴尬。由于它们会产生方波,所以这些谐波会在声音中增加很多寄生噪声。但更重要的是,PWM 输出不允许您改变输出的强度(电压),而这通常是正确再现声音所必需的。

    要产生声音,您需要一个数模转换器 (DAC),然后是适当的电路来调节该信号并将其放大以供扬声器使用。不幸的是,ATMega32u4 似乎没有这个功能。你应该选择另一个微控制器。

    关于电子设计的问题,建议大家搜索https://electronics.stackexchange.com

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-23
    • 1970-01-01
    • 2021-04-11
    • 2010-10-21
    • 2013-04-23
    • 1970-01-01
    • 2012-04-03
    • 1970-01-01
    相关资源
    最近更新 更多