【发布时间】: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