【问题标题】:Problems in AVR C combining ADC readings to generate PWM outputAVR C 中的问题结合 ADC 读数以生成 PWM 输出
【发布时间】:2011-10-22 20:47:28
【问题描述】:

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

我已使用单次转换模式成功地将我的 ADC 轮询回退到每通道 50Hz。我使用 Timer/Counter2 生成 PWM,使用 Timer/Counter1 进行计算,以便为 Timer/Counter2 设置比较值。这是 Timer/Counter1 的 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 输出。如果我将 both sensor_value_s 设置为查看同一个 ADC 通道,我会期望两个分量波的幅度调制相同。相反,我没有得到 PWM 输出。最让我困惑的是,如果我为硬编码幅度选择一个正好是 2 的幂的值,那么一切都很好。

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

(我已经发布了我的整个源代码 here 以使 SO 上的内容尽可能整洁。)

【问题讨论】:

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


    【解决方案1】:

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

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

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

    OCR2A = (sine_0 + sine_1) & 0xFF;
    

    【讨论】:

    • 我认为这不太可能。如果没有对数据类型进行操作的指令,编译器将生成正确的指令序列来执行算术运算或调用库函数。 8 位 CPU 的 C 编译器通常可以处理 16 位或 32 位长的数据类型。
    【解决方案2】:

    @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);
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-01-18
      • 2015-06-10
      • 1970-01-01
      • 2022-08-18
      • 2015-10-29
      • 1970-01-01
      • 2018-09-01
      • 1970-01-01
      相关资源
      最近更新 更多