Come realizzare uno scheduler (framework che consente al microcontrollore di eseguire più task contemporaneamente) con i PICMICRO.
Scheduler con PIC
Il firmware proposto nel listato 1, implementa un semplice scheduler ovvero un framework che consente al microcontrollore di lanciare più task in parallelo al fine di ottimizzare l’utilizzo del micro stesso. La tecnica usata è la seguente:
- viene impostato il Timer1 per produrre una interruzione ogni millisecondo;
- ad ogni interruzione di Timer1 vengono aggiornate alcune variabili che saranno usate nel programma principale per determinare gli intervalli di tempo associati ai vari task. Nell’esempio le variabili sono gestite in modo da avviare i task ad intervalli multipli di 1ms, 10ms, 100ms o 1s.
- Il programma principale controlla gli intervalli di tempo. Nell’esempio viene fatto lampeggiare un LED ogni 250ms, 500ms, 1s e 2s e viene resettato il watchdog timer ogni 10ms.
Le variabili t1mS, t10mS, t100mS e t1S vengono usate per capire se è trascorso rispettivamente 1ms, 10ms, 100ms o 1s, mentre le variabili t1mS0, t10mS0, t10mS1, t100mS0, tS0 e tS1 sono utilizzate per determinare gli intervalli di tempo con cui inne- scare i task. Ad esempio per far lampeggiare il LED ogni 250ms viene controllato se la variabile t10mS1 contiene 25 (poiché 25x10ms=250ms).
Il programma è scritto in C per un PIC16F877 a 20MHz utilizzando PICC di CCS.
#include <16f877.h> /* Configurazione del micro */ #fuses HS,NOPROTECT,NOWDT,BROWNOUT,PUT,NOLVP #device *=16 /* RAM 256 bytes */ /* Impostazione clock a 20 MHz. */ #use delay(clock=20000000) /* Definizioni per gli I/O */ #define LED0 PIN_B4 #define LED1 PIN_B5 #define LED2 PIN_B6 #define LED3 PIN_B7 // Variabili per la schedulazione unsigned char t1mS = 0, t10mS = 0, t100mS = 0; unsigned char t1mS0 = 0, t10mS0 = 0, // Conteggio per Watchdog timer t10mS1 = 0, // Lampeggio LED (250ms on/off) t100mS0 = 0, // Lampeggio LED (500ms on/off) tS0 = 0, // Lampeggio LED (1 secondo on/off) tS1 = 0; // Lampeggio LED (2 secondi on/off) void main(void); void init_pins(void); void init_timers(void); void init(void); void timer1_isr(void); /* Impostazioni dei pin di I/O */ void init_pins(void) { set_tris_b(0b00000000); /* B4..B7 uscite */ /* Impostazioni iniziali per le uscite */ output_low(LED0); output_low(LED1); output_low(LED2); output_low(LED3); } /* Inizializzazione */ void init(void) { init_pins(); init_timers(); // Abilitazione interruzione per Timer1 enable_interrupts(INT_TIMER1); enable_interrupts(GLOBAL); } /* Inizializzazione Timer1 */ void init_timers(void) { setup_timer_1(T1_INTERNAL | T1_DIV_BY_8); } /* Inizio programma principale*/ void main(void) { init(); for ( ; ; ) { if (t1mS0) { /* Trascorso 1ms */ t1mS0 = 0; } if (t10mS0) { /* Trascorsi 10ms */ /* azzeramento watchdog timer */ RESTART_WDT(); } if (t10mS1 == 25) { /* Trascorsi 250ms */ t10mS1 = 0; /* Commutazione LED0 */ output_bit(LED0, !input(LED0)); } if (t100mS0 == 5) { /* Trascorsi 500ms */ t100mS0 = 0; /* Commutazione LED1 */ output_bit(LED1, !input(LED1)); } if (tS0) { /* Trascorso 1s */ tS0 = 0; /* Commutazione LED2 */ output_bit(LED2, !input(LED2)); } if (tS1 == 2) { /* Trascorsi 2s */ tS1 = 0; /* Commutazione LED3 */ output_bit(LED3, !input(LED3)); } } } /* Fine programma principale */ #int_timer1 void timer1_isr(void) { // ISR per Timer1 set_timer1(-625); // 20 MHz/625 => 1 ms - latenza t1mS0++; if (t1mS++ == 9) { // 10 ms t1mS = 0; t10mS0++; t10mS1++; if (t10mS++ == 9) { // 100 ms t10mS = 0; t100mS0++; if (t100mS++ == 9) { // 1 s t100mS = 0; tS0++; tS1++; } } } }
