leOS - un semplice OS per schedulare piccoli task

Però se ho ben capito gli LCD possiedono questo buffer di disegno internamente

Una volta inviato un contenuto all'lcd, questo rimane visualizzato fino a nuovo ordine, quindi direi che il meccanismo è presente.

grazie dei consigli, però sia che aggiorni prima un buffer o che aggiorni direttamente il display leggendo i dati al momento, devo temporizzare comunque la print() sul display, quindi in pratica per la funzione per scrivere sul display devo per forza fare un controllo tipo il blinkwithoutdelay, o ho capito male?

sì, devi fare attenzione a non superare con le print la velocità di refresh dell'LCD (o comunque le 30 vole al secondo) per evitare l'effetto sfarfallio

@d407336:
sì, ovviamente i display LCD hanno il loro buffer (e non potrebbe essere altrimenti dato che devono mantenere i dati da visualizzare ;)) ma il vantaggio di un buffer software lato Arduino risiede nel fatto che non sei costretto a scrivere ogni singola modifica sul display ma aggiorni quest'ultimo solo ogni tot.

@tuxduino:
il seguente codice, che altro non è se non l'adattamento per l'uso coi LED del tuo sketch, blocca l'Arduino:

#include <leOS.h>

leOS myOS;


//variables to control the LEDs
byte led1Status = 0;
byte led2Status = 0;
byte led3Status = 0;
const byte LED1 = 7;
const byte LED2 = 8;
const byte LED3 = 9;

void setup() {
    pinMode(LED1, OUTPUT);
    pinMode(LED2, OUTPUT);
    pinMode(LED3, OUTPUT);
    
    myOS.begin();
    myOS.addTask(flashLed1, 500);
    myOS.addTask(flashLed2, 500);
}


//main loop
void loop() {
    digitalWrite(LED3, led3Status ? '3' : ' ');
    led3Status ^= 1;    
    delay(1000);
}


//first task
void flashLed1() {
    digitalWrite(LED1, led1Status ? '1' : ' ');
    led1Status^=1;
    delay(800);                // <<<<<<<<<<<<<<<< (*)
    digitalWrite(LED2, led1Status ? '1' : ' ');
}


//second task
void flashLed2() {
    digitalWrite(LED2, led2Status ? '2' : ' ');
    led2Status^=1;
}

Come fa a te a funzionare?

Come fa a te a funzionare?

E' quello che vorrei sapere anch'io :sweat_smile:

Può essere che c'entrino le chiamate all'lcd ? Non mi sembra un motivo plausibile a dire il vero, cmq stasera per scrupolo provo la tua versione con i led.

invece che aggiornare il buffer software ogni loop() non è più conveniente aggiornarlo solo nel momento in cui devo stamparlo sul display?
sul display devo stampare dei valori di alcune variabili e dei valori letti con analogRead(), quindi penso che sia inutile aggiornare di continuo il buffer, se poi lo stampo sul display solo una volta al secondo.
forse non ho capito quello che intendi?

certo, mai detto che il buffer lo fai ad ogni loop. il vataggio di avere i 2 buffer sotto mano, è anche quello di confrontarli, trovare le differenze ed aggiornare solo quelle. Il desktop KDE ha un bellissimo plugin che mostra di differenti colori le zone dello schermo in base a quando è stata l'ultima volta che è stata ridisegnata

d407336:
invece che aggiornare il buffer software ogni loop() non è più conveniente aggiornarlo solo nel momento in cui devo stamparlo sul display?
sul display devo stampare dei valori di alcune variabili e dei valori letti con analogRead(), quindi penso che sia inutile aggiornare di continuo il buffer, se poi lo stampo sul display solo una volta al secondo.
forse non ho capito quello che intendi?

Come ha detto lesto, non devi aggiornare il display ad ogni loop.
Puoi farlo benissimo ogni tot, sarai tu a stabilire quanto far passare tra un aggiornamento ed il successivo.

tuxduino:

Come fa a te a funzionare?

E' quello che vorrei sapere anch'io :sweat_smile:

Può essere che c'entrino le chiamate all'lcd ? Non mi sembra un motivo plausibile a dire il vero, cmq stasera per scrupolo provo la tua versione con i led.

Se parliamo della LiquidCrystal allegata all'IDE 1.0.2, gli ho dato un'occhiata ma non ho trovato cose eclatanti tipo manipolazioni dei timer od uso degli interrupt.

d407336:
grazie dei consigli, però sia che aggiorni prima un buffer o che aggiorni direttamente il display leggendo i dati al momento, devo temporizzare comunque la print() sul display, quindi in pratica per la funzione per scrivere sul display devo per forza fare un controllo tipo il blinkwithoutdelay, o ho capito male?

Non confondiamogli le idee (si stanno mettendo in campo tante variabili, fino ad addirittura la gestione di un monitor pc)
Io ti do' la mia risposta secca, per il tuo lavoro va bene Looper

leo72:

tuxduino:

Come fa a te a funzionare?

E' quello che vorrei sapere anch'io :sweat_smile:

Può essere che c'entrino le chiamate all'lcd ? Non mi sembra un motivo plausibile a dire il vero, cmq stasera per scrupolo provo la tua versione con i led.

Se parliamo della LiquidCrystal allegata all'IDE 1.0.2, gli ho dato un'occhiata ma non ho trovato cose eclatanti tipo manipolazioni dei timer od uso degli interrupt.

Sì, sto usando la 1.0.2.

Comunque non vedi lampeggiare i led perché

' ' != 0

:stuck_out_tongue:

Prova con questo:

#include <leOS.h>

leOS myOS;


//variables to control the LEDs
byte led1Status = 0;
byte led2Status = 0;
byte led3Status = 0;
const byte LED1 = 7;
const byte LED2 = 8;
const byte LED3 = 9;

void setup() {
    pinMode(LED1, OUTPUT);
    pinMode(LED2, OUTPUT);
    pinMode(LED3, OUTPUT);
    
    myOS.begin();
    myOS.addTask(flashLed1, 500);
    myOS.addTask(flashLed2, 500);
}


//main loop
void loop() {
    digitalWrite(LED3, led3Status ? HIGH : LOW);
    led3Status ^= 1;    
    delay(1000);
}


//first task
void flashLed1() {
    digitalWrite(LED1, led1Status ? HIGH : LOW);
    led1Status^=1;
    delay(800);                // <<<<<<<<<<<<<<<< (*)
    digitalWrite(LED2, led1Status ? HIGH : LOW);
}


//second task
void flashLed2() {
    digitalWrite(LED2, led2Status ? HIGH : LOW);
    led2Status^=1;
}

tuxduino:
Comunque non vedi lampeggiare i led perché

' ' != 0

Ho convertito al volo, non ho controllato più di tanto. :sweat_smile:

Prova con questo:

Oggi lo farò.... ma tu: hai provato questo codice? :stuck_out_tongue:

Usando come pin per i led 11, 12, 13 a rotazione il led sul 13 lampeggia sempre. Oggi sperabilmente dovrei riuscire a provare collegando contemporaneamente tre led :sweat_smile:

@tuxduino
Allora.... ho fatto un po' di test.
Intanto le certezze: una ISR di default non è interrotta da un'altra ISR.
Ho provato il tuo sketch e vedevo anch'io lampeggiare i 3 led. Una cosa che ho notato, però, è stata che i led 7 e 8 lampeggiavano entrambi con una frequenza che era la metà di quella del led 9, quindi non solo il led 8 ma anche il led sul pin 7, quello pilotato da un task in cui c'era un delay(800). Un delay che avrebbe dovuto inserire una certa differenza e quindi togliere la sincronizzazione dei flash.

Ho perciò preso un semplice sketch:

#include <leOS.h>

leOS myOS;


//variables to control the LEDs
byte led1Status = 0;
const byte LED1 = 7;

void setup() {
    pinMode(LED1, OUTPUT);
    Serial.begin(19200);    
    myOS.begin();
    myOS.addTask(flashLed1, 500);
}


//main loop
void loop() {
    digitalWrite(LED1, led1Status ? HIGH : LOW);
    led1Status ^= 1;    
}


//first task
void flashLed1() {
    delay(10000);
}

Se il delay avesse funzionato nei task come tu affermavi accadeva, il led avrebbe lampeggiato regolarmente ogni secondo. Invece vedevo un velocissimo lampeggio.

Ho preso un altro sketch:

#include <leOS.h>

leOS myOS;


//variables to control the LEDs
byte led1Status = 0;
const byte LED1 = 7;

void setup() {
    pinMode(LED1, OUTPUT);
    Serial.begin(19200);    
    myOS.begin();
    myOS.addTask(flashLed1, 500);
}


//main loop
void loop() {
    digitalWrite(LED1, led1Status ? HIGH : LOW);
    led1Status ^= 1;    
}


//first task
void flashLed1() {
    Serial.println(micros(), DEC);
    delay(10000);
    Serial.println(micros(), DEC);
}

Sul monitor seriale ho ottenuto:

1000372
1000632
1500140
1500344
1999844
2000056
2499560
2499768
2999264
2999480
3498984
3499192
3998700
3998904
4498420
4498620
4998132
4998332
5497828
5498040

In teoria la cosa dovrebbe essere impossibile perché delay è una funzione che attende del tempo sfruttando un contatore interno:

void delay(unsigned long ms)
{
	uint16_t start = (uint16_t)micros();

	while (ms > 0) {
		if (((uint16_t)micros() - start) >= 1000) {
			ms--;
			start += 1000;
		}
	}
}

Come vedi, ogni 1000 microsecondi, viene sottratto 1 ms al valore passato.
E micros() legge direttamente dal contatore agganciato al timer 0 (ed il timer continua a funzionare, solo che non viene più chiamato il relativo interrupt). Quindi non passa dalla ISR che aggiorna invece il valore di millis(). Però ovviamente, essendo gli interrupt fermi, il valore di timer0_overflow_count non viene incrementato per cui il ritardo non è esattamente calcolato.
Che tu metta un delay(1000) o un delay(10000) il risultato è di pochissimo differente.
Usando millis, invece, il tutto "muore". Ecco uno sketch di esempio:

#include <leOS.h>

leOS myOS;


//variables to control the LEDs
byte led1Status = 0;
const byte LED1 = 7;

void setup() {
    pinMode(LED1, OUTPUT);
    Serial.begin(19200);    
    myOS.begin();
    myOS.addTask(flashLed1, 500);
}


//main loop
void loop() {
    digitalWrite(LED1, led1Status ? HIGH : LOW);
    led1Status ^= 1; 
}


//first task
void flashLed1() {
    unsigned long tempMillis = millis() + 10000;
    while (tempMillis > millis()); //morte cerebrale....
}

attenzione, non è che muore ma che millis() non cambia mai (loop infinito)

@leo, per la cronaca io affermavo proprio che la delay() sembrava non funzionare. Il tuo esempmio in cui l'unica funzione del task è chiamare una delay(1000) conferma quanto avevo osservato anch'io in precedenza.

Direi che hai spiegato perfettamente perché la delay() è "istantanea" (perdona la semplificazione... :slight_smile: ) quando il task è chiamato dalla timer2 isr.

Grazie.

Per ora mi sembra di aver capito. Casomai rileggo la tua risposta un paio di volte... :slight_smile:

lesto:
attenzione, non è che muore ma che millis() non cambia mai (loop infinito)

Per "muore" intendo che si freeza lì perché appunto la millis, non vedendo più il valore che restituisce cambiato, genera un loop infinito.

tuxduino:
@leo, per la cronaca io affermavo proprio che la delay() sembrava non funzionare. Il tuo esempmio in cui l'unica funzione del task è chiamare una delay(1000) conferma quanto avevo osservato anch'io in precedenza.

Ah, ok. Non ricordavo più l'oggetto della discussione :sweat_smile:

Direi che hai spiegato perfettamente perché la delay() è "istantanea" (perdona la semplificazione... :slight_smile: ) quando il task è chiamato dalla timer2 isr.

Grazie.

Per ora mi sembra di aver capito. Casomai rileggo la tua risposta un paio di volte... :slight_smile:

A tua disposizione :wink:

sucsate un'ultima domanda sul display lcd e poi finisco l'off topic: quando ho il buffer pronto ed è il momento di inviarlo all'lcd, è meglio tenermi anche una copia del buffer precedente, confrontarlo con l'attuale e inviare al display solo le differenze, o va bene fare un clear() dell'lcd e inviare il buffer intero?
in pratica non dovrebbe cambiare niente, ma la soluzione migliore qual'è?
se facessi clear() prima sarebbe più semplice...

tornando a leOS
a me serve che un task venga eseguito ogni 1000 millisecondi esatti, quindi uso leOS e non leOS2 perchè 1000 non è multiplo di 16.
se però volessi eseguire il task ogni 2000 millisecondi (multiplo di 16), il task verrebbe eseguito ogni 2 secondi esatti o ogni 2 secondi ± 16ms ?

meglio le differenze. Eviti sfarfallamenti

d407336:
sucsate un'ultima domanda sul display lcd e poi finisco l'off topic: quando ho il buffer pronto ed è il momento di inviarlo all'lcd, è meglio tenermi anche una copia del buffer precedente, confrontarlo con l'attuale e inviare al display solo le differenze, o va bene fare un clear() dell'lcd e inviare il buffer intero?
in pratica non dovrebbe cambiare niente, ma la soluzione migliore qual'è?
se facessi clear() prima sarebbe più semplice...

Non voglio andare contro lesto ma, secondo la mia modesta opinione, tenere 2 buffer in memoria, fare un confronto fra i 2 e spedire solo le differenze è un'inutile spreco di memoria (2 matrici) e di tempo (devi comunque confrontare tutte le celle dei 2 buffer per trovare i cambiamenti).
Tieni 1 solo buffer per volta e riscrivilo ogni xxx ms senza fare un clear() primo, in modo da: occupare meno memoria possibile; impiegare il minor tempo possibile per gestirlo; evitare sfarfallii dell'immagine (sovrascrivere i dati non crea sfarfallamenti).

tornando a leOS
a me serve che un task venga eseguito ogni 1000 millisecondi esatti, quindi uso leOS e non leOS2 perchè 1000 non è multiplo di 16.
se però volessi eseguire il task ogni 2000 millisecondi (multiplo di 16), il task verrebbe eseguito ogni 2 secondi esatti o ogni 2 secondi ± 16ms ?

Il leOS è la prima versione ed è stato scritto basandolo sui timer per cui si ottiene una discreta precisione, ma non una precisione assoluta. Ricordati che stai facendo i conti con un risonatore ceramico (quello dell'Arduino) che è più impreciso di un quarzo per cui anche se setti 1000 ms non avrai un intervallo accurato al millisecondo.
Il leOS2 è meno preciso perché si basa sul watchdog però, a differenza del leOS, non usa timer. Quindi ne va prima di tutto fatta una scelta in base alle proprie necessità. Che si scelga il leOS o il leOS2, poi, l'intervallo scelto è quello poi che effettivamente si avrà: questo vale sia per il leOS, che misura gli intervalli in ms, sia per il leOS2, che li misura in tick.