Go Down

Topic: Timer 1 usato due volte in modi diversi (Read 3327 times) previous topic - next topic

ekjk

Ciao a tutti,
dato che il timer1 è l'unico che ha 16bit e a me servono tutti o quasi vorrei usarlo in questo modo.
Il timer è settato il CTC mode.

Prima lo uso per misurare la larghezza dell'impulso su un pin d'ingresso attivandolo nella chiamata all'ISR quando arriva un fronte di salita o discesa.
Supponendo che il primo fronte sia di salita io faccio partire il timer mettendo il prescaler giusto e OCR1A=10000 per esempio, in modo che quando arriva il fronte di discesa non sia scattata prima la ISR sul compare tra TCNT1 e OCR1A (CTC mode).

Appena arriva il fronte negativo leggo il valore di TCNT1 e così so la larghezza dell'impulso. FIno qui circa bene.
Poi fermo il timer mettendo tutti i CS a 0 e resetto TCNT1.
Da questo momento (quindi sempre nella isr chiamata al fronte negativo) voglio impostare OCR1A ad un valore ben preciso e variabile e poi far ripartire il timer impostando i CS come prima. Quando arriverà ad aver contato il valore in OCR1A esegue la ISR di compare avvenuto e li fa altre cose.

Il problema sta proprio qui...cioè lui non ricomincia a contare. Forse c'è qualche flag da azzerare?


leo72

Quando reinizializzi il timer, questo non riparte subito. Forse il ritardo tu lo rilevi come non ripartenza.
Inoltre leggiti bene il datasheet perché c'è la possibilità di utilizzare il timer 1 per misurare un evento (cap. 16.6)

Infine una domanda. Ma perché usi questa modalità così complicata?
Non potresti semplicemente attaccare al pin in questione un PCINT, un interrupt di cambio di stato, e contare il tempo che intercorre fra 2 cambi di stato successivi?

ekjk

#2
Jan 11, 2013, 08:54 am Last Edit: Jan 11, 2013, 09:44 am by ekjk Reason: 1
Potrei fare anche come dici tu però non so come fare per usare quella modalità e sul datasheet non ci capisco troppo.
Uso la ctc perchè quando poi arriva il fronte negativo devo farlo ripartire e farlo contare finchè non raggiunge il valore in ocr1A e a quel punto gestire l'interrupt generato.
Immagino che per fare come dici tu dovrei impostare il timer1 in una modalità per contare la larghezza di impulso (e dovrei settarlo al fronte di salita e farlo partire) e poi al fronte di discsa settarlo in ctc e farlo partire.

Se gentilmente potresti mostrarmi come farlo te ne sarei molto grato.
Grazie

ps: io ho necessità di usare il pin 3 come input perchè ormani ho la scheda fatta.

leo72

Il timer 1 in modalità Input Capture Unit lavora col pin ICP1 (pin D8 dell'Arduino) o con l'ADC per cui se la scheda è già fatta, allora non si può lavorare con esso.

Intanto bisognerebbe che tu postassi il codice che stai usando, almeno vediamo di analizzare su cosa stai lavorando e capire la logica che vuoi applicare

ekjk

Intanto grazie per i chiarimenti.

Non riesco a postare il codice ora ma appena possibile lo faccio.
Spero che sia chiaro ciò che vorrei fare.
Se riuscissi a resettare il timer sarei a cavallo.

Comunque utilizzando la funzione pulseIn() potrei leggere la durata dell'impulso o no? Svantaggi?

Ciò che voglio fare è questo:

1.Per sapere i giri di un motore mi voglio basare sulla larghezza dell'impulso del sensore e non solo sulla distanza tra due fronti alti.
2.All'accensione del motore devo contare i giri utilizzando magari attachInterrupt che fa partire una routinesul fronte alto: e questo lo so fare.
3.Allo stesso tempo però voglio sapere la larghezza dell'impulso d'ingresso, cioè il tempo tra fronti HIGH e LOW, così posso calcolare con la legge oraria quanto spazio in gradi corrisponde alla larghezza dell'impulso e alla sua frequenza: alpha=t*2pi*f, e la salvo in una variabile
4.Quindi conoscendo f data dal tempo tra HIGH e successivo HIGH e conoscendo t dato dal tempo tra HIGH e LOW conosco alpha.
5.Ora conoscendo alpha posso ricavare f (i giri motore) invertendo la relazione e contando la larghezza dell'impulso.
6.Questo mi permette di poter conoscere i giri motore appena finisce il segnale del sensore senza aspettare il nuovo fronte HIGH.

Spero sia chiaro, sggerimenti sono apprezzati. Grazie

ekjk

Qualche idea?

questo è il codice che uso ora, ma che calcola gli rpm solo dopo aver generato l'impulso di uscita.
Code: [Select]

void counterISR()
{     
  TCCR1B |= (1 << CS11);   
  val=!val;
  fire=true;
}

ISR(TIMER1_COMPA_vect)
{
  if(!NotRun && fire) PORTD = PORTD | B00010000;
  else PORTD =PORTD & B11101111;
 
  TCCR1B &= ~(1 << CS11); 
 
  fire=false;
  flag=true;
 
  if(val) tempo_iniziale=micros();

  if(!val)tempo_finale=micros();
         
  if(tempo_finale>=tempo_iniziale) {freq=(int)(1000000.0/(tempo_finale-tempo_iniziale));}
  if(tempo_finale<tempo_iniziale) {freq=(int)(1000000.0/(tempo_iniziale-tempo_finale));}
         
  if(freq<0) freq=0;
  if(freq>250) freq=250;
}


al fronte di salita in ingresso si va in counterISR dove abilito il conteggio di timer 1 in ctc.
Quando timer1 arriva a OCR1A (settata di volta in volta nel loop in base alla freq di rotazione) si passa in TIMER1_COMPA_vect che alza il pin di uscita così ritardato del valore che voglio e poi calcolo la freq di rotazione...lo faccio così perchè introduco meno ritardo rispetto a farla quando arriva il primo fronte nell counterISR.

Facendo così però, il ritardo che applico (OCR1A) è calcolato sulla frequenza tra un fronte e quello successivo e quindi il timer applica il vero valore di ritardo solamente al secondo fronte, perchè al primo fronte OCR1A non è settato al valore giusto in quanto per ricavare tale valore serve la frequenza che viene calcolata solo all'arrivo del fronte successivo, il secondo.

leo72

Forse con la pulseIn potresti risolvere. Su che ordine di grandezza di tempi stiamo ragionando? La pulseIn misura da 10 uS in su. Sarebbero sufficienti?
Stavo poi ragionando sul modo con cui usi il timer 1.
Che cosa faresti con CS11? Quel flag è usato per impostare il prescaler, non la modalità di funzionamento del timer

ekjk

Ho provato con la pulseIn() a misurare la durata dell'impulso, sotto i 10us non ci vado mai, ma ho notato che il codice rallenta molto.

CS11 lo metto a zero per fermare il timer (dato che secondo il prescaler che uso anche gli altri CS sono a zero) che sennò continuerebbe a contare generando un pwm.

leo72


Ho provato con la pulseIn() a misurare la durata dell'impulso, sotto i 10us non ci vado mai, ma ho notato che il codice rallenta molto.

Il codice rallenta perché la pulseIn ferma tutto e si mette in attesa dell'impulso, misurandone poi la durata.

Quote

CS11 lo metto a zero per fermare il timer (dato che secondo il prescaler che uso anche gli altri CS sono a zero) che sennò continuerebbe a contare generando un pwm.

Ah, ho capito. Togli il segnale di clock al timer.
Ricordati però che quando riattivi il timer dandogli il clock (mettendo CS12 ad 1), il timer potrebbe non ripartire subito, subendo un certo ritardo proporzionale al fattore di prescaler. Tu usi un prescaler di 256, mi pare.
void counterISR da chi viene chiamata?

ekjk

Ciao,
io uso un prescaler = 8, il tick diventa quindi 0.5us.
void counterISR viene chiamata da attachInterrupt sul pin 3 e su fronte RISING.


leo72


Ciao,
io uso un prescaler = 8, il tick diventa quindi 0.5us.

Sì, scusa. Avevo scritto CS12 per sbaglio e su quello mi basavo.

Quote

void counterISR viene chiamata da attachInterrupt sul pin 3 e su fronte RISING.



OK.

Comunque continuo a non capire la logica del tuo codice.
Vedendo le prime istruzioni della ISR agganciata all'interrupt di comparazione, tu fermi subito il timer.
Se ho capito è: arriva un segnale su un pin, richiamo un interrupt che fa partire il timer 1. Quando questo arriva al valore impostato da OC1A, fermo il timer. E poi?

ekjk

Quando il timer arriva al valore OCR1A per prima cosa faccio

Code: [Select]
if(!NotRun && fire) PORTD = PORTD | B00010000;
  else PORTD =PORTD & B11101111;


Alzo il pin 4.

Dopo di che fermo il timer e calcolo i giri basandomi sul fronte di salita attuale e su quello successivo.

ekjk

Non so se è chiaro, ma conoscendo la frequenza poi devo applicare un ritardo al segnale di uscita (pin 4) che scriverò in OCR1A e quando il timer arriva a tale valore alzo il pin 4 e fermo il timer.

leo72

Sì, ho capito.
Ma non posso aiutarti più di tanto perché per i tempi che servono a te occorrerebbe un oscilloscopio per vedere in quanto reagisce il timer a queste continue attivazioni/disattivazioni, strumento che io non ho. Cambiare valore di OCR1A in corsa non è un problema. Il problema secondo me nasce dall'attivazione/disattivazione del timer. Secondo me il timer riparte (non può non ripartire come dici tu) ma con un certo ritardo, per cui salti delle letture.

ekjk

Io c'è l'ho l'oscilloscopio...infatti verifico queste cose.
Non c'è un modo per imbrogliare il timer?

Io vorrei che si fermasse...Nella modalità count normale quando va in overflow che succede? Chiama una routine e poi riparte?

Go Up