Interrupt nei microcontrollori - interrupt GPIO con STM32


 Un'interruzione, nel lessico informatico interrupt, è un segnale inviato al processore che sospende temporaneamente l'esecuzione del programma principale per eseguire una routine di servizio dell'interruzione (ISR, Interrupt Service Routine). L’operazione può essere definita internamente alla memoria oppure definita dal programmatore.

Le interruzioni possono essere generate da diverse sorgenti, come temporizzatori, periferiche hardware (ad esempio, UART, ADC), o eventi esterni, come la pressione di un pulsante o il segnale di un sensore.

In questo articolo ci si occupa delle interrupt da GPIO ossia interruzioni generate da un segnale connesso a un pin di GPIO in input. Vengono definite EXTI, External interrupts, per la ragione di generare un interrupt da un segnale esterno.

Il compito del programmatore scrivere alcune righe di codice firmware in un'apposita funzione, nella quale si dichiara ciò che deve accadere alla generazione dell'interrupt. In sintesi, il programmatore deve definire il gestore dell'interruzione, ISR (interrupt service routine).
Inoltre, l'interrupt può essere generata sul fronte di salita o di discesa del segnale, a seconda delle necessità. La prima viene generata subito alla pressione del pulsante, mentre la seconda al rilascio dello stesso (figura 1).

Figura 1: Interrupt request (IR) al fronte di salita (in alto) o al fronte di discesa (in basso).

La scrittura di un'interruzione per microcontrollori ST può essere così fatta:

  1. Aprire il software STM32CubeIDE e creare un nuovo progetto, attenendosi al proprio microcontrollore in possesso;
  2. Configurare il clock a dovere, attenendosi sempre alle specifiche del proprio microcontrollore;
  3. Nella finestra principale, il file .ioc, scegliere un pin disponibile e cliccare sopra con il tasto sinistro. Tra le diverse funzioni che può assumere, presenti nel menù a tendina, impostare GPIO_EXTIx. Nel caso in esame, ho scelto il pin PC13 (bottone blu a bordo della scheda) e il suo interrupt sarà GPIO_EXTI13 (figura 2);
    Figura 2: settaggio dell'interruzione sul pin PC13.

  4. Recarsi sulla sinistra della finestra e aprire System Core -> NVIC e attivare la linea di interruzioni EXTI (figura 3). NVIC è il Nested Vectored Interrupt Controller, e ha il compito di gestire le interruzioni all'interno del MCU.
    Figura 3: attivazione della linea di interruzioni EXTI.

  5. Dopo aver attivato la linea delle interruzioni, è necessario impostare il fronte su cui inviare la richiesta di interruzione (IR) al MCU. Nella sezione System Core recarsi su GPIO e cliccare sul pin scelto come interrupt. Nel caso in esempio PC13 (figura 4). 
    Figura 4: selezione del pin di interrupt PC13.

    Nella paste inferiore della finestra compare l'area PC13-... configuration. Il primo menù disponibile è la scelta della modalità del GPIO GPIO Mode. Aprire il menù a tendina e scegliere, nel caso di interrupt lanciata dal fronte di salita, External Interrupt Mode with Rising edge trigger detection (figura 5). Ovviamente è possibile scegliere anche il fronte di discesa, cliccando su  External Interrupt Mode with Falling edge trigger detection. 
    Figura 5: selezione interrupt sul fronte di salita.

  6. Generare il codice (figura 6). Si aprirà direttamente la schermata di codifica al file main.c.
    Figura 6: generazione del codice.

Arrivati a questo punto, occorre scrivere nel programma ciò che la MCU deve fare alla ricezione dell'interruzione legata alla pressione del pulsante. Il fulcro di questa azione sta nella scrittura del corpo di una funzione già disponibile all'interno del programma. Si tratta della funzione Callback, definita void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){...}Non va dichiarata all'interno del main() e nemmeno del while(1) !! All'interno della funzione Callback, è buona pratica fare una verifica del PIN (e non della porta) tramite un if( GPIO_Pin == GPIO_PIN_13)così da evitare ambiguità nel caso fossero attivi più pulsanti di interrupt, e infine scrivere la routine ISR. 


Interrupt - interruttore per led

Se, ad esempio, volessi accendere/spegnere il LED verde LD2 onboard, sarà necessario fare una TogglePin(...) del PIN cui è collegato il LED. Nelle Nucleo, di solito, è il GPIO output PA5. Il codice della ISR è mostrato in figura 7.

Figura 7: dichiarazione della ISR.

Come mostrato in figura 7, è stato preso come riferimento per la locazione della funzione Callback all'interno del codice la sezione USER CODE BEGIN 4. Se  fosse stata dichiarata all'interno del main() o del while(1), avrebbe perso il suo significato. 

Interrupt - modifica di variabili

Non solo accensioni e spegnimenti. Le interrupt possono essere utilizzati per la modifica di variabili globali. Affinchè possano esserci risultati concreti, la variabile deve anch'essa essere dichiarata esternamente al main(). Si suggerisce la sezione di codice nel main.c USER CODE BEGIN PV come mostrato in figura 8.
Figura 8: variabile globale dichiarata nella sezione Private variables.

Nella stessa funzione Callback, che può rimanere nella sezione USER CODE BEGIN 4, ove già precedentemente dichiarata, basta solo cambiare il codice nel corpo. Può scriversi un codice che incrementi, ad esempio, la variabile globale alla pressione del pulsante, come mostrato in figura 9.
Figura 9: ISR di incremento della variabile.


In conclusione, può dirsi che l'utilizzo delle interruzioni in un sistema a microcontrollore è la scelta più giusta in gran parte dei casi. Se nel caso dell'accensione/spegnimento del led, si fosse fatto uso di una if, all'interno del while(1), che controlli a ogni ciclo del programma lo stato del pulsante, si creerebbe la condizione di busy waiting in cui la MCU "domanda" in continuazione se il pulsante è premuto o meno. L'interruzione, al contrario, esegue la sua routine solo se il pulsante viene premuto.

Commenti

Post popolari in questo blog

Conversione analogico - digitale (ADC) e digitale - analogico (DAC) con amplificatori operazionali

Tempo di propagazione nella logica pass-transistor - legge di Elmore (39)

Modello di Ebers & Moll delle correnti nel BJT - Guadagni di corrente (15)