ESP32 (12) – I/O e interrupts

luca 25/02/2017 0

In un precedente articolo ho iniziato a spiegarvi come utilizzare i pin del chip esp32 per attività di Input/Output. Oggi vediamo come utilizzare gli interrupts che tali pin ci offrono.

Interrupts

In informatica, un interrupt è un segnale che indica l’accadere di un evento che richiede immediata attenzione. Il segnale di interrupt interrompe quindi la normale esecuzione del programma e manda in esecuzione una particolare funzione, detta interrupt service routine (ISR) che ha il compito di reagire all’evento che si è scatenato.

Vi sono due tipi di interrupt: hardwaresoftware.

  • un interrupt hardware è generato da una periferica esterna alla cpu (ad esempio la pressione di un tasto su una tastiera)
  • un interrupt software è generato da un programma in esecuzione sulla cpu (ad esempio un driver che ha disponibili nuovi dati)

GPIO interrupts

In modalità GPIO, i pin del chip esp32 offrono diverse condizioni nelle quali possono generare un interrupt:

esp32-interrupts04

interrupts generati da pin in modalità I/Ogpio.h

Gli interrupt vengono generati in base alla variazione del segnale presente sul pin. Ad esempio, se il pin è configurato in modalità GPIO_INTR_POSEDGE, un interrupt sarà generato ogni volta che il segnale passa dallo stato logico 0 allo stato logico 1 (rising edge, ovvero sul “fronte di salita” del segnale). Al contrario, se configurato come GPIO_NEGEDGE, l’interrupt sarà generato sul fronte di discesa del segnale (ovvero nel passaggio 1->0).

Per configurare la modalità di interrupt di un pin possiamo utilizzare la funzione:

esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type);

a cui va indicato il pin (gpio_num) e la modalità (intr_type).

In alternativa, se stiamo configurando i pin usando un template, possiamo specificare la modalità di interrupt tra i parametri del template stesso:

typedef struct {
    [...]
    gpio_int_type_t intr_type;      /*!< GPIO interrupt type         */
} gpio_config_t;

ISR e framework

Come la gestione del pin, anche la gestione degli interrupt nel chip esp32 è abbastanza complessa: il chip infatti offre fino a 32 interrupts per ogni core, con diverse priorità… Fortunatamente il framework semplifica molto il loro utilizzo.

Per prima cosa dobbiamo installare il servizio che gestisce gli interrupts per i pin di I/O:

#define ESP_INTR_FLAG_DEFAULT 0
[...]
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);

Quindi possiamo indicare, per ogni pin, quale sarà la interrupt service routine che la cpu dovrà eseguire alla generazione dell’interrupt configurato per quel pin. E’ inoltre possibile passare dei parametri (args) alla ISR:

gpio_isr_handler_add(gpio_num, isr_handler, void* args);

E’ molto importante sapere che la interrupt service routine deve avere una breve durata di esecuzione, avendo interrotto il normale programma.

ISR e tasks

Ho preparato un esempio per mostrare l’utilizzo degli interrupts e la corretta gestione della interrupt service routine. Il programma, che trovate su Github, riceve – tramite interrupt – l’evento di pressione di un tasto e cambia di conseguenza lo stato di un led.

Avendo utilizzato, come per gli altri esempi di questo tutorial, la scheda di sviluppo ESP32 DevKit-C di Espressif, ho potuto sfruttare il pulsante BOOT già presente sulla scheda e collegato al pin GPIO 0:

esp32-interrupts05

Come si vede dallo schema, alla pressione del tasto il pin viene collegato a massa; dovrò quindi utilizzare la modalità di interrupt GPIO_NEGEDGE.

Sia il pin a cui è collegato il pulsante, sia quello a cui è collegato il led sono configurabili tramite menuconfig:

esp32-interrupts01

Poco sopra vi ho spiegato che la routine che gestisce un interrupt deve essere più breve possibile; per questo tutte le azioni da compiere alla pressione del tasto – cambio di stato del led e visualizzazione di una stringa sul monitor – sono inserite in un task dedicato. E’ quindi necessario capire come poter far comunicare la ISR con questo task, per notificargli l’avvenuta pressione del tasto. FreeRTOS mette a disposizione diversi meccanismi per la comunicazione tra tasks, dai più semplici (semaforimutex…) a quelli più complessi (code…). In questo caso è sufficiente una notifica binaria (“tasto premuto”), quindi ho scelto di utilizzare un semaforo binario.

A partire dalla versione 8.2, FreeRTOS mette a disposizione una modalità più leggeraveloce per questo tipo di notifiche: le task notifications. Questa funzionalità non è ancora stata completamente testata in modalità multicore e ho riscontrato diversi crash, per questo ho preferito utilizzare i più collaudati semafori.

Lo schema di funzionamento del programma è il seguente:

esp32-interrupts03

Per prima cosa, è necessario creare il semaforo:

SemaphoreHandle_t xSemaphore = NULL;
[...]
xSemaphore = xSemaphoreCreateBinary();

Alla pressione del tasto, viene eseguita la funzione:

void IRAM_ATTR button_isr_handler(void* arg) {
  xSemaphoreGiveFromISR(xSemaphore, NULL);
}

La ISR non fa altro che “dare” il semaforo, ovvero cambiare il suo stato in modo che un altro task, in attesa, possa essere sbloccato e continuare la sua esecuzione.

Il task che gestisce led e console seriale infatti ha il seguente codice:

void button_task(void* arg) {
  for(;;) {
    if(xSemaphoreTake(xSemaphore,portMAX_DELAY) == pdTRUE) {
      printf("Button pressed!\n");
      led_status = !led_status;
      gpio_set_level(CONFIG_LED_PIN, led_status);
    }
  }
}

Un loop infinito che attende la disponibilità del semaforo (portMAX_DELAY indica al task di attendere un tempo infinito) e, una volta ottenuto il semaforo, visualizza il messaggio Button pressed! su console e cambia lo stato al led.

Avrete notato che le funzioni che consentono di dare e prendere il semaforo sono leggermente diverse: all’interno delle ISR è infatti importante utilizzare le funzioni apposite (ad esempio xSemaforeGiveFromISR e non xSemaforeGive, che va usata all’interno di normali tasks)

DEMO

Leave A Response »

Questo sito usa i cookie per poterti offrire una migliore esperienza di navigazione maggiori informazioni

Questo sito utilizza i cookie per fonire la migliore esperienza di navigazione possibile. Continuando a utilizzare questo sito senza modificare le impostazioni dei cookie o clicchi su "Accetta" permetti al loro utilizzo.

Chiudi