Lors de l'exécution à un horodateur de 64 sur ATmega328, l'un de mes temporisateurs accélère pour des raisons inconnues à un moment particulier de l'exécution.
J'utilise deux minuteries sur ATmega328 pour générer la synchronisation nécessaire au TLC5940 (voir ci-dessous pourquoi; cela n'a pas d'importance pour la question). TIMER0
génère un signal d'horloge en utilisant Fast PWM on OC0B
et est configuré comme suit:
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
tourne une ligne de données pour générer une impulsion de suppression tous les 256 TIMER0
cycles et est configurée comme suit:
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
appelle un ISR en cas de débordement (tous les 256 cycles). L'ISR génère manuellement une impulsion de suppression et une impulsion de verrouillage si nécessaire:
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);
}
Le nop()
retard dans le code ci-dessus est juste pour rendre l'impulsion plus apparente sur la trace de l'analyseur logique. Voici à quoi ressemble la boucle de la main()
fonction: envoyez des données série, attendez qu'ISR s'occupe du verrouillage, puis recommencez:
for (;;) {
if (!fLatch) {
sendSerial();
fLatch = 1;
_delay_ms(1);
}
nop();
}
sendSerial()
fait des envois SPI ( code sur pastebin par souci de concision ). Mon problème est qu'après la sendSerial()
fin, en attendant fLatch
qu'il soit réglé sur faible (traité), le chronomètre accélère. Voici la trace de l'analyseur logique (j'ai découpé les zones où le même signal continue de rendre le graphique plus petit):
Sur le côté gauche, les canaux 0 et 1 montrent l'extrémité arrière des données SPI envoyées. Également à gauche, sur le canal 4, vous pouvez voir une impulsion de suppression. Sur le canal 2, l'impulsion d'horloge avance comme prévu. Juste là où se trouve l'écart dans l'image, il fLatch
est réglé à l' 1
intérieur de la main()
routine. Et peu de temps après, TIMER0
accélère d'environ un facteur 4. Finalement, l'impulsion de suppression et l'impulsion de verrouillage sont effectuées (canaux 3 et 4, tiers droit de l'image), et maintenant l'impulsion de synchronisation reprend sa fréquence régulière, et les données série sont envoyé encore. J'ai essayé de retirer la delay_ms(1);
ligne main()
, mais les mêmes résultats sont obtenus. Que se passe-t-il? Je dois noter que l'ATmega est cadencé sur un cristal de 20 MHz, puis ralenti de 64 fois à l'aide du code suivant:
CLKPR = 1<<CLKPCE;
CLKPR = (0<<CLKPS3)|(1<<CLKPS2)|(1<<CLKPS1)|(0<<CLKPS0);
À quoi cela sert-il: J'expérimente le contrôle du pilote LED TLC5940 : ces puces nécessitent une horloge externe plus une réinitialisation à la fin du cycle de synchronisation.
sendSerial()
est mon code qui envoie des données via SPI: il ne touche pas aux TCCR
registres (contrôle de la minuterie).