leOS - un semplice OS per schedulare piccoli task

Che il wdt_enable(WDTO_4S); non funzionasse sulla mega già lo avevi affrontato ti ricordi di questa discussione?
Arduino Forum, magari c'e' qualche link che può servirti, sembrerebbe che bisogna caricare un altro bootloader
Comunque da quando leggo in giro, nessuno è riuscito a fare sto benedetto reset usando WDT sul 2560, senza toccare il bootloader

ciao

pablos:
Che il wdt_enable(WDTO_4S); non funzionasse sulla mega già lo avevi affrontato ti ricordi di questa discussione?
Arduino Forum

Non mi ricordavo di questo bug. Non avendo la MEGA, era una problematica affrontata in quella discussione e poi scordata del tutto.

magari c'e' qualche link che può servirti, sembrerebbe che bisogna caricare un altro bootloader
Comunque da quando leggo in giro, nessuno è riuscito a fare sto benedetto reset usando WDT sul 2560, senza toccare il bootloader

Sì, esatto. Ma di questo me n'ero accorto anch'io. Ti avevo infatti già detto che una delle differenze del bootloader della MEGA rispetto all'Optiboot stesse proprio nel fatto che l'Optiboot resetta il Watchdog di suo perché poi lo usa come timeout.
Ad abbondanza, ho trovato anche un'altra discussione su un altro forum in cui si parla di un problema identico al tuo:
http://www.electroyou.it/phpBB2/viewtopic.php?f=17&t=32610
Usando una 2009 la scheda non si resetta ma va in blocco proprio quando dovrebbe venir resettata dal WDT. Questo mi fa pensare che questa caratteristica non possa essere usata su chip dove c'è un bootloader che non prevede la disabilitazione del watchdog al suo riavvio. Per contro, un chip standalone senza bootloader non soffre di questi problemi (vedi il mio test con un Tiny85) né l'Arduino UNO né la LEONARDO per via del fatto che i loro bootloader resettano il watchdog.

Quindi con la MEGA devo per forza usare il jmp $0000 al posto del watchdog per resettare la scheda.

Prova la versione allegata.
E' la 2.1.3. Ho inserito un check per cui se trova un certo #define, sa di avere a che fare con una Arduino MEGA e dovrebbe usare il jmp $0000 al posto del reset HW. Non è un reset HW , quindi al micro non viene mandato il segnale di reset ma viene fatta ripartire l'esecuzione del programma in memoria (bootloader compreso).
Per far ciò inserisci questa riga:

#define ARDUINO_MEGA

prima dell'inclusione della leOS2.

Quindi lo sketch di test "leOS2_reset_after_timeout" deve iniziare così:

#define ARDUINO_MEGA
#include "leOS2.h" //include the scheduler
leOS2 myOS; //create a new istance

Fammi sapere.

leOS2.cpp (10.8 KB)

Ho cambiato la CPP e aggiunto la riga #define ARDUINO_MEGA, compila, tutto ok ma mi addolora dirtelo si inchioda ancora dopo il 3 lampeggio. :sweat_smile:

ho visto che hai aggiunto questo

#ifdef ARDUINO_MEGA
asm volatile ("jmp 0x0000");

ma con il jump a 0x0000 dici al micro di ricaricare il boot dalla posiz di memoria 0?

ciao

pablos:
ma con il jump a 0x0000 dici al micro di ricaricare il boot dalla posiz di memoria 0?

Dico al micro di riavviare il programma. Siccome nella locazione $0000 c'è il jump all'inizio del codice eseguibile (allo sketch se non c'è il bootloader, a quest'ultimo se invece esso è presente) in pratica dovrei riavviare l'esecuzione del firmware. Se mi dici che non va, sono ad un vicolo cieco.... 8)

Mi puoi fare un ultimo tentativo col file .cpp allegato?

leOS2.cpp (10.9 KB)

Fatto, mi spiace, non va neanche questo. Meno male che la manovra di emergenza va quasi sempre alla prima. :~

Lascia stare Leo anche se ormai mi sa che per te è diventata una questione personale e di completezza :). Purtroppo non so come aiutarti non conosco i micro così nel dettaglio.

ciao

PS.

Ho cercato un po' qua e la "mega 2560 WDTO reset" e sono capiato qui http://www.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=7318&start=0

in fondo c'e' un pezzetto di sketch

e ho messo su questo velocemente che funziona ... fa il reset della Mega 2560 quando chiamo la reset(), non credo proprio faccia il controllo del blocco tramite watchdog.
non è la stessa cosa di quella che hai fatto tu, ma potrebbero esserci dati che possono indirizzarti o adattarlo alla tua lib, ad esempio perchè usa 0x000000 invece di 0x0000 non so se cambia molto

oppure cos'è quell EIND=0 (che ho provato a levarlo e resetta lo stesso :slight_smile: )

//#include <io.h>
const byte LED = 40; 

void setup() {
    delay(1000);
    Serial.begin(9600);
    Serial.println("Sono nel void setup");
    pinMode(LED, OUTPUT); //pin as output  
    blink3times();   
}

void blink3times() {
    for (byte i=0; i<3; i++) 
    {
        digitalWrite(LED, HIGH);
        delay(250);
        digitalWrite(LED, LOW);
        delay(250);
        Serial.println((String)i);
    }
      reset();
}

void loop() {
}

void reset( void )
{
  Serial.println("Sono nel void reset");
  delay(500);
  //EIND = 0;
  ((void (*)())0x000000)();
  Serial.println("Non dovrebbe stampare niente"); //infatti di qui non ci passa .. non stampa
}

wow FUNZIONA!!
Ho pasticciato un po' la tua lib cercando quale riga dava noia, nostante abbia messo #define ARDUINO_MEGA nello sketch ho come l'impressione che passasse lo stesso da quel #else che è messo per gli altri arduini, mi si inchiodava sempre. Però e tardino, ci ripasso domani.

//reset the MCU
void leOS2::reset(void) {

	SREG &= ~(1<<SREG_I);
	//wdt_reset(); //<<<<<<<<<<<<<<<<<<<<QUESTA lo fa schiantare
    wdt_disable();
    
    //for the Arduino MEGA 2560 we must use a software reset
    //because its bootloader leads to a board freeze!
//#ifdef ARDUINO_MEGA
	__asm__ __volatile__ ("jmp 0x000000"); //<<<<<<<<<<<<<<< ho aggiunto 2 zeri
  
//#else
    //wdt_enable(WDTO_1S);
    //SREG |= (1<<SREG_I);
    //while(1){}; //wait for reset
//#endif
}

fa 3 lampeggi, attende 10 secondi e si resetta!!!! E' quello che volevi no?
Ma questo reset lo dovrebbe fare anche se si inchioda l'MCU per i fatti suoi usando l'interrupt del watchdog ?

Riciao

leo72:

pablos:
ma con il jump a 0x0000 dici al micro di ricaricare il boot dalla posiz di memoria 0?

Dico al micro di riavviare il programma. Siccome nella locazione $0000 c'è il jump all'inizio del codice eseguibile (allo sketch se non c'è il bootloader, a quest'ultimo se invece esso è presente) in pratica dovrei riavviare l'esecuzione del firmware. Se mi dici che non va, sono ad un vicolo cieco.... 8)

dovrebbe essere corretto, ma se è una limitazione del boot-loader la vedo grigia, e mi sembra strano che non sia stato patchato (però comunque significa che in molti casi va aggiornato il boot-loader)

lesto:

leo72:

pablos:
ma con il jump a 0x0000 dici al micro di ricaricare il boot dalla posiz di memoria 0?

Dico al micro di riavviare il programma. Siccome nella locazione $0000 c'è il jump all'inizio del codice eseguibile (allo sketch se non c'è il bootloader, a quest'ultimo se invece esso è presente) in pratica dovrei riavviare l'esecuzione del firmware. Se mi dici che non va, sono ad un vicolo cieco.... 8)

dovrebbe essere corretto, ma se è una limitazione del boot-loader la vedo grigia, e mi sembra strano che non sia stato patchato (però comunque significa che in molti casi va aggiornato il boot-loader)

Il problema è certamente del bootloader. Quello della MEGA è l'unico del "vecchio tipo", per capirsi della famiglia della 2009. Da quando è arrivato l'Optiboot le cose sono cambiate (in meglio) ed il watchdog è resettato nel bootloader. Il problema è che il bootloader della MEGA emula un programmatore (l'STK500v2) differente da quello emulato dall'Optiboot (STK500v1); inoltre è molto grande ed emula anche un terminale (se non ho capito male) in cui puoi entrarci mandando 3 caratteri "!" quando si avvia. Credo che alla fine lo abbiano tenuto per una questione di retrocompatibilità ma forse sarebbe il caso, prima o poi, di aggiornarlo.

Tornando alla leOS2, la soluzione certa è la modifica del bootloader: ma è un'operazione che non voglio far fare perché forse l'1% degli utenti è disposto/capace a metter mani ad una cosa del genere.

@pablos:
la soluzione che hai messo tu è abbastanza semplice. Si tratta di creare un puntatore ad una funzione con indirizzo $0000. Chiamandola altro non fa il compilatore che creare un jump alla prima locazione di memoria. Devo ora capire perché $0000 non funziona e $000000 sì.

EIND è l'Extended Indirect Register, un registro usato in combinazione con altri 2 per i salti indiretti.

Ok. Vediamo se così ci siamo.
In allegato la 2.2.0. Ho inserito il define all'interno del file .h per cui con Arduino MEGA devi decommentare la riga 56.
A questo punto, viene usato un cold reset, ossia un reset software. Per far sì che sia un buon reset, ho messo anche la reimpostazione di tutte le porte del microcontrollore a tri-state, ossia la condizione delle porte che si ha quando si resetta effettivamente la scheda. In questo modo se abbiamo qualcosa pilotato dalla MEGA, esso verrà disattivo proprio come se si resettasse fisicamente la scheda. Le uniche cose che non tocco sono i timer, perché quelli vengono reimpostati dal core di Arduino, e la disattivazione di eventuali altre periferiche accese dall'utente ma non gestite dal core di Arduino.

Ah, pablos devi sostituire entrambi i file perché, come detto, ho modificato anche l'header della libreria.

Se così funziona, aggiorno anche la documentazione e rilascio questa versione pubblicamente anche sul mio sito.

leOS2-2.2.0.zip (4.79 KB)

@pablos:
la soluzione che hai messo tu è abbastanza semplice. Si tratta di creare un puntatore ad una funzione con indirizzo $0000. Chiamandola altro non fa il compilatore che creare un jump alla prima locazione di memoria. Devo ora capire perché $0000 non funziona e $000000 sì.

EIND è l'Extended Indirect Register, un registro usato in combinazione con altri 2 per i salti indiretti.

Io ho anche specificato che a prescindere dal jump questa riga nella tua lib
//wdt_reset(); //<<<<<<<<<<<<<<<<<<<<QUESTA lo fa schiantare ancora prima di arrivare al ("jmp 0x000000"); e non è nemmo una questione di tempi secondi o ms, si blocca prima di arrivarci.

Però non hai risposto alla mia domanda, questo salto glielo fa fare il programma sulla flash o no? l'interrupt che viene sollevato centra ancora qualcosa?

pablos:
Io ho anche spocificato che a prescindere dal jump questa riga nella tua lib
//wdt_reset(); //<<<<<<<<<<<<<<<<<<<<QUESTA lo fa schiantare ancora prima di arrivare al ("jmp 0x000000"); e non è nemmo una questione di tempi secondi o ms, si blocca prima di arrivarci.

Questa riga serve per resettare il contatore del WDT. Diciamo che era una precauzione in più per disattivare completamente il watchdog. La sequenza completa era:

  1. disattivazione globale di tutti gli interrupt (in modo da non avere nulla che bloccasse il resto del codice)
  2. reset del timer del Watchdog
  3. disattivazione del Watchdog

Dopo la tua segnalazione, ho tolto dalla lib "wdt_reset", che è l'istruzione che resettava il timer.

Però non hai risposto alla mia domanda, questo salto glielo fa fare il programma sulla flash o no?

A questo avevo risposto giorni fa:

leo72:

pablos:
Ok, ho in test uno sketch, appena termina carico il tuo.

Io lo avevo caricato per curiosità e ho notato il blocco, per la verità non so nemmeno a cosa serva XD XD
Un po' come con le cose nuove, si spacchettano, si attacca la spina, si premono un po' di bottoni e poi si leggono le istruzioni :wink:

ciao

E' una caratteristica secondo me molto interessante, che si può anche utilizzare in altri ambiti, ossia anche come semplice funzione anti-freeze (se uno non ha nessun task da far eseguire in background).

Nella sua essenza originale tale funzione serve a controllare se un task si sia bloccato. Ogni volta che il watchdog va in overflow viene lanciato un segnale di reset. La ruotine che intercetta il reset controlla se c'è un task in esecuzione. Se c'è, decrementa un contatore che rappresenta il timeout passato dall'utente in fase di setup. Se tale timeout va a zero prima che il task termini la sua esecuzione, significa che si è bloccato ed ha conseguentemente bloccato tutto il micro. A questo punto, il watchdog lancia un reset e pone fine al problema :wink:

Però possiamo usarla anche come antiblocco del codice principale. Prendiamo ad esempio un task lanciato ogni secondo che controlla se una certa variabile di sistema è su un determinato valore. Se la trova ad esempio su false, la rimette a true ed esce mentre se la trova a true resetta il micro.
Nel loop principale l'utente la imposta a false ad ogni ciclo. Va da sé che se il loop si blocca, tale variabile non può essere impostata a false. Quindi il task di controllo, vedendola a true, resetta il micro.

Niente non va più, ho provato mettendo i define, togliendoli, usando 0x0000 opp 0x000000, lo stacca attacca resetta, incrocia le braccia mouse-reset mi ha sfinito :smiley:

ma asm volatile ("jmp 0x0000"); non dovrebbe resettare anche la tua scheda se nel tuo sketch metti define ....MEGA? potresti provare se l'instradamento è corretto e le definizioni pure

Comunque la spiegazione che avevi dato

E' una caratteristica secondo me molto interessante, che si può anche utilizzare in altri ambiti, ossia anche come semplice funzione anti-freeze (se uno non ha nessun task da far eseguire in background).

l'avevo capita.
In qualche posto avevi spiegato che esiste un timer autonomo indipendente dai timer che controllano l'esecuzione dello sketch (se uso qualche termine scorretto chiedo venia), quindi se questo timer non viene azzerato in tempo va in overflow inviando un reset alla baracca.

Ora quello che non mi è chiaro è: se il controllo lo fai via software tramite librerie e si inchioda il software come può essere eseguito un reset. Da qualche parte attivi il controllo del timer "se TIMER WTD va in overflow" fammi un reset?

Quindi quello che ti ho chiesto prima è : "questo salto glielo fa fare il programma sulla flash tramite jump o lo fa il sistema autonomo?"
se usiamo un jump 0x0000 che è un comando software (soggetto a schianto e quindi non eseguito in caso di crash) perchè lo usiamo?

Non so se mi sono capito :astonished:

Inoltre visto che io non posso provarlo, tu hai mai provato a prendere un buffer e riempirlo di byte fino a quando l'MCU non è più in grado di gestirlo saturando la memoria, si dovrebbe inchiodare tutto, lo fa il reset?

Hai provato a simulare un problema di comunicazione sull ICSP di solito si inchioda, lo fa il reset?

pablos:
Niente non va più, ho provato mettendo i define, togliendoli, usando 0x0000 opp 0x000000, lo stacca attacca resetta, incrocia le braccia mouse-reset mi ha sfinito :smiley:

Ti faccio una domanda. Hai scaricato uno zip oppure 2 file .cpp e .h singoli?
Perché ti chiedo questo? Perché avevo messo i 2 file modificati ma poi mi ero accorto di non aver messo un'istruzione ed ho rimodificato la lib. La nuova versione è contenuta in uno zip.

ma asm volatile ("jmp 0x0000"); non dovrebbe resettare anche la tua scheda se nel tuo sketch metti define ....MEGA? potresti provare se l'instradamento è corretto e le definizioni pure

L'instradamento è corretto e jmp 0x0000 resetta il micro della mia Arduino. Il test l'avevo già fatto prima di pubblicare la nuova versione. Ho poi messo infatti anche l'azzeramento dello stato dei registri per i motivi che ti ho spiegato.
Però attendo che tu mi confermi se hai usato la lib zippata oppure no.

Ora quello che non mi è chiaro è: se il controllo lo fai via software tramite librerie e si inchioda il software come può essere eseguito un reset. Da qualche parte attivi il controllo del timer "se TIMER WTD va in overflow" fammi un reset?

Ok. Stiamo parlando di uno scheduler. Lo scheduler viene chiamato 1 volta ogni 16 ms circa e controlla se c'è un task da eseguire. Se c'è, lo lancia. Se invece c'è un task attivo, ne controlla lo stato. Se si accorge che un task è bloccato, inizia il conto alla rovescia di una variabile. Il valore di questa variabile è il timeout scelto dall'utente. Se il task non restituisce il controllo allo scheduler, si è piantato tutto per cui viene resettato il micro.
Come ti ho spiegato, puoi anche controllare tramite un task che il codice principale "giri" mettendo un flag. Se il flag resta su un dato valore per troppo tempo, il loop si è inchiodato.

Quindi quello che ti ho chiesto prima è : "questo salto glielo fa fare il programma sulla flash tramite jump o lo fa il sistema autonomo?"
se usiamo un jump 0x0000 che è un comando software (soggetto a schianto e quindi non eseguito in caso di crash) perchè lo usiamo?

Non so se mi sono capito :astonished:

Il jmp $0000 viene chiamato dallo scheduler in questa versione modificata. Nella leOS2 originale, invece, il reset hardware viene generato dal circuito del watchdog, che mette a LOW la linea di reset del microcontrollore. In questo caso si ha una vera reinizializzazione del micro, con il jmp $0000 si ha solo una ripartenza dall'inizio del programma.

sisi avevo preso lo zip al « Reply #420 on: Today at 11:17:48 AM »

Facciamo così ... appena mi è possibile prendo un altro arduino e cambio il bootloader

pablos:
sisi avevo preso lo zip al « Reply #420 on: Today at 11:17:48 AM »

OK

Facciamo così ... appena mi è possibile prendo un altro arduino e cambio il bootloader

Ultima prova. Sostituisci nel #define RESET_MCU che c'è all'inizio del file header l'istruzione asm volatile ("jump 0x000000");
con quella che avevi postato tu: ((void (*)())0x000000)();

@pablos:
hai fatto poi questo test? :sweat_smile:

No Leo, lo faccio domani promesso :slight_smile:

ciao

Ok.

Eccomi, non è che mi ricordo molto di quello che avevo fatto...

Allora ricapitoliamo, ho preso l'ultimo zip e sostituito i 2 file nella cartella Leos2-2.2.0.

le linee originali leOS2.h

#define RESET_MCU SREG |= (1<<SREG_I);\
				  wdt_disable();\
				  DDRA = 0; DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0;\
				  DDRG = 0; DDRH = 0; DDRJ = 0; DDRK = 0; DDRL = 0; PORTA = 0;\
				  PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;\
				  PORTG = 0; PORTH = 0; PORTJ = 0; PORTK = 0; PORTL = 0;\
				  EIND = 0;\
				  __asm__ __volatile__ ("jmp 0x0000");
#else

deve diventare così.. giusto?

#define RESET_MCU SREG |= (1<<SREG_I);\
				  wdt_disable();\
				  DDRA = 0; DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0;\
				  DDRG = 0; DDRH = 0; DDRJ = 0; DDRK = 0; DDRL = 0; PORTA = 0;\
				  PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;\
				  PORTG = 0; PORTH = 0; PORTJ = 0; PORTK = 0; PORTL = 0;\
				  EIND = 0;\
          
				  ((void (*)())0x000000)();   
                                  //__asm__ __volatile__ ("jmp 0x0000");
#else

così si inchioda ancora =(