J'utilise un Arduino Zéro et un arduino méga2560.
Pour accéder aux écrans, on a besoin des E/S. On peut utiliser les fonction digitalWrite et digitalRead qui sont très lentes, ou utiliser les accès aux ports directs (genre PORTK=...)qui sont plus rapides, mais qui dépendent de la carte.
Je te propose d'utiliser les fonctions digitalWrite et digitalRead dans un premier temps, et si par la suite les accès sont trop lents, on pourra toujours réécrire les fonctions de base.
Une bonne base pour avoir accès aux registres est d'utiliser en partie les fonctions du programme LCD_ID_Reader qui permet de lire l'ID de ce type de carte:
// adapted from LCD_ID_Reader from http://misc.ws/lcd_information
// controllers either read as 16-bit or as a sequence of 8-bit values
//-- Arduino UNO or Mega 2560 Plugged as shield
#define LCD_RST A4
#define LCD_CS A3
#define LCD_RS A2
#define LCD_WR A1
#define LCD_RD A0
#define LCD_D0 8
#define LCD_D1 9
#define LCD_D2 2
#define LCD_D3 3
#define LCD_D4 4
#define LCD_D5 5
#define LCD_D6 6
#define LCD_D7 7
void setup()
{
Serial.begin(115200);
while (!Serial) ;
Serial.println("Read Command on UNO shield");
lcdInit();
readCmd(7);
Serial.println("\nRead Registers on UNO shield");
lcdInit();
for (uint16_t i = 0; i < 256; i++) readReg(i, 7);
Serial.print("\nPas vu d'ID:");
Serial.print("\n ILI9320");
}
void loop()
{
}
void printhex(uint8_t val)
{
if (val < 0x10) Serial.print("0");
Serial.print(val, HEX);
}
void readReg(uint16_t reg, uint8_t n)
{
uint8_t val8;
lcdReset();
lcdSetWriteDir();
lcdWriteCommand(reg);
Serial.print("reg(0x");
printhex(reg >> 8);
printhex(reg);
Serial.print(")");
lcdSetReadDir();
while (n--) {
val8 = lcdReadData8();
Serial.print(" ");
printhex(val8);
}
lcdSetWriteDir();
Serial.print("\t");
switch (reg)
{
case 0x00: Serial.print("ST7781: 77 83 77 83 77 83 77");
Serial.print("\n HX8347: 47 ou 75?");
Serial.print("\n HX8367: 67");
Serial.print("\n ILI9325: 9325");
Serial.print("\n ILI9328: 9325");
Serial.print("\n ILI9331: 9331");
Serial.print("\n ILI9335: 9335");
Serial.print("\n R61505: 1505");
Serial.print("\n R61580: 1580");
Serial.print("\n RM68090: 6809");
Serial.print("\n S6D0129: 0129");
Serial.print("\n ST7781: 7783");
break;
case 0x04:
Serial.print("\n ST7787");
Serial.print("\n ST7789");
break;
case 0x32: Serial.print("HX8312: 0C"); break;
case 0x67: Serial.print("HX8346: 46"); break;
case 0xD3: Serial.print("ILI9341: 00 00 93 41 41 41 41");
Serial.print("\n ILI9340: 40");
Serial.print("\n ILI9342: 9342");
break;
}
Serial.println();
}
void readCmd(uint8_t n)
{
uint8_t val8;
lcdReset();
lcdSetReadDir();
Serial.print("Cmd ");
while (n--) {
Serial.print(" ");
printhex(lcdReadReg8());
}
lcdSetWriteDir();
Serial.print("\n SPFD5408: 5408");
Serial.print("\n OTM3225");
Serial.println();
}
void lcdInit()
{
pinMode(LCD_CS, OUTPUT);
digitalWrite(LCD_CS, HIGH);
pinMode(LCD_RS, OUTPUT);
digitalWrite(LCD_RS, HIGH);
pinMode(LCD_WR, OUTPUT);
digitalWrite(LCD_WR, HIGH);
pinMode(LCD_RD, OUTPUT);
digitalWrite(LCD_RD, HIGH);
pinMode(LCD_RST, OUTPUT);
digitalWrite(LCD_RST, HIGH);
}
void lcdReset()
{
digitalWrite(LCD_RST, LOW);
delay(2);
digitalWrite(LCD_RST, HIGH);
delay(10); //allow controller to re-start
}
void lcdWrite8(uint16_t data)
{
digitalWrite(LCD_D0, data & 1);
digitalWrite(LCD_D1, (data & 2) >> 1);
digitalWrite(LCD_D2, (data & 4) >> 2);
digitalWrite(LCD_D3, (data & 8) >> 3);
digitalWrite(LCD_D4, (data & 16) >> 4);
digitalWrite(LCD_D5, (data & 32) >> 5);
digitalWrite(LCD_D6, (data & 64) >> 6);
digitalWrite(LCD_D7, (data & 128) >> 7);
}
uint16_t lcdRead8()
{
uint16_t result = digitalRead(LCD_D7);
result <<= 1;
result |= digitalRead(LCD_D6);
result <<= 1;
result |= digitalRead(LCD_D5);
result <<= 1;
result |= digitalRead(LCD_D4);
result <<= 1;
result |= digitalRead(LCD_D3);
result <<= 1;
result |= digitalRead(LCD_D2);
result <<= 1;
result |= digitalRead(LCD_D1);
result <<= 1;
result |= digitalRead(LCD_D0);
return result;
}
void lcdSetWriteDir()
{
pinMode(LCD_D0, OUTPUT);
pinMode(LCD_D1, OUTPUT);
pinMode(LCD_D2, OUTPUT);
pinMode(LCD_D3, OUTPUT);
pinMode(LCD_D4, OUTPUT);
pinMode(LCD_D5, OUTPUT);
pinMode(LCD_D6, OUTPUT);
pinMode(LCD_D7, OUTPUT);
}
void lcdSetReadDir()
{
pinMode(LCD_D0, INPUT);
pinMode(LCD_D1, INPUT);
pinMode(LCD_D2, INPUT);
pinMode(LCD_D3, INPUT);
pinMode(LCD_D4, INPUT);
pinMode(LCD_D5, INPUT);
pinMode(LCD_D6, INPUT);
pinMode(LCD_D7, INPUT);
}
void lcdWriteData(uint16_t data)
{
lcdSetWriteDir();
digitalWrite(LCD_CS, LOW);
digitalWrite(LCD_RS, HIGH);
digitalWrite(LCD_RD, HIGH);
digitalWrite(LCD_WR, HIGH);
lcdWrite8(data >> 8);
digitalWrite(LCD_WR, LOW);
delayMicroseconds(10);
digitalWrite(LCD_WR, HIGH);
lcdWrite8(data);
digitalWrite(LCD_WR, LOW);
delayMicroseconds(10);
digitalWrite(LCD_WR, HIGH);
digitalWrite(LCD_CS, HIGH);
}
void lcdWriteCommand(uint16_t command)
{
lcdSetWriteDir();
digitalWrite(LCD_CS, LOW);
digitalWrite(LCD_RS, LOW);
digitalWrite(LCD_RD, HIGH);
digitalWrite(LCD_WR, HIGH);
lcdWrite8(command >> 8);
digitalWrite(LCD_WR, LOW);
delayMicroseconds(10);
digitalWrite(LCD_WR, HIGH);
lcdWrite8(command);
digitalWrite(LCD_WR, LOW);
delayMicroseconds(10);
digitalWrite(LCD_WR, HIGH);
digitalWrite(LCD_CS, HIGH);
}
uint8_t lcdReadData8()
{
uint8_t result;
lcdSetReadDir();
digitalWrite(LCD_CS, LOW);
digitalWrite(LCD_RS, HIGH);
digitalWrite(LCD_RD, HIGH);
digitalWrite(LCD_WR, HIGH);
digitalWrite(LCD_RD, LOW);
delayMicroseconds(10);
result = lcdRead8();
digitalWrite(LCD_RD, HIGH);
delayMicroseconds(10);
return result;
}
uint8_t lcdReadReg8()
{
uint8_t result;
lcdSetReadDir();
digitalWrite(LCD_CS, LOW);
digitalWrite(LCD_RS, LOW);
digitalWrite(LCD_RD, HIGH);
digitalWrite(LCD_WR, HIGH);
digitalWrite(LCD_RD, LOW);
delayMicroseconds(10);
result = lcdRead8();
digitalWrite(LCD_RD, HIGH);
delayMicroseconds(10);
return result;
}
On a ainsi les principaux accès.
Fonctions à garder:
− tous les #define qui donnent la correspondance des broches
− lcdInit qui met les broches de commande en sortie
− lcdRestet mais que l'on peut insérer dans lcdInit
− lcdSetWriteDir qui met les broches de données en sortie (pour écrire)
− lcdSetReadDir qui met les broches de données en entrée (pour lire)
− lcdWrite8 qui permet d'écrire 8 bits
− lcdRead8 qui permet de lire 8 bits
− lcdWriteData qui écrit une donnée 8 bits
− lcdWriteCommand qui écrit une commande 8 bits
− lcdReadData8 qui lit une donnée 8 bits
− lcdReadReg8 qui lit un registre 8 bits.
Pour les signaux de commande LCD_RST (A4) est une broche de RAZ, on l'actionne une fois en début de programme.
LCD_CS est le chip select: on doit le mettre à l'état bas pour accéder à l'écran. On peut d'ailleurs le passer bas dans le set up et le laisser bas en permanence. En le passant à l'état haut, cela permet d'utiliser toutes les autres broches (sauf Reset) comme on veut, on pourrait par exemple si on ne change pas l'affichage faire tourner un moteur...
LCD_RS mis à LOW, on lit/ecrit une commande (registres), mis à HIGH on lit/écrit une donnée (paramètre ou mémoire écran) Elle s'appelle D/CX dans le pdf
LCD_WR mettre à LOW pour écrire (WRX dans le pdf)
LCD_RD mettre à LOW pour lire (RDX dans le pdf)
============================================================================
Ces fonctions te paraissent compréhensibles?
Je comprends le sens des fonctions.
Mais la seule chose qui me gêne, c'est que ce n'est pas moi qui l'ai écrit ! Donc je comprends ta manière de faire, mais il va falloir que je trouve le sens de la logique, son fonctionnement exact .
Je comprends MIEUX pourquoi il est préférable d'utiliser une bibliothèque.
Mais encore un avantage de ce programme, c'est que on peut choisir les broches presque comme on veut !
Il faut être conscient d'une chose, on ne peut pas réinventer le monde. C'est pour cela que les nouveaux constructeurs de voitures créent des modèles assez proche des anciens.
Ce qu'il faut faire est de comprendre ce qui a déjà été fait, de s'en inspirer, de garder ce qui est bon et de modifier ce qui ne nous plaît pas.
Les fonctions décrites dans cet exemple on l'immense avantage d'être plus simples à comprendre, même si c'est loin d'être les meilleures. On s'inspire de ces fonctions, et on les modifient petit à petit. Ce n'est pas les fonctions que j'utilise.
Si tu veux, on peut reprendre morceau par morceau:
− compréhension de la partie
− suppressions de ce qui ne sert à rien
− écriture avec sa propre écriture
Mieux, non. Plus simple et plus rapide oui. Ecrire ses propres fonctions permet d'aller souvent plus vite avec un code plus petit. Par exemple Adafruit fait une seule bibliothèque pour tous les drivers, le choix étant fait à l'exécution; Du coup, on a le code de plusieurs drivers en mémoire au lieu d'un seul. C'est un peu comme si chaque voiture était équipée de toutes les roues de secours possibles. Il y a certainement des fonctions dont on n'a jamais besoin, et des fonctions utiles manquantes.
Si j'ai fait le choix d'écrire mes fonctions, c'est par défi, par jeu, mais aussi pour avoir accès à des fonctions que je trouve utiles (accents, fonte de taille variable, images plus compactes...)
Utiliser sa bibliothèque ou utiliser une bibliothèque existante, c'est comme porter un costume sur mesure ou un prêt à porter. Il y a surtout un problème de coût. Sauf qu'en info, je ne paye que le temps.
C'est vrai en théorie, j'utilise pour ces cartes un branchement avec une Mega qui met les 8 fils de données sur un seul port et dans le bon sens. Du coup on gagne beaucoup en vitesse.
Mais à priori, il suffirait de prendre la bibliothèque et de la modifier pour pouvoir utiliser les broches que l'on souhaite.
Par exemple en prenant la bibliothèque Elegoo, on trouve dedans les lignes:
#elif defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__)
// Arduino Mega, ADK, etc.
#ifdef USE_Elegoo_SHIELD_PINOUT
#define RD_PORT PORTF
#define WR_PORT PORTF
#define CD_PORT PORTF
#define CS_PORT PORTF
#define RD_MASK B00000001
#define WR_MASK B00000010
#define CD_MASK B00000100
#define CS_MASK B00001000
#define write8inline(d) { \
PORTH = (PORTH&B10000111)|(((d)&B11000000)>>3)|(((d)&B00000011)<<5); \
PORTB = (PORTB&B01001111)|(((d)&B00101100)<<2); \
PORTG = (PORTG&B11011111)|(((d)&B00010000)<<1); \
WR_STROBE; }
Il s'agit des lignes pour une MEGA (car defined(__AVR_ATmega2560__)
) et on a ici l'adressage des broches RD se trouve sur le portF sur le bit de poids 1. Les données passent par les ports H, B et G. Si on modifie le câblage, il suffit de modifier ces lignes.
Pas vraiment. La librairie Adafruit_ILI9341 est une librairie adaptée au contrôleur ILI9341.
Elle est dérivée de la librairie Adafruit_SPITFT et utilise également Adafruit_GFX pour les tracés et la gestion des polices ce caractères.
Adafruit_ST7735 et Adafruit_ST7789 reprennent le même principe d'héritage.
En fait quand on fait les comptes, la librairie Adafruit_ILI9341 ne fait pas grand chose. Le plus gros du travail est fait par Adafruit_SPITFT, et également Adafruit_GFX.
A mon sens le plus gros du travail réside dans la gestion des polices de caractères. Et Adafruit en fournit un certain nombre. Adafruit fournit également un convertisseur de polices en partant de polices truetype.
Vrai, mais c'est une limitation de la majeure parties des librairies.
Pouvoir afficher des caractères accentués impose d'adopter un codage, ISO-8859-1 pour le français. Mais ce codage sera différent pour de l'Allemand. Je ne connais pas de librairie proposant un tel choix.
On peut également adopter Unicode ou UTF8, mais c'est plutôt réservé aux systèmes embarqués riches (Linux) car les polices n'occupent plus du tout la même place en mémoire !
En résumé je dirais :
Pour des affichages de petite taille, afficher en anglais, ou utiliser des logos, des images.
Pour des affichages plus évolués, utiliser une RASPBERRY-PI.
Il y a aussi la solution serveur HTTP, utilisable sur de petits embarqués (ESPXX, ARDUINO ETHERNET). Dans ce cas, les polices sont affichées par le navigateur, donc n'occupent aucune place sur le serveur. Et cela ouvre la voie du JAVASCRIPT !
Quant à développer une librairie personnelle, pour les différents écrans que j'utilise, c'est totalement hors de question pour moi.
Je ne la connaissais pas, elle est récente. Mais 0 priori, elle gère les écrans SPI, et pas ceux qui nous intéresse ici en mode parallèle 8 bits.
Perso, j'utilise le utf8 restreint qui est compris par l'Arduino et par la console. Cela rajoute en gros les accents. Je n'ai pas besoin de l'ensemble des caractères UTF8. Ce qui fait que je peux afficher sur l'écran les chaînes qui passent le Serial.print
Pour moi, c'est un peu comme un jeu d'échec. Quand on joue une partie, cela n'apporte rien... que la satisfaction d'avoir construit quelque chose. Ecrire pour Uno ma librairie graphique, ma gestion des pas à pas, est finalement un but en soi. Même si au final, je peux m'en passer. C'est comme en bricolage, je me fais mon meuble à tiroirs, je pourrais en acheter un qui conviendrait à peu près.
Si j'aime bien la Uno, c'est que j'ai l'impression qu'on peut en comprendre une grande partie du fonctionnement et jouer avec les limites. C'était pareil du temps du DOS. Avec une esp32, je ne comprends pas ce que je fais
Il y en a d'autres, UTFT, etc.
Sauf qu'un meuble à tiroirs a des caractéristiques physiques bien plus variées qu'une librairie graphique.
La méthode fillRect() d'une librairie graphique prendra en paramètres x, y, width, height, color. La méthode fillRect() d'une librairie écrite par tes soins prendra exactement les mêmes paramètres, et fera la même chose. Il n'y a rien à inventer dans ce domaine. Par contre dans le domaine du meuble à tiroirs il y a énormément de possibilités d’innover.
On peut écrire tout ce que l'on veut, en partant de ZÉRO. Je ne vois pas pourquoi on n'appliquerait pas le même principe à toute librairie. Pourquoi utiliser le core ARDUINO dans ce cas ? Chacun peut écrire sa propre fonction digitalWrite() et l'appeler autrement, mais cela restreint fortement les possibilités de partage de code.
On n'a pas écrit les normes POSIX ou X11 pour les chiens, et ce n'est qu'un exemple.
En tous cas, écrire une librairie graphique n'est pas un conseil que je donnerais à un débutant, mais je reconnais toutefois que c'est un réflexe de débutant de réinventer la roue.
Quand tu auras compris le principe de l'édition de liens, tu verras qu'utiliser une seule fonction d'une librairie ne provoque pas l'inclusion de toutes les autres onctions dans l'exécutable final.
Si tu utilises uniquement la fonction drawPixel(), les fonctions drawCircle() ou fillRect() ne seront pas présentes dans le binaire. C'est la base du C.
Quant à la création d'objets, à de rares exceptions près, toutes les librairies ARDUINO sont écrites en C++, et cela ne crée pas d'ennuis particuliers.
Fallait pas prendre cet exemple, je ne suis pas le seul à utiliser digitalWriteFast()!
[edit]
Finalement ce choix n'est pas parfait. La fonction dont j'avais besoin limite le dessin non pas à l'écran mais à une zone de dessin incluse dans l'écran, pour moi le paramètre couleur est facultatif (on reprend alors la couleur précédente) et en mode inversion, la couleur est ignorée et fillrect inverse les pixels (permet de dessiner sans perdre les information précédentes, ce qui permet de dessiner et d'effacer sans sauvegarder une zone mémoire).
Mais écrire des librairies est un sport important. Il existe ainsi plusieurs bibliothèques pour gérer les pas à pas, les leds programmables, les cartes SD, les servos, la gestion des boutons etc...
Je consulte le datasheet en ce moment.
En fait, c'est assez compréhensible, mais il y a tellement d'informations parfois inutiles (pour notre sujet actuel) qu'il faut savoir quelles informations en extraire.
Par exemple, j'ai trouvé une broche permettant de dire à l'ILI9341 que j'envoie des bits.
Je vous tiens au courant.
C'est à dire une librairie uniquement valable pour AVR, donc non portable. Très peu pour moi.
Pour ma part si je veux être efficace, j'utilise directement les registres du processeur, tout en sachant que je perds le côté portabilité.
Finalement tu n'as plus qu'à publier ta fameuse librairie graphique, car pour l'instant à part en parler, tu n'as pas fait grand chose. On pourra ainsi l'adopter, ou pas.
Ce n'est certainement pas mon cas, car plutôt que de développer en partant de ZÉRO des API pas plus valables que celles d'autrui, je fais un effort de compréhension, en essayant de comprendre ce qu'ont fait les autres. Lire le code des autres est l'exercice le plus difficile qui soit.
Je te conseillerais de lire le code de la stack TCP/IP d'OpenBsd, ensuite tu nous diras si tu te sens capable d'écrire quelque chose d'équivalent.
Voilà le lien pour télécharger la librairie adafruit_gfx :
https://gammatroniques.fr/download/librairies-ecran-tactile
Mais j'utilisais aussi MCUFRIENDS_kbv :
https://github.com/prenticedavid/MCUFRIEND_kbv/archive/refs/heads/master.zip
Donc voilà les deux librairies que je recommande.
À savoir, j'ai déjà créé un sujet pour les deux librairies, avec les informations supplémentaires pour les installer et le complémentaire. Voici le lien : Librairie pour écran tactile pour Arduino
Je confirme, je trouve l'exercice très difficile.
Très honnêtement quand ce n'est pas trop compliqué je choisis la facilité en réécrivant à ma sauce C'est plus rapide et plus simple que de comprendre ce que d'autres ont fait.
Mais il faut ne pas se voiler la face : c'est reculer pour mieux sauter. On choisit la facilité, mais on ne s'entraine pas à comprendre les autres et quand il faut vraiment y passer c'est dur.
Ce n'est pas la tentation de réinventer la roue, mais c'est plutôt une réaction humaine normale : se prouver qu'on est capable de reproduire, a sa sauce, ce que d'autres ont fait.
Quand on est dans son domaine professionnel, on n'a rien à se prouver : on n'a pas réussi ou pas.
Quand on commence une activité à titre de loisir, c'est différent et on peut vouloir se faire plaisir.
La seule imposition est quand même de voir la réalité en face et de garder les pieds sur terre, mais ça, cela vient plutôt avec l'age.
Pour ma part je recommanderais d'aller se servir directement dans les githubs Adafruit, plutôt que de télécharger un ZIP sur un site exotique, où la mise à disposition de la dernière version n'est pas garantie.
+ 1
Et c'est valable pour tout y compris pour les datasheets.
Les sites exotiques sont souvent un caprice qui est vite laissé à l'abandon.
Je dis ça, je dis rien : en fait si vous avez lu le sujet que j'ai mis en lien ( Librairie pour écran tactile pour Arduino ), j'ai décrit mon problème, qui s'avère être que depuis la mise à jour à la dernière version de la librairie, un message d'erreur s'affichait lors de la compilation.

En tous cas, écrire une librairie graphique n'est pas un conseil que je donnerais à un débutant, mais je reconnais toutefois que c'est un réflexe de débutant de réinventer la roue.
Et aussi, qui à dit que j'aimerais faire une librairie graphique ?
Aussi, je ne voudrais pas réinventer la roue, puisque que je ne veux pas créer de librairie graphique ! Ce que je voudrais c'est du code dans un fichier ino .
Et plus précisément, je pense commencer avec mes deux écrans, puis avec tous les écrans, en créant des fonctions :
void detecter_id () {
// etc.
}
void tracer_point (uint16_t x, uint16_t y, byte r, byte g, byte b) {
//etc.
}
void tracer_ligne (uint16_t x1 , uint16_t y1, uint16_t x2, uint16_t y2, byte r, byte g, byte b) {
/*Bla bla et REbla bla ET patati patata.
BON OK, on à compris . 😂 */
}
À savoir, je suis sur téléphone des fois, comme sur ce message, et je n'ai pas vérifié cet extrait du code que je voudrais avec un compilateur.
Donc prévenez moi s'il y a des erreurs pour que je les corriges, car j'ai fait de tête, sans livre et sans notes (sur le moment, en fait).
Et sinon, qui trouve judicieux de créer des fonctions dans le code ino, de créer des librairies, d'utiliser des librairies déjà existantes et/ou d'utiliser des fonctions déjà existantes ? Voici le sondage :
(On parle que de l'écran, pas du reste pour ce sondage)
- Utiliser une librairie déjà existante
- Utiliser du code déjà existant
- Créer une librairie
- Créer son code

Aussi, je ne voudrais pas réinventer la roue, puisque que je ne veux pas créer de librairie graphique ! Ce que je voudrais c'est du code dans un fichier ino .
Il faudra que tu m'expliques la différence entre écrire des fonctions dans un .ino, et écrire des fonctions dans un fichier .cpp séparé.
Parce que moi je n'en vois pas, mis à part qu'une librairie est partagée entre plusieurs projets.

depuis la mise à jour à la dernière version de la librairie, un message d'erreur s'affichait lors de la compilation.
Plutôt que d'écrire ses propres fonctions, autant essayer de résoudre ces problèmes.
Mais trouves-tu judicieux d'utiliser une librairie, en sachant que :
-
Elle doit savoir à chaque exécution de quel pilote on utilise. Problème : code plus lourd, pour rien, alors que l'on à une place mémoire limitée.
-
On ne peut pas choisir les broches voulues. Problème : parfois des branchements supplémentaires
-
Les mises à jour existent ! Problème : il faut à chaque fois télécharger ! Et là, il suffit de modifier les fonctions (dans le code)
Ce n'est que ce qui est passé dans ma tête à ce moment-là. Je suppose qu'il y a quand même plus que ça.
Je viens d'explorer une librairie graphique et je viens de trouver que presque tout se tient sur la fonction "drawPixel", comme je l'avais pensé.
Continue, tu tiens le bon bout.
Tracking / Muted pour moi.