Come allestire un framework per consentire l’esecuzione di più task in parallelo su un PIC16F877
Disporre di uno scheduler per un PIC16F877 è sicuramente utile in quanto rende possibile l’esecuzione di più tasks in parallelo ottimizzando così le performance del micro. L’approccio, implementato nel listato 1, è il seguente:
- Inizializzazione di TIMER1 per generare un interrupt ogni milli-secondo; ad ogni interrupt vengono aggiornate alcune variabili gestibili anche dal programma principale per determinare gli intervalli di esecuzione delle applicazioni. Il listato 1 consente di eseguire task ad intervalli multipli di 1ms, 10ms, 100ms o 1 secondo.
- Nel programma principale vengono conteggiati i vari intervalli ed eseguite le relative applicazioni.
Le istruzioni nella routine di servizio dell’interrupt sono ridotte al minimo onde evitare di compromettere la temporizzazione. Il codice del listato 1 è scritto per i compilatori C di CCS. L’esempio mostra il cambio di stato di alcuni LED ogni 250ms, 500ms, 1 secondo e 2 secondi, oltre ad azzerare il watchdog timer ogni 10ms.
#include <16f877.h>
#fuses HS,NOPROTECT,NOWDT,BROWNOUT,PUT,NOLVP
/*eventuale spazio per bootloader*/
#ORG 0x1F00,0x1FFF {}
#device *=16
/* clock a 20 MHz. */
#use delay(clock=20000000)
/* Direttiva per l’interfaccia RS-232*/
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
/* definizioni I/0 */
#define LED0 PIN_B4
#define LED1 PIN_B5
#define LED2 PIN_B6
#define LED3 PIN_B7
// Variabili di schedulazione
unsigned char t1mS = 0,
t10mS = 0,
t100mS = 0;
unsigned char t1mS0 = 0,
t10mS0 = 0, // Temporizz. Per Watchdog timer
t10mS1 = 0, // LED blinking (ogni 0.25sec)
t100mS0 = 0, // LED blinking (ogni 0.5sec)
tS0 = 0, // LED blinking (ogni secondo)
tS1 = 0; // LED blinking (ogni 2 secondi)
void main(void);
void init_pins(void);
void init_timers(void);
void init(void);
void timer1_isr(void);
void
init_pins(void) {
set_tris_b(0b00000000);
/* B4 to B7 are outputs.
Rest are not used. */
output_low(LED0);
output_low(LED1);
output_low(LED2);
output_low(LED3);
} /* init_pins */
void
init(void) {
init_pins();
init_timers();
// Enable Timer1 interrupts
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
printf(“Scheduler demo\r\n”);
} /* init */
/* inizializzazione del Timer1 */
void
init_timers(void) {
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8);
} /* init_timers */
void
main(void) {
init();
for ( ; ; ) {
if (t1mS0) { /* trascorso 1ms */
t1mS0 = 0;
}
if (t10mS0) { /* trascorsi 10ms */
/* azzeramento del watchdog timer. */
RESTART_WDT();
}
if (t10mS1 == 25) {
/* trascorsi 250ms. */
t10mS1 = 0;
/* Toggle LED */
output_bit(LED0, !input(LED0));
}
if (t100mS0 == 5) {
/* trascorsi 500ms. */
t100mS0 = 0;
/* Toggle LED */
output_bit(LED1, !input(LED1));
}
if (tS0) {
/* trascorso 1 secondo */
tS0 = 0;
/* Toggle LED */
output_bit(LED2, !input(LED2));
}
if (tS1 == 2) {
/* trascorsi 2secondi */
tS1 = 0;
/* Toggle LED */
output_bit(LED3, !input(LED3));
}
}
} /* main */
#int_timer1
void timer1_isr(void) {
// Routine di servizio dell’interrupt
set_timer1(-625);
// For 20 MHz/625 => 1 ms - latency
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++;
}
}
}
} /* timer1_isr */
| Listato 1. |



Una combinazione di un for tipo while(1) e una serie di if, “semplice” ma efficace 🙂