Arduino_millis_no_Overflow


millis() ed il problema dell'overflow

In una precedente guida (https://www.realmeteo.com/arduino/millis/) avevamo visto come con la funzione millis(), sia possibile creare ritardi non bloccanti. Avevamo anche detto che questa funzione può essere affetta da un problema di overflow, qualora il nostro arduino venga tenuto acceso per più di 49 giorni. Questo non vuole dire che la funzione abbia un baco, ma semplicemente che, nel nostro modo comune di pensare, siamo portati ad usarlo in un modo errato, e non ci accorgiamo della cosa se non dopo 49 giorni. Ma da dove deriva questo, e come evitare il problema? La prima risposta è semplice, il contatore che viene automaticamente incrementato ogni millisecondo è un registro da 32 bit; questo significa che dopo 4.294.967.295 millisecondi, corrispondenti a 1193,05 ore, o a 49,7 giorni, il contatore si azzera e ricomincia da zero.

Siccome questo numero è piuttosto elevato per fare dei ragionamenti semplici, articolerò la descrizione del problema e della soluzione fingendo che il registro abbia solo 8 bit; in questo caso fittizio, il contatore arriva a 255 millisecondi e poi ricomincia da zero.

Faccio una parentesi di matematica binaria sui numeri da 8 bit, dove ribadisco, il numero massimo rappresentabile è 255, e poi, se aggiungo uno, riparte da zero. Quindi, tanto per fare qualche esempio di operazioni ad 8 bit, avremo questi risultati: 20+10=30; 230+10=240; 245+10=255; 246+10=0; 247+10=1; 248+10=2; 254+10=8; 255+10=9; 0+10=10 .... ma per le sottrazioni è la stessa cosa, ovvero 9-10=255; 8-10=254; 2-10=248; 1-10=247 ecc ecc.

Se queste cose sono chiare partiamo con la descrizione del problema, e sul come evitarlo:
Immaginiamo di dover generare con Arduino, una onda quadra da 50Hz, ovvero fare un toggle di un output, che sarà per 10 milliecondi ad 1 e per 10 millisecondi a 0.

Nella tabella seguente abbiamo, lo scorrere dei millisecondi nella prima colonna, il tempo ciclo nella seconda, la prossima meta nella terza e nella quarta l'operazione di controllo che erroneamente siamo portati a fare, per creare il ritardo da 10 millisecondi. All'inizio tutto funziona bene, ed ogni 10 millisecondi l'operazione di confronto fa invertire l'output, generando l'onda quadra a 50Hz. Ora spostiamoci nei pressi dell'evento nefasto. Al millisecondo 250 abbiamo l'ultimo toggle corretto, poi al 251 incontriamo il primo problema, un toggle inaspettato, e poi un altro a 252, ed altri ancora, fino al millisecondo 255
Ma non basta... segue un lungo periodo dove non ci sono toggle, ed arriviamo a millis 54 del secondo giro, dove tutto si sistema.
Quindi nella realtà avremo che i toggle non sono regolari, ma solo dopo 49 giorni!!!!


millis attuale TempoCiclo ProssimaMeta if (millis() >= ProssimamMeta) SI NO
1 10 10 NO - continua
2 10 10 if (2 >= 10) NO - continua
3 10 10 if (3 >= 10) NO - continua
4 10 10 if (4 >= 10) NO - continua
5 10 10 if (5 >= 10) NO - continua
6 10 10 if (6 >= 10) NO - continua
7 10 10 if (7 >= 10) NO - continua
8 10 10 if (8 >= 10) NO - continua
9 10 10 if (9 >= 10) NO - continua
10 10 10 if (10 >= 10) SI - TOGGLE - ProssimaMeta = ProssimaMeta +TempoCiclo
11 10 20 if (11 >= 20) NO - continua
12 10 20 if (12 >= 20) NO - continua
13 10 20 if (13 >= 20) NO - continua
14 10 20 if (14 >= 20) NO - continua
15 10 20 if (15 >= 20) NO - continua
16 10 20 if (16 >= 20) NO - continua
17 10 20 if (17 >= 20) NO - continua
18 10 20 if (18 >= 20) NO - continua
19 10 20 if (19 >= 20) NO - continua
20 10 20 if (20 >= 20) SI - TOGGLE - ProssimaMeta = ProssimaMeta +TempoCiclo
21 10 30 if (21 >= 30) NO - continua
22 10 30 if (22 >= 30) NO - continua
23 10 30 if (23 >= 30) NO - continua
24 10 30 if (24 >= 30) NO - continua
25 10 30 if (25 >= 30) NO - continua
26 10 30 if (26 >= 30) NO - continua
27 10 30 if (27 >= 30) NO - continua
28 10 30 if (28 >= 30) NO - continua
29 10 30 if (29 >= 30) NO - continua
30 10 30 if (30 >= 30) SI - TOGGLE - ProssimaMeta = ProssimaMeta +TempoCiclo
31 10 40 if (31 >= 40) NO - continua
32 10 40 if (32 >= 40) NO - continua
33 10 40 if (33 >= 40) NO - continua
34 10 40 if (34 >= 40) NO - continua
35 10 40 if (35 >= 40) NO - continua
36 10 40 if (36 >= 40) NO - continua
37 10 40 if (37 >= 40) NO - continua
38 10 40 if (38 >= 40) NO - continua
39 10 40 NO - continua
---- ---- ---- ----
241 10 250 if (241 >= 250) NO - continua
242 10 250 if (242 >= 250) NO - continua
243 10 250 if (243 >= 250) NO - continua
244 10 250 if (244 >= 250) NO - continua
245 10 250 if (245 >= 250) NO - continua
246 10 250 if (246 >= 250) NO - continua
247 10 250 if (247 >= 250) NO - continua
248 10 250 if (248 >= 250) NO - continua
249 10 250 if (249 >= 250) NO - continua
250 10 250 if (250 >= 250) SI - TOGGLE - ProssimaMeta = ProssimaMeta +TempoCiclo
251 10 4 if (251 >= 4) SI - TOGGLE - ProssimaMeta = ProssimaMeta +TempoCiclo
252 10 14 if (252 >= 14) SI - TOGGLE - ProssimaMeta = ProssimaMeta +TempoCiclo
253 10 24 if (253 >= 24) SI - TOGGLE - ProssimaMeta = ProssimaMeta +TempoCiclo
254 10 34 if (254 >= 34) SI - TOGGLE - ProssimaMeta = ProssimaMeta +TempoCiclo
255 10 44 if (255 >= 44) SI - TOGGLE - ProssimaMeta = ProssimaMeta +TempoCiclo
0 10 54 if (0 >= 54) NO - continua
1 10 54 if (1 >= 54) NO - continua
2 10 54 if (2 >= 54) NO - continua
3 10 54 if (3 >= 54) NO - continua
4 10 54 if (4 >= 54) NO - continua
5 10 54 if (5 >= 54) NO - continua
6 10 54 if (6 >= 54) NO - continua
7 10 54 if (7 >= 54) NO - continua
8 10 54 if (8 >= 54) NO - continua
9 10 54 if (9 >= 54) NO - continua
10 10 54 if (10 >= 54) NO - continua
11 10 54 if (11 >= 54) NO - continua
12 10 54 if (12 >= 54) NO - continua
13 10 54 if (13 >= 54) NO - continua
14 10 54 if (14 >= 54) NO - continua
15 10 54 if (15 >= 54) NO - continua
16 10 54 if (16 >= 54) NO - continua
17 10 54 if (17 >= 54) NO - continua
18 10 54 if (18 >= 54) NO - continua
19 10 54 if (19 >= 54) NO - continua
20 10 54 if (20 >= 54) NO - continua
21 10 54 if (21 >= 54) NO - continua
22 10 54 if (22 >= 54) NO - continua
23 10 54 if (23 >= 54) NO - continua
---- ---- ---- ----
49 10 54 if (49 >= 54) NO - continua
50 10 54 if (50 >= 54) NO - continua
51 10 54 if (51 >= 54) NO - continua
52 10 54 if (52 >= 54) NO - continua
53 10 54 if (53 >= 54) NO - continua
54 10 54 if (54 >= 54) SI - TOGGLE - ProssimaMeta = ProssimaMeta +TempoCiclo
55 10 64 if (55 >= 64) NO - continua
56 10 64 if (56 >= 64) NO - continua
57 10 64 if (57 >= 64) NO - continua
58 10 64 if (58 >= 64) NO - continua
59 10 64 if (59 >= 64) NO - continua
60 10 64 if (60 >= 64) NO - continua
61 10 64 if (61 >= 64) NO - continua
62 10 64 if (62 >= 64) NO - continua
63 10 64 if (63 >= 64) NO - continua
64 10 64 if (64 >= 64) SI - TOGGLE - ProssimaMeta = ProssimaMeta +TempoCiclo
65 10 74 if (65 >= 74) NO - continua
66 10 74 if (66 >= 74) NO - continua
67 10 74 if (67 >= 74) NO - continua
68 10 74 if (68 >= 74) NO - continua
69 10 74 if (69 >= 74) NO - continua
70 10 74 if (70 >= 74) NO - continua
71 10 74 if (71 >= 74) NO - continua
72 10 74 if (72 >= 74) NO - continua
73 10 74 if (73 >= 74) NO - continua
74 10 74 if (74 >= 74) SI - TOGGLE - ProssimaMeta = ProssimaMeta +TempoCiclo
75 10 84 if (75 >= 84) NO - continua
76 10 84 if (76 >= 84) NO - continua
77 10 84 if (77 >= 84) NO - continua
78 10 84 if (78 >= 84) NO - continua
79 10 84 if (79 >= 84) NO - continua
80 10 84 if (80 >= 84) NO - continua
81 10 84 if (81 >= 84) NO - continua
82 10 84 if (82 >= 84) NO - continua
83 10 84 if (83 >= 84) NO - continua




Ora la soluzione: invece di sommare il tempo ciclo per calcolare la prossima meta, occorre ragionare alla rovescia, calcolando il tempo trascorso rispetto all'ultimo evento.

Nella seconda tabella, a differenza dalla prima, viene memorizzato l'evento precedente, ed il confronto corretto da fare è: if (millis() - MetaPrecedente >= tempoCiclo) SI TOGGLE - NO continua.

Come possiamo vedere nella simulazione ad 8 bit non ci sono irregolarità e tutto fila liscio sempre e per sempre.


millis attuale TempoCiclo MetaPrecedente if (millis() - MetaPrecedente >= tempoCiclo) SI TOGGLE - NO continua
1 10 0 if (1 - 0) >= 10 NO - continua
2 10 0 if (2 - 0) >= 10 NO - continua
3 10 0 if (3 - 0) >= 10 NO - continua
4 10 0 if (4 - 0) >= 10 NO - continua
5 10 0 if (5 - 0) >= 10 NO - continua
6 10 0 if (6 - 0) >= 10 NO - continua
7 10 0 if (7 - 0) >= 10 NO - continua
8 10 0 if (8 - 0) >= 10 NO - continua
9 10 0 if (9 - 0) >= 10 NO - continua
10 10 0 if (10 - 0) >= 10 SI -TOGGLE - MetaPrecedente = MetaPrecedente + TempoCiclo
11 10 10 if (11 - 10) >= 10 NO - continua
12 10 10 if (12 - 10) >= 10 NO - continua
13 10 10 if (13 - 10) >= 10 NO - continua
14 10 10 if (14 - 10) >= 10 NO - continua
15 10 10 if (15 - 10) >= 10 NO - continua
16 10 10 if (16 - 10) >= 10 NO - continua
17 10 10 if (17 - 10) >= 10 NO - continua
18 10 10 if (18 - 10) >= 10 NO - continua
19 10 10 if (19 - 10) >= 10 NO - continua
20 10 10 if (20 - 10) >= 10 SI -TOGGLE - MetaPrecedente = MetaPrecedente + TempoCiclo
21 10 20 if (21 - 20) >= 10 NO - continua
22 10 20 if (22 - 20) >= 10 NO - continua
23 10 20 if (23 - 20) >= 10 NO - continua
24 10 20 if (24 - 20) >= 10 NO - continua
25 10 20 if (25 - 20) >= 10 NO - continua
26 10 20 if (26 - 20) >= 10 NO - continua
27 10 20 if (27 - 20) >= 10 NO - continua
28 10 20 if (28 - 20) >= 10 NO - continua
29 10 20 if (29 - 20) >= 10 NO - continua
30 10 20 if (30 - 20) >= 10 SI -TOGGLE - MetaPrecedente = MetaPrecedente + TempoCiclo
31 10 30 if (31 - 30) >= 10 NO - continua
32 10 30 if (32 - 30) >= 10 NO - continua
33 10 30 if (33 - 30) >= NO - continua
34 10 30 if (34 - 30) >= NO - continua
35 10 30 if (35 - 30) >= 10 NO - continua
36 10 30 if (36 - 30) >= 10 NO - continua
37 10 30 if (37 - 30) >= 10 NO - continua
38 10 30 if (38 - 30) >= 10 NO - continua
39 10 30 if (39 - 30) >= 10 NO - continua
--- --- --- ---
241 10 240 if (241 - 240) >= 10 NO - continua
242 10 240 if (242 - 240) >= 10 NO - continua
243 10 240 if (243 - 240) >= 10 NO - continua
244 10 240 if (244 - 240) >= 10 NO - continua
245 10 240 if (245 - 240) >= 10 NO - continua
246 10 240 if (246 - 240) >= 10 NO - continua
247 10 240 if (247 - 240) >= 10 NO - continua
248 10 240 if (248 - 240) >= 10 NO - continua
249 10 240 if (249 - 240) >= 10 NO - continua
250 10 240 if (250 - 240) >= 10 SI -TOGGLE - MetaPrecedente = MetaPrecedente + TempoCiclo
251 10 250 if (251 - 250) >= 10 NO - continua
252 10 250 if (252 - 250) >= 10 NO - continua
253 10 250 if (253 - 250) >= 10 NO - continua
254 10 250 if (254 - 250) >= 10 NO - continua
255 10 250 if (255 - 250) >= 10 NO - continua
0 10 250 if (0 - 250) >= 10 NO - continua
1 10 250 if (1 - 250) >= 10 NO - continua
2 10 250 if (2 - 250) >= 10 NO - continua
3 10 250 if (3 - 250) >= 10 NO - continua
4 10 250 if (4 - 250) >= 10 SI -TOGGLE - MetaPrecedente = MetaPrecedente + TempoCiclo
5 10 4 if (5 - 4) >= 10 NO - continua
6 10 4 if (6 - 4) >= 10 NO - continua
7 10 4 if (7 - 4) >= 10 NO - continua
8 10 4 if (8 - 4) >= 10 NO - continua
9 10 4 if (9 - 4) >= 10 NO - continua
10 10 4 if (10 - 4) >= 10 NO - continua
11 10 4 if (11 - 4) >= 10 NO - continua
12 10 4 if (12 - 4) >= 10 NO - continua
13 10 4 if (13 - 4) >= 10 NO - continua
14 10 4 if (14 - 4) >= 10 SI -TOGGLE - MetaPrecedente = MetaPrecedente + TempoCiclo
15 10 14 if (15 - 14) >= 10 NO - continua
16 10 14 if (16 - 14) >= 10 NO - continua
17 10 14 if (17 - 14) >= NO - continua
18 10 14 if (18 - 14) >= NO - continua
19 10 14 if (19 - 14) >= NO - continua
20 10 14 if (20 - 14) >= NO - continua
21 10 14 if (21 - 14) >= NO - continua
22 10 14 if (22 - 14) >= NO - continua
23 10 14 if (23 - 14) >= NO - continua




Chi volesse vedere un esempio reale, dove questo problema non è presente lo trova sull'ARDUINO IDE, in FILE -> DIGITAL -> MILLIS WITHOUT DELAY.

Per completare l'informazione, sappiate che è anche possibile forzare od azzerare il contatore millis() quando lo desideriamo fare, ad esempio una volta ogni 24 ore, per evitare il problema descritto sopra.
I puristi della programmazione deprecano questo metodo... decidete voi
Si chiama la seguente funzione quando si vuole azzerare il contatore millis:


extern unsigned long timer0_millis; //linea da mettere nelle dichiarazioni iniziali

// funzione vera e propria
void resetMillis() {
  cli(); //ferma interrupt
  timer0_millis = 0; //resetta contatore
  sei(); //riattiva interrupt
}


Luciano Pautasso




Se ti è piacuta questa guida clicca su uno o più banner pubblicitari. Ci aiuterai a sostenere le spese del server . Grazie



Cerca 


Categorie Articoli

Ultimi articoli

OPTA-FINDER-ARDUINO-COMPATIBLE   APRI 

whatsapp-alarm-repeater   APRI 

Arduino_Template_Menu_Eng   APRI 

Arduino_Template_Menu   APRI 

Power-Supply-with-Current-Control   APRI 

Vantaggi_Alimentatori-Controllo_Corrente   APRI 

Camping-La-Secca-Moneglia   APRI 

Safety-Relays   APRI 

Rele-di-sicurezza   APRI 

Internal-or-External-Watchdog   APRI 

Watchdog-interno-o-esterno   APRI 

Ripetitore-di-allarme-su-Whatsapp   APRI 

Bufala-in-crosta   APRI 

Home-Automation-ESPeriment   APRI 

ESPerimento-Domotica   APRI 

Arduino-measures-liquid-level   APRI 

Arduino-misura-livello-liquidi   APRI 

finder   APRI 

LORA-english-version   APRI 

Pluviometro-LORA   APRI 

Pillole_di_Promessi_Sposi   APRI 

LORA   APRI 

promessisposi-riscrittura   APRI 

Arduino_crashes   APRI 

Arduino_si_blocca   APRI 

Arduino_e_Trenino   APRI 

millis_no_overflow   APRI 

millis   APRI 

Arduino_millis_no_Overflow   APRI 

millis   APRI