Modulo MCP23017

 

 

Questa è la piedinatura del modulo CJMCU-2317 basato sull’MCP23017. Ti spieg


o il significato di ogni pin.

📌 Lato sinistro (Pin di configurazione e comunicazione I2C)

  1. A2, A1, A0Indirizzo I2C

    • Permettono di selezionare l’indirizzo I2C del chip (da 0x20 a 0x27).
    • Collegando A0, A1 e A2 a GND o VCC, puoi usare fino a 8 moduli sullo stesso bus I2C.
  2. RESETReset del chip

    • Deve essere collegato a VCC per il funzionamento normale.
    • Se collegato a GND, resetta il modulo.
    • Puoi usarlo con Arduino per un reset software.
  3. NC/SO e NC/CSPin non connessi (NC)

    • Probabilmente indicano pin inutilizzati nel modulo CJMCU.
  4. SDA/SILinea dati I2C

    • Si collega al pin SDA di Arduino (A4 su Arduino Uno).
  5. SCL/SCKLinea clock I2C

    • Si collega al pin SCL di Arduino (A5 su Arduino Uno).
  6. GNDMassa

    • Deve essere collegato al GND di Arduino.
  7. VCCAlimentazione

    • Può essere 3.3V o 5V, compatibile con Arduino.

📌 Lato destro (Porte di Input/Output del MCP23017)

Questi sono i 16 GPIO (General Purpose Input/Output) del MCP23017, divisi in due gruppi da 8:

  1. ITB/ITAInterrupt A e B

    • Generano segnali quando cambia lo stato dei pin di ingresso.
    • Utile per leggere pulsanti senza fare continuamente polling con digitalRead().
  2. B0/A0 → B7/A7Pin di I/O generici

    • Puoi configurarli come input o output, proprio come i pin di Arduino.
    • Il prefisso "B" o "A" indica a quale delle due porte appartiene il pin:
      • Porta A: A0 → A7
      • Porta B: B0 → B7

🛠 Come collegarlo ad Arduino

Per usare il modulo con Arduino, ecco le connessioni minime necessarie:

 

Pin CJMCU-2317 Arduino
VCC 5V o 3.3V
GND GND
SDA/SI A4 (SDA)
SCL/SCK A5 (SCL)
RESET 5V

 

Se vuoi collegare più moduli, devi configurare i pin A0, A1, A2 per assegnare un indirizzo I2C diverso a ciascun MCP23017.


Codice Arduino per testare il modulo

 

Per accendere e spegnere un LED tramite un pulsante temporaneo. Quando tengo premuto il pulsante il led si accende mentre se lo rilascio si spenge. Il pulsante in imput pulup. Ma facendo uso di un espandere i/o e precisamente il MCP23017 con l'uso di interrupt.

Ecco un codice Arduino per accendere e spegnere un LED utilizzando un pulsante collegato a un espansore I/O MCP23017. Il pulsante è configurato in modalità INPUT PULLUP, e il codice utilizza un interrupt per rilevare la pressione del pulsante.

Collegamenti:

  • MCP23017 collegato via I2C (SDA → A4, SCL → A5 su Arduino Uno)
  • Pulsante collegato a GND e a un pin del MCP23017 (es. GPA0)
  • LED collegato a un altro pin del MCP23017 (es. GPA1)

💡Codice:

#include <Wire.h>
#include <Adafruit_MCP23017.h>

Adafruit_MCP23017 mcp;

#define BUTTON_PIN 0  // GPA0 sul MCP23017
#define LED_PIN 1     // GPA1 sul MCP23017
#define INTERRUPT_PIN 2 // Il pin di interrupt su Arduino

volatile bool buttonState = false;

void IRAM_ATTR handleInterrupt() {
  buttonState = mcp.digitalRead(BUTTON_PIN) == LOW;  // Il pulsante è attivo basso
}

void setup() {
  mcp.begin(); // Indirizzo di default 0x20

  mcp.pinMode(BUTTON_PIN, INPUT);
  mcp.pullUp(BUTTON_PIN, HIGH);  // Abilita pull-up interno

  mcp.pinMode(LED_PIN, OUTPUT);
  mcp.digitalWrite(LED_PIN, LOW); // LED spento inizialmente

  // Configura l'interrupt sul pulsante
  mcp.setupInterruptPin(BUTTON_PIN, FALLING); // Trigger su pressione

  pinMode(INTERRUPT_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), handleInterrupt, FALLING);
}

void loop() {
  if (buttonState) {
    mcp.digitalWrite(LED_PIN, HIGH);
  } else {
    mcp.digitalWrite(LED_PIN, LOW);
  }
}

 

Essendo un pulsante temporaneo (momentary switch) che accende il LED solo quando è premuto, il rimbalzo (debounce) normalmente non è un problema.

Perché il debounce non è necessario?

  • Il codice controlla direttamente lo stato del pulsante nel loop(), quindi eventuali piccoli rimbalzi iniziali non influenzano il funzionamento complessivo.
  • Il pulsante è attivo solo mentre viene tenuto premuto, quindi anche se ci fossero rimbalzi all'inizio, dopo pochi millisecondi il pulsante rimane stabilmente premuto e il LED rimane acceso.

Quando servirebbe il debounce?

Il debounce diventa importante quando:

  1. Usi il pulsante per cambiare stato (esempio: premendo una volta accendi il LED, premendo di nuovo lo spegni).
  2. Il pulsante è connesso a un'interruzione (interrupt) molto sensibile, che potrebbe attivarsi più volte a causa dei rimbalzi.

Nel tuo caso, anche se usiamo un interrupt per il pulsante, il debounce non è critico, perché il LED è direttamente controllato dallo stato del pulsante.

 

Ecco un codice che trasforma il pulsante momentaneo in un interruttore con gestione dello stato e debounce.
Premendo il pulsante una volta, il LED si accende. Premendolo di nuovo, si spegne.


Funzionamento

  • Usa il MCP23017 con interrupt per rilevare la pressione del pulsante.
  • Debounce software per evitare attivazioni multiple dovute ai rimbalzi del pulsante.
  • Il LED cambia stato ad ogni pressione del pulsante.

Codice Arduino

#include <Wire.h>
#include <Adafruit_MCP23017.h>

Adafruit_MCP23017 mcp;

#define BUTTON_PIN 0  // Pulsante su GPA0 del MCP23017
#define LED_PIN 1     // LED su GPA1 del MCP23017
#define INTERRUPT_PIN 2 // Pin di interrupt collegato a Arduino

volatile bool buttonPressed = false;  
bool ledState = false;  

void IRAM_ATTR handleInterrupt() {
  buttonPressed = true;  
}

void setup() {
  mcp.begin();  // Inizializza MCP23017 all'indirizzo di default (0x20)

  mcp.pinMode(BUTTON_PIN, INPUT);
  mcp.pullUp(BUTTON_PIN, HIGH);  // Abilita pull-up interno (pulsante attivo LOW)

  mcp.pinMode(LED_PIN, OUTPUT);
  mcp.digitalWrite(LED_PIN, LOW); // LED spento inizialmente

  // Configura l'interrupt per il pulsante (falling edge = pressione)
  mcp.setupInterruptPin(BUTTON_PIN, FALLING);

  pinMode(INTERRUPT_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), handleInterrupt, FALLING);
}

void loop() {
  if (buttonPressed) {
    delay(50); // Debounce software

    if (mcp.digitalRead(BUTTON_PIN) == LOW) { // Verifica se è ancora premuto
      ledState = !ledState; // Cambia stato del LED
      mcp.digitalWrite(LED_PIN, ledState ? HIGH : LOW);
    }

    buttonPressed = false;
  }
}

 

Spiegazione

  1. Interrupt sul pulsante
    • Quando premi il pulsante, l'interrupt imposta buttonPressed = true.
  2. Debounce software
    • Dopo l'interrupt, il codice aspetta 50 ms per evitare rimbalzi.
    • Controlla di nuovo se il pulsante è ancora premuto, per confermare la pressione reale.
  3. Cambio stato del LED
    • Se la pressione è confermata, ledState viene invertito (true → false o viceversa).
    • Il LED viene aggiornato di conseguenza.

Risultato

Premi il pulsante → il LED si accende.
Premi di nuovo → il LED si spegne.
Non ci sono attivazioni multiple involontarie dovute ai rimbalzi.

 


Ricapitolando:

  • Il CJMCU-2317 è un breakout per MCP23017, con 16 I/O controllabili via I2C.
  • Occupa solo 2 pin (SDA, SCL) di Arduino, perfetto per tanti pulsanti o LED.
  • I pin A0, A1, A2 permettono di collegare fino a 8 moduli per un totale di 128 GPIO.
  • Ha interrupt utili per evitare polling continuo dei pulsanti.

Gli indirizzi

 

📌 Indirizzo base: 0x20

Il MCP23017 ha un indirizzo I2C di base 0x20.
Questo indirizzo può essere modificato usando i tre pin A2, A1, A0.

  • Se i pin sono collegati a GND (0) → Il bit corrispondente è 0.
  • Se i pin sono collegati a VCC (1) → Il bit corrispondente è 1.

Dato che ci sono 3 pin di configurazione, puoi avere 8 combinazioni di indirizzi (da 0x20 a 0x27).


🔢 Tabella degli indirizzi I2C

 

A2 A1 A0 Indirizzo I2C (decimale) Indirizzo I2C (esadecimale)
0 0 0 32 0x20
0 0 1 33 0x21
0 1 0 34 0x22
0 1 1 35 0x23
1 0 0 36 0x24
1 0 1 37 0x25
1 1 0 38 0x26
1 1 1 39 0x27

🛠

Esempio pratico

✅ Per usare l’indirizzo 0x20 (default)

  • Collega A2, A1, A0 a GND

✅ Per usare l’indirizzo 0x23

  • Collega A2 a GND
  • Collega A1 a VCC
  • Collega A0 a VCC

✅ Per usare l’indirizzo 0x27

  • Collega A2, A1, A0 a VCC

🔌 Schema di collegamento

Esempio di due moduli MCP23017 con indirizzi diversi:

  • Modulo 1 (Indirizzo 0x20)

    • A2 → GND
    • A1 → GND
    • A0 → GND
  • Modulo 2 (Indirizzo 0x21)

    • A2 → GND
    • A1 → GND
    • A0 → VCC

📜 Esempio di codice per Arduino con 2 MCP23017

 

#include <Wire.h>
#include <Adafruit_MCP23017.h>

Adafruit_MCP23017 mcp1; // Primo MCP23017 (0x20)
Adafruit_MCP23017 mcp2; // Secondo MCP23017 (0x21)

void setup() {
    mcp1.begin(0); // Indirizzo 0x20
    mcp2.begin(1); // Indirizzo 0x21

    mcp1.pinMode(0, OUTPUT); // Pin A0 su MCP23017-1
    mcp2.pinMode(0, OUTPUT); // Pin A0 su MCP23017-2
}

void loop() {
    mcp1.digitalWrite(0, HIGH); // Accendi LED sul primo modulo
    mcp2.digitalWrite(0, LOW);  // Spegni LED sul secondo modulo
    delay(1000);

    mcp1.digitalWrite(0, LOW);
    mcp2.digitalWrite(0, HIGH);
    delay(1000);
}

 

🔍 Conclusione

  • Collegando A0, A1 e A2 a VCC o GND, puoi impostare 8 indirizzi I2C diversi (0x20 - 0x27).
  • Se usi più moduli MCP23017, devi assegnare indirizzi diversi per evitare conflitti.
  • Con questo metodo, puoi controllare fino a 128 GPIO (8 moduli da 16 GPIO ciascuno).

Se vuoi collegare due moduli MCP23017 a Arduino, devi connettere i loro pin I2C (SDA e SCL) in parallelo e assegnare indirizzi diversi.


🔌 Collegamenti tra Arduino e due MCP23017

 

Pin MCP23017 Arduino UNO/Nano
VCC 5V (o 3.3V per ESP)
GND GND
SDA (SI) A4
SCL (SCK) A5
RESET VCC (per tenerlo attivo)
A2, A1, A0 VCC/GND (per cambiare indirizzo)

🛠 Schema pratico

Modulo 1 (Indirizzo 0x20)

  • A2 → GND
  • A1 → GND
  • A0 → GND

Modulo 2 (Indirizzo 0x21)

  • A2 → GND
  • A1 → GND
  • A0 → VCC

I2C (collegamento in parallelo)

  • SDA di entrambi i moduli → A4 di Arduino
  • SCL di entrambi i moduli → A5 di Arduino

📌 Note importanti

  1. I2C funziona in parallelo, quindi SDA e SCL devono essere condivisi.
  2. Indirizzi unici: Devi assegnare indirizzi diversi ai moduli MCP23017.
  3. Pull-up resistors: Se il bus I2C non è già usato con Arduino, è consigliato mettere due resistenze da 4.7kΩ su SDA e SCL.

Ora puoi gestire fino a 128 GPIO con 8 MCP23017! 🚀🔥

 

Se usi solo Arduino e i due moduli MCP23017, devi aggiungere due resistenze di pull-up su SDA e SCL per garantire una comunicazione stabile sul bus I2C.


📌 Perché servono le resistenze di pull-up?

Il protocollo I2C usa SDA (dati) e SCL (clock) come linee open-drain, cioè i dispositivi possono solo mettere a GND le linee, ma non possono portarle direttamente a VCC.
Le resistenze di pull-up servono per tenere SDA e SCL a livello alto (VCC) quando nessun dispositivo le sta usando. Senza di esse, il bus potrebbe non funzionare correttamente.


🔌 Schema dei collegamenti con le resistenze

  1. Collega una resistenza da 4.7kΩ tra SDA e VCC
  2. Collega una resistenza da 4.7kΩ tra SCL e VCC

Queste resistenze non devono stare in serie, ma collegate tra VCC e le linee SDA/SCL.


🛠 Come collegare tutto in parallelo con Arduino

Connessione Arduino Modulo 1 Modulo 2
VCC 5V VCC VCC
GND GND GND GND
SDA (A4) A4 SDA SDA
SCL (A5) A5 SCL SCL
Pull-up SDA 4.7kΩ → VCC - -
Pull-up SCL 4.7kΩ → VCC - -

📜 Conclusione

  • Le resistenze da 4.7kΩ vanno solo su SDA e SCL (una per ogni linea).
  • Collega tutto in parallelo, ovvero SDA di tutti i dispositivi insieme e SCL di tutti i dispositivi insieme.
  • Se Arduino ha già le pull-up interne attivate (su alcune schede le ha da 10kΩ), potresti provare senza le resistenze esterne. Ma se ci sono problemi di comunicazione, allora mettile.

 

🔹 Interrupt su MCP23017: Cosa Sono e Perché Usarli?

 

Gli interrupt ti permettono di rilevare cambiamenti di stato sui pin senza dover controllare continuamente con digitalRead(). Questo è utile quando hai molti pulsanti o sensori collegati e vuoi rispondere immediatamente senza sprecare risorse della CPU.


📌 Vantaggi degli Interrupt

Risparmio di CPU → Arduino non deve controllare continuamente lo stato dei pulsanti.
Risposta immediata → Appena cambia lo stato di un input, l’evento viene notificato.
Meno codice nel loop() → Non serve scrivere cicli continui di lettura, basta gestire l’evento quando avviene.


🔧 Come Funzionano gli Interrupt su MCP23017?

Il MCP23017 ha due pin di interrupt:

  • INTA → segnala quando cambia uno dei primi 8 pin (GPB0–GPB7).
  • INTB → segnala quando cambia uno degli ultimi 8 pin (GPA0–GPA7).

Quando un pulsante viene premuto o rilasciato, il MCP23017 genera un segnale sui pin INTA o INTB. Arduino può collegare questo segnale a un pin di interrupt hardware (come D2 o D3 su Arduino Uno) per eseguire un’azione immediata.


🛠 Esempio Pratico: Rilevare un Pulsante con Interrupt

Immaginiamo di collegare un pulsante al GPB0 del MCP23017 e vogliamo accendere un LED su Arduino quando il pulsante viene premuto.

 

Collegamenti

  • GPB0 ← Pulsante
  • INTA (Pin 19 del MCP23017)D2 di Arduino
  • SDA/SCL → Collega il MCP23017 al bus I²C di Arduino

Codice Arduino con Interrupt

 

#include <Wire.h>
#include <Adafruit_MCP23017.h>

Adafruit_MCP23017 mcp;

#define INTERRUPT_PIN 2  // D2 di Arduino, collegato a INTA del MCP23017
volatile bool buttonPressed = false;

void IRAM_ATTR buttonISR() {
    buttonPressed = true;
}

void setup() {
    Serial.begin(115200);
    mcp.begin();  // Inizializza il MCP23017

    pinMode(INTERRUPT_PIN, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), buttonISR, FALLING);

    mcp.pinMode(0, INPUT);
    mcp.pullUp(0, HIGH);  // Attiva la pull-up interna

    // Configura l’interrupt per attivarsi su cambiamento di stato
    mcp.setupInterruptPin(0, CHANGE);
}

void loop() {
    if (buttonPressed) {
        buttonPressed = false;
        
        // Legge lo stato del pulsante
        bool state = mcp.digitalRead(0);
        Serial.println(state ? "Pulsante Rilasciato" : "Pulsante Premuto");
    }
}

 


🚀 Cosa Fa Questo Codice?

🔹 Configura il pin GPB0 del MCP23017 come ingresso con pull-up.
🔹 Abilita gli interrupt sul MCP23017 per rilevare cambiamenti di stato.
🔹 Collega il pin INTA a D2 di Arduino e usa attachInterrupt() per gestire l’evento.
🔹 Quando il pulsante viene premuto, il codice stampa "Pulsante Premuto" e quando viene rilasciato, stampa "Pulsante Rilasciato".


🔥 Applicazioni Pratiche con Interrupt

  • Tastiere matriciali → Rilevare la pressione di tasti senza controllare continuamente.
  • Sensori di movimento (PIR) → Rispondere istantaneamente a un movimento.
  • Pulsanti di controllo → Ridurre il tempo di risposta in un sistema di input rapido.
  • Sistemi a basso consumo → Arduino può entrare in sleep mode e attivarsi solo quando necessario.

💡 Conclusione
Usare gli interrupt del MCP23017 permette di migliorare efficienza e reattività, eliminando la necessità di leggere continuamente i pin nel loop(). Ottima scelta se hai molti pulsanti o sensori digitali! 🎯

 

🔹 Come Riconoscere Quale Pulsante è Stato Premuto su MCP23017 con Interrupt?

Se hai 6 pulsanti collegati a 6 pin del MCP23017 e l'interrupt INTA connesso al pin D2 di Arduino, devi:

  1. Attivare gli interrupt sul MCP23017 per quei 6 pin.
  2. Quando l'interrupt scatta, leggere lo stato dei pin per capire quale è stato premuto.

📌 Schema di Collegamento

  • MCP23017 (I²C) → Arduino (SDA/SCL)
  • Pulsanti collegati ai pin 0-5 del MCP23017 con pull-up attivati.
  • INTA del MCP23017 → D2 di Arduino.

📷 Schema semplificato:

 

Pulsanti → GPB0–GPB5 del MCP23017

MCP23017 INTA → Arduino D2


🛠 Codice Arduino per Gestire i Pulsanti con Interrupt

 

#include <Wire.h>
#include <Adafruit_MCP23017.h>

Adafruit_MCP23017 mcp;

#define INTERRUPT_PIN 2  // D2 di Arduino
volatile bool interruptTriggered = false;

void IRAM_ATTR handleInterrupt() {
    interruptTriggered = true;
}

void setup() {
    Serial.begin(115200);
    mcp.begin();  // Inizializza il MCP23017

    pinMode(INTERRUPT_PIN, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), handleInterrupt, FALLING);

    // Configura i 6 pin come INPUT con pull-up e abilita gli interrupt
    for (int i = 0; i < 6; i++) {
        mcp.pinMode(i, INPUT);
        mcp.pullUp(i, HIGH);  // Attiva la pull-up interna
        mcp.setupInterruptPin(i, CHANGE);  // Genera interrupt su cambiamento di stato
    }
}

void loop() {
    if (interruptTriggered) {
        interruptTriggered = false;

        // Legge lo stato dei 6 pulsanti
        for (int i = 0; i < 6; i++) {
            if (mcp.digitalRead(i) == LOW) {  // Se il pulsante è stato premuto
                Serial.print("Pulsante premuto su pin: ");
                Serial.println(i);
            }
        }
    }
}

 

🚀 Cosa Fa Questo Codice?

Attiva gli interrupt su 6 pin del MCP23017.
Collega l’uscita INTA al pin D2 di Arduino e usa attachInterrupt().
Quando un pulsante viene premuto, Arduino legge i 6 pin per capire quale è cambiato.
Stampa su seriale il numero del pulsante premuto.


🔥 Possibili Miglioramenti

  • Usare gli interrupt con mirror su INTA: Se vuoi gestire più pin, puoi fare in modo che qualsiasi cambiamento su GPB0–GPB5 attivi l’INTA.
  • Evitare il "ghost press": Se hai rimbalzi nei pulsanti, puoi aggiungere un piccolo delay (debounce) o usare millis().
  • Far eseguire azioni specifiche: Puoi fare qualcosa di più utile invece di stampare il numero del pulsante.

💡 Conclusione
Questa è una tecnica potente per espandere i pin di input e gestirli in modo efficiente con interrupt! 🎯