Skip to Content

Premiers pas avec CrowPanel 2.1inch-HMI ESP32 Rotary Display

Premiers pas avec CrowPanel 2.1inch-HMI ESP32 Rotary Display

Le CrowPanel 2.1inch-HMI ESP32 Rotary Display par Elecrow est un grand module rond qui intègre un microcontrôleur ESP32-S3, un écran tactile circulaire IPS de 2,1 pouces avec une résolution de 480×480, et un encodeur rotatif avec bouton-poussoir dans une seule unité.

Dans ce tutoriel, nous allons parcourir les étapes essentielles pour commencer avec cet écran ; de la configuration initiale aux tests de ses capacités d’affichage et d’entrée. Vous apprendrez à lire les données de l’encodeur rotatif pour une saisie utilisateur fluide et à capturer les événements tactiles sur l’écran circulaire.

Pièces requises

Vous aurez uniquement besoin du CrowPanel 2.1inch-HMI ESP32 Rotary Display pour ce projet. Le module d’affichage est livré avec un câble USB et un connecteur GPIO, aucun matériel supplémentaire n’est nécessaire.

CrowPanel 2.1inch-HMI ESP32 Rotary Display

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

Matériel du CrowPanel 2.1inch-HMI ESP32 Rotary Display

Le CrowPanel 2.1inch-HMI ESP32 Rotary Display est un module d’affichage qui combine un écran IPS rond haute résolution, un écran tactile capacitif et un encodeur rotatif avec fonction bouton-poussoir.

Le module est construit autour du système sur puce ESP32‑S3R8 qui utilise un processeur dual-core Xtensa LX7 32 bits capable d’atteindre des fréquences jusqu’à 240 MHz. Il est équipé de 8 Mbit de PSRAM ainsi que de 16 Mbyte de mémoire flash embarquée.

La partie affichage est un panneau IPS RGB circulaire de 2,1 pouces (ST7701 contrôleur) avec une résolution de 480 × 480 pixels, doté d’une couche tactile capacitive supportant une opération tactile sur tout l’écran et d’un rétroéclairage.

CrowPanel 2.1inch-HMI ESP32 Rotary Display
CrowPanel 2.1inch-HMI ESP32 Rotary Display (source)

En termes d’opération physique, le matériel intègre un mécanisme de bouton rotatif « knob ». Le bouton peut tourner dans le sens horaire et antihoraire ainsi que s’enfoncer (fonctionnant comme un interrupteur) pour l’entrée utilisateur.

Pour la connectivité et l’extension, le module supporte à la fois des interfaces sans fil et filaires. Côté sans fil, il dispose du WiFi IEEE 802.11 a/b/g/n (2,4 GHz) et du Bluetooth Low Energy (BLE) / Bluetooth 5.0.

Côté filaire, le module expose une prise d’alimentation/interface 5 V qui sert à la fois d’alimentation et de port de programmation. De plus, la carte offre trois interfaces d’extension : une interface UART, une interface I2C, et un connecteur FPC (12 broches) pour une connectivité supplémentaire.

Connectors of the CrowPanel 2.1inch-HMI ESP32 Rotary Display
Connecteurs du CrowPanel 2.1inch-HMI ESP32 Rotary Display (source)

Définitions des broches

Le tableau suivant liste les broches nécessaires pour contrôler tous les composants du module d’affichage :

Groupe de fonctionSignal / UsageNuméro de brocheNotes
Interface I²CSDA38Ligne de données I²C principale
SCL39Ligne d’horloge I²C principale
Encodeur rotatifEncodeur A42Canal quadrature A
Encodeur B4Canal quadrature B
Rétroéclairage de l’affichageContrôle du rétroéclairage6Broche capable de PWM utilisée pour la luminosité du rétroéclairage LCD
Contrôle OLEDRESET–1Pas de broche reset utilisée (reset logiciel)
Interface d’affichage RGBCS16Ligne de sélection de puce
SCK2Horloge série d’affichage
SDA1Données série d’affichage
DE40Activation des données
VSYNC7Synchronisation verticale
HSYNC15Synchronisation horizontale
PCLK41Horloge pixel
Broches de données couleur RGB (format 5-6-5)R046Bit rouge 0
R13Bit rouge 1
R28Bit rouge 2
R318Bit rouge 3
R417Bit rouge 4
G014Bit vert 0
G113Bit vert 1
G212Bit vert 2
G311Bit vert 3
G410Bit vert 4
G59Bit vert 5
B05Bit bleu 0
B145Bit bleu 1
B248Bit bleu 2
B347Bit bleu 3
B421Bit bleu 4
PCF8574 I/O ExpanderAdresse I²C0x21Connecté aux broches 38 (SDA) et 39 (SCL)

Spécifications techniques

Le tableau suivant résume les spécifications techniques du module CrowPanel 2.1inch-HMI ESP32 Rotary Display :

Catégorie de spécificationDétails
Processeur principalSoC : Xtensa LX7 dual-core (32 bits) dans l’ESP32‑S3R8 (jusqu’à 240 MHz)
Mémoire & stockageRAM système : 512 kB SRAM ; PSRAM : 8 MB ; Flash : 16 MB
Panneau d’affichageTaille : 2,1 pouces ; Type : IPS ; Résolution : 480 × 480 pixels ; Tactile : couche tactile capacitive sur tout l’écran
Mécanismes d’entrée utilisateurBouton rotatif « knob » (rotation horaire/antihoraire + pression complète) ; entrée tactile capacitive sur la surface de l’écran
Connectivité sans filWiFi : IEEE 802.11 a/b/g/n (2,4 GHz) ; Bluetooth : BLE / Bluetooth 5.0
Ports d’interface / extensionEntrée 5 V (charge et programmation) ; interface UART : 1× UART0 + 1× UART1 via ZX-MX 1.25-4P ; interface I²C ; connecteur FPC 12 broches (alimentation/programmation et GPIO)
Boutons & indicateursBouton RESET embarqué ; bouton BOOT ; bouton-poussoir du knob (bouton de confirmation) ; LED indicatrice d’alimentation ; rétroéclairage LCD
Alimentation & tensionEntrée module : 5 V / 1 A ; niveau logique du chip principal : 3,3 V ; fonctionnement nominal du module à 5 V
Mécanique / physiqueDimensions : 79 × 79 × 30 mm ; poids net : 80 g ; coque : alliage d’aluminium + plastique + acrylique
Plage de températureFonctionnement : –20 °C à +65 °C ; stockage : –40 °C à +80 °C
Support logiciel / environnement de développementCompatible avec Arduino IDE, ESP-IDF, Lua RTOS, MicroPython, PlatformIO ; bibliothèque graphique UI : support LVGL

Installation du Core ESP32

Si c’est votre premier projet avec une carte de la série ESP32, vous devrez d’abord installer le core ESP32. Cependant, pour le CrowPanel 2.1inch Display, vous devrez installer une version spécifique (2.0.14) du core ESP32.

Commencez par ouvrir la boîte de dialogue Préférences dans l’Arduino IDE en sélectionnant « Preferences… » dans le menu « File ». Cela ouvrira la boîte de dialogue Préférences affichée ci-dessous.

Sous l’onglet Settings, vous trouverez une zone de saisie en bas de la boîte de dialogue intitulée « Additional boards manager URLs » :

Additional boards manager URLs in Preferences
URLs supplémentaires du gestionnaire de cartes dans Préférences

Dans ce champ, copiez l’URL suivante :

https://espressif.github.io/arduino-esp32/package_esp32_dev_index.json

Cela permettra à l’Arduino IDE de savoir où trouver les bibliothèques du core ESP32. Ensuite, nous installerons les cartes ESP32 via le Boards Manager.

Ouvrez le Boards Manager via « Tools -> Boards -> Board Manager ». Le gestionnaire de cartes apparaîtra dans la barre latérale gauche. Tapez « ESP32 » dans le champ de recherche en haut et vous verrez deux types de cartes ESP32 ; les « Arduino ESP32 Boards » et les cartes « esp32 by Espressif ». Nous voulons les « esp32 libraries by Espressif ».

Dans le menu déroulant, sélectionnez la version « 2.0.14 » puis cliquez sur le bouton « INSTALL ». La capture d’écran ci-dessous montre ce que vous devriez voir dans le BOARDS MANAGER après une installation réussie du Core ESP32 2.0.14 :

Install ESP32 Core 2.0.14
Installer le Core ESP32 2.0.14

Notez que vous pouvez installer des versions jusqu’à 2.0.17 mais pas 3.x. Les bibliothèques que nous installerons dans la section suivante ne fonctionneront pas avec le core ESP32 3.x.

Sélection de la carte

Enfin, nous devons sélectionner une carte ESP32. Pour le CrowPanel 2.1inch-HMI ESP32 Rotary Display, choisissez la carte générique « ESP32S3 Dev Module ». Pour cela, cliquez sur le menu déroulant puis sur « Select other board and port… »:

Drop-down Menu for Board Selection
Menu déroulant pour la sélection de la carte

Cela ouvrira une boîte de dialogue où vous pouvez taper « esp32s3 dev » dans la barre de recherche. Vous verrez la carte « ESP32S3 Dev Module » sous Boards. Cliquez dessus, sélectionnez le port COM pour l’activer, puis cliquez sur OK :

Board Selection Dialog "ESP32S3 Dev Module" board
Boîte de dialogue de sélection de carte « ESP32S3 Dev Module »

Notez que vous devez connecter la carte via le câble USB à votre ordinateur avant de pouvoir sélectionner un port COM. Connectez le câble USB fourni avec le module CrowPanel directement au port marqué « USB-5V-IN » comme montré ci-dessous :

USB Port of CrowPanel Display Module
Port USB du module CrowPanel Display

Paramètres de l’outil

Voici les paramètres que vous devez utiliser avec le module CrowPanel Display. Vous les trouverez dans le menu Tools de votre Arduino IDE.

Tool settings for CrowPanel 2.1inch-HMI ESP32 Rotary Display
Paramètres de l’outil pour CrowPanel 2.1inch-HMI ESP32 Rotary Display

Le plus important pour les exemples de code dans les sections suivantes est de régler « USB CDC on Boot » sur « Enabled ». Cela vous permet d’envoyer ou de recevoir des données via le port série, ce qui est nécessaire pour le débogage. Vous devrez aussi configurer correctement « OPI PSRAM », « Huge APP » et « Flash Size » pour les exemples de code d’affichage.

Installation des bibliothèques

Ensuite, nous devons installer des bibliothèques spécifiques et leurs versions pour que le code du CrowPanel 2.1inch-HMI ESP32 Rotary Display fonctionne.

Rendez-vous sur la page Elecrow github repo pour le CrowPanel 2.1inch-HMI Display. Cliquez sur le bouton vert « <> Code » puis sur « Download ZIP » pour télécharger le dépôt sous forme de fichier ZIP :

Décompressez ensuite le fichier ZIP pour en extraire le contenu. Vous devriez voir les fichiers suivants dans un dossier décompressé nommé « CrowPanel-2.1inch-HMI-ESP32-Rotary-Display-480-480-IPS-Round-Touch-Knob-Screen-master » :

Ignorez les fichiers liés à l’écran « CrowPanel 1.28inch … ». Nous devons seulement copier le contenu du dossier « example/libraries » dans le dossier « libraries » de l’Arduino IDE. Sous Windows, le dossier « libraries » se trouve généralement sous :

C:\Users\<username>\OneDrive\Documents\Arduino\libraries

Comme ce dossier contient déjà des bibliothèques installées, je vous recommande de le renommer temporairement, par exemple en « _libraries », et de créer un nouveau dossier nommé « libraries ». Cela évite les conflits avec vos bibliothèques déjà installées sans les perdre. Vous pourrez facilement revenir en arrière plus tard. L’image ci-dessous montre à quoi devrait ressembler votre dossier « Arduino » avec les bibliothèques :

Ensuite, copiez les fichiers du dossier « example/libraries » dans le nouveau dossier « libraries » comme montré ci-dessous :

Vous n’avez pas besoin de copier les bibliothèques barrées, car elles sont liées à l’interface LVGL que nous n’utiliserons pas. Dans les sections suivantes, je vous montrerai quelques exemples de code pour tester l’écran.

Exemple de code : Interface série

Nous commençons par tester la communication série. Ouvrez votre Arduino IDE, saisissez le code suivant et téléversez-le sur le module CrowPanel Display.

void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.println("Makerguides");
  delay(2000);
}

Puis ouvrez le Moniteur Série et vous devriez voir le texte « Makerguides » s’afficher toutes les deux secondes. Sinon, vérifiez que vos paramètres Tools et le débit en bauds (115200) sont corrects. En particulier, « USB CDC on Boot » doit être sur « Enabled ».

Exemple de code : Encodeur

Dans cet exemple, vous apprendrez à utiliser l’encodeur et son bouton. Nous utiliserons une approche basée sur interruption pour lire un encodeur rotatif quadrature et le PCF8574 pour lire l’état du bouton-poussoir de l’encodeur.

La rotation de l’encodeur est traitée dans une ISR rapide avec détection de direction, tandis que la boucle principale se concentre sur le rapport des changements et la lecture de l’état du bouton. Jetez un œil rapide au code avant que nous en discutions les détails.

#include <Wire.h>
#include <PCF8574.h>

// I2C to PCF8574
#define I2C_SDA_PIN 38
#define I2C_SCL_PIN 39

#define ENCODER_CLK 42  
#define ENCODER_DT 4   

volatile int counter = 0;
volatile int encState = 0;
volatile int oldState = -1;
volatile bool hasChanged = true;

PCF8574 pcf8574(0x21);

void IRAM_ATTR encoder_irq() {
  encState = digitalRead(ENCODER_CLK);
  if (encState != oldState) {
    counter += (digitalRead(ENCODER_DT) == encState) ? +1 : -1;
    oldState = encState;
    hasChanged = true;
  }
}

void initPins() {
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
  pcf8574.pinMode(P5, INPUT_PULLUP);  //encoder SW
  if (!pcf8574.begin()) {
    Serial.println("Can't init pcf8574");
  }
}

void initEncoder() {
  pinMode(ENCODER_CLK, INPUT_PULLUP);
  pinMode(ENCODER_DT, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), encoder_irq, CHANGE);
}

void setup() {
  Serial.begin(115200);
  initPins();
  initEncoder();
}

void loop() {
  if (hasChanged) {
    hasChanged = false;
    Serial.printf("COUNTER: %d\n", counter);
  }
  int button = pcf8574.digitalRead(P5, true);
  if (!button) {
    Serial.printf("BTN pressed\n");
    delay(500);
  }  
  delay(1);
}

Imports

Le sketch commence par inclure deux bibliothèques qui gèrent la communication I2C et l’expandeur d’E/S PCF8574. Ces headers doivent être inclus avant d’utiliser les objets Wire ou PCF8574.

#include <Wire.h>
#include <PCF8574.h>

Wire.h fournit l’interface I2C (TWI) standard d’Arduino. PCF8574.h encapsule l’accès bas niveau I2C au PCF8574 pour que vous puissiez traiter ses broches presque comme des broches digitales normales.

Constantes

Ensuite, le code définit les broches utilisées pour l’I2C et pour l’encodeur rotatif :

#define I2C_SDA_PIN 38
#define I2C_SCL_PIN 39

#define ENCODER_CLK 42  
#define ENCODER_DT 4   

I2C_SDA_PIN et I2C_SCL_PIN sélectionnent quelles broches ESP32 sont utilisées comme SDA et SCL pour le bus I2C matériel. Sur l’ESP32, vous pouvez router l’I2C vers différentes broches, donc il est nécessaire de les spécifier ici.

ENCODER_CLK et ENCODER_DT sont les deux sorties quadrature de l’encodeur rotatif. Le signal CLK est typiquement le canal principal utilisé pour l’interruption, et DT est le second canal utilisé pour déterminer la direction de rotation.

État global et variables volatiles

Le code utilise plusieurs variables globales pour suivre l’état de l’encodeur. Ces variables sont marquées volatile car elles sont modifiées dans une routine d’interruption.

volatile int counter = 0;
volatile int encState = 0;
volatile int oldState = -1;
volatile bool hasChanged = true;

counter contient la position accumulée de l’encodeur. Chaque pas de l’encodeur incrémente ou décrémente cette valeur.

encState représente le niveau logique actuel de la broche CLK de l’encodeur tel que vu dans le gestionnaire d’interruption. oldState garde l’état précédent de CLK pour que le code puisse détecter les transitions et éviter de compter plusieurs fois le même niveau.

hasChanged est utilisé comme un drapeau pour signaler à la boucle principale que le compteur a été mis à jour dans l’interruption. Marquer ces variables comme volatile indique au compilateur qu’elles peuvent changer à tout moment et ne doivent pas être mises en cache dans des registres, ce qui est crucial lorsqu’elles sont modifiées par une ISR.

Objet PCF8574

Le code crée ensuite une instance globale de la classe PCF8574, en spécifiant l’adresse I2C de la puce.

PCF8574 pcf8574(0x21);

Cette ligne indique à la bibliothèque PCF8574 que l’expandeur d’E/S est accessible à l’adresse I2C 0x21. Tous les appels suivants à pcf8574.pinMode ou pcf8574.digitalRead utiliseront cette adresse pour communiquer avec ce périphérique spécifique sur le bus I2C.

Routine d’interruption de l’encodeur

La partie la plus sensible au timing du code est la routine d’interruption (ISR) qui gère les changements de l’encodeur. Elle est placée en IRAM avec l’attribut IRAM_ATTR pour s’exécuter rapidement et de manière fiable sur l’ESP32.

void IRAM_ATTR encoder_irq() {
  encState = digitalRead(ENCODER_CLK);
  if (encState != oldState) {
    counter += (digitalRead(ENCODER_DT) == encState) ? +1 : -1;
    oldState = encState;
    hasChanged = true;
  }
}

IRAM_ATTR garantit que la fonction est stockée dans la RAM d’instruction au lieu de la flash, ce qui évite les délais liés à l’accès à la flash et est recommandé pour les ISR sur ESP32.

Dans l’ISR, le niveau actuel du signal CLK de l’encodeur est lu avec digitalRead(ENCODER_CLK) et stocké dans encState. Le code vérifie ensuite si cet état est différent de oldState. Cette condition filtre les appels redondants car l’interruption est déclenchée sur tout changement (front montant ou descendant) et le gestionnaire peut s’exécuter plusieurs fois avec le même niveau logique.

Si l’état a changé, la direction de rotation est calculée en comparant le signal DT avec CLK. La ligne suivante lit la broche DT et vérifie si son niveau correspond à celui de CLK :

counter += (digitalRead(ENCODER_DT) == encState) ? +1 : -1;

Pour un encodeur quadrature, cette relation indique la direction de rotation. Si DT égale CLK, l’encodeur tourne dans un sens et counter est incrémenté ; sinon, counter est décrémenté.

Après la mise à jour du compteur, oldState est mis à jour avec le nouvel état CLK et hasChanged est réglé à true. Ce drapeau informe la boucle principale qu’il y a de nouvelles données d’encodeur à traiter ou afficher. L’ISR reste courte et efficace, ce qui est une bonne pratique pour les routines d’interruption.

Initialisation des broches et de l’I2C

La fonction initPins configure le bus I2C et la broche PCF8574 utilisée pour le bouton-poussoir de l’encodeur.

void initPins() {
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
  pcf8574.pinMode(P5, INPUT_PULLUP);  //encoder SW
  if (!pcf8574.begin()) {
    Serial.println("Can't init pcf8574");
  }
}

Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN); initialise le périphérique I2C avec les broches SDA et SCL personnalisées.

pcf8574.pinMode(P5, INPUT_PULLUP); configure la broche P5 du PCF8574 en entrée avec une résistance de tirage interne. Cette broche est connectée au bouton-poussoir de l’encodeur. Une configuration pull-up signifie que la broche lira un niveau haut quand le bouton n’est pas pressé et bas quand il est pressé, en supposant que le bouton relie la broche à la masse.

pcf8574.begin() démarre la communication avec le PCF8574 à l’adresse I2C configurée. En cas d’échec, le code affiche un message d’erreur via Serial.println, ce qui aide à diagnostiquer les problèmes de câblage ou d’adresse.

Initialisation de l’encodeur

La fonction initEncoder configure les deux signaux de l’encodeur et attache une interruption à la broche CLK.

void initEncoder() {
  pinMode(ENCODER_CLK, INPUT_PULLUP);
  pinMode(ENCODER_DT, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), encoder_irq, CHANGE);
}

pinMode(ENCODER_CLK, INPUT_PULLUP); et pinMode(ENCODER_DT, INPUT_PULLUP); configurent les deux canaux de l’encodeur en entrée avec résistances pull-up internes. C’est une configuration typique pour les encodeurs rotatifs mécaniques, qui relient généralement les broches à la masse selon un code Gray lors de la rotation du bouton.

attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), encoder_irq, CHANGE); attache le gestionnaire d’interruption encoder_irq à la broche CLK de l’encodeur. Le mode CHANGE signifie que l’ISR sera déclenchée sur les fronts montant et descendant du signal. Cela offre une résolution plus élevée et un comptage plus précis, car l’encodeur est échantillonné à chaque transition.

Setup

La fonction setup est exécutée une fois au démarrage et fournit une séquence d’initialisation.

void setup() {
  Serial.begin(115200);
  initPins();
  initEncoder();
}

Serial.begin(115200); démarre le port série à 115200 bauds, utile pour le débogage et la surveillance. Elle doit être appelée avant tout appel à Serial.print ou Serial.printf.

initPins(); initialise le bus I2C et l’expandeur PCF8574. Cela garantit que le bouton de l’encodeur peut être lu et que le matériel I2C est prêt.

initEncoder(); configure les broches GPIO utilisées par l’encodeur rotatif et met en place l’interruption pour que les événements de rotation soient capturés dès le début de la boucle principale.

Loop

La fonction loop s’exécute en boucle et gère deux tâches principales : afficher la valeur de l’encodeur lorsqu’elle change et lire l’état du bouton-poussoir de l’encodeur.

void loop() {
  if (hasChanged) {
    hasChanged = false;
    Serial.printf("COUNTER: %d\n", counter);
  }
  int button = pcf8574.digitalRead(P5, true);
  if (!button) {
    Serial.printf("BTN pressed\n");
    delay(500);
  }  
  delay(1);
}

Le premier bloc vérifie si le compteur de l’encodeur a été mis à jour par l’ISR. Le drapeau hasChanged est mis à true dans l’interruption lorsque la position de l’encodeur change.

if (hasChanged) {
  hasChanged = false;
  Serial.printf("COUNTER: %d\n", counter);
}

Si hasChanged est vrai, le drapeau est immédiatement remis à false et la valeur actuelle de counter est affichée sur le moniteur série avec Serial.printf. Cela évite d’imprimer dans l’ISR et découple la logique critique d’interruption de la sortie série plus lente.

Ensuite, le code lit l’état du bouton-poussoir de l’encodeur, connecté au PCF8574.

int button = pcf8574.digitalRead(P5, true);
if (!button) {
  Serial.printf("BTN pressed\n");
  delay(500);
}

pcf8574.digitalRead(P5, true); lit le niveau logique actuel de la broche P5. Passer true en second argument indique généralement à la bibliothèque d’effectuer une lecture I2C immédiate plutôt que d’utiliser une valeur en cache, garantissant une lecture fraîche à chaque fois.

Comme la broche est configurée en pull-up, un bouton non pressé lit un niveau haut (non zéro), et un bouton pressé tire la ligne à la masse et lit un niveau bas (zéro). Donc, la condition if (!button) vérifie l’état pressé. Quand le bouton est détecté pressé, le code affiche "BTN pressed" et attend 500 millisecondes. Ce délai agit comme un simple anti-rebond et empêche le message d’être imprimé en continu tant que le bouton est maintenu.

Enfin, la boucle se termine par un court délai.

delay(1);

Ce petit délai donne au planificateur la possibilité de gérer les tâches en arrière-plan et évite une boucle très serrée et gourmande en CPU. Il assure aussi que les sous-systèmes I2C et série ont un peu de marge.

Sortie sur le moniteur série

La capture d’écran suivante montre ce que vous devriez voir sur le moniteur série lorsque vous tournez la bague de l’encodeur ou appuyez sur le bouton :

Exemple de code : Affichage et écran tactile

Le sketch suivant montre comment utiliser l’affichage et le pavé tactile. Il définit le fond d’écran en rouge, affiche le texte « Makerguides » au centre et dessine des cercles noirs à chaque point de contact sur l’écran :

Jetez un œil rapide au code complet ci-dessous avant que nous en discutions les détails :

#include <Wire.h>
#include <Arduino_GFX_Library.h>
#include <PCF8574.h>
#include <Adafruit_CST8XX.h>

// I2C to PCF8574
#define I2C_SDA_PIN 38
#define I2C_SCL_PIN 39
#define BKL_PIN 6

#define I2C_TOUCH_ADDR 0x15

PCF8574 pcf8574(0x21);

Adafruit_CST8XX tsPanel = Adafruit_CST8XX();

Arduino_ESP32RGBPanel *bus = new Arduino_ESP32RGBPanel(
  16 /* CS */, 2 /* SCK */, 1 /* SDA */,
  40 /* DE */, 7 /* VSYNC */, 15 /* HSYNC */, 41 /* PCLK */,
  46 /* R0 */, 3 /* R1 */, 8 /* R2 */, 18 /* R3 */, 17 /* R4 */,
  14 /* G0 */, 13 /* G1 */, 12 /* G2 */, 11 /* G3 */, 10 /* G4 */, 9 /* G5 */,
  5 /* B0 */, 45 /* B1 */, 48 /* B2 */, 47 /* B3 */, 21 /* B4 */
);

Arduino_ST7701_RGBPanel *gfx = new Arduino_ST7701_RGBPanel(
  bus,
  GFX_NOT_DEFINED,  // RST pin (not used, we reset via PCF8574)
  0,                // rotation
  false,            // IPS
  480, 480,         // width, height
  st7701_type5_init_operations,
  sizeof(st7701_type5_init_operations),
  true,       // BGR
  10, 4, 20,  // hsync front porch, pulse width, back porch
  10, 4, 20   // vsync front porch, pulse width, back porch
);

void initBacklight() {
  pinMode(BKL_PIN, OUTPUT);
  analogWrite(BKL_PIN, 200);
}

void initPins() {
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);

  pcf8574.pinMode(P0, OUTPUT);        //tp RST
  pcf8574.pinMode(P2, OUTPUT);        //tp INT
  pcf8574.pinMode(P3, OUTPUT);        //lcd power
  pcf8574.pinMode(P4, OUTPUT);        //lcd reset

  if (!pcf8574.begin()) {
    Serial.println("Can't init pcf8574");
  }

  // LCD
  pcf8574.digitalWrite(P3, HIGH);
  delay(100);
  pcf8574.digitalWrite(P4, HIGH);
  delay(100);
  pcf8574.digitalWrite(P4, LOW);
  delay(120);
  pcf8574.digitalWrite(P4, HIGH);
  delay(120);

  // Touchpad
  pcf8574.digitalWrite(P0, HIGH);
  delay(100);
  pcf8574.digitalWrite(P0, LOW);
  delay(120);
  pcf8574.digitalWrite(P0, HIGH);
  delay(120);
  pcf8574.digitalWrite(P2, HIGH);
  delay(120);

  if (!tsPanel.begin(&Wire, I2C_TOUCH_ADDR)) {
    Serial.println("No touchscreen found");
  } 
}

void initLCD() {
  gfx->begin();
  gfx->fillScreen(RED);
  gfx->setTextSize(5);
  gfx->setTextColor(WHITE);  
}

void drawOnLCD() {
  gfx->setCursor(90, 210);
  gfx->print("Makerguides");
}

void setup() {
  Serial.begin(115200);
  initPins();
  initBacklight();
  initLCD();
  drawOnLCD();
}

void loop() {
  if (tsPanel.touched()) {
    CST_TS_Point p = tsPanel.getPoint(0);
    Serial.printf("TOUCH: %d, %d\n", p.x, p.y);
    gfx->fillCircle(p.x, p.y, 10, BLACK);
    delay(10);
  }
}

Imports

Ce sketch commence par inclure les bibliothèques nécessaires pour l’I2C, les graphismes, l’expandeur d’E/S et le contrôleur tactile.

#include <Wire.h>
#include <Arduino_GFX_Library.h>
#include <PCF8574.h>
#include <Adafruit_CST8XX.h>

Wire.h est la même bibliothèque I2C que vous avez utilisée précédemment avec le PCF8574. Arduino_GFX_Library.h fournit l’abstraction graphique pour piloter le panneau RGB et le driver d’affichage ST7701. PCF8574.h gère à nouveau l’expandeur d’E/S sur le bus I2C. Adafruit_CST8XX.h est le pilote pour le contrôleur tactile capacitif CST8xx, connecté via I2C et utilisé pour lire les coordonnées tactiles de l’écran rond.

Constantes et configuration I2C

Le code définit les assignations de broches pour le bus I2C, le contrôle du rétroéclairage, et l’adresse I2C du contrôleur tactile. Cela suit le même schéma que dans l’exemple précédent où les broches SDA et SCL étaient explicitement configurées.

#define I2C_SDA_PIN 38
#define I2C_SCL_PIN 39
#define BKL_PIN 6

#define I2C_TOUCH_ADDR 0x15

I2C_SDA_PIN et I2C_SCL_PIN sélectionnent les broches ESP32 utilisées pour le bus I2C partagé qui connecte à la fois le PCF8574 et le contrôleur tactile CST8xx. BKL_PIN est une broche capable de PWM qui pilote le rétroéclairage LCD. I2C_TOUCH_ADDR spécifie l’adresse I2C 7 bits du contrôleur tactile pour que le pilote puisse communiquer avec lui.

Objets globaux : PCF8574, contrôleur tactile, bus RGB et panneau

Les objets globaux configurent l’accès à l’expandeur d’E/S, au contrôleur tactile et à l’affichage.

pcf8574(0x21) est le même que dans l’exemple précédent : il crée une instance liée à l’adresse I2C 0x21. Cette puce contrôle plusieurs lignes de commande telles que l’alimentation LCD, le reset LCD et le reset et interruption tactile.

tsPanel est une instance du pilote Adafruit CST8xx, créée avec le constructeur par défaut et initialisée plus tard avec begin() en utilisant l’interface Wire partagée et l’adresse tactile.

PCF8574 pcf8574(0x21);

Adafruit_CST8XX tsPanel = Adafruit_CST8XX();

Panneau RGB

L’objet suivant configure le bus de données RGB entre l’ESP32-S3 et le driver d’affichage ST7701. Il liste toutes les broches de timing et de couleur utilisées pour l’interface RGB parallèle.

Arduino_ESP32RGBPanel *bus = new Arduino_ESP32RGBPanel(
  16 /* CS */, 2 /* SCK */, 1 /* SDA */,
  40 /* DE */, 7 /* VSYNC */, 15 /* HSYNC */, 41 /* PCLK */,
  46 /* R0 */, 3 /* R1 */, 8 /* R2 */, 18 /* R3 */, 17 /* R4 */,
  14 /* G0 */, 13 /* G1 */, 12 /* G2 */, 11 /* G3 */, 10 /* G4 */, 9 /* G5 */,
  5 /* B0 */, 45 /* B1 */, 48 /* B2 */, 47 /* B3 */, 21 /* B4 */
);

Cet objet Arduino_ESP32RGBPanel définit le mappage exact entre les GPIO ESP32 et les signaux du panneau RGB. Les trois premières broches sont utilisées comme lignes de contrôle pour le bus du panneau (sélection de puce, horloge et données pour l’interface de configuration). DE, VSYNC, HSYNC, et PCLK sont les signaux de timing, similaires à ceux d’une interface d’affichage RGB classique. Les groupes restants mappent les cinq bits rouges, six bits verts et cinq bits bleus du bus couleur 16 bits (format 5-6-5) à des broches GPIO spécifiques.

L’objet graphique de haut niveau gfx représente l’écran LCD contrôlé par ST7701 piloté via ce bus RGB.

Arduino_ST7701_RGBPanel *gfx = new Arduino_ST7701_RGBPanel(
  bus,
  GFX_NOT_DEFINED,  // RST pin (not used, we reset via PCF8574)
  0,                // rotation
  false,            // IPS
  480, 480,         // width, height
  st7701_type5_init_operations,
  sizeof(st7701_type5_init_operations),
  true,       // BGR
  10, 4, 20,  // hsync front porch, pulse width, back porch
  10, 4, 20   // vsync front porch, pulse width, back porch
);

Le premier argument est le bus RGB défini précédemment. La broche de reset est définie à GFX_NOT_DEFINED car le panneau est réinitialisé via les broches PCF8574 au lieu d’un GPIO ESP32 direct. Le paramètre rotation est zéro, ce qui signifie l’orientation par défaut. Le drapeau false IPS est une option du panneau, et 480, 480 définissent la résolution d’affichage.

Le pointeur st7701_type5_init_operations et la taille fournissent la séquence d’initialisation bas niveau pour le driver ST7701, que la bibliothèque enverra au panneau au démarrage. Le drapeau true BGR indique au driver de traiter les données couleur en BGR au lieu de RGB.

Enfin, les valeurs de porch horizontal et vertical et de pulse définissent le timing RGB, similaires aux paramètres trouvés dans le timing vidéo classique : front porch, largeur d’impulsion et back porch pour HSYNC et VSYNC.

Initialisation du rétroéclairage

Le rétroéclairage est contrôlé séparément de l’électronique du panneau. La fonction initBacklight configure la broche du rétroéclairage et définit une luminosité initiale via PWM.

void initBacklight() {
  pinMode(BKL_PIN, OUTPUT);
  analogWrite(BKL_PIN, 200);
}

pinMode(BKL_PIN, OUTPUT); configure la broche du rétroéclairage en sortie. analogWrite(BKL_PIN, 200); active le PWM sur cette broche avec un rapport cyclique correspondant à un niveau de luminosité de 200 sur une échelle typique de 0 à 255. Cela vous permet d’ajuster la luminosité plus tard en écrivant une valeur différente sans changer le matériel.

Initialisation des broches et périphériques

La fonction initPins configure le bus I2C partagé, configure les broches PCF8574, effectue les séquences de reset et d’alimentation pour le LCD et le contrôleur tactile, et initialise le pilote tactile. Elle joue un rôle similaire à initPins dans l’exemple précédent mais avec plus de périphériques impliqués.

void initPins() {
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);

  pcf8574.pinMode(P0, OUTPUT);        //tp RST
  pcf8574.pinMode(P2, OUTPUT);        //tp INT
  pcf8574.pinMode(P3, OUTPUT);        //lcd power
  pcf8574.pinMode(P4, OUTPUT);        //lcd reset

  if (!pcf8574.begin()) {
    Serial.println("Can't init pcf8574");
  }

Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN); est identique en concept au sketch précédent : elle démarre le périphérique I2C avec des broches SDA et SCL personnalisées. Les quatre appels pcf8574.pinMode configurent des lignes spécifiques du PCF8574 en sorties : P0 pour le reset tactile, P2 pour la ligne d’interruption tactile, P3 pour l’alimentation LCD, et P4 pour le reset LCD. Comme avant, pcf8574.begin() initialise la communication avec l’expandeur et affiche un message d’erreur en cas d’échec.

Le bloc suivant effectue la séquence d’alimentation et de reset pour le LCD. Ces délais précis et ces motifs de basculement sont souvent requis par les contrôleurs LCD et sont codés selon la fiche technique de l’écran.

  // LCD
  pcf8574.digitalWrite(P3, HIGH);
  delay(100);
  pcf8574.digitalWrite(P4, HIGH);
  delay(100);
  pcf8574.digitalWrite(P4, LOW);
  delay(120);
  pcf8574.digitalWrite(P4, HIGH);
  delay(120);

Mettre P3 à haut active la ligne d’alimentation LCD. Après un court délai pour stabiliser l’alimentation, P4 est basculé pour générer une impulsion de reset correcte pour le panneau. La séquence haut, bas, haut avec des délais spécifiques assure que le contrôleur ST7701 démarre dans un état connu.

La section du pavé tactile effectue une séquence de reset très similaire, mais sur les lignes de reset et d’interruption du contrôleur tactile.

  // Touchpad
  pcf8574.digitalWrite(P0, HIGH);
  delay(100);
  pcf8574.digitalWrite(P0, LOW);
  delay(120);
  pcf8574.digitalWrite(P0, HIGH);
  delay(120);
  pcf8574.digitalWrite(P2, HIGH);
  delay(120);

Ici, P0 est basculé pour réinitialiser le contrôleur CST8xx, et P2 est mis à haut pour configurer la ligne d’interruption dans un état défini. Ces actions garantissent que le contrôleur tactile est prêt avant que le pilote tente de communiquer via I2C.

Enfin, le contrôleur tactile est initialisé avec le pilote Adafruit.

  if (!tsPanel.begin(&Wire, I2C_TOUCH_ADDR)) {
    Serial.println("No touchscreen found");
  } 
}

tsPanel.begin(&Wire, I2C_TOUCH_ADDR) indique à l’objet CST8xx d’utiliser l’instance globale Wire et le connecte à l’adresse I2C définie. Si cet appel échoue, le sketch affiche un message de diagnostic.

Initialisation du LCD

La fonction initLCD prépare le contexte graphique et configure un état de dessin basique.

void initLCD() {
  gfx->begin();
  gfx->fillScreen(RED);
  gfx->setTextSize(5);
  gfx->setTextColor(WHITE);  
}

gfx->begin(); initialise le panneau ST7701 via le bus RGB et exécute les opérations d’initialisation fournies. Après cet appel, l’écran est prêt à recevoir des commandes de dessin.

gfx->fillScreen(RED); efface tout l’écran avec un fond rouge. gfx->setTextSize(5); définit un facteur d’échelle de texte relativement grand pour que le texte soit facilement lisible sur l’écran rond 480×480. gfx->setTextColor(WHITE); définit la couleur de premier plan du texte pour les opérations de dessin suivantes.

drawOnLCD

La fonction drawOnLCD encapsule une action de dessin simple, plaçant un label texte dans la région centrale de l’écran.

void drawOnLCD() {
  gfx->setCursor(90, 210);
  gfx->print("Makerguides");
}

gfx->setCursor(90, 210); déplace le curseur de texte à la position (90, 210) en coordonnées pixels. Sur un écran 480×480, c’est à peu près le centre selon la taille de la police. gfx->print("Makerguides"); affiche ensuite la chaîne de texte avec la taille et la couleur configurées précédemment.

Setup

La fonction setup fournit à nouveau une séquence d’initialisation, similaire au sketch précédent qui configurait le port série, les broches et l’encodeur.

void setup() {
  Serial.begin(115200);
  initPins();
  initBacklight();
  initLCD();
  drawOnLCD();
}

Serial.begin(115200); démarre la communication série pour le débogage. initPins(); configure le bus I2C, les broches PCF8574, et effectue les séquences de reset LCD et tactile comme décrit ci-dessus. initBacklight(); active et règle la luminosité du rétroéclairage pour que le contenu de l’écran soit visible. initLCD(); initialise le pilote graphique et peint le fond rouge, et drawOnLCD(); place la chaîne initiale “Makerguides” sur l’écran.

Loop

La boucle principale vérifie constamment si le panneau tactile est touché. Lorsqu’un contact est détecté, elle lit les coordonnées et dessine un petit cercle noir à cette position.

void loop() {
  if (tsPanel.touched()) {
    CST_TS_Point p = tsPanel.getPoint(0);
    Serial.printf("TOUCH: %d, %d\n", p.x, p.y);
    gfx->fillCircle(p.x, p.y, 10, BLACK);
    delay(10);
  }
}

tsPanel.touched(); interroge le pilote CST8xx pour savoir si des points tactiles sont actifs. Si la fonction retourne vrai, au moins un doigt est sur l’écran. tsPanel.getPoint(0); récupère le premier point tactile sous forme d’une structure CST_TS_Point contenant les coordonnées x et y. Ces coordonnées sont affichées sur le moniteur série pour le débogage avec Serial.printf, de la même manière que vous avez affiché le compteur de l’encodeur et l’état du bouton précédemment.

gfx->fillCircle(p.x, p.y, 10, BLACK); dessine un cercle rempli de rayon 10 pixels en noir à l’emplacement du contact. L’appel delay(10); introduit une courte pause pour limiter la fréquence de mise à jour et éviter de saturer le bus I2C et le pilote graphique avec trop d’opérations par seconde.

Exemple de code : Affichage, tactile et encodeur

Ce dernier sketch rassemble les concepts des exemples précédents : configuration I2C et contrôle PCF8574, affichage RGB et gestion tactile, et encodeur rotatif piloté par interruption.

Le code vous permet de contrôler la luminosité de l’écran en tournant la bague de l’encodeur et d’afficher la valeur de luminosité actuelle (0…255) au centre de l’écran. Le bouton de l’encodeur déclenche un changement de couleur de l’écran en bleu et les événements tactiles sont toujours enregistrés sous forme de points noirs sur l’écran.

Jetez un œil rapide au code complet ci-dessous puis nous en discuterons les détails.

#include <Wire.h>
#include <Arduino_GFX_Library.h>
#include <PCF8574.h>
#include <Adafruit_CST8XX.h>

// I2C to PCF8574
#define I2C_SDA_PIN 38
#define I2C_SCL_PIN 39
#define BKL_PIN 6

#define ENCODER_CLK 42
#define ENCODER_DT 4

#define I2C_TOUCH_ADDR 0x15

volatile int brightness = 100;
volatile int encState = 0;
volatile int oldState = -1;
volatile bool brightnessChanged = true;

PCF8574 pcf8574(0x21);

Adafruit_CST8XX tsPanel = Adafruit_CST8XX();

Arduino_ESP32RGBPanel *bus = new Arduino_ESP32RGBPanel(
  16 /* CS */, 2 /* SCK */, 1 /* SDA */,
  40 /* DE */, 7 /* VSYNC */, 15 /* HSYNC */, 41 /* PCLK */,
  46 /* R0 */, 3 /* R1 */, 8 /* R2 */, 18 /* R3 */, 17 /* R4 */,
  14 /* G0 */, 13 /* G1 */, 12 /* G2 */, 11 /* G3 */, 10 /* G4 */, 9 /* G5 */,
  5 /* B0 */, 45 /* B1 */, 48 /* B2 */, 47 /* B3 */, 21 /* B4 */
);

Arduino_ST7701_RGBPanel *gfx = new Arduino_ST7701_RGBPanel(
  bus,
  GFX_NOT_DEFINED,  // RST pin (not used, we reset via PCF8574)
  0,                // rotation
  false,            // IPS
  480, 480,         // width, height
  st7701_type5_init_operations,
  sizeof(st7701_type5_init_operations),
  true,       // BGR
  10, 4, 20,  // hsync front porch, pulse width, back porch
  10, 4, 20   // vsync front porch, pulse width, back porch
);

void initBacklight() {
  pinMode(BKL_PIN, OUTPUT);
  analogWrite(BKL_PIN, brightness);
}

void initPins() {
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);

  pcf8574.pinMode(P0, OUTPUT);        //tp RST
  pcf8574.pinMode(P2, OUTPUT);        //tp INT
  pcf8574.pinMode(P3, OUTPUT);        //lcd power
  pcf8574.pinMode(P4, OUTPUT);        //lcd reset
  pcf8574.pinMode(P5, INPUT_PULLUP);  //encoder SW

  if (!pcf8574.begin()) {
    Serial.println("Can't init pcf8574");
  }

  // LCD
  pcf8574.digitalWrite(P3, HIGH);
  delay(100);
  pcf8574.digitalWrite(P4, HIGH);
  delay(100);
  pcf8574.digitalWrite(P4, LOW);
  delay(120);
  pcf8574.digitalWrite(P4, HIGH);
  delay(120);

  // Touchpad
  pcf8574.digitalWrite(P0, HIGH);
  delay(100);
  pcf8574.digitalWrite(P0, LOW);
  delay(120);
  pcf8574.digitalWrite(P0, HIGH);
  delay(120);
  pcf8574.digitalWrite(P2, HIGH);
  delay(120);

  if (!tsPanel.begin(&Wire, I2C_TOUCH_ADDR)) {
    Serial.println("No touchscreen found");
  } 
}

void IRAM_ATTR encoder_irq() {
  encState = digitalRead(ENCODER_CLK);
  if (encState != oldState) {
    brightness += (digitalRead(ENCODER_DT) == encState) ? -5 : +5;
    brightness = constrain(brightness, 5, 255);
    oldState = encState;
    brightnessChanged = true;
  }
}

void initEncoder() {
  pinMode(ENCODER_CLK, INPUT_PULLUP);
  pinMode(ENCODER_DT, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), encoder_irq, CHANGE);
}

void initLCD() {
  gfx->begin();
  gfx->fillScreen(RED);
  gfx->setTextSize(10);
  gfx->setTextColor(WHITE);  
}

void updateBrightness() {  
  analogWrite(BKL_PIN, brightness);
  gfx->fillScreen(RED);
  gfx->setCursor(150, 200);
  gfx->printf("%3d", brightness);
}

void setup() {
  Serial.begin(115200);
  initPins();
  initEncoder();
  initBacklight();
  initLCD();
  updateBrightness();
}

void loop() {
  int button = pcf8574.digitalRead(P5, true);
  if (!button) {
    Serial.printf("BTN pressed\n");
    gfx->fillScreen(BLUE);
  }

  if (tsPanel.touched()) {
    CST_TS_Point p = tsPanel.getPoint(0);
    Serial.printf("TOUCH: %d, %d\n", p.x, p.y);
    gfx->fillCircle(p.x, p.y, 10, BLACK);
  }

  if (brightnessChanged) {
    brightnessChanged = false;
    updateBrightness();
  }  

  delay(100);
}

Imports

Ce sketch combine tout des exemples précédents : périphériques I2C, affichage RGB, tactile, et encodeur rotatif piloté par interruption, et inclut les bibliothèques nécessaires :

#include <Wire.h>
#include <Arduino_GFX_Library.h>
#include <PCF8574.h>
#include <Adafruit_CST8XX.h>

Constantes et état global

La section suivante définit les broches pour l’I2C, le rétroéclairage, les signaux de l’encodeur, et l’adresse du contrôleur tactile. Elle introduit aussi plusieurs variables globales volatile qui contiennent la luminosité actuelle et l’état de l’encodeur, similaire à l’exemple précédent du compteur d’encodeur :

// I2C to PCF8574
#define I2C_SDA_PIN 38
#define I2C_SCL_PIN 39
#define BKL_PIN 6

#define ENCODER_CLK 42
#define ENCODER_DT 4

#define I2C_TOUCH_ADDR 0x15

volatile int brightness = 100;
volatile int encState = 0;
volatile int oldState = -1;
volatile bool brightnessChanged = true;

I2C_SDA_PIN et I2C_SCL_PIN configurent le bus I2C partagé, comme avant. BKL_PIN est la broche PWM utilisée pour piloter le rétroéclairage LCD. ENCODER_CLK et ENCODER_DT sont les signaux quadrature de l’encodeur rotatif, identiques dans leur rôle à l’exemple précédent. I2C_TOUCH_ADDR contient l’adresse du contrôleur tactile CST8xx.

brightness stocke la valeur actuelle de luminosité du rétroéclairage. Elle est déclarée volatile car modifiée dans une routine d’interruption. encState et oldState sont utilisés pour détecter les transitions sur la ligne CLK de l’encodeur, et brightnessChanged est un drapeau informant la boucle principale qu’un nouveau niveau de luminosité est disponible.

Objets PCF8574, tactile et affichage

Les objets globaux pour l’expandeur d’E/S, le panneau tactile et le bus d’affichage sont les mêmes que dans l’exemple précédent affichage et tactile. Ils définissent comment l’ESP32 interagit avec les puces externes et le panneau RGB.

PCF8574 pcf8574(0x21);

Adafruit_CST8XX tsPanel = Adafruit_CST8XX();

Arduino_ESP32RGBPanel *bus = new Arduino_ESP32RGBPanel(
  16 /* CS */, 2 /* SCK */, 1 /* SDA */,
  40 /* DE */, 7 /* VSYNC */, 15 /* HSYNC */, 41 /* PCLK */,
  46 /* R0 */, 3 /* R1 */, 8 /* R2 */, 18 /* R3 */, 17 /* R4 */,
  14 /* G0 */, 13 /* G1 */, 12 /* G2 */, 11 /* G3 */, 10 /* G4 */, 9 /* G5 */,
  5 /* B0 */, 45 /* B1 */, 48 /* B2 */, 47 /* B3 */, 21 /* B4 */
);

L’objet pcf8574 est lié à l’adresse 0x21 et est responsable de l’alimentation LCD, du reset et du bouton-poussoir de l’encodeur. L’objet tsPanel encapsule le contrôleur tactile CST8xx. Le pointeur bus définit le mappage des signaux RGB et de timing vers les GPIO ESP32 exactement comme avant, en utilisant votre tableau de broches R0–R4, G0–G5 et B0–B4.

L’objet graphique de haut niveau pour le panneau RGB ST7701 est ensuite créé au-dessus de ce bus.

Arduino_ST7701_RGBPanel *gfx = new Arduino_ST7701_RGBPanel(
  bus,
  GFX_NOT_DEFINED,  // RST pin (not used, we reset via PCF8574)
  0,                // rotation
  false,            // IPS
  480, 480,         // width, height
  st7701_type5_init_operations,
  sizeof(st7701_type5_init_operations),
  true,       // BGR
  10, 4, 20,  // hsync front porch, pulse width, back porch
  10, 4, 20   // vsync front porch, pulse width, back porch
);

Comme avant, cela encapsule la séquence d’initialisation bas niveau ST7701, les paramètres de timing et le format couleur. La broche de reset est marquée indéfinie car le reset est piloté via les broches PCF8574 pendant initPins.

Initialisation du rétroéclairage

La fonction d’initialisation du rétroéclairage prend la valeur actuelle brightness et l’applique à la broche PWM du rétroéclairage :

void initBacklight() {
  pinMode(BKL_PIN, OUTPUT);
  analogWrite(BKL_PIN, brightness);
}

pinMode(BKL_PIN, OUTPUT); configure la broche du rétroéclairage en sortie digitale. analogWrite(BKL_PIN, brightness); démarre la sortie PWM sur cette broche en utilisant la valeur brightness comme rapport cyclique.

Initialisation des broches et périphériques

La fonction initPins est une version étendue de celle vue précédemment. Elle configure l’I2C, les broches PCF8574, la séquence d’alimentation et de reset du LCD, la séquence de reset tactile et configure aussi la broche PCF8574 qui lit le bouton-poussoir de l’encodeur.

void initPins() {
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);

  pcf8574.pinMode(P0, OUTPUT);        //tp RST
  pcf8574.pinMode(P2, OUTPUT);        //tp INT
  pcf8574.pinMode(P3, OUTPUT);        //lcd power
  pcf8574.pinMode(P4, OUTPUT);        //lcd reset
  pcf8574.pinMode(P5, INPUT_PULLUP);  //encoder SW

  if (!pcf8574.begin()) {
    Serial.println("Can't init pcf8574");
  }

Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN); démarre le bus I2C avec les broches spécifiées, comme dans les sketches précédents. P0, P2, P3 et P4 sont configurées en sorties pour contrôler le reset tactile, la ligne d’interruption tactile, l’alimentation LCD et le reset LCD. P5 est configurée en INPUT_PULLUP car elle est connectée à l’interrupteur de l’encodeur. Cela reflète la configuration de P5 comme entrée bouton d’encodeur dans l’exemple encodeur seul.

La séquence de timing d’alimentation et de reset du LCD suit, identique dans sa structure au code d’affichage précédent.

  // LCD
  pcf8574.digitalWrite(P3, HIGH);
  delay(100);
  pcf8574.digitalWrite(P4, HIGH);
  delay(100);
  pcf8574.digitalWrite(P4, LOW);
  delay(120);
  pcf8574.digitalWrite(P4, HIGH);
  delay(120);

P3 active la ligne d’alimentation LCD, puis P4 est basculée avec des délais spécifiques pour effectuer un reset matériel du contrôleur ST7701.

La séquence de reset et de configuration du pavé tactile est aussi la même qu’avant.

  // Touchpad
  pcf8574.digitalWrite(P0, HIGH);
  delay(100);
  pcf8574.digitalWrite(P0, LOW);
  delay(120);
  pcf8574.digitalWrite(P0, HIGH);
  delay(120);
  pcf8574.digitalWrite(P2, HIGH);
  delay(120);

P0 est pulsé pour réinitialiser le contrôleur CST8xx et P2 est mis à haut pour établir un état défini pour la ligne d’interruption tactile.

Le contrôleur tactile est ensuite initialisé. tsPanel.begin(&Wire, I2C_TOUCH_ADDR) lie le pilote CST8xx au bus I2C partagé et à l’adresse spécifiée, et affiche un message de diagnostic si le périphérique ne peut être trouvé :

  if (!tsPanel.begin(&Wire, I2C_TOUCH_ADDR)) {
    Serial.println("No touchscreen found");
  } 
}

Routine d’interruption de l’encodeur

Le gestionnaire d’interruption de l’encodeur est similaire à la fonction encoder_irq vue précédemment, mais au lieu de maintenir un compteur de position, il met à jour la variable brightness par pas de cinq.

void IRAM_ATTR encoder_irq() {
  encState = digitalRead(ENCODER_CLK);
  if (encState != oldState) {
    brightness += (digitalRead(ENCODER_DT) == encState) ? -5 : +5;
    brightness = constrain(brightness, 5, 255);
    oldState = encState;
    brightnessChanged = true;
  }
}

IRAM_ATTR garantit que l’ISR est placée en RAM d’instruction pour une exécution rapide sur ESP32, comme discuté précédemment. À l’intérieur de la fonction, encState est réglée au niveau logique actuel de la broche CLK de l’encodeur avec digitalRead(ENCODER_CLK). La condition if (encState != oldState) assure que le code ne réagit que lorsque le signal CLK change réellement, évitant plusieurs mises à jour sur le même niveau.

La direction de rotation est à nouveau déterminée en comparant le signal DT avec l’état actuel de CLK. Dans ce sketch, la condition est inversée par rapport à l’exemple précédent pour donner une sensation naturelle d’augmentation et de diminution de la luminosité.

La ligne suivante soustrait 5 ou ajoute 5 à la variable brightness selon la phase relative des signaux quadrature. Une rotation positive augmente la luminosité, une rotation négative la diminue.

brightness += (digitalRead(ENCODER_DT) == encState) ? -5 : +5;

Et la ligne suivante garantit que la luminosité reste dans une limite inférieure définie de 5 (éviter un écran complètement éteint) et une limite supérieure de 255 (valeur PWM maximale pour pleine luminosité) :

brightness = constrain(brightness, 5, 255);

oldState est mise à jour avec le nouvel état CLK, et brightnessChanged est réglée à true pour notifier la boucle principale que le rétroéclairage et l’affichage à l’écran doivent être rafraîchis. Comme avant, tout travail lourd comme les entrées/sorties série et graphiques est évité dans l’ISR et géré dans la boucle principale.

Initialisation de l’encodeur

La fonction initEncoder configure les broches de l’encodeur et attache l’interruption à la ligne CLK. C’est effectivement le même schéma que dans votre sketch encodeur original.

void initEncoder() {
  pinMode(ENCODER_CLK, INPUT_PULLUP);
  pinMode(ENCODER_DT, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), encoder_irq, CHANGE);
}

Les deux canaux de l’encodeur sont configurés en entrées avec résistances pull-up internes. L’interruption est attachée à la broche CLK avec attachInterrupt, en mode CHANGE pour déclencher sur les fronts montant et descendant. À chaque changement, le gestionnaire encoder_irq est invoqué, mettant à jour la luminosité.

Initialisation du LCD et affichage de la luminosité

La fonction d’initialisation du LCD fait apparaître le panneau ST7701 et configure les paramètres de texte. Elle est similaire à la fonction initLCD précédente, mais utilise une taille de texte plus grande pour afficher la valeur de luminosité de façon bien visible :

void initLCD() {
  gfx->begin();
  gfx->fillScreen(RED);
  gfx->setTextSize(10);
  gfx->setTextColor(WHITE);  
}

gfx->begin(); initialise le contrôleur d’affichage et envoie la séquence de configuration. gfx->fillScreen(RED); met un fond rouge. gfx->setTextSize(10); choisit un facteur d’échelle grand pour que la valeur numérique de la luminosité ressorte clairement. gfx->setTextColor(WHITE); configure le blanc comme couleur de premier plan pour le rendu du texte.

La fonction updateBrightness lie la valeur logique de luminosité au rétroéclairage physique et à l’affichage à l’écran.

void updateBrightness() {  
  analogWrite(BKL_PIN, brightness);
  gfx->fillScreen(RED);
  gfx->setCursor(150, 200);
  gfx->printf("%3d", brightness);
}

analogWrite(BKL_PIN, brightness); met à jour le rapport cyclique PWM, modifiant effectivement l’intensité du rétroéclairage LED. L’écran est ensuite à nouveau effacé en rouge avec gfx->fillScreen(RED);.

Le curseur est placé aux coordonnées (150, 200), et gfx->printf("%3d", brightness); affiche la luminosité sous forme d’un nombre à trois chiffres.

Setup

La fonction setup initialise tous les sous-systèmes : série, broches I2C et puces externes, encodeur, rétroéclairage et LCD, et affiche enfin la luminosité initiale à l’écran.

void setup() {
  Serial.begin(115200);
  initPins();
  initEncoder();
  initBacklight();
  initLCD();
  updateBrightness();
}

Serial.begin(115200); démarre l’UART pour la sortie de débogage. initPins(); prépare le bus I2C, le PCF8574, le LCD et le contrôleur tactile comme discuté précédemment. initEncoder(); active l’interface encodeur pilotée par interruption. initBacklight(); applique la valeur initiale brightness au pin du rétroéclairage. initLCD(); initialise le contexte graphique, et updateBrightness(); synchronise immédiatement l’affichage à l’écran et le PWM du rétroéclairage avec la valeur de luminosité actuelle.

Loop

La boucle principale vérifie périodiquement le bouton de l’encodeur, l’entrée tactile et le drapeau de mise à jour de la luminosité. Elle réagit à chacun de ces événements en utilisant les périphériques configurés précédemment.

void loop() {
  int button = pcf8574.digitalRead(P5, true);
  if (!button) {
    Serial.printf("BTN pressed\n");
    gfx->fillScreen(BLUE);
  }

  if (tsPanel.touched()) {
    CST_TS_Point p = tsPanel.getPoint(0);
    Serial.printf("TOUCH: %d, %d\n", p.x, p.y);
    gfx->fillCircle(p.x, p.y, 10, BLACK);
  }

  if (brightnessChanged) {
    brightnessChanged = false;
    updateBrightness();
  }  

  delay(100);
}

Le premier bloc lit le bouton-poussoir de l’encodeur via le PCF8574. pcf8574.digitalRead(P5, true); lit la broche P5 avec une transaction I2C immédiate. Comme P5 est configurée en INPUT_PULLUP, le bouton lit haut lorsqu’il est relâché et bas lorsqu’il est pressé. La condition if (!button) détecte l’état pressé. Lors de la pression, le sketch affiche un message sur le moniteur série et remplit l’écran en bleu, fournissant une indication visuelle simple que le bouton a été pressé.

Le bloc suivant gère l’entrée tactile capacitive, réutilisant la même logique que dans l’exemple précédent de dessin sur le LCD. Si tsPanel.touched() retourne vrai, au moins un point tactile est actif. La fonction tsPanel.getPoint(0); récupère le premier point tactile, qui est ensuite affiché sur le moniteur série. gfx->fillCircle(p.x, p.y, 10, BLACK); dessine un petit cercle noir aux coordonnées tactiles, permettant à l’utilisateur de dessiner par-dessus le contenu actuel de l’écran.

Le troisième bloc vérifie si l’ISR de l’encodeur a mis à jour la variable brightness. Si brightnessChanged est vrai, la boucle principale efface le drapeau et appelle updateBrightness();. Cela applique la nouvelle luminosité au rétroéclairage et redessine la valeur numérique à l’écran. Tourner rapidement l’encodeur génèrera plusieurs interruptions et mettra brightnessChanged à vrai à plusieurs reprises, mais la boucle garantit que les mises à jour de luminosité sont traitées dans le contexte principal où il est sûr d’effectuer les opérations série et graphiques.

Le dernier delay(100); introduit une courte pause pour limiter la fréquence de la boucle et lisser l’interaction utilisateur sans surcharger le CPU ou l’I2C.

Conclusions

Ce tutoriel vous a fourni des exemples de code pour débuter avec le CrowPanel 2.1inch-HMI ESP32 Rotary Display. Consultez la Wiki d’Elecrow pour plus d’informations et pour d’autres exemples de code, voyez la github repo.

Notez que vous devez installer des bibliothèques plus anciennes et une version plus ancienne du core ESP32 (2.0.14) pour que le code fonctionne. Certains exemples de code utilisent aussi la bibliothèque LVGL, que j’ai évitée ici pour garder le code simple.

Si vous cherchez un module d’affichage similaire avec une bague d’encodeur rotatif, jetez un œil au CrowPanel 1.28inch-HMI ESP32 Rotary Display ou au Matouch 1.28″ ToolSet_Controller. Si vous avez seulement besoin d’un écran rond (sans bague d’encodeur rotatif), le tutoriel Digital Clock on CrowPanel 1.28″ Round Display pourrait être utile.

Enfin, si vous souhaitez en savoir plus sur les encodeurs rotatifs, consultez notre tutoriel How To Interface A Quadrature Rotary Encoder.

Si vous avez des questions, n’hésitez pas à les laisser dans la section commentaires.

Bon bricolage 😉