ATmega328でクロックプリスケーラー64で実行すると、不明な理由により、実行中の特定の時間に私のタイマーの1つがスピードアップします。
ATmega328で2つのタイマーを使用して、TLC5940が必要とするクロッキングを生成しています(理由については以下を参照してください。これは質問には重要ではありません)。TIMER0
Fast PWM onを使用してクロック信号を生成し、OC0B
次のように設定されます。
TCCR0A = 0
|(0<<COM0A1) // Bits 7:6 – COM0A1:0: Compare Match Output A Mode
|(0<<COM0A0) //
|(1<<COM0B1) // Bits 5:4 – COM0B1:0: Compare Match Output B Mode
|(0<<COM0B0)
|(1<<WGM01) // Bits 1:0 – WGM01:0: Waveform Generation Mode
|(1<<WGM00)
;
TCCR0B = 0
|(0<<FOC0A) // Force Output Compare A
|(0<<FOC0B) // Force Output Compare B
|(1<<WGM02) // Bit 3 – WGM02: Waveform Generation Mode
|(0<<CS02) // Bits 2:0 – CS02:0: Clock Select
|(1<<CS01)
|(0<<CS00) // 010 = clock/8
;
OCR0A = 8;
OCR0B = 4;
TIMSK0 = 0;
TIMER2
データラインをいじって、256 TIMER0
サイクルごとにブランキングパルスを生成し、次のように設定されます。
ASSR = 0;
TCCR2A = 0
|(0<<COM2A1) // Bits 7:6 – COM0A1:0: Compare Match Output A Mode
|(0<<COM2A0) //
|(0<<COM2B1) // Bits 5:4 – COM0B1:0: Compare Match Output B Mode
|(0<<COM2B0)
|(0<<WGM21) // Bits 1:0 – WGM01:0: Waveform Generation Mode
|(0<<WGM20)
;
TCCR2B = 0
|(0<<FOC2A) // Force Output Compare A
|(0<<FOC2B) // Force Output Compare B
|(0<<WGM22) // Bit 3 – WGM02: Waveform Generation Mode
|(1<<CS22) // Bits 2:0 – CS02:0: Clock Select
|(0<<CS21)
|(0<<CS20) // 100 = 64
;
OCR2A = 255;
OCR2B = 255;
TIMSK2 = 0
|(1<<TOIE2); // Timer/Counter0 Overflow Interrupt Enable
TIMER2
オーバーフロー時にISRを呼び出します(256サイクルごと)。ISRは手動でブランキングパルスを生成し、必要に応じてラッチパルスを生成します。
volatile uint8_t fLatch;
ISR(TIMER2_OVF_vect) {
if (fLatch) {
fLatch = 0;
TLC5940_XLAT_PORT |= (1<<TLC5940_XLAT_BIT); // XLAT -> high
for (int i=0;i<10;i++)
nop();
TLC5940_XLAT_PORT &= ~(1<<TLC5940_XLAT_BIT); // XLAT -> high
}
// Blank
TLC5940_BLANK_PORT |= (1<<TLC5940_BLANK_BIT);
for (int i=0;i<10;i++)
nop();
TLC5940_BLANK_PORT &= ~(1<<TLC5940_BLANK_BIT);
}
nop()
上記のコードの遅延は、ロジックアナライザのトレースでパルスをより明確にするためのものです。main()
関数のループは次のようになります。シリアルデータを送信し、ISRがラッチを処理するのを待ってから、もう一度やり直します。
for (;;) {
if (!fLatch) {
sendSerial();
fLatch = 1;
_delay_ms(1);
}
nop();
}
sendSerial()
いくつかのSPI送信を行います(簡潔にするために、ペーストビンのコード)。私の問題は、sendSerial()
完了後、fLatch
低(処理済み)に設定されるのを待っている間、クロッキングタイマーがスピードアップすることです。ロジックアナライザーのトレースは次のとおりです(同じ信号がグラフィックを小さくし続けている領域を切り取っています)。
左側のチャネル0と1は、送信されるSPIデータの末尾を示しています。また、左側のチャネル4には、ブランキングパルスが表示されます。チャネル2では、クロッキングパルスが期待どおりに変化します。画像内のギャップのすぐ近くが、ルーチン内にfLatch
設定さ1
れてmain()
います。そして、その後すぐにTIMER0
約4倍にスピードアップします。最終的に、ブランキングパルスとラッチパルスが実行され(チャネル3および4、画像の右3分の1)、クロックパルスが通常の周波数に戻り、シリアルデータが再び送った。でdelay_ms(1);
線を抜いてみましたmain()
が、同じ結果が得られます。どうしたの?ATmegaは20Mhzクリスタルからクロックされ、次のコードを使用して64倍スローダウンすることに注意してください:
CLKPR = 1<<CLKPCE;
CLKPR = (0<<CLKPS3)|(1<<CLKPS2)|(1<<CLKPS1)|(0<<CLKPS0);
用途:TLC5940 LEDドライバーの制御を実験しています。これらのチップには、外部クロックに加えて、クロッキングサイクルの最後にリセットが必要です。
sendSerial()
SPIを介してデータを送信する私のコードですTCCR
。(タイマー制御)レジスターには触れません。