@ekjk: ma non si era stabilito che dovevi usare CTC/timer2?
Quello che ti consiglio è fare le prove col codice che ti ho dato cambiando il timer1 col 2 e usando il CTC al posto di OVF. Almeno parti da un punto fermo!
TCCR1A e TCCR1B cosa sono (non ho il manuale sottomano)?
perchè prima fai
TCCR1A &= ~((1<<WGM11) | (1<<WGM10));
TCCR1B &= ~((1<<WGM12) | (1<<WGM13));
e poi
Ho trovato questo codice in rete che usa il timer1 in CTC e funziona..L'ho adattato in questo modo
// avr-libc library includes
#include <avr/io.h>
#include <avr/interrupt.h>
#define OUT 4
volatile int dt=200;
volatile int val=0;
void setup()
{
pinMode(OUT, OUTPUT);
// initialize Timer1
cli(); // disable global interrupts
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
// set compare match register to desired timer count:
// turn on CTC mode:
TCCR1B |= (1 << WGM12);
TCCR1B &= ~(1 << WGM10);
TCCR1B &= ~(1 << WGM11);
// Set CS10 and CS12 bits for 8 prescaler:
TCCR1B |= (1 << CS11);
TCCR1B &= ~(1 << CS12);
TCCR1B &= ~(1 << CS10);
// enable timer compare interrupt:
TIMSK1 |= (1 << OCIE1A);
// enable global interrupts:
sei();
attachInterrupt(1,ingresso,CHANGE);
}
void loop()
{
}
void ingresso()
{
OCR1A = dt-16;
TCCR1B |= (1 << WGM12);
TCCR1B &= ~(1 << WGM10);
TCCR1B &= ~(1 << WGM11);
TCCR1B |= (1 << CS11);
TCCR1B &= ~(1 << CS12);
TCCR1B &= ~(1 << CS10);
val=!val;
}
ISR(TIMER1_COMPA_vect)
{
if(val) {digitalWrite(OUT, 1);}
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0;
delayMicroseconds(300);
digitalWrite(OUT, 0);
}
Praticamente ad ogni cambiamento di stato del pin 3 ho un interrupt che fa partire il timer1 e quando finisce di contare genera il segnale di uscita di 300us di durata.
Perfetto, e l'ho implementato nel mio sketch e fino a ieri funzionava, ovvero in dt settavo il valore già pronto in modo da ottenere il mio delay e il segnale di uscita era stabile e non ballava avanti e indietro sull'oscilloscopio.
Oggi vado a provare e inizia a ballare e non si ferma, prima era un chiodo. Non so proprio il perchè.
Ho provato a copiare di nuovo il codice pari pari ma niente. Questo sketch funziona, ma quando lo implemento nel mio funziona in modo instabile.
Ottimo!
Il principio di funzionamento è, comunque, praticamente lo stesso.
Quel che mi pare strano in questo codice, però, è l'uso di una istruzione bloccante come delayMicroseconds() dentro una routine interrupt...
ISR(TIMER1_COMPA_vect)
{
if(val) {digitalWrite(OUT, 1);}
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0;
assolutamente no! stano che non vada in loop infinito, senza contare che è una gran bella stupidata, visto che durante quei 300microsec perderà tutti gli interrupt
assolutamente no! stano che non vada in loop infinito, senza contare che è una gran bella stupidata, visto che durante quei 300microsec perderà tutti gli interrupt
Esatto!!
Questa parte del codice, infatti, mi è sembrata subito "anomala", ma ho pensato che magari ci fosse una qualche (strana?) spiegazione, dato che ekjk dice che lo sketch gli funziona bene...
che funzioni può essere, magari hanno cambiato nell'ide 1.0 le funzioni di delay per essere ok anche negli interrupt, ma che funzioni BENE ne dubito, per il motivo che perde interrupt
E' illogico, è vero. E non dovrebbe essere questione di IDE, le ISR sono impostate a livello di avr-gcc ed il compilatore rende ogni ISR atomica di default, a meno che non vengano definite altrimenti. Atomiche significa che non sono interrompibili da altre ISR per cui non capisco come delayMicroseconds, che usa il timer 0, riesca a funzionare.
leo72:
E' illogico, è vero. E non dovrebbe essere questione di IDE, le ISR sono impostate a livello di avr-gcc ed il compilatore rende ogni ISR atomica di default, a meno che non vengano definite altrimenti. Atomiche significa che non sono interrompibili da altre ISR per cui non capisco come delayMicroseconds, che usa il timer 0, riesca a funzionare.
Mistero risolto.
La funzione delayMicroseconds non usa il timer 0 ma si basa sul tempo effettivo di esecuzione di alcune istruzioni, farcita con qualche istruzione assembly.
leo72:
Mistero risolto.
La funzione delayMicroseconds non usa il timer 0 ma si basa sul tempo effettivo di esecuzione di alcune istruzioni, farcita con qualche istruzione assembly.
quello che mi immaginavo. Ma si tratta di una modifica recente o per la microsec è sempre stato così?
comunque ripeto ad ekjk che anche se funziona, logicamente è errato usare una delay all'interno di una ISR
Si, ho visto il codice di delayMicroseconds()...sfrutta in particolare l'struzione "brne" che è un salto condizionato entro i 128 byte, se il test NON è zero, in questo modo si realizza una "attesa impegnata" sfruttano le tempistiche "naturali" del processore. (Leo l'assembly 6502/10 usava BNE, ricordi? )
Cmq, nella applicazione di ekjk, rimane il fatto che non è l'impiego più ortodosso, inoltre, come dice Lesto, potrebbe perdersi degli interrupt se la frequenza in ingresso supera i 3KHz.
@ lesto ed altri: so benissimo che è un errore scrivere così ma funziona bene e tenete conto che il mio segnale d'ingresso supera molto difficilmente i 240Hz, quindi non dovrei perdere interrupt.
Una soluzione più elegante per evitare la delay ma ottenere un impulso di durata voluta? sempre con il timer1 in CTC?
l'ideale è usando il timer.
se non fai nient'altro, puoi settare una variabile ai micros()+delay in cui invertire il segnale.
poi nel loop se il micros() è >= della variabile cambi lo stato al pin. in questo modo la ISR non è bloccante!
più che una variabile tempo servirebbe un array, altrimenti se perdi un numero di spari di interrupt ti disallinei (però se ne perdi pari ti riallinei)
Ecco il codice modificato non bloccante, problema: l'impulso di uscita fa dei battimenti sulla sua durata, a volte è come se facesse write 1 e write 0, quindi durata piccolissima, altre volte invece ha durata di 300us impostata dal codice.
Se scrivi mentre anche l'interrupt altera l'0output, è normale che si verifichino eventi del genere.
Dovresti forse approcciare il problema in un'altra maniera, ossia se la questione è quella di far variare la frequenza degli impulsi in base ad un evento, modifica i contatori del timer in modo che la modifica della frequenza avvenga in automatico.
Scusa ma come fa a scrivere quando l'interrupt altera l'output?
Arriva un interrupt sul fronte di salita e se val=1 allora genero 1 in uscita.
Poi torno nel loop e se sono trascorsi 300us allora scrivo 0 in uscita.
Tieni conto che prima che arrivi un altro interrupt, sta volta sul fronte di discesa, almeno trascorrono 2ms se sono a basse freq d'ingresso, caso in cui sto facendo le prove. E comunque sul fronte di discesa non succede nulla perchè non attivo il contatore e nemmeno una write.