Newsgroup: it.hobby.elettronica.digitale
Titolo: Esperienze con RTOS e dspic?

Qualcuno di voi ha avuto a che fare con RTOS su dspic?
In particolare... qualcuno ha provato FreeRTOS (
www.frertos.org
)?
Funziona bene?
Grazie

 Funziona bene?
Io ho provato FreeRTOS, ma non sui dspic. Però mi astengo dal fare 
commenti in quanto sono di parte :-)
Ciao

 qualcuno ha provato FreeRTOS (
www.frertos.org
)? Funziona bene?
Non ho esperienza diretta, ma rilancio una domanda: a cosa serve un 
sistema operativo realtime su dei micro di fascia bassa? Nelle 
applicazioni che ho sviluppato per lavoro fin'ora è sempre stato 
sufficiente mettere le cose lente in una lista di callback, chiamate una 
alla volta ad ogni giro di main, e le cose da fare in tempo sempre in una 
lista però chiamate tramite interrupt a tempo... Al massimo job slicing, 
ma quando proprio si vuole essere rapidi... Per di più, molti fra i 
sistemi operativi free non hanno la preemption, quindi tanto vale...
Quando avete avuto un bisogno assoluto di un sistema realtime? 

Quando avete avuto un bisogno assoluto di un sistema realtime? 
Mai, però fa più fico parlare di os real-time   ;-)
--
ciao
  Stefano

 Quando avete avuto un bisogno assoluto di un sistema realtime? 
Questa è una domanda difficile, perchè è la stessa cosa che chiedere:
"Quando avete avuto un bisogno assoluto di programmare in C rispetto 
all'assembly?". Quando l'applicazione diventa complessa e quando si 
hanno risorse conviene usare il C invece dell'assembly con tutti i 
vantaggi del caso. Ecco, per me vale la stessa cosa per gli RTOS, solo 
su un livello diverso :-)
Ciao

Francesco Sacchi ha scritto:

Bè... non c'è che dire la risposta mi sembra più che esauriente!!!
Sapresti consigliarmi qualche buon testo per approfondire sullo sviluppo 
di software embedded ed in particolare di micro kernel? Io mi trovo a 
lavorare con un dsPic della famiglia dspic30... credo che, da quello che 
ho capito,  convenga orientarmi verso un kernel giusto!?!?!
Grazie

 ho capito,  convenga orientarmi verso un kernel giusto!?!?!
Direi che ci puoi iniziare a pensare, i dspic30 cominciano a essere 
della dimensione giusta. Poi come dicevo dipende dalla complessità della 
tua applicazione. Se scegli questa strada ti consiglio comunque di 
partire da roba già fatta opensource. FreeRTOS è il più usato, funziona, 
ma a me non piace.
Come design è per esempio carino NutOS 
(
http://www.ethernut.de/en/software/index.html
) ma che io sappia non ha 
il port per i dspic.
Magari per prendere ispirazione puoi dare un'occhiata anche al nostro 
(
www.bertos.org
) che però attualmente non ha un port per dspic.
Ciao

 quando sono bloccati dall'I/O. 
Puoi fare un esempio pratico?

 gestisce il display arriva a durare qualche centinaio di millisecondi.
Quello che porti come esempio per me è il classico caso per cui vale la 
pena di fare job slicing. Associ ad un timer la procedura che aggiorna il 
display una variabile alla volta. Il tempo di esecuzione nel caso 
peggiore passa dalle centinaia di millisecondi alle decine, e tutto il 
software diventa più snello. 
Tanto, l'occhio non ha grandi richieste di velocità, anzi: ho sempre 
notato che aggiornando a raffica un display non si capisce più niente 
(soprattutto se la grandezza è affetta da rumore). Per migliorare si può 
mettere un filtrino numerico, ma allora che senso ha visualizzare per 
cento volte lo stesso valore? Tanto vale visualizzare un valore ogni 
cento e non sprecare tempo fra filtri e redraw...
Se si inizia a fare preemption per stare dietro all'IO c'è qualcosa che 
inizia a scricchiolare :-P

 su un livello diverso :-)
Pensandoci, per me non è tanto questione di complessità del progetto (uno 
scheduler statico toglie tutti i problemi delle priorità e dei conflitti 
e se è fatto bene è molto più realtime e snello, imho), quanto di 
usabilità.
Mi viene da dire che un kernel serio inizi a diventare utile quando chi 
sviluppa l'applicazione finale non è lo stesso che ha sviluppato il 
dispositivo. Io non sento il bisogno di avere un kernel per i dispositivi 
che progetto, perchè già uno scheduler in C mi sembra talmente di alto 
livello che mi annoia. Un mio collega, che vuole scrivere un'altra 
applicazione per lo stesso dispositivo, all'inizio impazzisce, perché 
deve imparare a conoscere bene i vincoli hardware (e software), e deve 
prepararsi a lottare con il watchdog... Se avesse a disposizione un 
kernel minimamente serio sotto, potrebbe creare il suo task, metterlo in 
lista e fine.


--
ciao
  Stefano

 Puoi fare un esempio pratico?
Ti faccio vedere il nostro driver dell'ADC per gli AVR.
Ho tagliato alcune parti in modo da rendere chiari i concetti, il 
driver completo può essere trovato qui:
http://www.bertos.org/browser/trunk/cpu/avr/drv/adc_avr.c
Questa è la funzione che legge il canale correntemente selezionato 
dall'adc.
uint16_t adc_hw_read(void)
{
	// Ensure another convertion is not running.
	ASSERT(!(ADCSRA & BV(ADSC)));
	// Start convertion
	ADCSRA |= BV(ADSC);
	// Ensure IRQs enabled.
	ASSERT(IRQ_ENABLED());
	adc_process = proc_current();
	sig_wait(SIG_ADC_COMPLETE);
	return(ADC);
}
Come vedi, subito dopo aver dato il via alla conversione il processo 
corrente viene salvato in una variabile globale del modulo stesso e 
poi ci si mette in attesa con la funziona sig_wait fino all'arrivo del 
segnale SIG_ADC_COMPLETE. La funziona sig_wait() non fa altro che 
togliere il processo corrente dalla ready list e chiamare lo 
scheduler. Il processo corrente non verrà più rieseguito fino a che 
l'ADC non finisce il suo lavoro generando l'interrupt.
Ed ecco come è fatta l'ISR collegata al segnale dell'ADC:
ISR(ADC_vect)
{
	sig_signal(adc_process, SIG_ADC_COMPLETE);
}
La funzione sig_signal() non fa altro che inviare al processo 
adc_process (quello precendentemente salvato) il segnale che aspettava 
(SIG_ADC_COMPLETE). Questo fa si che esso venga reinserito nella lista 
dei processi attivi.
Come vedi tutto è molto semplice, ci sono pochissme righe di codice, 
poco spreco di memoria e grandi vantaggi.
Per questo cerco di usare il kernel quando è possibile :-)
Ciao

 livello che mi annoia.
Bhè, il tuo scheduler se non è un kernel poco ci manca. Come dicevo 
prima quando il resto del mondo dice che usa un RTOS fa in realtà 
quello che stai già facendo tu.
Quindi siamo perfettamente d'accordo, il tuo micro kernel è quello che 
per me rappresenta la comodità e facilità di sviluppo e che cerco di 
usare quando possibile.
Anche a me le cose di alto livello annoiano. Pensavo che fare un 
kernel fosse complicato e che si perdesse il contatto con l'hardware.
Dopo essermi sporcato un po' le mani devo dire che non è così: le 
soluzioni adottate in questo ambito devono essere veramente furbe se 
non vuoi correre il rischio di creare una cosa lenta e sprecona.
E questo porta tutta una serie di sfide che ho scoperto essere davvero 
divertenti da affrontare.
Ciao

 inizia a scricchiolare :-P
Ok, allora la soluzione è un'altra.

 aggiornamento.
E' un classico T6963C. Prima di poter scrivere nella RAM video bisogna 
verificare che il controller sia libero controllando il suo stato.
1. Invia il comando di leggi stato
2. Leggi lo stato
3. Lo stato indica che il controller è libero?
4. Se sì vai avanti, altrimenti torna ad 1.
5. Scrive il dato nella VRAM
Come vedi si crea un loop (passi 1-2-3) la cui durata è dettata dal 
controller ed ho visto che, in alcuni casi, è abbastanza lento.
Tenendo presente, poi, che le mie librerie di scrittura scrivono font 
grafici pixel per pixel (la scrittura di ogni pixel è una scrittura come 
sopra), ti lascio immaginare i tempi di attesa.
La mia routine di gestione del display che rinfresca tutte le variabili 
a video può durare centinaia di ms.

 poco spreco di memoria e grandi vantaggi.
Ok, grazie per le delucidazioni. Prima o poi mi deciderò a studiare 
meglio la possibilità di usare anche io uno scheduler, anche piccolo, 
per gestire i processi in modo più semplice.
Ad oggi sono riuscito a fare senza, ma ho il sospetto che potrei perdere 
parecchio tempo in meno con uno scheduler durante lo sviluppo e, 
soprattutto, durante la manutenzione del software.

 divertenti da affrontare.
mi aggancio qui per chiedere/chiederti una cosa (sono nuovo con gli OS su uC 
ma con i PIC e altri uC me la cavo bene):
ho un mio lettore mp3 fatto con il classico schema: uC+DSP+IDE
ora, per imparare a gestire un OS su uC, vorrei riscrivere il tutto ed usare 
appunto un RTOS per poi, in futuro, scrivere il mio kernel+scheduler
quello che non mi č chiaro č: un task che legge dalla CF o dall'hard-disk 
come puň essere interotto da un altro task e poi riprendere a fare quello 
che stava facendo prima??? se il task che interrompe va a leggere dati dalla 
periferica ide poi quando viene ripreso il task originale chi riscive tutti 
i registri ide nel modo corretto? non č un spreco di tempo?
probabilmente sbaglio ottica di approccio ma accetto consigli
grazie!
-ice- 

 i registri ide nel modo corretto? non è un spreco di tempo?
Vediamo se ho capito.
Hai il task1 che usa una periferica. Durante l'attesa dell'I/O della 
periferica il task1 viene schedulato e viene passato il controllo al 
task2 che anche lui vuole usare la stessa periferica?
In questo caso, siccome la periferica è una sola e i task che vogliono 
accederci sono 2 o più bisogna che si sincronizzino. Il modo più 
semplice è usare un semaforo o mutex.
Il task1, prima di accedere all'ide, acquisisce il semaforo associato.
Poi viene schedulato perchè è in attesa. Viene risvegliato il task2 
che però, quando prova a prendere il semaforo, lo trova già occupato e 
viene schedulato anche lui.
In pratica si forma una coda di task, ognuno aspetta il suo turno per 
poter usare il disco.
Se invece non intendevi questo ti prego di riformulare la domanda.
Ciao

 poter usare il disco.
interessante soluzione... ricodo di aver studiato queste cose quando ho 
preparato l'esame di sistemi-operativi (appunto) ma mai avrei creduto di 
poter appplicare ai micro questi concetti
non vedo l'ora di poter fare qualche prova, mi incurioscise molto questo 
argomento!
per caso il tuo rtos non ha il port per pic18?
grazie!
-ice- 

 per caso il tuo rtos non ha il port per pic18?
Il nostro (non l'ho fatto solo io :-) ) RTOS supporta pienamente solo 
ARM e AVR per adesso. Ogni contributo è comunque più che ben accetto!
Se ti va puoi dare un'occhiata al sito e iscriverti alla mailing list.
Ciao

 Se ti va puoi dare un'occhiata al sito e iscriverti alla mailing list.
ti ringrazio per i consigli!
-ice- 

a video può durare centinaia di ms.
Ci credo, ci credo.
--
ciao
  Stefano

 dello Z80), posso vedere che il tempo di inattività supera il 50%.
Che intendi per tempo di inattività? Inattività di cosa? Del controller 
oppure del tuo micro?
Io non ho tempi di inattività perchè nel ciclo principale richiamo in 
continuazione il refresh del display.

continuazione il refresh del display.
Io lo chiamo solo quando ho effettivamente qualcosa da scrivere.
Ma sono io che cerco sempre soluzioni estreme per tirare fuori il meglio
dall'hardware che ho.
Se sei soddisfatto del tuo modo e funziona, non devi cambiarlo.
--
ciao
  Stefano

 Se sei soddisfatto del tuo modo e funziona, non devi cambiarlo.
In realtà non sono molto soddisfatto proprio per il fatto che la
seriale
risponde piuttosto lentamente per colpa del display che impiega un
sacco di tempo.
Visto che ci sei, altra domanda sul T6963C. Come colleghi il suo
reset,
normalmente disponibile sul connettore del display, al resto del
circuito.
Ero abituato a collegarlo direttamente a Vcc, ma ho avuto problemi su
alcuni display: all'accensione (1 volta su 100) il controller sembrava
non
funzionare, come se non si fosse resettato. Poi ho collegato il reset
ad un supervisore dell'alimentazione e il problema sembra essere
scomparso. Poi ho visto che sul datasheet consigliano di mettere
un condensatore e allora ho avuto la conferma del problema.

un condensatore e allora ho avuto la conferma del problema.
--
ciao
  Stefano

 
http://www.onsemi.com/pub_link/Collateral/MC34064-D.PDF
Ed hai fatto benissimo... è la soluzione che ho adottato io ora... 
troppo tardi :(

Salve, scusate l'intrusione ma vorrei sfruttare questo post per avere 
alcune delucidazioni inerenti a questo argomento e dsPic.
1) Come viene implementato, a grandi linee, un semplice scheduler?
2) Non è uno spreco di tempo ogni volta che si deve fare un context 
switch? Si può ovviare? Come?
2) Un semplicissimo kernel è formato dal solo scheduler? Cos'altro?
3) Il vantaggio di avere un kernel + scheduler rispetto ad un 
programma 		fatto su misura? Semplifica solo la lettura e la scrittura 
del codice?
Spero di non essere stato troppo esigente nelle domande... ma il fatto è 
che non ho molto chiaro questo aspetto della programmazione e non riesco 
a trovare info alla portata del mio inglese!
Grazie per l'aiuto.

 a trovare info alla portata del mio inglese!
Purtroppo l'inglese in queste cose è fondamentale, prima lo impari 
meglio è.
Comunque per ogni eventuale dubbio il ng è qui :-)
Ciao

 compagnia.
Ok, mi hai tirato in causa ed eccomi qui... :)
Stavo rileggendo la tua frase scritta qualche post fa:
 > Caso 2: il display *non* può generare un interrupt quando è pronto.
 > Questa soluzione è più brutta e più sprecosa ma se l'hardware non è
 > fatto a modo non ci possiamo fare nulla.
 > Devi continuamente pollare in spin il tuo flag, ma tra un poll e il
 > successivo, puoi switchare di contesto. Oppure se sai quanto più o
 > meno devi attendere puoi chiamare un delay che però usa il timer nel
 > modo indicato nel caso 1.
Quindi supponiamo di avere uno scheduler da richiamare con la funzione 
schedule(). Supponiamo che la mia funzione di scrittura di un carattere 
8x10 sul display sia (molto grossolanamente, forse c'è qualche errore 
sui contatori):
---
void
lcd_writechar( unsigned short x,
                unsigned short y,
                unsigned char glyph[10][8] )
{
x=x0;
y=y0;
for( row=0; row<10; row++ ) {
   for( col=0; col<8; col++ )
     lcd_bitset( x, y++, glyph[row,col] );
   x++;
   y=y0;
}
---
La funzione lcd_bitset() è, come ti dicevo, bloccante. Nel senso che 
tenta di scrivere il pixel fino a quando il controller non si libera. 
Qualcosa del genere:
---
void
lcd_bitset( unsigned short x,
             unsigned short y,
             unsigned short bit )
{
   unsigned char b;
   lcd_setpointer( x, y );
   b = lcd_readbyte();
   <modifica del byte b in base alle coordinate (x,y) e bit>
   while( !lcd_writebyte(b) )
     ;
}
---
Come puoi vedere la fregatura è il loop while (la lcd_writebyte() 
ritorna 0 se il controller è occupato e non ha potuto effettivamente 
scrivere il byte voluto).
Avendo a disposizione uno scheduler, come si scriverebbero queste due 
funzioni (lcd_bitset() e lcd_writechar()) in modo da non spinnare con il 
ciclo while(), ma permettere di ripartire dallo stesso punto quando il 
controllo ritorna al processo che gestisce il display?

 controllo ritorna al processo che gestisce il display?
Siccome non siamo nel caso 2 la soluzione è un po' meno efficiente, ma 
in ogni caso, supponendo di avere schedule() è sufficiente chiamarla 
quando stai spinnando.
Basta modificare solo la funzione lcd_bitset() in questo modo:
void
lcd_bitset( unsigned short x,
             unsigned short y,
             unsigned short bit )
{
   unsigned char b;
   lcd_setpointer( x, y );
   b = lcd_readbyte();
   <modifica del byte b in base alle coordinate (x,y) e bit>
   while( !lcd_writebyte(b) )
     schedule();
}
Sì, è una sola riga di codice in più :-)
La schedule si occupa di salvare il contesto attuale, switchare di 
processo, e quando tocca di nuovi all'lcd, di ripristinare il contesto 
attuale.
Ciao

 Siccome non siamo nel caso 2 ...
Errata, volevo dire:
Siccome *siamo* nel caso 2...
Ciao

quando stai spinnando.
E' una cosa simile, se vuoi piů professionale ma naturalmente a prezzo di piů
risorse impiegate, a quello che faccio io quando durante la scrittura se il
controller č busy vado a testare i flags e se necessario faccio altre
operazioni.
Il nocciolo del discorso č che deve comunque sbloccare il loop sul display, se
non lo fa ma resta sempre inchiodato lě, il problema non si risolve.
--
ciao
  Stefano

 attuale.
Spettacolo, non pensavo fosse così semplice!
Quindi la funzione schedule() serve un altro processo se pronto, 
salvando il contesto della lcd_bitset().
Immagino che la funzione schedule() non possa essere scritta 
semplicemente in C. Che tipo di operazioni vanno fatte sullo stack per 
il salvataggio del contesto del processo che vuole andare in attesa?
Viene salvato l'intero stack o solo stack pointer relativo a quel processo?
Esiste qualche semplice implementazione di scheduler cooperative da 
poter studiare?

 non lo fa ma resta sempre inchiodato lì, il problema non si risolve.
Decisamente è questo il punto. Però sbloccare il ciclo semplicemente 
ritornando dalla funzione, mi obbliga a fare una sorta di salvataggio 
del contesto all'interno dello funzione, gestendo una specie di macchina 
a stati mediante variabili statiche.
Avendo un po' di RAM a disposizione, si potrebbero allocare due array 
che rappresentano una sorta di VideoRAM (nel mio caso, 2x128x64/8=2048 
byte).
Tutte le operazioni a display vengono fatte, anzichè direttamente 
communicando con il controller del display, in RAM.
A questo punto una funzione di refresh confronta il valore di tutti i 
pixel e, se cambiati, li comunica al controller.
Così si minimizzano le operazioni di scrittura verso il display ed è 
molto semplice salvare la posizione in cui si è interrotta la scrittura 
per controller occupata (basta un pointer all'array della VRAM).
L'unico svantaggio è il quantitativo non indifferente di RAM.

 poter studiare?
Sarò di parte, ma ritengo che il nostro scheduler sia uno dei più 
minimali:
http://www.bertos.org/browser/trunk/kern/proc.c
Nella stessa directory trovi anche le funzioni (in assembly) che 
switchano di contesto per le varie CPU supportate.
Il cuore del kernel è nella proc_schedule() che conta solo 60 righe di 
codice (la maggior parte dei quali sono commenti).
Sempre nella stessa directory trovi le implementazioni di semafori, 
segnali e messaggi, tutte fatti in modo molto minimale e compatto.
Ciao

L'unico svantaggio è il quantitativo non indifferente di RAM.
Infatti, a quel punto anche lo scheduler può essere conveniente come utilizzo di
risorse.
Otterresti lo stesso risultato anche salvando i registri nel momento in cui il
display è busy e passando ad un altra routine, come faccio io.
In questo caso il C è uno svantaggio perchè non permette un livello così basso
di controllo, cosa che invece in asm si risolve con qualche PUSH e POP.
--
ciao
  Stefano

 display è busy e passando ad un altra routine, come faccio io.
Eh sì, l'effetto è simile, solo un tantinello più complicato da scrivere 
e brutto da leggere.