InputPort OutputPort come utilizzare e configurare le porte digitali del Netduino

Pin digitali

La scheda Netduino prende spunto dal progetto Arduino, infatti, la forma della scheda e la disposizione dei connettori è identica a quella di Arduino UNO. I pin digitali sono 14 e possono essere configurati via software come ingressi o uscite (InputPort OutputPort). La possibilità di decidere programmaticamente la funzione di ognuno di questi pin permette una elevata flessibilità nell’adattare Netduino al mondo esterno.

La tensione di lavoro dei pin è di 3.3Vdc ma è possibile lavorare anche con tensioni in ingresso di 5Vdc (TTL). Per quanto riguarda la corrente prelevabile abbiamo valori massimi di 8 mA; i pin 2, 3, e 7 riescono a fornire 16mA massimi.

I pin sono collegati direttamente al chip Atmel, occorre fare molta attenzione ai collegamenti esterni, un corto circuito o una tensione di sovraccarico potrebbero danneggiare irreparabilmente il microcontrollore.

Configurazione pin in uscita

Vediamo come configurare un pin digitale in uscita, utilizzando la classe OutputPort (Microsoft.SPOT.Hardware.OutputPort). I parametri da passare al costruttore sono l’identificativo del pin che vogliamo usare come uscita e il suo stato iniziale (Livello logico alto pari a3.3Vdc oppure livello logico basso pari a 0Vdc).
Ecco un esempio che mostra come configurare il pin0 del Netduino come uscita

//Pin 0 in uscita, inizializzato a livello logico basso
OutputPort pin0 = new OutputPort(Cpu.Pin.GPIO_Pin0, false);

Se stiamo realizzando un circuito di esempio consiglio di inizializzare la porta in uscita a false (livello logico basso) per evitare di collegare per errore il pin a massa (corto circuito) con conseguente possibile rottura della porta del chip Atmel.
La classe Pins enumera tutte le porte del Netduino disponibili per la configurazione come uscite o ingressi.

L’operazione fondamentale disponibile per il pin è il cambio di livello utilizzando il metodo Write() dell’oggetto appena istanziato.
Il codice seguente mostra come utilizzare il metodo Write()

//Mette a livello alto (3.3Vdc) il pin D0
pin0.Write(true);
//Mette a livello basso (0Vdc) il pin D0
pin0.Write(false);
[/csharp]

Sebbene il pin0 sia configurato come uscita, possiamo leggere, tramite il metodo Read(), il livello logico impostato con il metodo Write():

[csharp]
//creo una variabile bool per memorizzare lo stato della porta
Boolean Livello = false;
//Mette a livello alto (3.3Vdc) il pin D0
pin0.Write(true);
//leggo lo stato del livello
Livello = pin0.Read();
//restituisce true
Debug.Print(Livello.ToString());
//Mette a livello basso (0Vdc) il pin D0
pin0.Write(false);
//leggo lo stato del livello
Livello = pin0.Read();
//restituisce false
Debug.Print(Livello.ToString());

 

La proprietà InitialState restituisce lo stato del livello al momento della creazione dell’oggetto

//creo una variabile bool per memorizzare lo stato della porta
Boolean Livello = false;
//Pin 0 in uscita, inizializzato a livello logico basso
OutputPort pin0 = new OutputPort(Cpu.Pin.GPIO_Pin0, false);
//Mette a livello alto (3.3Vdc) il pin D0
pin0.Write(true);
//leggo lo stato del livello al momento della creazione
//dell'oggetto pin0
Livello = pin0.InitialState;
//restituisce false
Debug.Print(Livello.ToString());

La proprietà ID fornisce il nome del piedino del microcontrollore Atmel (come definito nel datasheet)

//Creo un oggetto Pin
Cpu.Pin nomePin;
//Pin 0 in uscita, inizializzato a livello logico basso
OutputPort pin0 = new OutputPort(Cpu.Pin.GPIO_Pin0, false);
//Ottengo il nome del pin riferito al microcontrollore Atmel
nomePin = pin0.Id;
Debug.Print(nomePin.ToString());

Configurazione pin in ingresso

Per accettare segnali in ingresso dobbiamo decidere quali pin utilizzare e configurarli con la classe InputPort (Microsoft.SPOT.Hardware.InputPort). In questo caso i parametri da passare al costruttore sono 3, il primo definisce l’identificativo del pin da utilizzare come ingresso, il secondo permette di attivare il filtro antirimbalzo (glitch filter) e il terzo permette di attivare l’eventuale resistenza di pull-up interna al microcontrollore.
Il codice seguente utilizza il pin3 del Netduino come ingresso.

//Definisco il pin 3 come ingresso, abilito l'antirimbalzo e la reistenza di pullup
InputPort pin3 = new InputPort(Cpu.Pin.GPIO_Pin3, true, Port.ResistorMode.PullUp);

Il metodo Read() permette di leggere il livello logico presente sul pin di ingresso. Read() restituisce un valore booleano in funzione del livello logico.

Boolean Livello = false;
//Pin3 in ingresso
InputPort pin3 = new InputPort(Cpu.Pin.GPIO_Pin0, true, Port.ResistorMode.PullUp);
//leggo dal pin3
Livello = pin3.Read();

Le proprietà esposte dall’oggetto sono ID, GlitchFilter e Resistor

Boolean Livello = false;
Boolean StatoFiltro= false;
Port.ResistorMode Resistenza;
//Variabile per contenere il nome del pin
Cpu.Pin nomePin;

//nome del pin come indicato nel datasheet del microcontrollore
nomePin = pin3.Id;
Debug.Print(nomePin.ToString());
//stato del filtro antirimbalso
StatoFiltro = pin3.GlitchFilter;
Debug.Print("StatoFiltro " + StatoFiltro.ToString());
//tipo di resistenza selezionato
Resistenza = pin3.Resistor;
Debug.Print("Resistenza " + Resistenza.ToString());

 

GlitchFilter

Il filtro antirimbalzo permette di eliminare gli impulsi spurii che si formano nei contatti metallici di un pulsante. All’atto della chiusura del pulsante il contatto non avviene immediatamente ma esiste un piccolo tempo di assestamento che da luogo a degli impulsi spurii che possono essere interpretati erroneamente dal Netduino. Attivando il filtro, introduciamo un certo ritardo prima di leggere lo stato dell’ingresso, in questo modo possiamo avere una lettura sicura poichè il contatto meccanico si è ormai stabilizzato.
La figura mostra gli impulsi spurii generati dall’interruttore:

Filtro antirimbalzo del Netduino

ResistorMode

Questa proprietà è molto importante perché permette di eseguire delle letture sicure del livello logico in ingresso quando, il circuito esterno, non imposta un riferimento di tensione sul pin. Le resistenze di pull up permettono di forzare un livello logico alto stabile sul pin d’ingresso. Nel caso del microcontrollore SAM7X possiamo utilizzare solo le resistenze di PullUp, dato che non sono presenti quelle di pull down (Impostando Port.ResistorMode.PullDown otteniamo un eccezione).

Impostare le resistenze di Pull-up sul Netduino

Abbiamo visto che per leggere dai pin dobbiamo richiamare il metodo Read(), quindi se vogliamo tenere sempre sotto controllo lo stato dell’ingresso è necessario eseguire ciclicamente il metodo Read();  sarebbe comodo un sistema che monitorizzi l’ingresso in modo da rilevare i cambiamenti di stato. Fortunatamente il Netduino ed il .Net micro framework mettono a disposizione la classe InterruptPort che svolge proprio questa funzionalità.

Configurazione pin in ingresso con InterruptPort

La classe InterruptPort permette di utilizzare un pin come ingresso e di eseguire un metodo ogni qualvolta che si verifica un cambiamento di stato su tale pin. Vediamo come configurare un pin in ingresso con la classe InterruptPort

InterruptPort pin0 = new InterruptPort(Cpu.Pin.GPIO_Pin0, false,
Port.ResistorMode.PullUp,
 Port.InterruptMode.InterruptEdgeBoth);

Il costruttore è molto simile alla classe InputPort ma in questo caso abbiamo un parametro aggiuntivo che definisce in che modo deve essere generato l’interrupt per quel pin. Le possibili opzioni sono:

InterruptEdgeBoth L’interrupt viene generato sia sul fronte di salita che sul fronte di discesa del segnale in ingresso
InterruptEdgeHigh L’interrupt viene generato solo sul fronte di salita del segnale in ingresso
InterruptEdgeLevelHigh L’interrupt viene generato quando il segnale in ingresso diventa alto
InterruptEdgeLevelLow L’interrupt viene generato quando il segnale in ingresso diventa basso
InterruptEdgeLow L’interrupt viene generato solo sul fronte di discesa del segnale in ingresso
InterruptNone non viene generato nessun interrupt

Interrupt segnale in ingresso

Le opzioni sono diverse e riescono a soddisfare tutte le variazioni di segnale che possono interessare il pin in ingresso.
La parte importante è la possibilità di generare un evento ogni volta che si verifica un’interrupt. Il codice di esempio mostra come farlo

public static void Main()
{
//Definisco il pin0 come InterruptPort
//L'interrupt viene generato sul fronte di salita
InterruptPort pin0 = new InterruptPort(Cpu.Pin.GPIO_Pin0, false,
Port.ResistorMode.PullUp,
Port.InterruptMode.InterruptEdgeHigh);
//Creo un evento quando si scatena un interrupt
//Ogni volta che viene generato un evento viene eseguito il metodo EventoPinIngresso
pin0.OnInterrupt += new NativeEventHandler(EventoPinIngresso);

while(true)
{
//eseguo qualcosa
Thread.Sleep(100);
}
}

public static void EventoPinIngresso(uint data1, uint data2, DateTime time)
{
//eseguo qualcosa quando viene generato un interrupt
Debug.Print(data1.ToString());
Debug.Print(data2.ToString());
Debug.Print(time.ToString());
}

 

In questo modo ogni volta che il segnale in ingresso scatena un interrupt, Netduino esegue il codice contenuto in EventoPinIngresso.
La firma del metodo EventoPinIngresso deve contenere due valori uint e una DateTime. Il primo valore (data1) contiene il numero del pin del microcontrollore, il secondo contiene il livello logico sul pin e il terzo (time) contiene il tempo in cui è stato scatenato l’evento.
Da notare che la gestione dell’evento è asincrona e quindi il Netduino continua ad eseguire il codice del ciclo while ed esegue autonomamente il metodo EventoPinIngresso quando si verifica un interrupt.

Questa funzionalità permette di creare codice che reagisce autonomamente agli eventi esterni e fa in modo che il Netduino si adatti meglio alle esigenze progettuali moderne.