2

我正在为 ATMega328P 编写一个程序,该程序将从多个 ADC 通道读取数据,将它们组合成一个信号并通过 PWM 输出该信号。

我已经使用单次转换模式成功地将我的 ADC 轮询回退到每通道 50Hz。我使用 Timer/Counter2 生成 PWM,使用 Timer/Counter1 进行计算,以便为 Timer/Counter2 设置比较值。这是定时器/计数器 1 的 ISR:

// Interrupt service routine called to generate PWM compare values
ISR(TIMER1_COMPA_vect)
{
    // Grab most recent ADC reading for ADC0
    uint32_t sensor_value_0 = adc_readings[0];

    // Get current value for base waveform from wavetable stored in sinewave_data
    uint32_t sample_value_0 = pgm_read_byte(&sinewave_data[sample_0]);

    // Multiply these two values together
    // In other words, use the ADC reading to modulate the amplitude of base wave
    uint32_t sine_0 = (sample_value_0 * sensor_value_0) >> 10;

    // Do the same thing for ADC2    
    uint32_t sensor_value_1 = adc_readings[1];
    uint32_t sample_value_1 = pgm_read_byte(&sinewave_data[sample_1]);
    uint32_t sine_1 = (sample_value_1 * sensor_value_1) >> 10;

    // Add channels together, divide by two, set compare register for PWM
    OCR2A = (sine_0 + sine_1) >> 1;

    // Move successive ADC base waves through wavetable at integral increments
    // i.e., ADC0 is carried by a 200Hz sine wave, ADC1 at 300Hz, etc.
    sample_0 += 2;
    sample_1 += 3;

    // Wrap back to front of wavetable, if necessary
    if (sample_0 >= sinewave_length) {
        sample_0 = 0;
    }

    if (sample_1 >= sinewave_length) {
        sample_1 = 0;
    }
} // END - Interrupt service routine called to generate PWM compare values

我的问题是我没有得到 PWM 输出。如果我将其中一个设置为或sensor_value_0并将另一个设置为从 ADC 读取,我会得到一个全幅分量波和一个调幅分量波。但是,如果我为硬编码的模拟幅度选择不同的值,我就不那么幸运了(例如)。任何其他值都不会给我 PWM 输出。如果我将两个s 设置为查看同一个 ADC 通道,我会期望两个分量波的幅度调制相同。相反,我没有得到 PWM 输出。对我来说最令人困惑的是,如果我为硬编码幅度选择一个正好是 2 的幂的值,那么一切都很好。sensor_value_11024sensor_value_1023 sensor_value_

整个二次幂的部分让我觉得这似乎是一个我没有看到的有点棘手的问题。你能看出我肯定错过了什么吗?我会很感激任何提示!

(我已经在这里发布了我的全部资源,以使 SO 上的内容尽可能整洁。)

4

2 回答 2

0

您的问题可能是由您正在开发的 AVR 的体系结构引起的。ATMega328p 具有 8 位寄存器,类似于大多数其他 AVR 芯片。这意味着您正在使用的 32b 值必须由编译器存储在内存中,并在每次对它们执行算术运算时分成四个单独的寄存器。事实上,没有任何算术指令可以同时在多个寄存器上执行,所以我真的不确定编译器在做什么!

我很想知道你的代码的反汇编是什么,但我的猜测是 gcc 正在使用 MUL 指令来执行sample_value_0 * sensor_value_0代码。该指令对两个 8b 值进行操作并产生一个 16b 值,因此如果您看到奇怪依赖于两个的倍数产生结果的原因,我不会感到惊讶。

我会说尝试通过更改变量的数据类型来修改这段代码。使用和和。uint8_t_ _ 然后,为确保所有内容都适合 8b寄存器,请将分配更改为:sensor_value_*sample_value_*uint16_tsine_*OCR2A

OCR2A = (sine_0 + sine_1) & 0xFF;
于 2011-10-26T02:20:18.550 回答
0

@Devrin,我很欣赏你的回应,但只是操纵类型并没有为我做这件事。这就是我最终做的事情:

uint8_t sine_0 = (pgm_read_byte(&sinewave_data[sample_0]) >> 5) * (adc_readings[1] >> 5);
uint8_t sine_1 = (pgm_read_byte(&sinewave_data[sample_1]) >> 5) * (adc_readings[2] >> 5);
OCR2A = (sine_0 >> 1) + (sine_1 >> 1);

从本质上讲,我已经立即完成了所有的换档工作,而不是等到最后一分钟。不幸的是,我失去了很多精度,但至少代码按预期工作。现在,我将开始重新启动以找出问题的最初原因。

于 2011-11-15T10:56:27.060 回答