buongiorno, sono nuovo del blog.
Avrei una domanda da porre, per curiosità/cultura più che altro.
Su di un Arduino UNO R3 H
ho iniziato ad utilizzare i timer interni, in interrupt.
C'è una cosa che non capisco:
misurando con oscillografo la mia uscita su PIN13, sembra tutto ok: i tempi sono stabilissimi.
Utilizzando invece le risorse interne al progetto ( millis() e Serial.println() ), sia nel Monitor che nel Plotter si vedono delle variazioni random di + o - 1ms (o anche più).
Se qualcuno mi può fare cortesemente luce!!! Grazie
Questo è un estratto del codice:
/***********************************************************************
Utilizza il TIMER1 (a 16bit) - utilizzato dal sistema per il SERVO
Impostato a 1 secondo:
varia da 999ms a 1001ms.
Impostato a 100 ms:
varia da 99ms a 101ms.
Impostato a 10 ms:
varia da 9ms a 11ms.
Impostato a 5 ms:
varia da 4ms a 6ms.
Impostato a 1 ms:
varia da 0,9ms a 1,1ms.
Sembra che gli dia noia la Serial.println() .....
CON OSCILLOSCOPIO I TEMPI SONO STABILI
***********************************************************************/
/// #include <util/atomic.h> // this library includes the ATOMIC_BLOCK macro.
unsigned volatile long tm;
unsigned volatile long tmOld;
///////////////////////////////////////////////////////////////////
void setup()
{
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
// disabilita il TIMER 1
TIMSK1 &= ~(1<<TOIE1);
// configuro il Timer1 in modalità “Normal” (cioè nessun PWM o CTC o fast PWM…ma solo “contatore”).
TCCR1A &= ~((1<<WGM11) | (1<<WGM10));
TCCR1B &= ~((1<<WGM12) | (1<<WGM13));
// il timer 1 deve generare un interrupt solo in caso di overflow
TIMSK1 &= ~(1<<OCIE1A);
// configura il prescaler per dividere 1024 (xxxx.x101)
TCCR1B |= (1<<CS12) | (1<<CS10); // setta "1" nei bit CS10 e CS12
TCCR1B &= ~(1<<CS11); // setta "0" nel bit CS11
// valore di partenza del contatore per ottenere il periodo di interrupt desiderato
// calcola il valore da inserire per arrivare all'overflow
// 15625 = 16000000 / 1024
// 49911 = 65536 - 15625
// 49911 = 0xC2F7
// TCNT1 = 0xC2F7; // 1 secondo
// TCNT1 = 0xF9E5; // 100msec
// TCNT1 = 0xFF63; // 10msec
TCNT1 = 0xFFB1; // 5msec
// TCNT1 = 0xFFF0; // 1msec
// ABILITA il TIMER 1
TIMSK1 |= (1<<TOIE1);
}
/////////////////////////////////////////////////////////////////////////////
// INTERRUPT SERVICES ROUTINE
//
// funzione isr chiamata all'overflow del TIMER1
////////////////////////////////////////////////////////////////////////////
ISR(TIMER1_OVF_vect)
{
// E' necessario ricaricare il timer
// TCNT1 = 0xC2F7; // 1 secondo
// TCNT1 = 0xF9E5; // 100msec
// TCNT1 = 0xFF63; // 10msec
TCNT1 = 0xFFB1; // 5msec
// TCNT1 = 0xFFF0; // 1msec
//----INIZIO codice utente----------------------------
tmOld = tm;
tm = millis();
// tm = micros();
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
//----FINE codice utente----------------------------
}
///////////////////////////////////////////////////////////////////
void loop()
{
char sApp[20];
unsigned long t;
unsigned long o;
// Il delay non ferma gli interrupt
delay(500);
//----INIZIO CODICE NON INTERROMPIBILE-------------
// ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
// {...}
// code with interrupts blocked (consecutive atomic operations will not get interrupted)
// oppure
noInterrupts();
t = tm;
o = tmOld;
sprintf(sApp, "%02ld", t-o);
interrupts();
//----FINE CODICE NON INTERROMPIBILE---------------
Serial.println(sApp);
Serial.println(t - o);
}
Ti segnalo che, nella sezione in lingua Inglese, si può scrivere SOLO in Inglese ... quindi, per favore, la prossima volta presta più attenzione in quale sezione metti i tuoi post; questa volta esso è stato spostato, da un moderatore della sezione di lingua Inglese, nella sezione di lingua Italiana ... la prossima volta potrebbe venire direttamente eliminato. Grazie.
... cortesemente, come prima cosa, leggi attentamente il REGOLAMENTO della sezione Italiana del forum, (... e, per evitare future possibili discussioni/incomprensioni, prestando sempre molta attenzione al punto 15), dopo di che, come da suddetto regolamento, fai la tua presentazione NELL'APPOSITA DISCUSSIONE spiegando bene quali esperienze hai in elettronica e programmazione, affinché noi possiamo conoscere la tua esperienza ed esprimerci con termini adeguati.
Grazie,
Guglielmo
P.S.: Ti ricordo che, purtroppo, fino a quando non sarà fatta la presentazione nell’apposita discussione, nel rispetto del succitato regolamento nessuno ti risponderà (eventuali risposte o tuoi ulteriori post, verrebbero temporaneamente nascosti), quindi ti consiglio di farla al più presto.
non si potrebbe fare come si fa con i processori Intel, AMD cioè leggere il valore del contatore interno dei cicli di clock?
Se esiste ovviamente!!!
Grazie di nuovo
Non dovresti però, su tempi relativamente brevi (70 sec), avere problemi con la la micros() che, al contrario della millis(), NON è influenzata dalle ISR dato che usa tutt'altra tecnica.
Trovi i sorgenti di entrambe le funzioni nella cartella del "core" Arduino.
Si ma secondo me non è un problema (solo) di millis() nella ISR, o forse anche della digitalWrite(), ma (anche) del fatto che scrivendo dei dati su seriale, tanto più se a soli 9600, si induce un ritardo (comunque sommato a quel -poco- tempo necessario per calcolare la differenza tra due unsigned long e per la sprintf()). D'altronde se hai identificato la causa rimuovendo il codice per la seriale lasciando solo il timer, è evidente che dipenda da quella parte di codice.
Se hai bisogno di codice time critical dovresti evitare (o almeno tenere presente) la scrittura seriale e/o quant'altro ti occorra per monitorare o gestire gli eventi. In particolare mi sfugge non solo per quale motivo disabiliti i timer ma anche quale sia la tua esigenza ossia perché tu debba usare i timer in quel modo: non ti basta la precisione di millis() o al limite microseconds() (che tra l'altro dipendono tutti dall'oscillatore interno, non particolarmente stabile)?
grazie Guglielmo,
proverò ad usare micros().
Eventualmente vedrò come fare per gestire lo scavalco.
Magari guardando il sorgente riesco a capirlo meglio.
Scusa la mia ignoranza dilagante, cosa intendi per "core" Arduino?
Dove lo posso trovare?
Come ti ho già detto, tutto questo ha l'unico scopo pratico di avere uno strumento di misura da poter utilizzare all'interno dei miei codici.
grazie
E' il "framework" sviluppato dai vari produttori (per le schede Arduino dal team Arduino, per le altre da vari sviluppatori) per ... semplificare la programmazione.
Se sei su Windows, vai nella cartella dell'IDE e in "arduino\hardware\arduino\avr\cores" hai il "core" per Arduino UNO
docdoc grazie,
ho letto da qualche parte che, essendo il micro a 8 bit, la gestione di una variabile di tipo long viene spezzata in più cicli e quindi il risultato finale potrebbe non essere veritiero se inframezzato da interrupt che modificano quella variabile.
Come ho detto a Guglielmo, sto solo facendo autodidattica ma vorrei anche implementare uno strumento per misurare tempi interni senza bisogno di output digitali/oscillografo.
Magari in qualche progetto futuribile mi potrebbe servire
si, uso Windows 10 64 bits ed il corrispondente Arduino IDE 2.02.
Come vedi mi sa che non c'è
Forse si trova solo nell'installazione della versione a 32bit?
Si, ovviamente una long è composta da 4 bytes e quindi ogni tuo accesso richiede una serie di cicli di clock e ... se un interrupt scatta mentre la stai leggendo e la va a modificare ... tu hai letto una parte prima della modifica ed una parte dopo.
Per questo la AVR libc (libreria sempre inclusa quando si compila per agli Arduino con le classiche MCU AVR) mette a disposizione <util/atomic.h> dove trovi ciò che ti serve per evitare il problema
Come detto mi rifiuto di usare la v2 dell'IDE ... ancora incompleta, con svariati buchi ed i fase di sviluppo ... io mi riferisco sempre ad Arduino IDE classico v 1.x.x
Comunque c'è anche per la v2 ... solo che è in un'altra cartella ...
bastardi....
bisogna sempre rincorrerli.
Chissà perché spostano qualsiasi cosa!!!!
Sto usando la versione 2 perché mi comodo l'Intellisense, sono abituato così con microsoft VS.
Se mi dici che non è affidabile lo butto.
Grazie
buongiorno,
ho fatto come mi ha suggerito Guglielmo: usato micros() invece di millis().
Ora funziona perfettamente.
Il limite dell'overflow dopo circa 71 minuti l'ho superato in qualche modo:
quando mi accorgo che la lettura attuale ritornata da micros() è minore di quella eseguita il giro precedente, salto questo giro confermando il giro precedente e mi rifaso poi al giro successivo.
Non è bellissimo, ma funziona.
Per quindi l'argomento si può chiudere qui.
Grazie a tutti.
Il problema del superamento della capacità della variabile che memorizza il tempo nelle temporizzazioni con millis() e micros() si risolve con la forma: if (micros()-t_prec >= periodo) {}