2

複数のADCチャネルから読み取り値を取得し、それらを単一の信号に結合し、この信号をPWMを介して出力するATMega328P用のプログラムを作成しています。

シングル変換モードを使用して、ADC ポーリングをチャンネルあたり 50Hz に戻すことに成功しました。PWM 生成には Timer/Counter2 を使用し、Timer/Counter2 の比較値を設定するために必要な計算には Timer/Counter1 を使用しています。これは、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 から読み取るように設定した場合、1 つの完全振幅成分波と振幅変調された成分波が得られます。ただし、ハードコードされたモック振幅に別の値を選択した場合、それほど幸運ではありません (1023たとえば、 )。それ以外の値では、PWM 出力が得られません。両方 の を同じ ADC チャネルを見るように設定sensor_value_すると、2 つの成分波の振幅が同じように変調されることが予想されます。代わりに、PWM 出力が得られません。私にとって最も紛らわしいのは、厳密に 2 の累乗であるハードコードされた振幅の値を選択した場合、すべてがうまくいくということです。

2 の累乗の部分全体が、これを私が見ていない少し厄介な問題のように思わせます。私が明らかに見逃していたに違いないものが見えますか?ヒントをいただければ幸いです。

( SO で可能な限り整理するために、ソース全体をここに投稿しました。)

4

2 に答える 2

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 に答える
0

問題は、開発中の AVR のアーキテクチャが原因である可能性があります。ATMega328p には、他のほとんどの AVR チップと同様に、8 ビットのレジスタがあります。これは、使用している 32b 値がコンパイラによってメモリに格納され、演算を実行するたびに 4 つの個別のレジスタに分割される必要があることを意味します。実際、一度に複数のレジスターに対して実行する算術命令は存在しないため、コンパイラーが何をしているのかはよくわかりません!

コードの逆アセンブリが何であるかを知りたいのですが、私の推測では、gcc は MUL 命令を使用してsample_value_0 * sensor_value_0コードを実行しています。この命令は 2 つの 8b 値で動作し、16b 値を生成するため、2 の倍数への奇妙な依存関係が原因で結果が生成されても驚かないでしょう。

変数のデータ型を変更して、このコード ブロックを作り直してみてください。および、およびに使用uint8_tします。次に、すべてが 8bレジスタに収まるように、割り当てを次のように変更します。sensor_value_*sample_value_*uint16_tsine_*OCR2A

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