Accelerazione del timer AVR su ATmega328


9

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). TIMER0genera un segnale di clock utilizzando Fast PWM on OC0Bed è 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;

TIMER2regola una linea di dati per generare un impulso di blanking ogni 256 TIMER0cicli 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

TIMER2chiama 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 fLatchdi 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):

inserisci qui la descrizione dell'immagine

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 1all'interno della main()routine. E subito dopo si TIMER0accelera 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.


Se si dispone di un debugger, provare a interrompere il codice quando il timer è troppo veloce e rileggere il registro di configurazione di quel timer. Una volta trovato quello con il valore errato, attiva un breakpoint su questa modifica del registro e vedi quale parte del tuo codice sta funzionando male. Immagino che il problema si trovi in ​​una libreria esterna che puoi usare e che usa quel timer per cose interne come ritardi.
Blup1980,

Due problemi: a) Non ho un programmatore JTAG, quindi non ho modo di eseguire il debug del chip b) Non cambio mai il valore del registro timer dopo l'impostazione mostrata sopra, quindi non mi aspetto che i valori del registro timer siano in realtà cambia. È ingenuo?
angelatlarge

1
In realtà una libreria che usi potrebbe cambiare le impostazioni UART. Vedo che usi una funzione sendSerial (). Fa parte del tuo codice o è una libreria esterna? Potresti non essere tu a modificare le impostazioni, ma un pezzo di codice all'interno di una libreria chiamata. Ti suggerisco di usare la tua porta seriale per emettere i parametri di configurazione e provare a capire cosa è cambiato. Puoi anche guardare l'origine delle librerie usate (se presenti) e assicurarti che non usino anche quel timer.
Blup1980,

1
A parte ciò che @ Blup1980 ha suggerito un'altra cosa che potrebbe valere la pena provare è rimuovere il TLC5940 per assicurarsi che non stia facendo nulla di strano con la linea dell'orologio.
PeterJ,

@ Blup1980 Non sono sicuro di vedere la rilevanza di UART: non sto usando USART per SPI, ma solo le strutture SPI "normali". sendSerial()è il mio codice che invia dati tramite SPI: non tocca i TCCRregistri (controllo timer).
angelatlarge

Risposte:


1

Per un debug veloce, proverei a fare la stessa cosa usando Arduino Library per TLC5940 e vedo se sta diventando veloce o no. Se funziona con la libreria, puoi verificarne l'origine e confrontarla con la tua. Poiché hai familiarità con AVR, dovresti facilmente convertire la sorgente Arduino in AVR nativo.

Nel caso in cui non si sappia come caricare gli schizzi Arduino compilati su AVR: quando si compila lo schizzo, viene creato un file esadecimale (è possibile visualizzare la posizione esatta del file attivando la modalità dettagliata nelle impostazioni). Puoi caricare quell'esagono sul tuo AVR con il tuo programmatore preferito.

Spero che sia d'aiuto

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.