Cronometro

Ciao
Sto cercando di creare un cronometro visualizzato su 6 display a 7 segmenti comandati da una decodifica BCD/7segmenti in multiplexing.
Per dare il clock di incremento del tempo ho usato la libreria leOS in modo tale che ogni 10 microsecondi incrementi una variabile, più precisamente la variabile dei centesimi di secondo.

Ho realizzato il circuito che funziona perfettamente, però l'incremento dei microsecondi è sballato e non di pochissimo;
per verificare nel programma ho inserito la funzione millis() nel void comandato da leOS in modo da vedere se effettivamente c'è questo errore; facendo questa prova ha effettivamente verificato che c'è un errore nella tempistica, infatti l'incremento non è sempre costante e non è quello che ho impostato su leOS!

Cosa può essere secondo voi??

di seguito vi riporto il programma in questione:

#include <leOS.h>

leOS schedulatore;

int massa_csec = 2;
int massa_dsec = 3;
int massa_sec1 = 4;
int massa_sec2 = 5;
int massa_min1 = 6;
int massa_min2 = 7;

int a = 8;
int b = 9;
int c = 10;
int d = 11;

int numeri_a[10]={LOW,HIGH,LOW,HIGH,LOW,HIGH,LOW,HIGH,LOW,HIGH}; //array per comunicare con decodifica BCD to 7 segment
int numeri_b[10]={LOW,LOW,HIGH,HIGH,LOW,LOW,HIGH,HIGH,LOW,LOW};
int numeri_c[10]={LOW,LOW,LOW,LOW,HIGH,HIGH,HIGH,HIGH,LOW,LOW};
int numeri_d[10]={LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,HIGH,HIGH};
int num_csec =0;// centesimi di secondo
int num_dsec =0;// decimi di secondo
int num_sec1 =0;// secondi
int num_sec2 =0;// secondi
int num_min1 =0;// minuti
int num_min2 =0;// minuti

void setup(){

pinMode(massa_csec, OUTPUT);
pinMode(massa_dsec, OUTPUT);
pinMode(massa_sec1, OUTPUT);
pinMode(massa_sec2, OUTPUT);
pinMode(massa_min1, OUTPUT);
pinMode(massa_min2, OUTPUT);

pinMode(a, OUTPUT);
pinMode(b, OUTPUT);
pinMode(c, OUTPUT);
pinMode(d, OUTPUT);

digitalWrite(massa_csec, HIGH); // situazioni iniziale per multiplexing
digitalWrite(massa_dsec, HIGH);
digitalWrite(massa_sec1, HIGH);
digitalWrite(massa_sec2, HIGH);
digitalWrite(massa_min1, HIGH);
digitalWrite(massa_min2, HIGH);

Serial.begin(9600);
Serial.println("Cronometro");
schedulatore.begin();
schedulatore.addTask(incrementa, 10);

}

void loop(){
if(num_csec>9){
num_csec=0;
num_dsec=num_dsec++;
}
if(num_dsec>9){
num_dsec=0;
num_sec1=num_sec1++;
}
if(num_sec1>9){
num_sec1=0;
num_sec2=num_sec2++;
}
if(num_sec2>5){
num_sec2=0;
num_min1=num_min1++;
}
if(num_min1>9){
num_min1=0;
num_min2=num_min2++;
}
if(num_min2>9){
num_min2=0;
}

}
void incrementa(){
Serial.println(millis());
num_csec=num_csec++; //incremento centesimi di secondo
digitalWrite(massa_min2, HIGH); //comandi decodifica in multiplexing per 6 display!
digitalWrite(a, numeri_a[num_csec]);
digitalWrite(b, numeri_b[num_csec]);
digitalWrite(c, numeri_c[num_csec]);
digitalWrite(d, numeri_d[num_csec]);
digitalWrite(massa_csec, LOW);
delay(1);
digitalWrite(massa_csec, HIGH);
digitalWrite(a, numeri_a[num_dsec]);
digitalWrite(b, numeri_b[num_dsec]);
digitalWrite(c, numeri_c[num_dsec]);
digitalWrite(d, numeri_d[num_dsec]);
digitalWrite(massa_dsec, LOW);
delay(1);
digitalWrite(massa_dsec, HIGH);
digitalWrite(a, numeri_a[num_sec1]);
digitalWrite(b, numeri_b[num_sec1]);
digitalWrite(c, numeri_c[num_sec1]);
digitalWrite(d, numeri_d[num_sec1]);
digitalWrite(massa_sec1, LOW);
delay(1);
digitalWrite(massa_sec1, HIGH);
digitalWrite(a, numeri_a[num_sec2]);
digitalWrite(b, numeri_b[num_sec2]);
digitalWrite(c, numeri_c[num_sec2]);
digitalWrite(d, numeri_d[num_sec2]);
digitalWrite(massa_sec2, LOW);
delay(1);
digitalWrite(massa_sec2, HIGH);
digitalWrite(a, numeri_a[num_min1]);
digitalWrite(b, numeri_b[num_min1]);
digitalWrite(c, numeri_c[num_min1]);
digitalWrite(d, numeri_d[num_min1]);
digitalWrite(massa_min1, LOW);
delay(1);
digitalWrite(massa_min1, HIGH);
digitalWrite(a, numeri_a[num_min2]);
digitalWrite(b, numeri_b[num_min2]);
digitalWrite(c, numeri_c[num_min2]);
digitalWrite(d, numeri_d[num_min2]);
digitalWrite(massa_min2, LOW);
delay(1);
}

la minima risoluzione del leOS è il millisecondo, non il microsecondo :wink:
A parte questo errore ortografico, ogni task presente nello schedulatore del leOS viene eseguito con gli interrupt arrestati. Siccome sia la funzione millis() che la funzione Serial sono basate sugli interrupt, il tuo comando Serial.println(millis()) dato all'interno del task verrà eseguito:

  1. leggendo il valore di millis che non è incrementato perché l'interrupt agganciato all'overflow del timer 0 che viene usato per incrementare il valore di millis è stato disattivato;
  2. la Serial.println mette i dati nel buffer seriale, da cui i dati sono prelevati e spediti materialmente sulla seriale da un interrupt, per cui essi verranno spediti quando il task sarà terminato.

La somma di questa concomitanza di eventi genera il salto di millisecondi fra ciò che segna millis e ciò che calcola il leOS.

Quindi se tolgo il funzione millis e serial il clock del mio cronometro dovrebbe essere perfetto?
Grazie

Secondo me dovresti separare la funzione di incremento con quella di visualizzazione che potresti far richiamare ad ogni loop.

void incrementa(){
 Serial.println(millis());
  num_csec++; //incremento centesimi di secondo
}

Non serve scrivere variabile=variabile++, ma basta variabile++

void visualizza(){
  digitalWrite(massa_min2, HIGH);            //comandi decodifica in multiplexing per 6 display!
  digitalWrite(a, numeri_a[num_csec]);
  digitalWrite(b, numeri_b[num_csec]);
  digitalWrite(c, numeri_c[num_csec]);
  digitalWrite(d, numeri_d[num_csec]);
  digitalWrite(massa_csec, LOW);
  delay(1);
  digitalWrite(massa_csec, HIGH);
  digitalWrite(a, numeri_a[num_dsec]);
  digitalWrite(b, numeri_b[num_dsec]);
  digitalWrite(c, numeri_c[num_dsec]);
  digitalWrite(d, numeri_d[num_dsec]);
  digitalWrite(massa_dsec, LOW);
  delay(1);
  digitalWrite(massa_dsec, HIGH);
  digitalWrite(a, numeri_a[num_sec1]);
  digitalWrite(b, numeri_b[num_sec1]);
  digitalWrite(c, numeri_c[num_sec1]);
  digitalWrite(d, numeri_d[num_sec1]);
  digitalWrite(massa_sec1, LOW);
  delay(1);
  digitalWrite(massa_sec1, HIGH);
  digitalWrite(a, numeri_a[num_sec2]);
  digitalWrite(b, numeri_b[num_sec2]);
  digitalWrite(c, numeri_c[num_sec2]);
  digitalWrite(d, numeri_d[num_sec2]);
  digitalWrite(massa_sec2, LOW);
  delay(1);
  digitalWrite(massa_sec2, HIGH);
  digitalWrite(a, numeri_a[num_min1]);
  digitalWrite(b, numeri_b[num_min1]);
  digitalWrite(c, numeri_c[num_min1]);
  digitalWrite(d, numeri_d[num_min1]);
  digitalWrite(massa_min1, LOW);
  delay(1);
  digitalWrite(massa_min1, HIGH);
  digitalWrite(a, numeri_a[num_min2]);
  digitalWrite(b, numeri_b[num_min2]);
  digitalWrite(c, numeri_c[num_min2]);
  digitalWrite(d, numeri_d[num_min2]);
  digitalWrite(massa_min2, LOW);
  delay(1);
}
void loop(){
.......
visualizza();
}

Ho già provato a separare la funzione di incremento da quella di visulizzazione inserendola nel loop, ma così facendo non aggiorna costantemente tutte le variabili e quindi si sballa il conteggio!!

Ho provato in mille altri modi ma quello che scopro sempre alla fine è che ottengo un cronometro che ogni minuto sballa di 4-5secondi!!!

Non riesco a capire dove sia il problema!!!!
Pultroppo non ho a disposizione un'oscilloscopio per verificare che leOS faccia incrementare effettivamente la variabile ogni 10 millisecondi!!!

Il problema risiede in:

  1. l'Arduino usa un risonatore ceramico che non ha una grossa precisione. Perdere qualche secondo nel giro di pochi minuti non è strano, è nella norma;
  2. ricordati che ora il tuo Arduino sta usando 2 timer (lo 0 e il 2) con 2 ISR che si bloccano a vicenda, quindi i tempi vengono alterati anche da questo.

Se vuoi una precisione maggiore, puoi usare il timer 2 tramite il modulo Real-Time Counter dell'Atmega328 collegato ad un quarzo esterno da 32768 Hz. Lavorando con uno standalone puoi disattivare il timer 0 e lasciar lavorare solo il timer 2 impostato per avere 1 overflow ogni millisecondo. A questo punto il cronometro sarebbe molto preciso.

OK!!
Quindi il problema riguarda l'oscillatore che usa arduino!!!

Ti ringrazio per la soluzione cha mi hai proposto soltanto che non ho la più pallida idea di come fare ad attuarle!!
Potresti darmi una mano o uno spunto!

grazie

zinim:
OK!!
Quindi il problema riguarda l'oscillatore che usa arduino!!!

Ti ringrazio per la soluzione cha mi hai proposto soltanto che non ho la più pallida idea di come fare ad attuarle!!
Potresti darmi una mano o uno spunto!

grazie

Scaricati gli schemi ed il codice del mio Micrologio, lì trovi ciò che ti ho appena detto, ossia un Atmega con oscillatore interno e quarzino esterno da 32768, oltre che la gestione dei timer per far ciò che ti avevo appena spiegato. Dai un'occhiata al codice e dimmi se ci capisci qualcosa.... nel caso ne riparliamo :wink:

ok! grazie
prove e in caso ti faccio sapere

ciao

ho dato un po' un occhiata al progetto ma mi sembra di capire che questo metodi mi implica di non utilizzare la scheda arduino ma solo il microprocessore!

è così?
grazie

Sì, ma te l'avevo già specificato :stuck_out_tongue:

leo72:
Se vuoi una precisione maggiore, puoi usare il timer 2 tramite il modulo Real-Time Counter dell'Atmega328 collegato ad un quarzo esterno da 32768 Hz. Lavorando con uno standalone puoi disattivare il timer 0 e lasciar lavorare solo il timer 2 impostato per avere 1 overflow ogni millisecondo. A questo punto il cronometro sarebbe molto preciso.

il fatto è che il passo successivo del mio progetto sarebbe stato salvare alcuni tempi su una memoria sd usando una shield apposita!!

Stavo pensando se magari fosse possibile creare un oscillatore esterno e collegarlo ad uno dei pin digitali di arduino impostandolo come input e nel programma fare incrementare la variabile ad ogni cambio di stato del pin digitale!?? Per la comunicazione con la decodifica, userei leOS!

secondo te potrebbe essete fattibile??

Grazie

Puoi agganciare un interrupt esterno e crearti una ISR per aggiornare la variabile.