Pilote logiciel (Driver) d'un composant ST7789 pour LCD pour Pico
Ajouter un petit LCD sur un Pico n'est pas rare. Mais, cela peut être un chouïa compliqué. Mon petit pilote peut aider à mettre cela en oeuvre. Je dois tout d'abord dire que ce petit pilote est une digréssion de ce que fournit Waveshare pour ses produits. Ces gars fournissent pas mal de choses avec lerus produits. Enfin une marque qui ne se contente pas de supporter que Python. Que les autres en prennent de la graine. En plus, ils ont un wiki pas mal foutu, et notamment, pour leurs LCD. CEla dit, la limite est que ce qu'ils fournissent supporte tous leurs LCD ou OLED dans un même package. C'est compréhensible, mais cela impose, soit de trainer plein de code qui ne vous sert à rien, soit d'extraire unqiuement ce dont vous avez besoin... Et il y en a un peu partout dans leur projet. Bref, c'est sympa de leur part, mais pas très efficace. Alors je suis reparti de leur code pour en extraire uniquement ce qui concerne les LCD pilotés par un composant ST7789 et j'ai porté tout cela dans des classes C++. J'en ai également profité pour séparer dans deux classes différentes, d'une part les accès matériel, et d'autre part les fonctions de tracé. Tout cela dans le but de pouvoir, à terme, utiliser les mêmes fonctions de tracés sur un matériel différent. En plus cela permet de disposer de plusierus environnements de tracés pour un seul et même LCD.
Installation
Commencez par télécharger le fichier suivant : Driver_ST7789.tar.gz
Copier le fichier téléchargé vers le répertoire "/home/pi" puis extraire ce fichier. L'extraction créera un répertoire nommé "Driver_ST7789" :
tar zxvf Driver_ST7789.tar.gz
Le répertoire "Driver_ST7789" contiendra la code source et un exmeple d'(utilisation).
Si vous souhaitez modifier le code source, je vous conseille de créer un autre répertoire et d'y copier le contenu du répertoire "/home/pi/Driver_ST7789" vers un autre répertoire. Cela vous évitera de "polluer" le répertoire "/home/pi/Driver_ST7789" et de conserver une version d'origine du code source.
Utilisation
Architecture
Ce pilote utilise 3 classes :
- Une classe singleton nommée CST7789 qui gère les accès matériel,
- Une classe nommée CST7789_PaintDevice qui offre des fonctions de tracé simples,
- Une classe statique nommée CLCDFont qui expose des polices de caractères.
Le principe d'utilisation est assez simple :
On commence par instancier le singleton de la classe CST7789 (En ce qui concerne l'instantiation d'un singleton, voir le paragraphe : Classe singleton CST7789). On l'initialise via sa méthode initialize.
Ensuite on crée autant que nécessaire d'objets de la classe CST7789_PaintDevice. On trace dans ces objets via leurs méthodes. Si on doit écrire du texte, on utilise une police sélectionnée via la classe statique CLCDFont.
Finalement, lorsque le tracé est terminé, on met à jour le LCD via la méthode displayOnLCD de la classe CST7789_PaintDevice. Vous pouvez voir les objets de la classe CST7789_PaintDevice
comme des buffers de tracé "off screen" pour un LCD.
L'extrait de pseudo code suivant montre un exemple simple (la macro "D_LCD" est documentée au paragraphe Classe singleton CST7789) :
// Declaration et assignation du LCD
CST7789* pLCD = D_LCD;
// On l'initialise et on definie son eclairage
pLCD->initialize();
pLCD->setBackLight(100);
// On declare un paint device que l'on rattache au LCD
CST7789_PaintDevice* pPaintDevice = new CST7789_PaintDevice(pLCD);
// Choix d'une police
CLCDFont::selectFont(LARGE_LCDFONT);
// On met le fond en bleu fonce
pPaintDevice->clear(CST7789::DarkBlue);
// On ecrit notre chaine au coordonnées X=20, Y=20 en couleur jaune
pPaintDevice->drawString("HELLO WORLD", 20, 20, CST7789::Yellow);
// Et on balance tout ca au LCD
pPaintDevice->displayOnLCD();
// .../...
Fichier d'inclusion
Comme ce pilote utilise 3 classes qui ont chacune leur propre fichier d'entête (extension .h), un fichier d'inclusion nommée "lcd_st7789.h" est fourni pour incure tous les ficheirs d'entêtes des 3 classes. Il suffit donc d'inclure ce fichier et le tour est joué.
Classe singleton CST7789
Le pilote logiciel ST7789 est implémentée sous la forme d'une classe nommée CST7789. Cette classe est du type singleton. Cela veut dire que vous ne pouvez l'instancier qu'une seule fois. C'est assez normal, vu le fait que vous ne pouvez avoir qu'un seul composant ST7789 relié à votre Pico. Pour s'assurer qu'il n'est possible de ne créer qu'une seule instance de cette classe, son constructeur est privé. Vous ne pouvez pas l'utiliser. A la place, la classe met à disposition une méthode statique de type "factory" nommée ST7789. Cette méthode retourne un pointeur vers le seul et unique objet créé. Si celui-ci n'existe pas au moment de l'appel à cette méthode de factory, celle-ci le crée et le retourne. C'est toujours le cas lors du premier appel. Si cet objet existe la méthode de factory se contente de retourner le pointeur vers ce dernier. C'est toujours le cas à partir du deuxième appel.
Cette méthode de factory reçoit 9 paramètres ! Ouf !!! C'est normal, il faut définir le bus SPI utilisé, ses lignes TX, SCK et CS. Le ST7789 utilise en plus 2 sorties numériques nommées RESET et DC et une sortie PWM pour régler l'éclairage. Et finalement il faut préciser au pilote la largeur et la hauteur du LCD qu'il contrôle.
On peut toutefois se simplifier la vie... Si vous utilisez un LCD de Waveshare : 2 inch LCD Display Module for Raspberry Pi Pico, 65K Colors, 320×240, SPI, ou si vous respectez le schéma de cablage suivant avec un LCD de 320 par 240 pixels, vous pouvez ne passer aucun paramètres car ce sont ceux pris par défaut par la factory :

En plus de définir des paramètres par défaut, une macro nommée "D_LCD" est aussi définie comme un raccourci vers la méthode de factory ST7789. Vous disposez donc de plusierus stratégies pour instantier et/ou utiliser le singleton. Les exemples suivants sont tous équivalents. Ils montrent comment instancier le singleton et appeler sa méthode initialize. Ils partent tous du principe que vous utlisez les paramètres par défaut de la factory :
La première stratégie consiste à se créer une variable pointeur vers le singleton :
pLCD = CST7789::ST7789();
pLCD->initialize();
La seconde stratégie est identique mais utlise la macro de raccourci :
pLCD = D_LCD;
pLCD->initialize();
Comme la macro et la factory ramène toujours les pointeur vers le singleton, on peut se passer de définir une variable. Le code suivant donne strictemetn, le même résultat que les exemples précédent. Dans ce cas, à chaque fois que vous voulez accéder au singleton, il suffit de précéder l'appel par la macro "D_LCD" :
Bien sur, si vous utlisez un cablage différent de celui proposé ci-dessus, vous devrez utiliser la factory en lui passant tous les paramètres :
pLCD = CST7789::ST7789(spi0, 18, 19, 17, 8, 12, 13, 320, 240);
pLCD->initialize();
Gestion des couleurs
Ce ST7789 peut coder les couleurs selon deux normes : RGB565 et RGB18. La norme RGB565 autorise 65 536 couleurs, la norme RGB18 autorise 262 144 couleurs.
Ce pilote ne supportent que la norme RGB565. La raison à cela est que la norme RGB18 impose 3 octets par pixels au lieu de 2 pour la norme RGB565. Cela augmente le traffic sur le bus SPI de 50%. Hors, sur des afficheurs à peine plus grands qu'un timbre poste, la différence de qualité des couleurs entre ces deux normes est à peine visible. C'est la raison du choix de la norme RGB565. En plus, cela simplifie le code.
De plus le composant ST7789 a un codage bien à lui de la norme RGB565.
La plupart du temps sur un PC on utilise des coulerus codées en RGB 32 bit avec 8 bit par couleru fondamentale et 8 bit pour la voie alpha définissant la transparence. Afin de passer d'un codage de ce type (sans prise en compte de la voie alpha) au codage RGB565 du ST7789, la classe CST7789 exporte une fonction nommée RGB888ToRGB565.
Enfin cette classe exporte 17 constantes codées en RGB565 pour les couleurs de base.
Exemple : Hello World
Un exemple est fourni avec le pilote afin de vérifier le bon fonctionnement de l'ensemble. Le fichier source constitue la base du projet. Le code complet est le suivant :
* \brief Programme d'exemple pour le pilote Pico ST7789 pour LCD
* \author WickedCpp
* \date 21 mars 2024
*/
// entetes standard Pico
#include "stdio.h"
#include "pico/stdlib.h"
// entete ST7789
#include "lcd_st7789.h"
int main()
{
stdio_init_all();
// Declaration et assignation du LCD
CST7789* pLCD = D_LCD;
// On l'initialise et on definie son eclairage
D_LCD->initialize();
D_LCD->setBackLight(100);
// On declare un paint device que l'on rattache au LCD
CST7789_PaintDevice* pPaintDevice = new CST7789_PaintDevice(pLCD);
// Choix d'une police
CLCDFont::selectFont(LARGE_LCDFONT);
// On définie la position de la chaine à écrire sur le LCD en plain centre
uint16_t x = (pLCD->LCDWidth() / 2) - (CLCDFont::stringWidth("HELLO WORLD") / 2);
uint16_t y = (pLCD->LCDHeight() / 2) - (CLCDFont::height() / 2);
int i = 0;
RGB565 color;
do
{
// On change de couleur à chaque boucle selon la valeur de la variable i
switch(i)
{
case 0:
color = CST7789::White;
break;
case 1:
color = CST7789::Red;
break;
case 2:
color = CST7789::Green;
break;
case 3:
color = CST7789::Cyan;
break;
case 4:
color = CST7789::Magenta;
break;
case 5:
default:
color = CST7789::Yellow;
break;
}
// On met le fond en bleu fonce
pPaintDevice->clear(CST7789::DarkBlue);
// On ecrit notre chaine
pPaintDevice->drawString("HELLO WORLD", x, y, color);
// Et on balance tout ca au LCD
pPaintDevice->displayOnLCD();
// On increment on on ramène i à 0
i = i < 5 ? i+1 : 0;
// on fait tourner la boucle toutes les secondes
sleep_ms(1000);
} while(true);
return 0;
}
Types
Le pilote ST7789 définie les types suivants :
Couleurs
Encodage des couleurs en 16 bit : RGB565 :
typedef uint16_t RGB565;
La classe CST7789 définie les couleurs de base en 16 bit : RGB565 :
const RGB565 CST7789::White = 0xFFFF;
const RGB565 CST7789::Red = 0xF800;
const RGB565 CST7789::DarkRed = 0x8000;
const RGB565 CST7789::Green = 0x07E0;
const RGB565 CST7789::DarkGreen = 0x0400;
const RGB565 CST7789::Blue = 0x001F;
const RGB565 CST7789::DarkBlue = 0x0010;
const RGB565 CST7789::DarkCyan = 0x0410;
const RGB565 CST7789::Cyan = 0x07FF;
const RGB565 CST7789::Magenta = 0xF81F;
const RGB565 CST7789::DarkMagenta = 0x8010;
const RGB565 CST7789::Yellow = 0xFFE0;
const RGB565 CST7789::DarkYellow = 0x8400;
const RGB565 CST7789::DarkGray = 0x8410;
const RGB565 CST7789::Gray = 0xA514;
const RGB565 CST7789::LightGray = 0xC618;
Polices de caractères
La classe CLCDFont définie les polices de caractères suivantes :
{
TINY_LCDFONT, // Largeur 6 pixels - Hauteur 8 pixels
SMALL_LCDFONT, // Largeur 7 pixels - Hauteur 9 pixels
MEDIUM_LCDFONT, // Largeur 11 pixels - Hauteur 18 pixels
LARGE_LCDFONT, // Largeur 16 pixels - Hauteur 26 pixels
} LCDFONT;
Macros
Le pilote ST7789 définie les macros suivantes :
Paramètres par défaut
#define ST7789_SPI_SCK 10 // SPI1 SCK sur GPIO pin 10
#define ST7789_SPI_TX 11 // SPI1 TX sur GPIO pin 11
#define ST7789_DO_DC 8 // Sortie logique DC (Data/Command) sur GPIO pin 8
#define ST7789_SPI_CS 9 // SPI CS sur GPIO pin 10
#define ST7789_DO_RST 12 // Sortie logique RST (Reset) sur GPIO pin 12
#define ST7789_PWM_BL 13 // Sortie PWM BL (Backlight) sur GPIO pin 13
#define ST7789_LCD_WIDTH 320 // 320 pixels de large
#define ST7789_LCD_HEIGHT 240 // 240 pixels de hauteur
Raccourci vers la factory
Classe CST7789
Méthodes publiques statiques
| CST7789* | ST7789(spi_inst_t* spibus = ST7789_SPI_PORT, const uint& unSpiSckGpio = ST7789_SPI_SCK, const uint& unSpiTxGpio = ST7789_SPI_TX, const uint& unSpiCsGpio = ST7789_SPI_CS, const uint& unST7789DoDCGpio = ST7789_DO_DC, const uint& unST7789DoRSTGpio = ST7789_DO_RST, const uint& unST7789PwmBLGpio = ST7789_PWM_BL, const uint16_t& unLCDWidth = ST7789_LCD_WIDTH, const uint16_t& unLCDHeight= ST7789_LCD_HEIGHT) | RGB565 | RGB888ToRGB565(uint8_t unRed, uint8_t unGreen, uint8_t unBlue) |
Méthodes publiques
| void | initialize() |
| void | reset() |
| void | setBackLight(const uint& unPercent) |
| void | terminate() |
| void | clear(const RGB565& rgb565Color) |
| uint16_t | LCDWidth() |
| uint16_t | LCDHeight() |
Détails des Méthodes
Méthodes publiques statiques
Description : Méthode de factory de l'objet singleton de la classe CST7789. Voir le paragraphe Classe singleton CST7789 pour une description complète de cette méthode.
Paramètre : spibus - type spi_inst_t* - Identificateur du bus SPI auquel est reliée le composaant ST7789. La valeur par défaut est ST7789_SPI_PORT (spi1).
Paramètre : unSpiSckGpio - type const uint& - Pin GPIO pour la ligne SCK du bus SPI. La valeur par défaut est ST7789_SPI_SCK (10).
Paramètre : unSpiTxGpio - type const uint& - Pin GPIO pour la ligne TX du bus SPI. La valeur par défaut est ST7789_SPI_TX (11).
Paramètre : unSpiCsGpio - type const uint& - Pin GPIO pour la ligne CS du bus SPI. La valeur par défaut est ST7789_SPI_CS (9).
Paramètre : unST7789DoDCGpio - type const uint& - Pin GPIO pour la ligne DC du ST7789. La valeur par défaut est ST7789_DO_DC (8).
Paramètre : unST7789DoRSTGpio - type const uint& - Pin GPIO pour la ligne RST du ST7789. La valeur par défaut est ST7789_DO_RST (12).
Paramètre : unST7789PwmBLGpio - type const uint& - Pin GPIO pour la ligne PWM du ST7789. La valeur par défaut est ST7789_PWM_BL (13).
Paramètre : unLCDWidth - type const uint16_t& - Largeur du LCD relié au ST7789. La valeur par défaut est ST7789_LCD_WIDTH (320).
Paramètre : unLCDHeight - type const uint16_t& - Hauteur du LCD relié au ST7789. La valeur par défaut est ST7789_LCD_HEIGHT (240).
Valeur de retour : Pointeur vers l'objet singleton de type CST7789.
Description : Retourne une couleur codée en RGB565 à partir de leur composantes de base RGB codées en 8 bit.
Paramètre : unSpiSckGpio - type uint8_t - Composante rouge.
Paramètre : unSpiSckGpio - type uint8_t - Composante verte.
Paramètre : unSpiSckGpio - type uint8_t - Composante bleu.
Valeur de retour : Couleur codée en RGB565.
Méthodes publiques
Description : Initialise l'objet singleton et le composant ST7789. Cette méthode doit être appelée avant toute autre méthode de la classe.
Description : Ramène le ST7789 à son état à sa mise sous tension.
Description : Cette méthode permet de régler l'éclairage du LCD.
Paramètre : unPercent - type const uint& - Valeur d'éclairage comprise entre 0% (LCD éteint) et 100% (Eclairage maximal).
Description : Termine le travail avec ld LCD. Cette méthode devrait être appelée en fin de programme.
Description : Efface le contenu du LCD et le rempli de la couleur passée en paramètre.
Paramètre : rgb565Color - type const RGB565& - Couleur à appliquer au LCD.
Description : Retourne la largeur du LCD passée via la fonction de factory.
Valeur de retour : Largeur du LCD.
Description : Retourne la hauteur du LCD passée via la fonction de factory.
Valeur de retour : Hauteur du LCD.
Classe CST7789_PaintDevice
Constructeur
| CST7789_PaintDevice(CST7789* pLCD) |
Destructeur
| ~CST7789_PaintDevice() |
Méthodes publiques
| void | clear(const RGB565& rgb565Color) |
| bool | setPixel(const uint16_t& unX, const uint16_t& unY, const RGB565& rgb565Color) |
| bool | drawHLine(const uint16_t& unX, const uint16_t& unY, const uint16_t& unLenght, const uint16_t& unThickness, const RGB565& rgb565Color) |
| bool | drawVLine(const uint16_t& unX, const uint16_t& unY, const uint16_t& unLenght, const uint16_t& unThickness, const RGB565& rgb565Color) |
| bool | fillRect(const uint16_t& unX, const uint16_t& unY, const uint16_t& unWidth, const uint16_t& unHeight, const RGB565& rgb565Color) |
| bool | drawRect(const uint16_t& unX, const uint16_t& unY, const uint16_t& unWidth, const uint16_t& unHeight, const uint16_t& unThickness, const RGB565& rgb565Color) |
| bool | drawString(const char* const pString, const uint16_t& unX, const uint16_t& unY, const RGB565& rgb565Color) |
| void | drawPixelsBuffer(const RGB565* const pBuffer) |
| void | displayOnLCD() |
Détails des Méthodes
Méthodes publiques
Description : Efface le contenu du LCD et le rempli de la couleur passée en paramètre.
Paramètre : rgb565Color - type const RGB565& - Couleur à appliquer au LCD.
Description : Met le pixel à la position unX et unY à la couleur rgb565Color.
Paramètre : unX - type const uint16_t& - Position X du pixel. Comprise entre 0 et la largeur du LCD -1.
Paramètre : unY - type const uint16_t& - Position Y du pixel. Comprise entre 0 et la hauteur du LCD -1.
Paramètre : rgb565Color - type const RGB565& - Couleur à appliquer au pixel.
Valeur de retour : Booléen indiquant le succès ou l'echec de la fonction. Une valeur de retour false indique généralement que le tracé est hors limite du LCD.
Description : Trace une ligne horizontale le longueur unLenght et d'épaisseur unThickness à partir de la position unX et unY à la couleur rgb565Color.
Paramètre : unX - type const uint16_t& - Position X de la ligne. Comprise entre 0 et la largeur du LCD -1.
Paramètre : unY - type const uint16_t& - Position Y de la ligne. Comprise entre 0 et la hauteur du LCD -1.
Paramètre : unLenght - type const uint16_t& - Longueur de la ligne. Comprise entre 1 et la largeur du LCD -1.
Paramètre : unThickness - type const uint16_t& - Epaisseur de la ligne. Comprise entre 1 et la hauteur du LCD -1.
Paramètre : rgb565Color - type const RGB565& - Couleur à appliquer à la ligne.
Valeur de retour : Booléen indiquant le succès ou l'echec de la fonction. Une valeur de retour false indique généralement que le tracé est hors limite du LCD.
Description : Trace une ligne verticale le longueur unLenght et d'épaisseur unThickness à partir de la position unX et unY à la couleur rgb565Color.
Paramètre : unX - type const uint16_t& - Position X de la ligne. Comprise entre 0 et la largeur du LCD -1.
Paramètre : unY - type const uint16_t& - Position Y de la ligne. Comprise entre 0 et la hauteur du LCD -1.
Paramètre : unLenght - type const uint16_t& - Longueur de la ligne. Comprise entre 1 et la hauteur du LCD -1.
Paramètre : unThickness - type const uint16_t& - Epaisseur de la ligne. Comprise entre 1 et la largeur du LCD -1.
Paramètre : rgb565Color - type const RGB565& - Couleur à appliquer à la ligne.
Valeur de retour : Booléen indiquant le succès ou l'echec de la fonction. Une valeur de retour false indique généralement que le tracé est hors limite du LCD.
Description : Trace et rempli un rectangle de longueur unWidth et de hauteur unHeight à partir de la position unX et unY à la couleur rgb565Color.
Paramètre : unX - type const uint16_t& - Position X du rectangle. Comprise entre 0 et la largeur du LCD -1.
Paramètre : unY - type const uint16_t& - Position Y du rectangle. Comprise entre 0 et la hauteur du LCD -1.
Paramètre : unWidth - type const uint16_t& - Largeur du rectangle. Comprise entre 1 et la largeur du LCD -1.
Paramètre : unHeight - type const uint16_t& - Hauteur du rectangle. Comprise entre 1 et la hauteur du LCD -1.
Paramètre : rgb565Color - type const RGB565& - Couleur à appliquer au rectangle.
Valeur de retour : Booléen indiquant le succès ou l'echec de la fonction. Une valeur de retour false indique généralement que le tracé est hors limite du LCD.
Description : Trace un cadre de longueur unWidth et de hauteur unHeight à partir de la position unX et unY et d'épaisseur unThickness à la couleur rgb565Color.
Paramètre : unX - type const uint16_t& - Position X du cadre. Comprise entre 0 et la largeur du LCD -1.
Paramètre : unY - type const uint16_t& - Position Y du cadre. Comprise entre 0 et la hauteur du LCD -1.
Paramètre : unWidth - type const uint16_t& - Largeur du cadre. Comprise entre 1 et la largeur du LCD -1.
Paramètre : unHeight - type const uint16_t& - Hauteur du cadre. Comprise entre 1 et la hauteur du LCD -1.
Paramètre : unThickness - type const uint16_t& - Epaisseur des bords du cadre.
Paramètre : rgb565Color - type const RGB565& - Couleur à appliquer au cadre.
Valeur de retour : Booléen indiquant le succès ou l'echec de la fonction. Une valeur de retour false indique généralement que le tracé est hors limite du LCD.
Description : Trace un texte à partir de la position unX et unY à la couleur rgb565Color.
Paramètre : pString - type const char* const - Texte à écrire.
Paramètre : unX - type const uint16_t& - Position X du texte. Comprise entre 0 et la largeur du LCD -1.
Paramètre : unY - type const uint16_t& - Position Y du texte. Comprise entre 0 et la hauteur du LCD -1.
Paramètre : rgb565Color - type const RGB565& - Couleur à appliquer au tex.
Valeur de retour : Booléen indiquant le succès ou l'echec de la fonction. Une valeur de retour false indique généralement que le tracé est hors limite du LCD.
Description : Rempli le LCD à partir du contenu du buffer passé en paramètre. Ce buffer doit être d'une taille égale à la largeur du LCD multipliée par la hauteur du pixel. Il est organisé par lignes successives. Cette fonction ests utilisable pour, à titre d'exemple, afficher une image.
Paramètre : pBuffer - type const RGB565* const - Buffer de pixels à tracer.
Description : Transfère le contenu de l'objet (donc ddu tracé) vers le LCD.
Classe CLCDFont
Méthodes publiques statiques
| void | selectFont(const LCDFONT& eFont) |
| uint16_t | width() |
| uint16_t | height() |
| uint16_t | stringWidth(const char* const pString) |
Détails des Méthodes
Méthodes publiques statiques
Description : Sélectionne une des 4 polices disponibles. Cette police sera ensuite utilisée par tous les appels à la méthode drawString pour tracer des chaines de caractères.
Paramètre : eFont - type const LCDFONT& - Identificateur de la police à sélectionner.
Description : Retourne la largeur d'un caractère de la police sélectionnée.
Valeur de retour : Largeur d'un caractère.
Description : Retourne la hauteur d'un caractère de la police sélectionnée.
Valeur de retour : Hauteur d'un caractère.
Description : Retourne la taille horizontale d'un texte selon la police sélectionnée.
Paramètre : pString - type const char* const - Chaine dont on veut connaître la logneur sur le LCD selon la police sélectionnée.
Valeur de retour : Longueur de la chaine sur le LCD.