Tutorial C18 - Sviluppo di uno Scheduler di base

Transcript

Tutorial C18 - Sviluppo di uno Scheduler di base
Embedded Software – Sviluppo di uno Scheduler di base per PIC18
Tutorial
Embedded Software
Sviluppo di uno Scheduler di base per PIC18
Pagina 1
Embedded Software – Sviluppo di uno Scheduler di base per PIC18
Sommario
1.
INTRODUZIONE .................................................................................................................................................. 3
2.
COSA CI SERVE? .................................................................................................................................................. 3
3.
UN PO’ DI TEORIA… ............................................................................................................................................ 4
4.
…DALLA TEORIA ALLA PRATICA .......................................................................................................................... 5
5.
IMPLEMENTAZIONE ........................................................................................................................................... 6
Pagina 2
Embedded Software – Sviluppo di uno Scheduler di base per PIC18
1. Introduzione
Questa guida si propone di illustrare lo sviluppo di uno scheduler di base per microcontrollori della
serie PIC18. In questa guida si farà uso di MPLab IDE 8.87 come ambiente di sviluppo e di MPLab
C18 Student Edition come compilatore. Entrambi i prodotti sono free e liberamente scaricabili dal
sito web della Microchip Technology.
2. Cosa ci serve?
Il progetto di esempio fornito a corredo del tutorial è stato sviluppato con un PIC18F8722 come
target. Si tratta di un dispositivo general puporse, con ottima dotazione di memoria FLASH (128
kB), RAM (circa 4 kB) e dotato di ben 1024 byte di EEPROM, che costituiscono un quantitativo più
che sufficiente per pensare di non dover utilizzare una EEPROM esterna, semplificando
notevolmente lo sviluppo di applicazioni. Completano la dotazione hardware due porte USART, 2
porte i2c/SPI, il solito ADC a 10-bit e ben 5 CCP/PWM (così tanti PWM mi fanno pensare ad un bel
rover con 4 ruote motrici).
Quindi, in sostanza, la dotazione hardware per poter testare lo sviluppo si riduce ad una scheda
con PIC18F8722 (io ho usato una PICDEM PIC18, ottima scheda ad un prezzo ragionevole) e ad un
qualsiasi programmatore Microchip (direi un PICkit2 o un PICkit3, fa lo stesso…). Chiaramente se
non avete una scheda con un PIc18F8722 (ma chi non ne ha una in casa???) potete benissimo
utilizzare una qualsiasi scheda con sopra un PIC18, non vi serve nemmeno il quarzo, perché
l’esempio si appoggia sull’oscillatore interno.
Per quanto riguarda invece le risorse software vi serve:
-
MPLab IDE 8.87 o superiore,
MPLab C18 Student Edition 3.40 o superiore.
Potete scaricare entrambi i prodotti dal sito della Microchip (www.microchip.com).
Figura 2.1 Logo di MPLab IDE
Pagina 3
Embedded Software – Sviluppo di uno Scheduler di base per PIC18
3. Un po’ di teoria…
Prima di iniziare, faremo qualche richiamo teorico, in modo da rendere più facile la comprensione
dell’implementazione. Cominciamo quindi con il chiarire cosa si intende per scheduler. Con il
termine scheduler si indentifica un programma che, con una strategia (sotto forma di un
algoritmo) ben precisa alla base, regola l’accesso dei vari programmi in esecuzione su di un
processore, alle risorse di sistema.
istema. Laa periodicità con cui i vari processi vengono chiamati dallo
scheduler è detta, in genere, tempo di scheduling. Un esempio di scheduler è riportato in figura
3.1.
Figura 3.1 Esempio di scheduler
Di fatto, uno scheduler, costituisce la base di ogni sistema operativo, in quanto è l’algoritmo che
regola l’esecuzione dei vari processi. Gli obbiettivi di uno scheduler
heduler sono in genere volti alla
massimizzazione della velocità e della stabilità del sistema, garantendo il massimo througput
possibile (riduzione dei tempi in cui una determinata risorsa rimane inutilizzata), ed evitando che
un processo possa entrare in uno stato di attesa perenne (starvation).
Chiaramente l’implementazione di uno scheduler può essere più o meno complessa, a seconda di
quanto è complessa la politica di scheduling. Ad esempio, uno scheduler può determinare l’ordine
di esecuzione dei vari processi in base a delle priorità, che possono essere fissate al principio
oppure possono variare in funzione di alcuni parametri dinamici, come il tempo medio di
esecuzione, la percentuale di utilizzo della risorsa, etc. Questo tipo di implementazione è
chiaramente molto più complessa rispetto ad un caso molto più semplice in cui lo scheduler
chiama in sequenza i vari processi secondo un ordine fissato a principio e non modificabile.
Una differenziazione fondamentale tra schedulers è quella che distingue uno scheduler
preemptive da uno non-preemptive.
preemptive. Uno scheduler preemptive ha la facoltà di sottrarre l’uso del
processore ad un processo mentre quest’ultimo è in esecuzione, mentre uno scheduler nonnon
preemptive non ha questo privilegio e per assegnare il processore
processore ad un determinato processo
deve attendere che il processo in esecuzione termini.
Pagina 4
Embedded Software – Sviluppo di uno Scheduler di base per PIC18
4. …dalla teoria alla pratica
A questo punto possiamo passare alla descrizione dell’implementazione. Quello che ci
proponiamo di realizzare è un semplice scheduer non-preemptive, con politica di scheduling di
tipo FCFS (First Come First Serve): il primo processo in attesa viene eseguito immediatamente, e gli
altri a seguire man mano che il processo attivo completa la sua esecuzione.
Uno schema di principio dello scheduler è riportato in figura 4.1.
Figura 4.1 Schema dello scheduler
La struttura che ci proponiamo di realizzare è costituita da un totale di 10 files. Vediamo nel
dettaglio i contenuti di ogni singolo file e poi passeremo ad una descrizione dettagliata delle parti
più importanti. In tabella 4.1 e 4.2 sono riportate le descrizioni dei file .c e .h che compongono lo
scheduler.
Source Files
Main.c
Main del programma, contiene le chiamate agli interrupts, le chiamate a tutte
le routine di inizializzazione e la chiamata principale allo scheduler.
Scheduler.c
Contiene lo scheduler ed alcuni servizi di inter-task communication.
SystemInit.c
Contiene tutte le routine di inizializzazione.
SystemManager.c E’ il manager del sistema e coordina il passaggio di stato dei vari task.
Tabella 4.1 Source files
Header Files
p18f8722.h
Scheduler.h
SystemInit.h
SystemManager.h
Timers.h
Portb.h
File di intestazione del microcontrollore utilizzato.
File di intestazione dello scheduler, contiene tutti i tipi e le variabili esportate.
File di intestazione di SystemInit.c.
File di intestazione di SystemManager.c.
File di libreria C18 per la gestione dei timer.
File di libreria C18 per la gestione della portb.
Tabella 4.1 Header files
Pagina 5
Embedded Software – Sviluppo di uno Scheduler di base per PIC18
5. Implementazione
Per prima cosa cerchiamo di capire come è stato strutturato lo scheduler. Uno scheduler, come
abbiamo visto nella parte introduttiva, ha il compito di lanciare in esecuzione i vari task,
garantendo che il tempo di chiamata di ogni singolo task sia fissato e il più possibile stabile. Dato
che lo scheduler che stiamo analizzando è di tipo non-preemptive, questo è vero solo a condizione
che il programmatore presti attenzione al fatto che la somma del tempo di esecuzione dei suoi
task non ecceda il tempo di scheduling. Per farlo ci sono degli accorgimenti che si possono
prendere nella programmazione e chiaramente abbiamo a disposizione dei metodi per verificare
che tale condizione sia rispettata, almeno nella maggioranza dei casi. Abbiamo quindi l’obiettivo di
generare un tempo di chiamata fisso, indipendete dal flusso del programma. Il modo più semplice
di fare una cosa del genere è affidarsi ad una risorsa hardware che ci possa fornire tale
temporizzazione: usiamo un timer. Nello specifico ci serviamo dell’interrupt generato dall’overflow
del timer 0.
Generiamo tutte le inizializzazioni necessarie a creare un interrupt sull’overflow del timer 0 alla
frequenza di 1ms. Da questo tick di sistema, tramite un opportuno contatore, potremo generare
qualsiasi tempo di scheduling. Purtroppo il C18 ha una gestione un po’ scomoda degli interrupt,
che vincola il programmatore a gestire tutti gli hook nel main. In questo caso noi gestiamo
l’interrupt sul timer 0 come riportato nel listato 4.1:
/* ISR Function */
void High_Int_Event (void)
{
/* Service variable */
static unsigned int InteruptCounter = 0;
/* -- Timer0 Interrupts every 1 ms -- */
if (INTCONbits.TMR0IF == 1)
{
/* Reset the flag */
INTCONbits.TMR0IF = 0;
/* Load the timer */
WriteTimer0(PRELOAD_VALUE);
/* Increment local counter */
InteruptCounter++;
/* If scheduling time is elapsed */
if (InteruptCounter >= SCHEDULING_TIME_MS)
{
/* Reset counter */
InteruptCounter = 0;
/* Main scheduler timebase flag */
MainSystemTimebaseFlag = CallTaskPhase;
}
}
}
Listato 4.1 Gestione dell’interrupt sull’overflow del timer 0
Pagina 6
Embedded Software – Sviluppo di uno Scheduler di base per PIC18
Dove la define SCHEDULING_TIME_MS ci consente di agire sul tempo di scheduling (come visibile nel
listato 4.2), e la variabile MainSystemTimebaseFlag ci dice che il tempo di scheduling è trascorso e si può
iniziare un nuovo ciclo. Tale deve essere resettata via software dopo l’uso
#define MILLISECOND_1000_MS
#define MILLISECOND_500_MS
#define MILLISECOND_100_MS
#define MILLISECOND_10_MS
#define SCHEDULING_TIME_MS
((unsigned int)(1000))
((unsigned int)(500))
((unsigned int)(100))
((unsigned int)(10))
MILLISECOND_10_MS
Listato 4.2 Impostazione del tempo di scheduling
A questo punto, per chiamare correttamente il nostro scheduler, non dobbiamo fare altro che
inserire la chiamata alla funzione che implementa il nostro scheduler (MainScheduler()) all’interno
di un ciclo infinito, avendo cura di resettare a mano la variabile MainSystemTimebaseFlag, come
detto in precedenza. L’implementazione è riportata nel listato 4.3.
/* Endless loop */
while(FOREVER)
{
if (MainSystemTimebaseFlag == CallTaskPhase)
{
/* Call System Scheduler */
MainScheduler();
/* Reset flag */
MainSystemTimebaseFlag = WaitTriggerPhase;
}
}
Listato 4.3 Chiamata principale allo scheduler
A questo punto possiamo passare all’analisi della funzione che implementa lo scheduler vero e
proprio, analizzandone prima le strutture dati. Per rendere più flessibile questo oggetto ho
pensato di prevedere una fase di inizializzazione dei task e la gestione di una fase di basso
consumo, che può tornare molto utile in caso di sistemi alimentati a batteria. Sono quindi stati
previsti un paio di tipi enumerativi, che costituiscono i tipi con cui sono dichiarate le variabili di
stato dello scheduler e dei task (vedi listato 4.4):
/* Main system state type */
typedef enum MainSystemStateEnum {
InitializationState,
RunningState,
LowPowerState
} MainSystemStateType;
/* Task state type */
typedef enum TaskStateEnum {
Initializing,
InitializationComplete,
Pagina 7
Embedded Software – Sviluppo di uno Scheduler di base per PIC18
Running,
GoToLowPower,
LowPower,
GoToRunning
} TaskStateType;
Listato 4.4 Enums per la dichiarazione dello stato dello scheduler e dei task
Quindi il sistema prevede di avere tre fasi attive: Initialization, RunningState e LowPower, mentre i
task possono generare richieste di transito tra questi stati sfruttando le richieste:
InitializationComplete, GoToLowPower e GoToRunning.
A questo punto possiamo dare uno sguardo alla macchina a stati principale dello scheduler, la cui
implementazione è riportata nel listato 4.5:
/************************************************************************
* Function:
void MainScheduler (void)
* Input:
None
* Output:
None
* Author:
F.Ficili
* Description:
Main system scheduler
* Date:
28/04/12
************************************************************************/
void MainScheduler (void)
{
switch (MainSystemState)
{
/* System Initialization Phase */
case InitializationState:
/* --- Call Initialization Phase Task --- */
break;
/* System Normal operaion Phase */
case RunningState:
/* -- Call Normal Operation Phase Task -- */
break;
/* System low consumption Phase */
case LowPowerState:
/* ----- Call Low Power Phase Task ----- */
break;
/* Default */
default:
break;
}
}
Listato 4.5 Implementazione della macchina a stati principale dello scheduler
Pagina 8
Embedded Software – Sviluppo di uno Scheduler di base per PIC18
Come potete vedere è tutto molto semplice. Infine, il nostro scheduler è anche corredato di un
paio di strumenti generici per una migliore gestione del sistema. In particolare è stato previsto un
task di SystemManagment (presente all’interno del file SystemManagment.c), che permette di
gestire in maniera abbastanza semplice le transizioni di stato. Inoltre sono state previste delle
funzioni di generazione di eventi che possono essere utilizzate come strumenti di inter-task
communication. Chiaramente questa implementazione è solo una traccia, che può servire per lo
sviluppo di schedulers più complessi.
Nei prossimi tutorial vedremo qualche esempio di realizzazione di task, per meglio comprendere
come poter sfruttare le caratteristiche dello scheduler descritto in questo articolo.
Pagina 9