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
Documenti analoghi
Tutorial C18 - Sviluppo di un task base per PIC18
Nel tutorial “Sviluppo di uno scheduler di base per PIC18” è stato presentato il progetto di un
semplicissimo scheduler per PIC18 di tipo FCFS. Su questo primordiale oggetto baseremo lo
sviluppo di...