Go Down

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

dasty

Ciao Leo

complimenti per l'ottima libreria


leggendo un po tutto il thread e le info postate da te ho visto che usi il timer2 per far funzionare la tua libreria,

io ho fatto un test ho utilizzato l'esempio lesos2task con i tre led a questo ho aggiunto un task che dovrebbe colleagarsi ad un sensore colleato attraverso il protocollo i2c e inviare il risultato sulla serieale.

lo sketch si carica ma si blocca subito al primo ciclo

mi chieso se pure l'I2C si basa sul timer2
io possiedo un arduino mega e mi chiedevo se potevo far funzionare il leos con il timer4 e constatare il funzionamento

inoltre non sarebbe carico far scegliere all'utente quali timer far utilizzare?!

che dritta potresti darmi per risolvere il mio problema :-) :-P
di sicuro sbaglio qualcosa....

ti ringrazio in anticipo e complimenti per il lavoro ben fatto

saluti

leo72


Ciao Leo

complimenti per l'ottima libreria


leggendo un po tutto il thread e le info postate da te ho visto che usi il timer2 per far funzionare la tua libreria,

La libreria si basa su un timer del microcontrollore. Sull'Atmega328 uso il timer 2 perché è un timer ad 8 bit e mi risulta facile gestirlo dato che si manipola 1 registro contatore da 8 bit al posto di quelli a 16 bit del timer 1. Il timer 0, anch'esso ad 8 bit, è usato dall'Arduino per la funzione millis().
Su altri microcontrollori uso i timer che sono disponibili.

Quote

io ho fatto un test ho utilizzato l'esempio lesos2task con i tre led a questo ho aggiunto un task che dovrebbe colleagarsi ad un sensore colleato attraverso il protocollo i2c e inviare il risultato sulla serieale.

lo sketch si carica ma si blocca subito al primo ciclo

mi chieso se pure l'I2C si basa sul timer2

L'I2C sul 328 e sul Mega2560 è integrato nell'hardware ma è gestito da un'interfaccia software basata sugli interrupt. Se tenti di leggere o spedire dati via I2C da dentro ad un task, è normale che si blocchi tutto perché ogni task viene eseguito all'interno dell'ISR (Internet Service Routine) che viene attivata dall'overflow del contatore del timer ed ogni ISR è atomica, ossia non  può essere interrotta da un'altra ISR. Parlo però per ipotesi perché senza analizzare il codice non so esattamente cosa stai facendo.

Quote

io possiedo un arduino mega e mi chiedevo se potevo far funzionare il leos con il timer4 e constatare il funzionamento

Sul Mega2560 uso il timer 2. Hai qualche particolare necessità per dover usare il timer 4?

Quote

inoltre non sarebbe carico far scegliere all'utente quali timer far utilizzare?!

Carina come idea ma non so se hai dato un'occhiata al file leos.cpp, nella parte finale. La funzione setTimer() imposta i timer e, come vedi, non è proprio stato semplice trovare i valori giusti per impostare tutti i registri per impostare i timer  :smiley-sweat:
Spostare poi lo scheduler su un altro timer cambia poco, sempre un timer perdi.

Quote

che dritta potresti darmi per risolvere il mio problema :-) :-P
di sicuro sbaglio qualcosa....

Beh, intanto posta il codice per capire dove sta il problema  ;)

Quote

ti ringrazio in anticipo e complimenti per il lavoro ben fatto

saluti


Grazie a te per l'interesse

dasty

ciao Leo


ecco il codice con cui stavo facendo i test

http://pastebin.com/7LX1dFDZ


thanks in anticipo

leo72

E' come pensavo. Hai messo l'uso dell'I2C all'interno di un task, è per questo che ti si blocca.
Anche la Serial non va bene gestirla in un task. In un task devono starci operazioni computazionalmente poco gravose o che utilizzino l'HW in maniera diretta.

Hai 2 strade:
1) rivedi il codice facendo in modo di lasciare al di fuori del 3° task tutto quello che utilizza interrupt, oppure
2) provi ad usare il looper: è uno schedulatore software. Puoi ovviamente intervallare l'esecuzione dei task anche se perdi in precisione assoluta nella loro esecuzione ma dentro ad un task puoi infilarci tutto quello che ti pare, dato che si tratta in pratica di una semplice funzione che richiama altre funzioni eseguita all'interno del loop principale.

dasty


E' come pensavo. Hai messo l'uso dell'I2C all'interno di un task, è per questo che ti si blocca.
Anche la Serial non va bene gestirla in un task. In un task devono starci operazioni computazionalmente poco gravose o che utilizzino l'HW in maniera diretta.


non ci sarebbe un modo per gestire I2C(e la Serial) in maniera diretta?

Quote

Hai 2 strade:
1) rivedi il codice facendo in modo di lasciare al di fuori del 3° task tutto quello che utilizza interrupt, oppure
2) provi ad usare il looper: è uno schedulatore software. Puoi ovviamente intervallare l'esecuzione dei task anche se perdi in precisione assoluta nella loro esecuzione ma dentro ad un task puoi infilarci tutto quello che ti pare, dato che si tratta in pratica di una semplice funzione che richiama altre funzioni eseguita all'interno del loop principale.


le due soluzioni che giustamente hai sottolineato le ho provate entrambe ed ovviamente funzionano

se dovessi optare per il looper non ci sarebbe un modo anche utilizzando un RTc esterno per migliorare la precisione dell'esecuzione dei task?

scusami per tutte queste domande :-P

grazie per le delucidazioni

lesto

forse si può usare in maniera diretta, ma bisogna modificare pesantemente le librerie per renderle non bloccanti. E' un lavoro con cui mi sono cimentato, funziona ma sarebbe da riscrivere per essere fatto apposta e non un brutale hack, devi sostituire la libreria originale Wire e sottocartella utils con questo codice https://github.com/lestofante/arduinoSketch/tree/master/asincI2Ctest2_asinc, trovi anche un esempio di utilizzo.
sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

leo72


non ci sarebbe un modo per gestire I2C(e la Serial) in maniera diretta?

Ti ha risposto lesto. Effettivamente ha modificato tempo fa la Wire per renderla non bloccante. Ma qui lascio a lui la parola perché non ho seguito il progetto.

Quote

se dovessi optare per il looper non ci sarebbe un modo anche utilizzando un RTc esterno per migliorare la precisione dell'esecuzione dei task?

Tutto dipende da cosa fa il tuo loop. Mi spiego: se programmi un intervallo di 500 ms tra un'esecuzione e l'altra, il task prende 50 ms ed il loop 100 ms, hai un tempo di esecuzione di 150 ms su un intervallo di 500 ms. Quindi sei a posto: il tuo task sarà sempre eseguito secondo quanto stabilito. Ma se mettiamo che tu abbia programmato un intervallo di 100 ms, allora il tuo task sarebbe eseguito sempre con 50 ms di ritardo. E' in questo che difetta il looper dal leOS.

Quote

scusami per tutte queste domande :-P

grazie per le delucidazioni

Figurati.

m_ri

non per sminuire il lavoro di leo(nell'80% dei casi va più che bene :D ),ma se hai esigenze particolari ti direi di cercare "freertos arduino"..è un SO minimale realtime per arduino..e direi che puoi farci cose avanzate..

leo72

E' vero, ci sono tanti RTOS in circolazione che funzionano molto meglio del leOS... ma a che prezzo? Avete dato un'occhiata alla complessità del codice necessario per fare un semplice blink, ad esempio con freeRTOS?

Posto il codice di UNOBlink:
Code: [Select]
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
////    main.c
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////

#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#include <avr/io.h>

/* Scheduler include files. */
#include <FreeRTOS.h>
#include <task.h>
#include <queue.h>
#include <semphr.h>

/* serial interface include file. */
#include <lib_serial.h>

/* LCD (Freetronics 16x2) interface include file. */
#include <hd44780.h>


/*-----------------------------------------------------------*/
/* Create a handle for the serial port. */
xComPortHandle xSerialPort;

static void TaskBlinkRedLED(void *pvParameters); // Main Arduino Mega 2560, Freetronics EtherMega (Red) LED Blink

static void TaskBlinkGreenLED(void *pvParameters); // Main Arduino Uno 328p (Green) LED Blink
/*-----------------------------------------------------------*/

/* Main program loop */
int16_t main(void) __attribute__((OS_main));

int16_t main(void)
{

    // turn on the serial port for debugging or for other USART reasons.
xSerialPort = xSerialPortInitMinimal( 115200, portSERIAL_BUFFER, portSERIAL_BUFFER); //  serial port: WantedBaud, TxQueueLength, RxQueueLength (8n1)

avrSerialPrint_P(PSTR("\r\n\n\nHello World!\r\n")); // Ok, so we're alive...

    xTaskCreate(
TaskBlinkRedLED
,  (const signed portCHAR *)"RedLED" // Main Arduino Mega 2560, Freetronics EtherMega (Red) LED Blink
,  256 // Tested 9 free @ 208
,  NULL
,  3
,  NULL ); // */

    xTaskCreate(
TaskBlinkGreenLED
,  (const signed portCHAR *)"GreenLED" // Main Arduino Uno 328p (Green) LED Blink
,  256 // Tested 9 free @ 208
,  NULL
,  3
,  NULL ); // */


avrSerialPrintf_P(PSTR("\r\n\nFree Heap Size: %u\r\n"),xPortGetFreeHeapSize() ); // needs heap_1 or heap_2 for this function to succeed.

vTaskStartScheduler();

avrSerialPrint_P(PSTR("\r\n\n\nGoodbye... no space for idle task!\r\n")); // Doh, so we're dead...

}

/*-----------------------------------------------------------*/


static void TaskBlinkRedLED(void *pvParameters) // Main Red LED Flash
{
    (void) pvParameters;;
    portTickType xLastWakeTime;
/* The xLastWakeTime variable needs to be initialised with the current tick
count.  Note that this is the only time we access this variable.  From this
point on xLastWakeTime is managed automatically by the vTaskDelayUntil()
API function. */
xLastWakeTime = xTaskGetTickCount();

int8_t i;
uint8_t j;

lcd_Init();

DDRB |= _BV(DDB7);

    while(1)
    {

    PORTB |=  _BV(PORTB7);       // main (red IO_B7) LED on. EtherMega LED on
vTaskDelayUntil( &xLastWakeTime, ( 10 / portTICK_RATE_MS ) );

lcd_Locate (0, 0);
lcd_Printf_P(PSTR("HighWater @ %u"), uxTaskGetStackHighWaterMark(NULL));
lcd_Cursor (1);

#if _USE_FUEL
lcd_Locate (1, 0);
lcd_PutFuel (--i, 0);
if (i < 0) i = 6;
#endif

#if _USE_BAR
lcd_Locate (1, 2);
lcd_PutBar (j++, 14, 2);
#endif

PORTB &= ~_BV(PORTB7);       // main (red IO_B7) LED off. EtherMega LED off
vTaskDelayUntil( &xLastWakeTime, ( 40 / portTICK_RATE_MS ) );

// xSerialPrintf_P(PSTR("RedLED HighWater @ %u\r\n"), uxTaskGetStackHighWaterMark(NULL));
    }

}

/*-----------------------------------------------------------*/
static void TaskBlinkGreenLED(void *pvParameters) // Main Green LED Flash
{
    (void) pvParameters;;
    portTickType xLastWakeTime;
/* The xLastWakeTime variable needs to be initialised with the current tick
count.  Note that this is the only time we access this variable.  From this
point on xLastWakeTime is managed automatically by the vTaskDelayUntil()
API function. */
xLastWakeTime = xTaskGetTickCount();

DDRB |= _BV(DDB5);

    while(1)
    {
    PORTB |=  _BV(PORTB5);       // main (red PB5) LED on. Arduino LED on
vTaskDelayUntil( &xLastWakeTime, ( 100 / portTICK_RATE_MS ) );

PORTB &= ~_BV(PORTB5);       // main (red PB5) LED off. Arduino LED off
vTaskDelayUntil( &xLastWakeTime, ( 400 / portTICK_RATE_MS ) );

xSerialPrintf_P(PSTR("GreenLED HighWater @ %u\r\n"), uxTaskGetStackHighWaterMark(NULL));
    }
}
/*-----------------------------------------------------------*/



Questo è il codice di leOS_BlinkWithoutMillis:
Code: [Select]
//include the OS
#include "leOS.h"

leOS myOS; //create a new istance of the class leOS

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

//program setup
void setup() {
    myOS.begin(); //initialize the scheduler
    pinMode(LED1, OUTPUT);
   
    //add the tasks at the scheduler
    myOS.addTask(flashLed1, 1000);
}


//main loop
void loop() {
    //empty
}


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


leOS è limitato ma è semplicissimo da usare.

m_ri

#234
Sep 18, 2012, 07:35 pm Last Edit: Sep 18, 2012, 07:43 pm by m_ri Reason: 1
son d'accordo che non è facilissimo da implementare..anche se per la maggior parte dei casi basta un po' di copia e incolla..

dipende dalle tue necessità: se hai bisogno di avere tempi precisi con funzioni che usano interrupt,puoi o usare leOS + wire non bloccante(xò se tu volessi anche usare la seriale?),o RTOS..
altrimenti usi il looper,è semplice da usare,non ha problemi con interrupt,ma non garantisce tempi brevi..

leo72

In tanti anni che programmo ho imparato una cosa, che cioè il risultato ottimale è quello che ha il miglior rapporto prezzo/prestazioni.
Fare un codice semplicistico comporta un costo in termini di impegno di progettazione veramente minimo ma, per contro, hai prestazioni anch'esse minime. L'altra faccia della medaglia è un codice esageratamente ricercato, con un costo di progettazione molto elevato e prestazioni anch'esse al top. Alla fine, il risultato finale è identico: ognuno dei due programmi eseguirà il compito assegnato.

Vale la pena usare freeRTOS o FemtoOS per modificare un paio di porte in background? Non credo proprio. Vale la pena usare il leOS per gestire un multitasking preemptivo? Non credo proprio. Come dici giustamente tu, ognuno deve scegliere gli strumenti pensando al miglior rapporto per il proprio progetto.

dasty

ragazzi scusate la mia assenza, dopo diversi test ecco i primi risultati:

ho utilizzato le librerie:
1. leos
2. asincI2Ctest2_asinc (non bloccante)

devo dire che questa accoppiata funziona egregiamente.

La semplicità della realizzazzione dei task di leo, e l'utilizzo asincrono della wire di lesto
possono dare nuovi spunti a chi vuole realizzare sitemi anche complessi.

In questi giorni ho avuto modo di studiare e testare anche i sistemi RTOS (chibios e freertos), questi ultimi nonostante le loro caratteristiche importanti che si basano sui sistemi operativi real time, non risultano essere semplici nell'utilizzo, inoltre come accennato anche da leo ad esempio per scrivere un banale blick_led bisogna scrivere ben 4 volte il numero di righi di codice rispetto all'esempio di leos.

Certo ancora manca da implementare semafori, code ed altre logiche... ecc ecc
A mio avviso già questo è un passo avanti
ed il lavoro di leos non ha nulla da invidiare ai sistemi RTOS

questo è il mio umile giudizio

leo72


Certo ancora manca da implementare semafori, code ed altre logiche... ecc ecc
A mio avviso già questo è un passo avanti
ed il lavoro di leos non ha nulla da invidiare ai sistemi RTOS

questo è il mio umile giudizio

:* Grasssie

PS:
sto usando il leOS su un Attiny85, è un progettino a cui sto lavorando da 2 giorni a questa parte (ecco perché sono un po' latitante, oltre agli impegni di lavoro): 3 task che fermo e riavvio, con intervalli differenti, abbinati alla gestione di un segnale PWM, a 2 letture analogiche e sleep/risveglio. E funziona tutto perfettamente. Siccome i 3 task dovevano girare "intrecciati" tra di loro e con il resto del codice, l'uso del leOS mi ha semplificato non poco la stesura del codice.

tuxduino

Quote
Certo ancora manca da implementare semafori, code ed altre logiche... ecc ecc


Leo, correggimi se sbaglio ma penso che leOS difficilmente otterrà questo tipo di servizi, perché lo porterebbero alla fine ad essere molto simile ad un "vero" rtos, togliendogli la semplicità e la "snellezza" che lo contraddistinguono ora.

Per essere un po' più costruttivo, dico che la comunicazione inter-thread è facilmente ottenibile con l'uso di variabili globali e la keyword "volatile". Non che ciò sostituisca mutex, code e quant'altro, ma credo che sia sufficiente per l'ambito di utilizzo cui leOS si riferisce.

(disclaimer: finora ho fatto pochi test per confermare la mia precedente affermazione, anche se sono stati tutti positivi).

m_ri

i semafori non sarebbero molto complessi da fare: quando voglio testarlo o cambiare il valore,disabilito temporaneamente gli interrupt(ovviamente da ripristinare dopo)..al posto di usare il wait che blocca la funzione corrente,fare un wait a cui passare la funzione contenente la sezione critica..

Go Up