AVR C中的问题结合ADC读数以生成PWM输出

时间:2011-10-22 20:47:29

标签: c bit-manipulation arduino avr avr-gcc

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

我使用单一转换模式成功地将每次通道的ADC轮询退回到50Hz。我正在使用T / C2来产生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_0sensor_value_1设置为1024并将另一个sensor_value_设置为从ADC读取,我会得到一个全幅度分量波和一个幅度 - 调制分量波。但是,如果我为硬编码的模拟振幅选择不同的值,我就不那么幸运了(例如1023)。任何其他值都不会给我输出PWM。如果我设置两个 sensor_value_来查看相同的ADC通道,我会期望两个分量波的幅度被相同地调制。相反,我没有PWM输出。对我来说最让人困惑的是,如果我为硬编码幅度选择一个精确的2次幂,那么一切都很好。

整个两部分的力量使我觉得这是一个我看不到的有点麻烦的问题。你能看出我必须明白错过的东西吗?我很感激任何提示!

(我已经发布了我的整个来源here,以便在SO上尽可能保持整洁。)

2 个答案:

答案 0 :(得分:0)

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

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

我会说尝试通过更改变量的数据类型来重新编写这段代码。对uint8_tsensor_value_*使用sample_value_*uint16_t使用sine_*。然后,为了确保所有内容都适合8b OCR2A寄存器,请将赋值更改为:

OCR2A = (sine_0 + sine_1) & 0xFF;

答案 1 :(得分: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);

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