我正在为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_0
或sensor_value_1
设置为1024
并将另一个sensor_value_
设置为从ADC读取,我会得到一个全幅度分量波和一个幅度 - 调制分量波。但是,如果我为硬编码的模拟振幅选择不同的值,我就不那么幸运了(例如1023
)。任何其他值都不会给我输出PWM。如果我设置两个 sensor_value_
来查看相同的ADC通道,我会期望两个分量波的幅度被相同地调制。相反,我没有PWM输出。对我来说最让人困惑的是,如果我为硬编码幅度选择一个精确的2次幂,那么一切都很好。
整个两部分的力量使我觉得这是一个我看不到的有点麻烦的问题。你能看出我必须明白错过的东西吗?我很感激任何提示!
(我已经发布了我的整个来源here,以便在SO上尽可能保持整洁。)
答案 0 :(得分:0)
您的问题可能是由您正在开发的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;
答案 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);
基本上,我已经完成了所有的转换,而不是等到最后一分钟。不幸的是,我失去了很多精确度,但至少代码按预期工作。现在,我将开始重新启动以找到问题的最初原因。