VI - Programmation IDE Arduino

VI - Programmation de PCF8574 à laide de l'IDE Arduino


Cet exemple est tiré de l’excellent site    de Jérôme TOMSKI


Tutorial PCF8574 arduino, pour ajouter des entrées sorties en i2c, explication fonctionnement module PCF 8574, 8574A, et 8575, avec exemples de code


Exemple: ajouter des entrées à son Arduino, et afficher leur état (PCF 8574 + librairie Wire)


Branchement module PCF8574 sur Arduino Nano, avec broches configurées en entrée, sur pins P0 à P7, incluant réseau de résistances 10k en pull-up à +5V


Cet exemple nous allons le simuler avec Proteus


élaboration du schéma structurel ( XX_Uno_PCF8574_V1_c.pdsprj )


Le code source

/*
   ______               _                  _///_ _           _                   _
  /   _  \             (_)                |  ___| |         | |                 (_)
  |  [_|  |__  ___  ___ _  ___  _ __      | |__ | | ___  ___| |_ _ __ ___  _ __  _  ___  _   _  ___
  |   ___/ _ \| __|| __| |/ _ \| '_ \_____|  __|| |/ _ \/  _|  _| '__/   \| '_ \| |/   \| | | |/ _ \
  |  |  | ( ) |__ ||__ | | ( ) | | | |____| |__ | |  __/| (_| |_| | | (_) | | | | | (_) | |_| |  __/
  \__|   \__,_|___||___|_|\___/|_| [_|    \____/|_|\___|\____\__\_|  \___/|_| |_|_|\__  |\__,_|\___|
                                                                                      | |
                                                                                      \_|
  
 Description :   Programme permettant de lire les entrées P7 à P0 d'un expander PCF8574,
                  en utilisant la librairie Wire (native sous "Arduino IDE")
  Auteur :        Jérôme TOMSKI (https://passionelectronique.fr/)
  Créé le :       26.06.2022

*/

#include <Wire.h>
#define adresseDeLaPucePCF8574 0x20       // Adresse I2C du module PCF8574 (attention, cela varie suivant la configuration des broches A2/A1/A0 de cette puce)

 byte reponseI2C = 0x00 ;
 byte old_reponseI2C = 0x01;

// ========================
// Initialisation programme
// ========================
void setup() {

  // Initialisation de la liaison série (Arduino Uno  -> PC)
  Serial.begin(9600);
  Serial.println(F("===================================================="));
  Serial.println(F("Exemple   (PCF8574) : Lecture des Entrées P7 à P0,  "));
  Serial.println(F("                      en utilisant la librairie Wire"));
  Serial.println(F("===================================================="));
  Serial.println("");

  // Initialisation du contrôleur I2C Arduino
  Wire.begin();

  // Test si la puce PCF 8574 répond bien (dans ce cas, elle renverra la valeur "0" en "endTransmission", si c'est le cas) 
  Wire.beginTransmission(adresseDeLaPucePCF8574);
  if(Wire.endTransmission() != 0) {
    Serial.print(F("Le PCF8574 ne répond pas à l'adresse 0x"));
    Serial.println(adresseDeLaPucePCF8574, HEX);
    Serial.println(F("Arrêt du programme."));
    while(1);
  }

  // Passage des toutes les E/S du PCF8574 en "entrée"
  // ... à noter que les 3 lignes suivantes ne sont pas indispensables, car c'est le mode de démarrage par défaut du PCF8574
  Wire.beginTransmission(adresseDeLaPucePCF8574);
  Wire.write(0b11111111);                             // Mise à 1 des pins P7 à P0 du PCF8574
  Wire.endTransmission();
          // Remarques :
          //    - mettre à "1" une E/S du PCF8574 revient à permettre d'utiliser cette broche en ENTRÉE
          //    - on aura tout de même une tension +Vcc présente sur cette broche, si celle-ci n'est reliée à rien
          //    - cet état haut est dit "faible", en ce sens où on peut l'amener à la masse sans créer de court-circuit
          //      (c'est l'équivalent d'une pull-up, qui serait activée sur cette broche, si vous préférez)
}

// =================
// Boucle principale
// =================
void loop() {

  // Envoi d'une demande de lecture de données, auprès du PCF8574
  Wire.requestFrom(adresseDeLaPucePCF8574, 1);                    // Le "1" signifie qu'on envisage de lire 1 seul octet en retour

  // Récupération de l'octet en question
  if(Wire.available()) {
    reponseI2C = Wire.read();                 // Lecture de l'octet qu'on attendait en retour
    if (reponseI2C != old_reponseI2C) { 
      old_reponseI2C = reponseI2C ;                          
      afficheLes8bitsDeLaValeur(reponseI2C);         // Affichage sur le moniteur série de l'IDE Arduino
    }                                                  // Les "1" diront que l'entrée est à l'état haut (+Vcc, par exemple)
                                                       // Les "0" diront que l'entrée est à l'état bas (0 volt, par exemple)
  } else {
    Serial.println(F("[ERREUR] Impossible de récupérer la valeur I2C renvoyée par le PCF8574"));
    Serial.println(F("Pause du programme."));
    delay(3000);
    old_reponseI2C = !reponseI2C ;
    // Serial.println(F("Arrêt du programme."));
    // while(1); // Arrêt du programme
  }

  // … et on reboucle à l'infini (avec une petite pause au passage)
  delay(100);
}
  
// =========================================================================================
// Fonction : afficheLes8bitsDeLaValeur
//    - permet d'afficher tous les 0 et 1 des 8 bits composant la valeur jointe en paramètre
// =========================================================================================
void afficheLes8bitsDeLaValeur(byte valeurAafficher) {

  // Affichage des bits un à un, sur le moniteur série
  Serial.print((valeurAafficher >> 7) & 0b00000001);      // On décale 7 fois le 8ème bit pour l'amener en position 0, puis on fait un "ET logique" pour le comparer à la valeur 1
  Serial.print((valeurAafficher >> 6) & 0b00000001);      // On décale 6 fois le 7ème bit pour l'amener en position 0, puis on fait un "ET logique" pour le comparer à la valeur 1
  Serial.print((valeurAafficher >> 5) & 0b00000001);      // On décale 5 fois le 6ème bit pour l'amener en position 0, puis on fait un "ET logique" pour le comparer à la valeur 1
  Serial.print((valeurAafficher >> 4) & 0b00000001);      // On décale 4 fois le 5ème bit pour l'amener en position 0, puis on fait un "ET logique" pour le comparer à la valeur 1
  Serial.print((valeurAafficher >> 3) & 0b00000001);      // On décale 3 fois le 4ème bit pour l'amener en position 0, puis on fait un "ET logique" pour le comparer à la valeur 1
  Serial.print((valeurAafficher >> 2) & 0b00000001);      // On décale 2 fois le 3ème bit pour l'amener en position 0, puis on fait un "ET logique" pour le comparer à la valeur 1
  Serial.print((valeurAafficher >> 1) & 0b00000001);      // On décale 1 fois le 2ème bit pour l'amener en position 0, puis on fait un "ET logique" pour le comparer à la valeur 1
  Serial.print((valeurAafficher >> 0) & 0b00000001);      // On décale 0 fois le 1er  bit pour l'amener en position 0, puis on fait un "ET logique" pour le comparer à la valeur 1
  Serial.println();

  // À noter qu'un "1" affiché indiquera que l'entrée en question est au niveau haut (et inversement : si un 0 s'affiche, c'est alors qu'elle est au niveau bas)
}



Simulation sous Proteus 


Aide librairie " Wire.h " --> https://www.arduino.cc/reference/en/language/functions/communication/wire/

La description

Cette bibliothèque vous permet de communiquer avec des appareils I2C/TWI. Sur les cartes Arduino avec la disposition R3 (brochage 1.0), le SDA (ligne de données) et le SCL (ligne d'horloge) se trouvent sur les en-têtes de broches à proximité de la broche AREF. L'Arduino Due a deux interfaces I2C/TWI SDA1 et SCL1 sont proches de la broche AREF et l'autre est sur les broches 20 et 21.

À titre de référence, le tableau ci-dessous indique où se trouvent les broches I2C/TWI sur différentes cartes Arduino.


Carte

Broches I2C/TWI

Uno, Ethernet

A4 (SDA), A5 (SCL)

Mega2560

20 (SDA), 21 (SCL)

Léonard

20 (SDA), 21 (SCL), SDA1, SCL1


Depuis Arduino 1.0, la bibliothèque hérite des fonctions Stream, ce qui la rend cohérente avec les autres bibliothèques de lecture/écriture. Pour cette raison, send()et receive()ont été remplacés par read()et write().


Les versions récentes de la librairie Wire peuvent utiliser des timeouts pour éviter un blocage face à certains problèmes sur le bus, mais cela n'est pas (encore) activé par défaut dans les versions actuelles. Il est recommandé de toujours activer ces délais d'attente lors de l'utilisation de la bibliothèque Wire. Voir la fonction Wire.setWireTimeout pour plus de détails.


Remarque : Il existe des versions 7 et 8 bits des adresses I2C. 7 bits identifient le périphérique et le huitième bit détermine s'il est en cours d'écriture ou de lecture. La bibliothèque Wire utilise des adresses de 7 bits partout. Si vous avez un exemple de code qui utilise une adresse 8 bits, vous voudrez supprimer le bit bas (c'est-à-dire décaler la valeur d'un bit vers la droite), ce qui donne une adresse entre 0 et 127. Cependant, les adresses de 0 à 7 ne sont pas utilisés car sont réservés, la première adresse pouvant être utilisée est 8. Veuillez noter qu'une résistance de rappel est nécessaire lors de la connexion des broches SDA/SCL. Veuillez vous référer aux exemples pour plus d'informations. La carte MEGA 2560 a des résistances pull-up sur les broches 20 et 21 intégrées.


L'implémentation de la bibliothèque Wire utilise un tampon de 32 octets, donc toute communication doit être dans cette limite. Les octets en excès dans une seule transmission seront simplement supprimés.


Pour utiliser, inclure, cette bibliothèque :

#include <Wire.h>

Les fonctions

begin()

La description

Cette fonction initialise la bibliothèque Wire et rejoint le bus I2C en tant que contrôleur ou périphérique. Cette fonction ne doit normalement être appelée qu'une seule fois.

Syntaxe

Wire.begin()

Wire.begin(address)

Paramètres

address : l'adresse de l'esclave 7 bits (facultatif) ; s'il n'est pas spécifié, rejoignez le bus en tant que périphérique contrôleur.

Retour  Aucun.

read()

La description

Cette fonction lit un octet qui a été transmis d'un dispositif périphérique à un dispositif contrôleur après un appel requestFrom()ou a été transmis d'un dispositif contrôleur à un dispositif périphérique. read()hérite de la classe utilitaire Stream.

Syntaxe

Wire.read()

Paramètres

Aucun.

Retour

L'octet suivant reçu.

end()

La description

Désactivez la bibliothèque Wire, en inversant l'effet de Wire.begin(). Pour utiliser à nouveau la bibliothèque Wire après cela, appelez Wire.begin()à nouveau.

Syntaxe  Wire.end()

Paramètres Aucun.

Retour Aucun.

setClock()

La description

Cette fonction modifie la fréquence d'horloge pour la communication I2C. Les périphériques I2C n'ont pas de fréquence d'horloge de travail minimale, mais 100 KHz est généralement la référence.

Syntaxe

Wire.setClock(clockFrequency)

Paramètres

  • clockFrequency : la valeur (en Hertz) de l'horloge de communication souhaitée. Les valeurs acceptées sont 100000 (mode standard) et 400000 (mode rapide). Certains processeurs prennent également en charge 10000 (mode basse vitesse), 1000000 (mode rapide plus) et 3400000 (mode haute vitesse). Veuillez vous référer à la documentation spécifique du processeur pour vous assurer que le mode souhaité est pris en charge.

Retour  Aucun.

requestFrom()

La description

Cette fonction est utilisée par le contrôleur pour demander des octets à un périphérique. Les octets peuvent ensuite être récupérés avec les fonctions available()et read(). À partir d'Arduino 1.0.1, requestFrom()accepte un argument booléen modifiant son comportement pour la compatibilité avec certains appareils I2C. Si vrai, requestFrom()envoie un message d'arrêt après la demande, libérant le bus I2C. Si false, requestFrom()envoie un message de redémarrage après la requête. Le bus ne sera pas libéré, ce qui empêche un autre appareil maître de demander entre les messages. Cela permet à un appareil maître d'envoyer plusieurs requêtes tout en contrôlant. La valeur par défaut est true.

Syntaxe

Wire.requestFrom(address, quantity)

Wire.requestFrom(address, quantity, stop)

Paramètres

  • adresse : l'adresse esclave 7 bits de l'appareil à partir duquel demander des octets.
  • quantité : le nombre d'octets à demander.
  • stop : vrai ou faux. true enverra un message d'arrêt après la requête, libérant le bus. False enverra continuellement un redémarrage après la demande, en gardant la connexion active.

Retour

  • byte : le nombre d'octets renvoyés par le périphérique.

onReceive()

La description

Cette fonction enregistre une fonction à appeler lorsqu'un périphérique reçoit une transmission d'un contrôleur.

Syntaxe

Wire.onReceive(handler)

Paramètres

  • handler : la fonction à appeler lorsque le périphérique reçoit des données ; cela devrait prendre un seul paramètre int (le nombre d'octets lus à partir du périphérique contrôleur) et ne rien renvoyer.

Retour

Aucun.

beginTransmission()

La description

Cette fonction commence une transmission vers le périphérique I2C avec l'adresse donnée. Ensuite, mettez les octets en file d'attente pour la transmission avec la write()fonction et transmettez-les en appelant endTransmission().

Syntaxe

Wire.beginTransmission(address)

Paramètres

  • adresse : l'adresse 7 bits de l'appareil vers lequel transmettre.

Retour

Aucun.

onRequest()

La description

Cette fonction enregistre une fonction à appeler lorsqu'un contrôleur demande des données à un périphérique.

Syntaxe

Wire.onRequest(handler)

Paramètres

  • handler : la fonction à appeler, ne prend aucun paramètre et ne renvoie rien.

Retour

Aucun.

endTransmission()

La description

Cette fonction termine une transmission vers un périphérique qui a été commencée par beginTransmission()et transmet les octets qui ont été mis en file d'attente par write(). À partir d'Arduino 1.0.1, endTransmission()accepte un argument booléen modifiant son comportement pour la compatibilité avec certains appareils I2C. Si vrai, endTransmission()envoie un message d'arrêt après la transmission, libérant le bus I2C. Si faux, endTransmission()envoie un message de redémarrage après la transmission. Le bus ne sera pas libéré, ce qui empêchera un autre appareil contrôleur de transmettre entre les messages. Cela permet à un dispositif de contrôle d'envoyer plusieurs transmissions tout en contrôlant. La valeur par défaut est true.

Syntaxe

Wire.endTransmission() Wire.endTransmission(stop)

Paramètres

  • stop : vrai ou faux. True enverra un message d'arrêt, libérant le bus après la transmission. False enverra un redémarrage, gardant la connexion active.

Retour

  • 0 : succès.
  • 1 : données trop longues pour tenir dans le tampon de transmission.
  • 2 : NACK reçu à la transmission de l'adresse.
  • 3 : NACK reçu à la transmission des données.
  • 4 : autre erreur.
  • 5 : temporisation

setWireTimeout()

La description

Définit le délai d'attente pour les transmissions filaires en mode maître.

Remarque : ces délais d'attente sont presque toujours une indication d'un problème sous-jacent, tel que des appareils qui se comportent mal, du bruit, un blindage insuffisant ou d'autres problèmes électriques. Ces délais d'attente empêcheront votre croquis de se bloquer, mais ne résoudront pas ces problèmes. Dans de telles situations, il y aura souvent (également) une corruption des données qui n'entraîne pas de dépassement de délai ou d'autre erreur et reste non détectée. Ainsi, lorsqu'un délai d'attente se produit, il est probable que certaines données précédemment lues ou écrites soient également corrompues. Des mesures supplémentaires peuvent être nécessaires pour détecter ces problèmes de manière plus fiable (par exemple, sommes de contrôle ou lecture de valeurs écrites) et les résoudre (par exemple, réinitialisation complète du système). Ce délai d'attente et ces mesures supplémentaires doivent être considérés comme une dernière ligne de défense, lorsque cela est possible, la cause sous-jacente doit être corrigée à la place.

Syntaxe

Wire.setWireTimeout(timeout, reset_on_timeout)

Wire.setWireTimeout()

Paramètres

  • timeout a timeout: timeout en microsecondes, si zéro alors la vérification du timeout est désactivée
  • reset_on_timeout : si vrai, le matériel Wire sera automatiquement réinitialisé à l'expiration du délai

Lorsque cette fonction est appelée sans paramètres, un délai d'attente par défaut est configuré qui devrait être suffisant pour empêcher les blocages dans une configuration typique à maître unique.

Retour  Aucun.

write()

La description

Cette fonction écrit des données à partir d'un périphérique en réponse à une demande d'un contrôleur ou met en file d'attente des octets pour transmission d'un contrôleur à un périphérique (entre les appels à beginTransmission()et endTransmission()).

Syntaxe

Wire.write(value) Wire.write(string) Wire.write(data, length)

Paramètres

  • value : une valeur à envoyer sous la forme d'un seul octet.
  • string : une chaîne à envoyer sous la forme d'une série d'octets.
  • data : un tableau de données à envoyer sous forme d'octets.
  • longueur : le nombre d'octets à transmettre.

Retour

Le nombre d'octets écrits (la lecture de ce nombre est facultative).

clearWireTimeoutFlag()

La description

Efface l'indicateur de délai d'attente.

Les délais d'expiration peuvent ne pas être activés par défaut. Consultez la documentation pour Wire.setWireTimeout()plus d'informations sur la configuration des délais d'attente et leur fonctionnement.

Syntaxe

Wire.clearTimeout()

Paramètres

Aucun.

Retour

  • bool : la valeur actuelle du drapeau

available()

La description

Cette fonction renvoie le nombre d'octets disponibles pour la récupération avec read(). Cette fonction doit être appelée sur un périphérique contrôleur après un appel vers requestFrom()ou sur un périphérique à l'intérieur du onReceive()gestionnaire. available()hérite de la classe utilitaire Stream.

Syntaxe

Wire.available()

Paramètres

Aucun.

Retour

Le nombre d'octets disponibles pour la lecture.

getWireTimeoutFlag()

La description

Vérifie si un délai d'attente s'est produit depuis la dernière fois que l'indicateur a été effacé.

Cet indicateur est défini chaque fois qu'un délai d'attente se produit et effacé lorsque Wire.clearWireTimeoutFlag()est appelé ou lorsque le délai d'attente est modifié à l'aide de Wire.setWireTimeout().

Syntaxe

Wire.getWireTimeoutFlag()

Paramètres

Aucun.

Retour

  • bool : la valeur actuelle du drapeau

Créé avec HelpNDoc Personal Edition: Créez sans effort une documentation professionnelle avec l'interface utilisateur propre de HelpNDoc