Lo scopo di questo tutorial è illustrare un semplice metodo per interfacciare la ricevente di un qualsiasi radiocomando usato in ambito modellistico con un microcontrollore. Tale bisogno nasce dall’esigenza di leggere i segnali inviati dal radiocomando al modello R/C in modo da poterli trattare ed utilizzare in base a determinate scelte e comportamenti da imporre a quest’ultimo in modo automatico e sulla base di informazioni provenienti dall’esterno e lette da sensori. Una tipica applicazione, che oggi sta trovando sempre maggiore interesse, è quella dello sviluppo di FC (Flight Controller) per gestire l’assetto di UAV come quadricotteri ed esacotteri, in generale di multirotori. Per cui, i comandi inviati dal pilota tramite il radiocomando, serviranno per gestire i riferimenti imposti al sistema di controllo che genererà i segnali adeguati per i motori che generalmente sono differenti da quelli inviati direttamente dal radiocomando.
I segnali della ricevente
Prima di scendere nel dettaglio, diamo uno sguardo a ciò che la ricevente ci fornisce.
A seconda della ricevente a disposizione avremo la possibilità di prelevare un segnale PPM o una serie di segnali impulsivi corrispondenti ai singoli canali. Il segnale PPM è composto da onde rettangolari i cui fronti di salita individuano un intervallo mediamente pari a 1.5 ms, che può variare da 1 ms a 2 ms in funzione della posizione dello stick del radiocomando. L’ampiezza di tale intervallo definisce la posizione che avrà il servo. Ogni treno di onde è seguito da un intervallo piuttosto lungo (“Synchro Blank Time”) in modo da permettere di individuare l’inizio del treno di segnali. In generale, ogni treno di impulsi ha una cadenza di 20 ms, cioè viene generato ad una frequenza di 50Hz. Ad ogni intervallo, individuato tra due fronti di salita di impulsi successivi del treno, è associato un canale: nell’immagine il treno è composto da 6 intervalli (t1,…,t6) che corrispondono a 6 canali (CH1,…,CH6).
Implementazione
La soluzione che viene proposta è stata pensata per una ricevente a 4CH che non abbia a disposizione un segnale PPM ed è implementabile con qualsiasi microcontrollore che abbia a disposizione quattro pin di interruzione esterna o di interrupt on-change. Nel caso specifico è stato utilizzato un PIC 16F690.
L’idea è quella di scandire, con l’aiuto delle interruzioni, l’occorrenza dei fronti di salita e di discesa degli impulsi corrispondenti a ogni singolo canale. La porta A del PIC 16F690 può essere utilizzata come una porta ad ingressi digitali configurata in modo da generare un’interruzione per ogni cambiamento avvenuto sui suoi pin. Il conteggio dell’ampiezza dell’intervallo di ogni impulso avviene tramite l’utilizzo di un timer: esso deve essere configurato in modo tale da avere la risoluzione (in us) desiderata. Dato che i diversi impulsi possono avere un’ampiezza che varia tra 1000us e 2000us, è il timer del microcontrollore è stato configurato in modo da incrementare il proprio contatore ogni us. Per cui, avendo un PIC con un oscillatore interno di 8MHz e tenendo conto che il timer incrementa il proprio contatore ogni ciclo istruzione (è composto da 4 periodi di oscillazione) si ha che utilizzando un Prescaler di 2 abbiamo una risoluzione di 1/[(fosc/4)/Prescaler] = 1/[(8000000/4)/2] = 1us.
In riferimento al circuito riportato in alto, se il CH1 della ricevente è collegato al pin RA0 del PIC, il CH2 al pin RA1, il CH3 al pin RA2 e il CH4 al pin RA4, si ha che il comportamento logico da implementare per il microcontrollore diventa:
- Sul fronte di salita di CH1 settare il contatore a 0
- Sul fronte di discesa di CH1 il contenuto del contatore rappresenta l’ampiezza dell’impulso associato al primo canale (t1)
- Sul fronte di discesa di CH2 il contenuto del contatore meno t2 rappresenta l’ampiezza dell’impulso associato al secondo canale (t2)
- Sul fronte di discesa di CH3 il contenuto del contatore meno t1 e t2 rappresenta l’ampiezza dell’impulso associato al terzo canale (t3)
- E così via…
Un esempio di codice
Una possibile implementazione con CCS C Compiler è la seguente. Nel main vengono inserite le direttive per la configurazione del MCU.
void main (void) { setup_timer_1(T1_INTERNAL|T1_DIV_BY_2); disable_interrupts(GLOBAL); clear_interrupt(INT_RA); enable_interrupts(INT_RA); enable_interrupts(GLOBAL); set_timer1(0); while(1) {} }
In particolare:
setup_timer_1(T1_INTERNAL|T1_DIV_BY_2)
imposta l’oscillatore interno come oscillatore di riferimento per il contatore ed un prescaler di 2 unità.
enable_interrupts(INT_RA)
attiva la gestione delle interrupt-on-change sulla porta A.
int16 pulse[4] = {0, 0, 0, 0}; char sendData[8] = {0, 0, 0, 0, 0, 0, 0, 0}; int value = 0; #int_ra void ra_isr(void) { // Leggo le variazioni avvenute sulla Porta A. ‘value’ contiene un 1 sul bit corrispondente al pin in ingresso su cui c’è stata una variazione value = input_change_a(); // Fronte di salita su RA0 if(bit_test(value,0) && input(PIN_A0)) { // Azzero il contatore set_timer1(0); } // Fronte di discesa su RA0 else if(bit_test(value,0) && !input(PIN_A0)) { pulse[0] = get_timer1(); } // Fronte di discesa su RA1 else if(bit_test(value,1) && !input(PIN_A1)) { pulse[1] = get_timer1() - pulse[0]; } // Fronte di discesa su RA2 else if(bit_test(value,2) && !input(PIN_A2)) { pulse[2] = get_timer1() - pulse[0] - pulse[1]; } // Fronte di discesa su RA4 else if(bit_test(value,4) && !input(PIN_A4)) { pulse[3] = get_timer1() - pulse[0] - pulse[1] - pulse[2]; // Separo la variabile int16 in due byte separati in modo da inviarla tramite seriale al PC sendData[0] = pulse[0]; sendData[1] = pulse[0] >> 8; sendData[2] = pulse[1]; sendData[3] = pulse[1] >> 8; sendData[4] = pulse[2]; sendData[5] = pulse[2] >> 8; sendData[6] = pulse[3]; sendData[7] = pulse[3] >> 8; // Invio dei dati al PC tramite USART fputc(sendData[0], pc); fputc(sendData[1], pc); fputc(sendData[2], pc); fputc(sendData[3], pc); fputc(sendData[4], pc); fputc(sendData[5], pc); fputc(sendData[6], pc); fputc(sendData[7], pc); } // Cancello i flag delle interruzioni clear_interrupt(INT_RA); }
Per ricostruire i dati inviati dal PIC basta creare una piccola applicazione che acquisisce in sequenza gli 8 byte e a due a due li elabora come segue:
valore in us = byteBasso + byteAlto * 255.
Lo stesso funzionamento può essere utilizzato avendo a disposizione una ricevente con segnale PPM: anzi, la cosa diventa più interessante, in quanto è possibile ottenere le informazioni su tutti i canali a disposizione tramite un unico pin di interruzione.
Nell’immagine è riportato il circuito realizzato, calato in un contesto più complesso. La scheda mostrata è quella utilizzata come interfaccia con la FCB basata su ARM CortexM3 in fase di sviluppo per un quadricottero.
Per scaricare il codice completo clicca qui.