Posticipo segnale d'ingresso al variare della frequenza dello stesso

@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

TCCR1B |= (1<<CS11);
TCCR1B &= ~(1<<CS10);
TCCR1B &= ~(1<<CS12);

Con le prime 2 righe si setta il modo overflow, per il timer2 in CTC dovrebbe diventare

  TCCR2A &= ~(1<<WGM20);  
  TCCR2A |= (1<<WGM21); 
  TCCR2B &= ~(1<<WGM22);

con le restanti 3 si imposta il prescaler.

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.

Consigli?

Risolto, avevo una serial print che non stampava niente ma faceva casini :slight_smile:

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;

delayMicroseconds(300);
digitalWrite(OUT, 0);
}

francamente non sapevo che si potesse fare.

Comunque se ti funziona...meglio così. :wink:

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? :slight_smile: )

	__asm__ __volatile__ (
		"1: sbiw %0,1" "\n\t" // 2 cycles
		"brne 1b" : "=w" (us) : "0" (us) // 2 cycles
	);

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.

dalubar:
(Leo l'assembly 6502/10 usava BNE, ricordi? :slight_smile: )

Branch on Result Non Zero.
Ne è passato di tempo..... programmavo il 7501 (la CPU del C16 erede del 6510) in assembly qualcosa come 28 anni fa.... :sweat_smile:

@ 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)

Non ho capito bene...micros()+delay dove la devo mettere?

nalle ISR

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.

void loop()
{
    if(micros()-width>=300)
    {
        digitalWrite(OUT, 0);
    }
}

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)
{
    TCCR1A = 0;     
    TCCR1B = 0;
   
    if(val) {digitalWrite(OUT, 1);width=micros();}

}

width è definita volatile unsigned long.

Come risolvere? il discorso dell'array di lesto non l'ho capito, Lesto puoi scrivere il codice per favore?

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.

Non capisco perchè quindi.

Scusami ma non ho capito.
Il codice che hai messo sono porzioni e non si capisce cosa faccia l'intero programma. Non posso aiutarti meglio di così.