Quando si esegue con un prescaler di clock di 64 su ATmega328, uno dei miei timer accelera per motivi sconosciuti in un determinato momento dell'esecuzione.
Sto usando due timer su ATmega328 per generare il clock richiesto da TLC5940 (vedi sotto sul perché; questo è irrilevante per la domanda). TIMER0
genera un segnale di clock utilizzando Fast PWM on OC0B
ed è impostato come segue:
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
regola una linea di dati per generare un impulso di blanking ogni 256 TIMER0
cicli ed è impostato come segue:
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
chiama un ISR in caso di overflow (ogni 256 cicli). L'ISR genera manualmente un impulso di soppressione e un impulso di blocco, se necessario:
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);
}
Il nop()
ritardo nel codice sopra è solo per rendere l'impulso più evidente sulla traccia dell'analizzatore logico. Ecco main()
come appare il loop nella funzione: invia alcuni dati seriali, attendi che ISR si occupi del latch e quindi riprova:
for (;;) {
if (!fLatch) {
sendSerial();
fLatch = 1;
_delay_ms(1);
}
nop();
}
sendSerial()
esegue alcuni invii SPI ( codice su pastebin per brevità ). Il mio problema è che dopo il sendSerial()
completamento, in attesa fLatch
di essere impostato su basso (elaborato) il timer di clock accelera. Ecco la traccia dell'analizzatore logico (ho eliminato le aree in cui lo stesso segnale continua a ridurre la grafica):
Sul lato sinistro, i canali 0 e 1 mostrano la coda dei dati SPI inviati. Sempre a sinistra, sul canale 4, puoi vedere un impulso di cancellazione. Sul canale 2 l'impulso di clock si blocca come previsto. Proprio attorno a dove si trova il divario nell'immagine, fLatch
è impostato 1
all'interno della main()
routine. E subito dopo si TIMER0
accelera di circa un fattore 4. Alla fine, vengono eseguiti l'impulso di soppressione e l'impulso di blocco (canali 3 e 4, terzo di destra dell'immagine), e ora l'impulso di clock riprende la sua frequenza regolare e i dati seriali sono Inviato di nuovo. Ho provato a togliere la delay_ms(1);
linea main()
, ma si ottengono gli stessi risultati. Cosa sta succedendo? Dovrei notare che l'ATmega è spento da un cristallo da 20Mhz e quindi rallentato di 64x usando il seguente codice:
CLKPR = 1<<CLKPCE;
CLKPR = (0<<CLKPS3)|(1<<CLKPS2)|(1<<CLKPS1)|(0<<CLKPS0);
A cosa serve: sto sperimentando il controllo del driver LED TLC5940 : questi chip richiedono un clock esterno più un reset alla fine del ciclo di clock.
sendSerial()
è il mio codice che invia dati tramite SPI: non tocca i TCCR
registri (controllo timer).