Task leOS, strano problema.

Ciao a tutti, sto utilizzando la libreria leOS per eseguire questo semplice task ogni x secondi (ancora devo decidere).
La funzione è questa:

void printInfo()
{
for(int i=0; i<4; i++)
{
lcd.setCursor(0,0);
lcd.print(i);
Serial.println(i);
delay(1000);
}   
}

Il problema è che non segue il delay di 1 secondo, è normale che leOS non possa fare questo?

Infatti da seriale si vede velocemente che scorrono le cifre..

Consigli?

Grazie

Non può perché il delay è una funzione che ferma l'esecuzione del codice per il numero di ms specificato. Per funzionare usa leggere i millisecondi da millis, ma questa funzione è basata su un contatore che viene incrementato da un interrupt agganciato all'overflow del timer 0.
Il leOS usa uno schedulatore inserito in un altro timer e viene richiamato all'interno di un interrupt. All'interno di un interrupt, vengono bloccati tutti gli altri interrupt, per cui il delay non funzionerà e la tua funzione congelerà il microcontrollore.
E poi perché devi mettere un'attesa di 1 secondo all'interno di una funzione? Stai usando male il leOS. Spiega cose devi fare che troviamo la soluzione più adatta.

leo72:
Non può perché il delay è una funzione che ferma l'esecuzione del codice per il numero di ms specificato. Per funzionare usa leggere i millisecondi da millis, ma questa funzione è basata su un contatore che viene incrementato da un interrupt agganciato all'overflow del timer 0.
Il leOS usa uno schedulatore inserito in un altro timer e viene richiamato all'interno di un interrupt. All'interno di un interrupt, vengono bloccati tutti gli altri interrupt, per cui il delay non funzionerà e la tua funzione congelerà il microcontrollore.
E poi perché devi mettere un'attesa di 1 secondo all'interno di una funzione? Stai usando male il leOS. Spiega cose devi fare che troviamo la soluzione più adatta.

Ok, ora mi è chiaro perchè il delay non funziona.
Cerco di riassumere un po la situzione anche se non ho il codice sotto mano ora.

Nel loop ho due principali funzioni

leggiPulsanti();
navigaMenu();

leggiPulsanti mi restituisce un'intero.
navigaMenu è utilizzata dalla libreria menubeckend.

Navigo senza problemi nel menu, ho utilizzato 4 tasti totali (destra, sinistra, enter ed esc) e riesco tranquillamente ad accedere a menu e sottomenu e, nel caso utilizzare un menu(aumentare/diminuire setting ed altro)...
Il problema ora mi è sorto nel menu chiamato "info".

(Non ho detto che sto utilizzando un display 16x2 gestito tramite liquidcrystal.h)

In questo menu io vorrei che una volta avuto accesso, del testo scorresse, in modo verticale.

Ecco la fonte del problema, il testo che scorre in verticale, io ho pensato di risovlerlo così:

void stampaInfo()
{
char* testo[]={"riga1", "riga2", "riga3", "rirga4"}

for(int i=0; i<4; i++)
{
lcd.setCursor(0,0);
lcd.print(testo[i]);
lcd.setCursor(0,1);
lcd.print(testo[i+1]);

delay(1000); //questo è il delay che mi serve prima che il testo scorra verso sopra
lcd.clear()
} 
}

Ora, questo è il codice che viene eseguito se io entro nel menu info, il problema sta proprio nel delay che ferma il codice e non mi permette di poter controllare la pressione eventuale del tasto ESC (che mi permetterebbe di uscire dal menu info e andare al menu principale).
(In realta premendo ripentutamente il tasto ESC alla fine ci riesco anche, ma è poco user friendly xD )

Ho pensato subito a leOS!:slight_smile: E le soluzioni (da me pensate all'inzio) erano 2:

Implemento leOS ogni tot ms per leggere i tasti e tolgo quella funzione dal loop(): Sarebbe l'ideale ma siccome per ora la funzione che legge i tasti mi restiutisce un'intero non mi andava di modificarla.

Ho pensato allora di fare il contrario(il mio problema): Al setup inizializzo il tastk di leOS per la stampa del testo e lo metto in pausa(non lo avvio subito).
Quando premo enter per entrare nel menu info attivo il task..
A questo punto lui doveva farmi la funzione con il delay che genere il problema
(nel frattempo la lettura dei pulsanti è sempre attiva)..
..e quindi se premo ESC torno al menu principale e rimetto in pausa il tastk leOS visto che non mi serve..

Da qui il problema, leOS non può fare il delay..

Spero di aver spiegato + o - bene ma purtroppo senza codice davanti è difficile..:confused:

Grazie!

O sono io duro oppure il problema è troppo semplice.
Vediamo se ho capito: tu vuoi far eseguire al leOS un task con un intervallo di 1 secondo.
Soluzione: myOS.addTask(stampaInfo, 1000);
Devi semplicemente aggiungere il task con l'intervallo desiderato. Sarà poi lo scheduler a richiamarlo 1 volta al secondo, non serve mettere un delay di 1 secondo nel task.

Era questo?

Mmm no, il delay che io metto serve per dare il tempo all'utente di leggere la scritta che scorre, visto che nel modo che l'ho strutturata dovrebbe funzionare così:

Le stringe che abbiamo: "Riga1" , "Riga2" , "Riga3" , "Riga 4"

quindi parte la funzione e scrive sul display 16x2:

Riga1
Riga2
delay(quello che provoca il danno xD )
Riga2
Riga3
delay(quello che provoca il danno xD )
Riga3
Riga4

Il delay quindi che sta nel ciclo for invece serve per dare quei (es: 600 ms) di tempo a chi legge le info prima che il testo scorra verso sopra.

Allora fai semplicemente un task differente. Invece del for, usa un incremento del contatore e ad ogni chiamata del task visualizzi una riga differente, ed hai risolto.

E cioè io creo il task e gli metto come intervallo il tempo di scrolling che voglio una riga e l'altra.
premo enter di invio e avvio il task.

fuori abbiamo le stringhe e il contatore:
char* testo[]={"riga1", "riga2", "riga3", "rirga4"}
int contatore = 0;

void stampaInfo()
{
lcd.setCursor(0,0);
lcd.print(testo[contatore ]);
lcd.setCursor(0,1);
lcd.print(testo[contatore +1]);
delay(1000); //questo è il delay che mi serve prima che il testo scorra verso sopra

//qui faccio un controllo sul contatore
if (contatore>NUM_RIGHE)
{
contatore=0;
}
else
{
contatore++;
}

lcd.clear()
}

Non so se intendi una cosa del genere.. ma a primo impatto forse può funzionare, purtroppo non riesco a provare ho scritto ste righe al volo..

Sì, esatto (togliendo ovviamente quel delay).

Uh è vero! I copia-incolla sono e saranno una rovina..:slight_smile:

Nik_90:

void stampaInfo()

{
lcd.setCursor(0,0);
lcd.print(testo[contatore ]);
lcd.setCursor(0,1);
lcd.print(testo[contatore +1]);
delay(1000); //questo è il delay che mi serve prima che il testo scorra verso sopra

//qui faccio un controllo sul contatore
if (contatore>NUM_RIGHE)
{
contatore=0;
}
else
{
contatore++;
}

lcd.clear()
}




Non so se intendi una cosa del genere.. ma a primo impatto forse può funzionare, purtroppo non riesco a provare ho scritto ste righe al volo..

Controlla bene, questo codice non è corretto.
Rivedi la condizione per azzerare il contatore e la scrittura della seconda riga. (possibilità di index out of bound)

Cosi come lo hai scritto tu esplode! :grin:

Boh forse stai vedendo qualcosa che io ancora non vedo, come ho detto ho scirtto quelle righe al volo quindi di sicuro qualche errore ce, per sicurezza comunque ho rifatto il tutto in C.
Cercando di essere il più fedele possibile a quello che avevo scritto io è uscito questo:

#include <stdio.h>
#define NUM_RIGHE 4

int main()
{
    int contatore = 0;
    char testo[] = "1234";
    int i = 1;
    while (i == 1)
    {
          
          printf("Contatore: %i \n\n\n\n", contatore);
          printf("Riga A: %c \n" , testo[contatore]);
          printf("Riga B: %c \n" , testo[contatore+1]);
          
          if(contatore >= (NUM_RIGHE-1)) //per far vedere 4 righe ho bisogno di 4 cicli
          {
                       contatore = 0;
                       }
                       
                       else
                       {
          contatore++;
          }
          getchar();
          }
    
    
    
    }

Questo riproduce lo scorrere del display di un testo di 4 righe.
L'unico errore che ho potuto notare ora rispetto allo sketch di prima è appunto la condizione di controllo del contatore che da:

if (contatore>NUM_RIGHE)

è passata a:

(contatore >= (NUM_RIGHE-1))

Ma questo almeno in C non provoca nessun errore se non il fatto di avere un paio di cicli in più per azzerare il contatore.

E' questo giusto? Con arduino mi provocherebbe un index out of bound no?
Vabè ripeto, domani che rientro potrò provare nel caso..:slight_smile:

Grazie

Mi sembra strano che non ti dia errore, visto che tu hai un array da 4 elementi e ad un certo punto chiedi di visualizzare il quinto... però te lo lascio con riserva.(forse semplicemente perchè in C gli array sono comunque puntatori e nel tuo caso andresti a puntare fuori dallo spazio assegnato, che in questo caso è vuoto, ma se ci fosse già stato allocato qualcos'altro potresti avere un risultato inatteso)

Cosi come hai modificato va bene e ti esclude dal modificare anche ala seconda riga da visualizzare.
Praticamente come hai fatto tu vedrai l'ultima riga da visualizzare sempre nella seconda riga del display, non avrai mai la condizione in cui l'ultima riga da visualizzare passa sulla prima riga del display e la seconda viene visualizzata vuota (che potrebbe essere graficamente più accattivante).

Per quanto riguarda la condizione , un semplice 'contatore = NUM_RIGHE-1' non ti piaceva ? :grin:
Essendo un ciclo incrementale unitario non avrai mai la possibilità di andare oltre.

niki77:
Praticamente come hai fatto tu vedrai l'ultima riga da visualizzare sempre nella seconda riga del display, non avrai mai la condizione in cui l'ultima riga da visualizzare passa sulla prima riga del display e la seconda viene visualizzata vuota (che potrebbe essere graficamente più accattivante).

O.o Hai ragione, questo non l'avevo notato! No io voglio la grafica accattivamente 8) anche questa cosa è da modificare, l'ultima riga da visualizzare deve spostarsi sulla prima riga del display.. Ci vorrebbe un'altra condizione nella srittura della seconda riga.. domani che torno vediamo che succede..

niki77:
Mi sembra strano che non ti dia errore, visto che tu hai un array da 4 elementi e ad un certo punto chiedi di visualizzare il quinto... però te lo lascio con riserva.(forse semplicemente perchè in C gli array sono comunque puntatori e nel tuo caso andresti a puntare fuori dallo spazio assegnato, che in questo caso è vuoto, ma se ci fosse già stato allocato qualcos'altro potresti avere un risultato inatteso)

hai azzeccato, nessun errore, ma vai a giocare con un area di memoria in cui potrebbe esserci qualsiasi cosa. è il principio degli attachi overflow; sfruttando errori simili vai a ignettare codice o modificare variabili (per esempio la password)

Il C e il C++ non effettuano alcun controllo, e i SO controllano solo che non esci dall'area di memoria assegnata al processo/tread.

Voto anch'io per
if (contatore == (NUM_RIGHE-1))

Tornando all'errore.

L'unico errore che ho potuto notare ora rispetto allo sketch di prima è appunto la condizione di controllo del contatore che da:

if (contatore>NUM_RIGHE)

è passata a:

(contatore >= (NUM_RIGHE-1))

Secondo me il primo dà errore per questo motivo: una stringa char è composta sempre da un elemento in più, il terminatore "\0".
Quindi quando contatore è uguale a NUM_RIGHE, si va a leggere il 5° elemento della stringa, cioè l'elemento testo[4], che è appunto \0. E non dà giustamente errore. Però lui ha provato il programma sul computer, e come dice giustamente lesto, lì c'è un SO che controlla che il programma "stia nel suo", ossia che non esca dalla memoria che deve usare. Quando appunto contatore è uguale NUM_RIGHE, viene ancora incrementato, andando ad assumere il valore di 5. Adesso punterebbe al 6° elemento, che è al di fuori dell'array. Ed il SO giustamente s'inca@@a, mentre se la stessa operazione l'avesse eseguita sul 328, l'indice sarebbe semplicemente uscito al di fuori della stringa, andando a pescare a caso nella SRAM del microcontrollore.

leo72:
....una stringa char è composta ...

A parte il fatto che non è una stringa ma un array di char , l'array di char non dovrebbe avere terminatore!

Uh, è vero. E' un array.

no, il SO non si incazza se sfori un array, ma se sfori in un area di memoria non dedicata al tuo programma (il SO ti da un bel blocco all'inizio, per evitare che tu gli rompa le balle spesso)

io credo che l'errore sia quì racchiuso:

printf("Riga A: %c \n" , testo[contatore]);
printf("Riga B: %c \n" , testo[contatore+1]);

abbiamo conatore che va da 0 a 3 (giusto), ma la riga B la andiamo a prendere dall'elemento conatatore+1, quindi di fatto usiamo il range da 1 a 4 (sbagliato)

ora, se ci va di culo e dalla locazione di memoria 4 in poi troviamo relativamente presto uno \0 visualizziamo garbage. Ora, è poco probabile trovare uno \0 lì per caso, considerando che abbiamo un range di 256 valori (probabilità di 1/256). quindi probabilmente in un atmega sfondi la ram e chissà cosa succede.

Su un PC, oltre ad avere un sacco di ram in più (non parlo della ram totale, ma di quella assegnata al programma), hai anche il controllo del SO, quindi se nella ram assegnata non trovi lo \0 ci pensa il SO a mandarti in crash.

@niki77 & leo: gli array di char sono dette stringhe, da non confondere con le String che invece sono un oggetto. per riprova, esiste una libreria ansi C, la string.h, che lavora sugli array di char.
e no, gli array di char SONO terminati a \0. Anche la classe Stringa al suo interni usa un array di char terminato con \0. solo che non te lo fa vedere.

Si, mi sa che ha ragione lesto...
O meglio, non lo so, ma ho scritto due righe di codice che danno un risultato interessante...

#include <stdio.h>

int main()
{
    char testo[] = "1234";
	char testo1[] = "1234";

	char * ptr = testo;
	char * ptr1 = testo1;

	ptr+=4;

	*ptr = 'A';

	printf("testo: %s \n", testo);

	ptr1+=5;

	*ptr1 = 'A';

    printf("testo1: %s \n", testo1);

	getchar();


}

il risultato a schermo è il seguente :

testo: 1234A> "
testo1: 1234

Questo non mi garantisce che il compilatore riservi in coda a testo e testo1 un \0 , ma mi garantisce che printf e surrogati quando si chiede la rappresentazione stringa di un char[] la ritiene terminata con il \0
Nel primo esempio avendo fatto una manovra catso modificando a mano l'indirizzo del puntatore ho praticamente sovrascritto il teorico \0 facendo si che richiedendo poi la visualizzazione a schermo di quella variabile in formato stringa , essa non si sia limitata a 1234 come da dichiarazione, e nemmeno ad 1234A , ma bensi si sia spinta fino a trovare nuovamente un \0

:roll_eyes:
P.S. sto cominciando a nutrire dubbi sull'utilità di questi esperimenti :stuck_out_tongue_closed_eyes:

RI P.S. ora ho la certezza che dichiarando un char[] in quella maniera viene riservato il \0 in coda:

int main()
{
    char testo[] = "1234";

	printf("testo: %s \n", testo);
	printf("length: %u \n", sizeof(testo));

	getchar();


}

Per l'utilità dei test ho ancora dubbi ... :disappointed_relieved:

quando usi " " il compilatore aggiunge sempre /0 riservando il giusto spazio.

ciò non avviene con lo ' ', che però non salva una stringa ma un carattere solo (se ci metti più caratteri CREDO che li sommi)

quindi il test 1 potevi farlo anche:

char testo3[4];
char prova4 = 'p'; //se siamo fortunati prova4 si trova nella ram subito dopo test3, quindi possiamo verificare l'overflow!

println("%c\n", prova4 );

testo3[0] = '1';
testo3[1] = '2';
testo3[2] = '3';
testo3[3] = '4';
//andiamo un pò nel regno dell'overflow
testo3[4] = '0';

println("%c\n", prova4 ); //venghino siori e siore, prova4 modificata senza trucco e senza inganno!

testo3[10] = '\0';//assicuriamoci di non andare in overflow di ram va.. forziamo il tappo a 10 caratteri

println("%s", testo3); //quì stampa 1234 e del garbage! se la ram èp stata assegnata contigua, il primo carattere di garbage è prova4!

il secondo test invece non ha senso; sizeof(testo) ti ritorna il numero di byte usati dal PUNTATORE testo, non la dimensione della stringa / array; in c non esiste alcun metodo per conoscere la dimensione di un array, a meno che te stesso non ne tenga conto o con un contatore, o con un carattere di tappo (tipo \0)