Go Down

Topic: leOS - un semplice OS per schedulare piccoli task (Read 45333 times) previous topic - next topic

tuxduino

Quote
Interrupt Service Routine, non Internet.


Forse in Arduino 3 o 4 ci sarà proprio una Internet service routine, nel senso che Internet sarà così pervasiva che le funzionalità di comunicazione di base relative ad Internet saranno gestite direttamente in hardware, un po' come i Timer adesso...  :smiley-eek:

leo72



Quest'ultimo caso è quello che ci interessa: creando una ISR (Internet Service Routine) che intercetta il reset, possiamo gestire lo scheduler dei processi da eseguire in automatico senza utilizzare timer del microcontrollore, evitando quindi che il nostro sketch vada in conflitto con altre librerie che anch'esse usano lo stesso timer.


Interrupt Service Routine, non Internet.  :smiley-mr-green:

Uh oh....  :smiley-sweat: :smiley-sweat:

m_ri

leo,lo so che sto andando un po' OT,ma vedo che ti sei studiato la cosa...la mia domanda è: se l'esecuzione è piantata nel codice richiamato da un interrupt,c'è modo invocare cmq l'interrupt associato al watchdog?o l'unica cosa che posso fare è reset?

PaoloP

Non ho capito la domanda.
Comunque Leo ha scritto che, a parte l'ATmega8 (quelli che usa Testato per intenderci), in tutti gli altri micro è possibile associare al "Cane da Guardia" il reset del micro, un interrupt (quindi una TUA funzione) oppure tutti e due. (questa ultima possibilità mi lascia perplesso  :smiley-roll-sweat:)

leo72

@m_ri:
vediamo se ho capito.
Abbiamo leOS2 in esecuzione. Esso lancia un task, all'interno del quale un utente inavvertitamente ha messo un delay(10) e la CPU resta bloccata in eterno nella ISR perché il delay in un interrupt è bloccante (non sto a spiegare i motivi, li sappiamo).
Il WDT continuerà a girare perché il contatore è HW quindi non è bloccato dal SW però la sua segnalazione dell'interrupt cadrà nel vuoto, dato che il micro sta già eseguendo l'interrupt associato all'overflow del contatore del WDT.

@Paolo:
il datasheet riporta le modalità selezionabili per l'overflow del WDT:
Quote

Mode - Action
Stopped - None
Interrupt Mode - Interrupt
System Reset Mode - Reset
Interrupt and System Reset Mode - Interrupt, then go to System Reset Mode
System Reset Mode - Reset**

**questa è l'unica possibile quando è attivo il fuse WDTON. Se il fuse è disattivato, si possono impostare via software tutte le altre.

m_ri

perfetto..in realtà no,perchè speravo di poter lanciare una routine prima di resettare il micro..grazie :)

leo72


perfetto..in realtà no,perchè speravo di poter lanciare una routine prima di resettare il micro..grazie :)

Secondo il datasheet...

Leggi:
Quote
Interrupt and System Reset Mode - Interrupt, then go to System Reset Mode

io leggo che si può: "Interrupt, e POI in reset"
Quindi lanci la routine e poi vai in reset. Non so però quanto può durare la routine, se cioè che un termine fissato entro il quale il chip viene resettato oppure se il WDT aspetta l'uscita dalla ISR e poi resetta.

m_ri

aspe..ma se avessi larduino piantato dentro un interrupt,NON posso invocare altri interrupt prima del reset(o meglio,lo invoco,ma lui non lo esegue finchè non si esce dall'interrupt corrente,il quale è piantato)..giusto?
e il reset verrebbe eseguito al termine dell'interrupt?

leo72

Per default, avr-gcc compila tutte le routine di reset "atomiche", quindi non possono essere interrotte

In questo caso non hai bisogno del leOS2, ma del watchdog puro.
Attivi il watchdog, se non resetti il suo contatore il watchdog ti resetta il micro.
Si può fare con il leOS1, puoi impostare il WatchDog in modo indipendente ad esempio per tempi lunghi (2/4/8 secondi) e poi metti un task che resetta il WDT un po' prima del reset.
Siccome il WDT è indipendente, se il suo contatore va in overflow esso resetta il micro indipendentemente da cosa il micro sta facendo (anche se è freezato in un loop infinito).

Questo sketch fa quel che dico e che mi pare stia dicendo tu:
Code: [Select]

#include <avr/wdt.h>
#include "leOS.h"

leOS myOS;
byte led1Status = 0;
const byte LED1 = 13;

//program setup
void setup() {
    wdt_disable();
    myOS.begin(); //initialize the scheduler
    pinMode(LED1, OUTPUT);
   
    //add the tasks at the scheduler
    myOS.addTask(flashLed1, 150);
    myOS.addTask(resetWdt, 3500);
    myOS.addTask(freeze, 3000);
    wdt_enable(WDTO_4S);
}


//main loop
void loop() {
}


//first task
void flashLed1() {
    led1Status^=1;
    digitalWrite(LED1, led1Status);
}


//second task
void resetWdt() {
    wdt_reset();
}


void freeze() {
    while(1) {}
}

Creo 3 task.
Il primo fa lampeggiare un LED, il secondo resetta il WDT ed il terzo freeza il micro.
Siccome il freeze arriva prima del reset del contatore del WDT, il WDT resetta il micro senza problemi.
Puoi verificare commentando la riga dove aggiungo il task che blocca tutto e vedere come il LED lampeggi senza interruzioni.

m_ri

non ero tatno interessato al nuovo leoS,ma alle tue conoscenze nel campo   :)..se imposto un wd con reset,questo mi resetta il micro anche se sono in un'istruzione atomica e fin qua son d'accordo...se però il wd è impostato sia x reset che x lanciare interrupt,cosa capita se il micro era freezato in un'istruzione atomica??mi fa cmq il reset,o aspetta l'esecuzione dell'interrupt invocato?

leo72

Allora, la situazione è ingarbugliata ed ho studiato 1 oretta per capirci qualcosa  :smiley-sweat:

Prendi questo codice e caricatelo sull'Arduino, mettendo 3 led sui pin D7, D8 e D9:

Code: [Select]

#include <avr/wdt.h>
#include "leOS.h"
leOS myOS;

const byte LED1 = 7;
byte statusLed1 = 1;
const byte LED2 = 8;
const byte LED3 = 9;

void setup() {
    //per sicurezza, SEMPRE disattivare il WDT appena avviato lo sketch
    MCUSR = 0;
    wdt_disable();
    myOS.begin();
    pinMode(LED1, OUTPUT);
    pinMode(LED2, OUTPUT);
    pinMode(LED3, OUTPUT);
    //avviso che sono partito
    for (byte i=0; i<2; i++) {
        digitalWrite(LED3, HIGH);
        delay(100);
        digitalWrite(LED3, LOW);
        delay(100);
    }
    myOS.addTask(resetWdt, 3500); //resetto il WDT
    myOS.addTask(blinkLed1, 250); //lampeggio un LED
    myOS.addTask(freezeMcu, 4500); //congelo l'MCU
    setWDT(); //imposto il WDT
}

void loop() {}

void resetWdt() {
    wdt_reset();
}

void blinkLed1() {
    digitalWrite(LED1, statusLed1);
    statusLed1 ^=1;
}

void freezeMcu() {
    //inverti i commenti sulle 2 righe sottostanti
    //while(1) {};
    myOS.pauseTask(resetWdt);
}

void setWDT() {
    //fermo il WDT
    MCUSR = 0;
    wdt_disable();
   //disabilito gli interrupt
    SREG &= ~(1<<SREG_I);
    WDTCSR |= ((1<<WDCE) | (1<<WDE));
    WDTCSR = ((1<<WDE) | (1<<WDP3) | (1<<WDIE));
    SREG |= (1<<SREG_I);
}

ISR(WDT_vect){
    digitalWrite(LED2, HIGH);
    delayMicroseconds(50000);
    digitalWrite(LED2, LOW);
    //WDTCSR |= (1<<WDIE); //decommenta e commenta
}

Questo codice attiva 3 task: un lampeggio di un LED, un reset del WDT ed un freeze.
Se freezo il micro con un ciclo infinito in un task, sia che reimposti il bit WDIE (quello che attiva l'interrupt sul WDT) sia che non lo faccia, il micro freezato non esegue l'ISR del WDT ma si resetta al timeout del watchdog.

Se invece blocco il reset del WDT, quindi non un freeze, se ho il bit WDIE reimpostato nella ISR, il micro esegue la ISR del WDT ma non resetta nulla. Se invece disattivo la reimpostazione del bit WDIE, accade una cosa buffa:
1) al timeout preimpostato, viene eseguita la ISR ma non viene resettato il micro
2) al successivo timeout NON viene eseguita la ISR ma viene subito resettato il micro.

Quest'ultima modalità, sul datasheet, è riportata come voluta ad esempio per far salvare la configurazione del microcontrollore se una routine ha freezato la CPU: eseguendo la ISR si può salvare dei dati prima del riavvio.

m_ri

guardiamo se ho capito bene:
-se il micro è freezato,allora il wd scatena solo il reset(anche se era nella modalità interrupt+reset)
-altrimenti se non è freezato,e se ho impostato il wd per lanciare interrupt+reset,al primo watchdog viene invocato l'interrupt..se poi voglio resettarlo,invoco un altro watchdog..

leo72

#297
Nov 14, 2012, 09:39 am Last Edit: Nov 14, 2012, 10:09 am by leo72 Reason: 1

guardiamo se ho capito bene:
-se il micro è freezato,allora il wd scatena solo il reset(anche se era nella modalità interrupt+reset)

Esattamente, perché una ISR non può interrompere un'altra ISR (a meno di non modificare le librerie Avr).

Quote

-altrimenti se non è freezato,e se ho impostato il wd per lanciare interrupt+reset,al primo watchdog viene invocato l'interrupt..se poi voglio resettarlo,invoco un altro watchdog..

Non esattamente. Il timer del WatchDog scorre sempre. La prima volta lui manda un segnale di interrupt: se il micro è freezato, ovviamente non può essere intercettato (caso precedente), altrimenti sì e quindi si può eseguire un compito. Poi il timer del WatchDog riparte, e la seconda volta spedisce un reset, e questo ovviamente funziona sempre  ;)

Con interrupt+reset attivi, la sequenza è:
1) interrupt
2) reset
Se si reimposta nella ISR il bit WDIE, al prossimo timeout del contatore del WDT il micro non resetta ma riesegue prima l'interrupt. Solo se il bit WDIE non viene reimpostato, al successivo timeout il WDT resetta il micro.

Secondo me è una caratteristica mooolto interessante, perché dà modo appunto di usare il watchdog normalmente ma anche di resettarlo se qualcosa lo pianta. Anzi, nella prox versione del leOS2 la implemento  ;)

EDIT:
no, perché il WDT nel leOS2 è impostato per girare su un tempo di 16 ms. Può capitare che un task possa durare più di 16 ms, ed avrei il reset al successivo timeout del WDT.

tuxduino

Leo, quindi nella modalità ISR+RESET è la ISR stessa a decidere se al "prossimo giro"(*) il micro verrà resettato o se verrà lanciata di nuovo la ISR, giusto ?

Se è così, allora sarebbe possibile usare la ISR per salvare informazioni di stato in caso di freeze anche se tale salvataggio comportasse un tempo "lungo" (ad esempio salvare su eeprom una serie di variabili o un array). Tipo:

wdt_isr():
    if l'operazione di salvataggio non è ancora stata avviata
        lancia l'operazione di salvataggio dati
        reimposta il bit WDIE
    else
        if il salvataggio dati NON è completo
            reimposta il bit WDIE
        else
            // il salvataggio è completo, non facciamo nulla e lasciamo scadere il wdt provocando il reset del micro
        endif
    endif


(*) cioè al prossimo scadere del wdt

lesto

no leo, vorrei analizzare meglio la situazione.

Se il watchdog lancia una funzione, che retta il watchdog e poi fa qualcosa. se dura più del prossimo interrupt watchdog, che non si resetta, mi aspetto che la funzione venga lanciata di nuovo, bloccando temporaneamente le prima esecuzione. Ovviamente se ci sono variabili globali (e se usi librerie tipo Serial, Wire, etc..) in uso nasce un casino della madonna, perchè potresti usare valori temporanei.
In oltre ogni richiamo è una chiamata a funzione in stack, il che può causare uno stack overflow. Notare che comunque la seconda chiama riazzera il watchdog, quindi nessun problema di restet

il problema sussiste se invece il secondo interrupt viene ignorato perchè la ISR è già in esecuzione. In tal caso hai ragione a eliminare il reset, ma sinceramente cercherei una soluzione migliore, per esempio forzare il richiamo della ISR, ma una variabile ti avvisa se c'è qualche funzione pendente in esecuzione. In tal caso resetti il timer ma NON lanci l'esecuzione del nuovo codice. Potresti usare un puntatore a funzione al posto della variabile, se è NULL allora fai partire la funzione richiesta, se non è null e la funzione schedulata è differente da quella in esecuzione (e in modalità DEBUG), allora lanci un alert per avvisare l'utente del ritardi di esecuzione del task a causa della sua funzione precedente troppo pesante, o perchè ha schedulato due task per avvenire assieme.

Seguendo questo principio della modalità DEBUG, puoi avvisare l'utente quando stai effettivamente lanciando una sua funzione, calcolando anche l'errore rispetto all'orario impostato.

bhe direi che per ora ti ho rotto le balle abbastanza  :smiley-mr-green:
sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

Go Up