Test d'un écran tactile TFT LCD 2.4" ILI9341 controleur Spfd5408 sur Mega2560

Bonjour,
J'utilise Arduino1.6.9
Je viens vers vous pour m'aider à tester un écran 2.4"sur Mega.
Pour l'instant,j'ai reussi grace aux librairies SPFD5408 de:( Joao Lopes F. - joaolopesf@gmail.com)

Telechargé ici:GitHub - JoaoLopesF/SPFD5408: Adafruit Libraries changed to works in TFT 2.4 shields with the SPFD5408 controller for Arduino Uno and Mega (Discontinued library)

-SPFD5408_calibrate:
J'ai du modifier legerement la librairie "SPFD5408_TouchScreen.cpp" ligne:163

return TSPoint(1023 - x, 1023 - y, z);

car les coordonnées etaint inversées.

je poste aussi le sketch avec les modifications pour mega qui vont bien.

Voila le sketch modifié:

// bibliothèque SPFD5408 

#include <SPFD5408_Adafruit_GFX.h>    // bibliothèque graphique de base
#include <SPFD5408_Adafruit_TFTLCD.h> // bibliothèque spécifique-Hardware
#include <SPFD5408_TouchScreen.h>     // bibliothèque tactile

// Valeur étalonnée
#define SENSIBILITY 300
#define MINPRESSURE 10
#define MAXPRESSURE 1000

//Ce sont les broches pour le shield sur Mega2560
#define YP A3 
#define XM A2 
#define YM 9  
#define XP 8 

/*
//Macros remplacées par des variables
#define TS_MINX 150
#define TS_MINY 120
#define TS_MAXX 920
#define TS_MAXY 940
*/
short TS_MINX=150;
short TS_MINY=120;
short TS_MAXX=920;
short TS_MAXY=940;

// Initialise le Tactile:

TouchScreen ts = TouchScreen(XP, YP, XM, YM, SENSIBILITY);

// Brochage LCD:

#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
#define LCD_RESET A4 // En option: Arduino broche reset

// Attribuer des noms lisibles par l'homme à des valeurs communes de couleurs 16 bits:
#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF

// Initialise le LCD

Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

// Dimensions

uint16_t width = 0;
uint16_t height = 0;

// Boutons

#define BUTTONS 3
#define BUTTON_CLEAR 0
#define BUTTON_SHOW 1
#define BUTTON_RESET 2

Adafruit_GFX_Button buttons[BUTTONS];

uint16_t buttons_y = 0;

La partie setup

//-- Setup

void setup(void) {

  // Initialise le controleur
  
  tft.reset();
  
  tft.begin(0x9341);

  tft.setRotation(0); // Necessaire pour Mega,changer de 0-4 pour rotation

  width = tft.width() - 1;
  height = tft.height() - 1;

  initializeButtons();
  
  // Bordure

  drawBorder();
  
  // Initialise écran
  
  tft.setCursor (12, 20);
  tft.setTextSize (3);
  tft.setTextColor(RED);
  tft.println("Bibliotheque");
  tft.setCursor (55, 85);
  tft.setTextColor(GREEN);
  tft.println("SPFD5408");
  tft.setCursor (55, 150);
  tft.setTextSize (2);
  tft.setTextColor(BLUE);
  tft.println("Calibration");

  tft.setCursor (60, 250);
  tft.setTextSize (1);
  tft.setTextColor(BLACK);
  tft.println("Touchez pour proceder");

  // Attendre tactile

  waitOneTouch();

  // Le calibrer
  
  calibrate_TS();

  // Attendre tactile

  waitOneTouch();

  // Calibration

  showCalibration();

}

Et enfin,la boucle

// -- Boucle

void loop()
{
  // Test de calibration
  
  TSPoint p;

  // Attendre tactile
  
  digitalWrite(13, HIGH);
  
  p = waitOneTouch();
  
  digitalWrite(13, LOW);

  // Carte des valeurs
  
//  p.x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width());
//  p.y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height());

  p.x = mapXValue(p);
  p.y = mapYValue(p);
  
  // Dessiner un point
  
  tft.fillCircle(p.x, p.y, 3, BLUE);
  
  // Afficher point d'écran tactile (TSPOINT)
  
  showTouched(p);
  
  // Boutons

  // vérifier s'ils ont été pressées
  
  for (uint8_t b=0; b<BUTTONS; b++) {
    
    if (buttons[b].contains(p.x, p.y)) {

        // Action

        switch  (b) {

          case BUTTON_CLEAR:

              // Nettoie

              showCalibration();

              break;
              
          case BUTTON_SHOW:

              // Nettoie

              showResults();

              tft.println();
              tft.println("Touchez pour procéder");

              waitOneTouch();

              showCalibration();
              
              break;
              
          case BUTTON_RESET:

              // Nouvelle calibration

              calibrate_TS();

              waitOneTouch();

              showCalibration();

              break;
        }
    }
  }

}

// Calibration de l'écran tactile (resistif)

void calibrate_TS(void) {

  // Basé sur le code écrit dans https://forum.arduino.cc/index.php?topic=223769.15
  
  TSPoint p1, p2;
  int16_t temp;
  int32_t tempL;
  
  tft.fillScreen(BLACK);
  
  tft.fillCircle(4,4,4,WHITE); //montrer le premier point
  tft.setCursor(5, 30); 
  tft.setTextColor(WHITE); 
  tft.setTextSize(1);
  tft.println("Touchez le point");

  uint16_t limit = 40;
  
  do {
  
    p1 = waitOneTouch();

  } while (!(mapXValue(p1) < limit && mapYValue(p1) < limit));
  
  tft.fillScreen(BLACK);

  tft.fillCircle(234,314,4,WHITE); //montrer le 2ème point
  tft.setCursor(50, 280);
  tft.println("Touchez l'autre point");
  delay (500); // anti- rebond

  do {
  
    p2 = waitOneTouch();
  
  } while (!(mapXValue(p2) > (width - limit) && mapYValue(p2) > (height - limit)));
  
  tft.fillScreen(BLACK);
  
  delay (300); 
  
  temp=p2.x-p1.x; // Calculer les nouveaux coefficients, obtenir différence X
  tempL=((long)temp*1024)/(tft.width()-20);
  TS_MINX=p1.x-( (tempL*10)>>10);// 10 pixels du bord
  TS_MAXX=p1.x+( (tempL*tft.width())>>10);// 220 pixels entre points
  temp=p2.y-p1.y; // ¨obtenir différence Y
  tempL=((long)temp*1024)/(tft.height()-20);
  TS_MINY=p1.y-( (tempL*10)>>10);// 10 pixels du bord
  TS_MAXY=TS_MINY+( (tempL*tft.height())>>10);
  
  // Show results

  showResults();
  
//  p1.x = map(p1.x, TS_MAXX,TS_MINX, tft.width(), 0);
//  p1.y = map(p1.y, TS_MAXY,TS_MINY, tft.height(), 0);
//  p2.x = map(p2.x, TS_MAXX,TS_MINX, tft.width(), 0);
//  p2.y = map(p2.y, TS_MAXY,TS_MINY, tft.height(), 0);

    p1.x = mapXValue(p1);
    p1.y = mapYValue(p1);
    p2.x = mapXValue(p2);
    p2.y = mapYValue(p2);

  tft.println();
  tft.setTextSize (2);
  tft.setTextColor(YELLOW);
  tft.println("Points touches:");
  tft.setTextColor(WHITE);
  tft.print("Pt 1: ");tft.print(p1.x);tft.print(" : ");tft.println(p1.y);
  tft.print("Pt 2: ");tft.print(p2.x);tft.print(" : ");tft.println(p2.y);

  tft.println();

  // Wait a touch
  tft.setTextSize(1);
  tft.setTextColor(RED);
  tft.println("Touchez pour proceder");

  waitOneTouch();
}
// attendre 1 touche pour revenir au point

TSPoint waitOneTouch() {
  
  TSPoint p;
  
  do {
    p= ts.getPoint(); 
  
    pinMode(XM, OUTPUT); //Pins re-configuré pour le contrôle du TFT
    pinMode(YP, OUTPUT);
  
  } while((p.z < MINPRESSURE )|| (p.z > MAXPRESSURE));
  
  return p;
}

// Dessiner une bordure

void drawBorder () {

  uint16_t width = tft.width() - 1;
  uint16_t height = tft.height() - 1;
  uint8_t border = 10;

  tft.fillScreen(RED);
  tft.fillRect(border, border, (width - border * 2), (height - border * 2), WHITE);
  
}

// Afficher un écran de calibration

void showCalibration() {
  
  // Clear
  
  tft.fillScreen(BLACK);
  tft.setTextSize (1);

  // Header
  
  tft.fillRect(0, 0, width, 10, RED);

  tft.setCursor (40, 0);
  tft.setTextColor(WHITE);
  tft.println("*** Test de calibration ***");

  // Footer

  TSPoint p; // Seulement pour montrer les valeurs initiales
  p.x=0;
  p.y=0;
  p.z=0;
  
  showTouched(p);

  // Buttons

  for (uint8_t i=0; i<3; i++) {
      buttons[i].drawButton();
  }
  
}

// Afficher les coordonnées     

void showTouched(TSPoint p) {

  uint8_t w = 40; // Largeur
  uint8_t h = 10; // Hauteur
  uint8_t x = (width - (w*2)); // X
  uint8_t y = 11; // Y
  
  tft.fillRect(x, y, w*2, h, WHITE); // Pour le nettoyage

  tft.drawRect(x, y, w, h, RED); // For X
  tft.drawRect(x+w+2, y, w*2, h, RED); // For Y

  tft.setTextColor(BLACK);
  tft.setCursor(x+2, y + 1);
  tft.print("X: ");
  showValue(p.x);
  
  tft.setCursor(x+2+w+2, y + 1);
  tft.print("Y: ");
  showValue(p.y);

}

// Afficher une valeur de TSPoint
void showValue (uint16_t value) {

  if (value < 10)
    tft.print("00");
  if (value < 100)
    tft.print("0");
    
  tft.print(value);
  
}

// Show results of calibration

void showResults() {

  tft.fillScreen(BLACK);
  
  // Header
  
  tft.fillRect(0, 0, width, 10, RED);

  tft.setCursor (40, 0);
  tft.setTextColor(WHITE);
  tft.println("*** Resultats de l'etalonnage ***");

  // Results
  
  tft.setCursor(5, 30);
  tft.setTextSize(2);
  tft.setTextColor(YELLOW);
  tft.println("Apres Etalonnage: ");
  tft.setTextColor(WHITE);
  tft.print("TS_MINX= ");tft.println(TS_MINX);
  tft.print("TS_MINY= ");tft.println(TS_MINY);
  tft.println();
  tft.print("TS_MAXX= ");tft.println(TS_MAXX);
  tft.print("TS_MAXY= ");tft.println(TS_MAXY);
  tft.setTextSize(1);
  tft.setTextColor(RED);
}

// Initialise les boutons

void initializeButtons() {

  uint16_t x = 40;
  uint16_t y = height - 20;
  uint16_t w = 75;
  uint16_t h = 20;
  
  uint8_t spacing_x = 5;
  
  uint8_t textSize = 1;

  char buttonlabels[3][20] = {"Nettoie", "Affiche", "Recalibre"};
  uint16_t buttoncolors[15] = {RED, BLUE, RED};

  for (uint8_t b=0; b<3; b++) {
    buttons[b].initButton(&tft,                           // TFT object
                  x+b*(w+spacing_x),  y,                  // x, y,
                  w, h, WHITE, buttoncolors[b], WHITE,    // w, h, outline, fill, 
                  buttonlabels[b], textSize);             // text
  }

  
  buttons_y = y;
  
}

// Carte la coordonnée X
  
uint16_t mapXValue(TSPoint p) {

  uint16_t x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width());

  //Corriger l'offset du toucher. L'étalonnage manuel
  //x+=1;
  
  return x;

}

// Carte la coordonnée Y

uint16_t mapYValue(TSPoint p) {

  uint16_t y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height());

  //Corriger l'offset du toucher. L'étalonnage manuel
  //y-=2;

  return y;
}

Le sketch SPFD5408_graphictest fonctionne sans modification

Le sketch SPFD5408_rotationtest fonctionne avec la simple modification des broches tactiles du shield sur Mega:

#define YP A3 
#define XM A2 
#define YM 9  
#define XP 8

Le sketch SPFD5408_tftpaint fonctionne avec la meme modification.

Mon probleme est avec le sketch spfd5408_tftbmp

J’ai essayé avec les librairies SPI,SD,SdFAT…

Meme lorsque j’arrive a initialiser la carte,a lire le BMP,impossible de l’afficher sur l’ecran.Le Moniteur serie du compilateur me dit:" BMP Format not reconized".

Pour le faire fonctionner,j’ai changé la valeur dans ‘Sd2Card.h’ de la librairie SD:

#define MEGA_SOFT_SPI 1 à la place de 0

Et j’ai aussi changé: #define SD_SPI_CONFIGURATION 2 à la place de 0

dans la librairie:SdFatConfig.h

Là,j’arrive aleatoirement a initialiser ma micro sd,lire le fichier mais jamais l’afficher sur l’ecran.
C’est frustrant et je reconnait que je suis largué.
Si quelqu’un veut me donner un coup de main,je suis preneur.Merci

Le BMP est-il bien du bon format ? (oui, il y a des bmp qui se permettent des fantaisies)

Essaye avec ceux-ci, que j'ai fait tourner avec succès sur un afficheur ILI9340 2.2" .

http://arduino.amt69.org/images/flower.BMP
http://arduino.amt69.org/images/hibiscus.bmp
http://arduino.amt69.org/images/woof.bmp

Le dernier provient de la bibliothèque Adafruit.

Merci,je les ai telechargées.

je n'arrive plus a faire fonctionner l' SPI.
Je pense que j'ai du encore trop essayer de modifier et désomais ça fonctionne plus.Je suis bon pour une reinstall des bibliothèques et reconfig....
Une misere ce systeme..
Merci de ton aide.

J'ai mis un schéma de branchement afficheur SPI avec emploi d'un 4050 pour abaisser les tensions sur
cette page

Voilà une photo du shield sur le Mega.
Le systeme utilise le Mode SdFatSoftSpi qui permet donc de faire fonctionner le lecteur sur les pin 10-11-12-13 du mega. Cela fonctionne tres bien en lecture,écriture de fichier,même lecture de fichier image,j’ai même pu charger un fichier de configuration,mais lorsqu’il s’agit d’afficher les images à l’ecran,alors là,c’est toujours fichier non reconnu…
C’est là que je bloque.
J’ai essayé les BMP que tu m’as fournis mais sans succès.

Dans le sujet du lien que tu m’as fournis,tu précises que tu n’utilises plus le lecteur au dos de la carte et je commence à me demander si mes soucis ne viendraient pas de là!!!

be853c66897273942a9e92f06f22fdbc040044a1.jpg

pilote:
Voilà une photo du shield sur le Mega. Le systeme utilise le Mode SdFatSoftSpi qui permet donc de faire fonctionner le lecteur sur les pin 10-11-12-13 du mega.

Je ne connaissais pas ce shield. je pensais à une liaison SPI.

pilote:
Dans le sujet du lien que tu m'as fournis,tu précises que tu n'utilises plus le lecteur au dos de la carte et je commence à me demander si mes soucis ne viendraient pas de là!!!!

Si tu as une lecteur de carte séparé essaye le. Dans les animations que j'ai faites en Mai, j'ai consacré une séance à l'afficheur sans les images (texte et dessin géométrique) et une autre aux cartes SD et à l'affichage d'image.
http://arduino.amt69.org/Arduino2015-lecon14.html
http://arduino.amt69.org/Arduino2015-lecon17.html

Salut,micol.
Merci de ton aide,j’ai commandé un lecteur externe pour essai, en chine donc,pas avant quelques semaines.
Ce que je comprends pas c’est que j’arrive a lire,ecrire dans la carte et même voir les fichier BMP mais pas a les afficher.
Ca fait plusieurs semaines et j’en arrive a me perdre dans mes bibliotheques et sketchs!!!
J’utilise le mode SOFT SPI qui permet de se connecter sur les broches 10-11-12-13 du mega.

Je vais essayer de poster un sketch qui fonctionne en lecture mais qui refuse d’afficher.

Ce petit module à 6€ est pourtant tres sympa.Je trouve vraiment dommage de bloquer là dessus ce qui me bloque la suite du developpement de mon interface depuis 2 semaines.

Voici un sketch SdFat qui fontionne:

#include <SPI.h>
#include <SdFat.h>
//SdFat sd;

// Numéros des broches dans les modèles doivent être des constantes.
const uint8_t chipSelect = 10;
const uint8_t SOFT_MISO_PIN = 12;
const uint8_t SOFT_MOSI_PIN = 11;
const uint8_t SOFT_SCK_PIN  = 13;

SdFile myFile;

// SdFat logiciel modèle SPI
SdFatSoftSpi<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> sd;

void setup() {
  Serial.begin(9600);
  while (!Serial) {}  // attendre Leonardo
  Serial.println("Tapez un caractere pour commencer");
  while (Serial.read() <= 0) {}
  delay(400);  //  problème de réinitialisation Due

  // Initialisation Sd Fat ou imprimer un message d'erreur détaillé et arrêt
  // Utilisez demi-vitesse comme la bibliothèque native.
  // changer à SPI_FULL_SPEED pour plus de performance.
  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
    sd.initErrorHalt();
  }

  // ouvrir le fichier comme la bibliothèque native SD
  if (!myFile.open("test.txt", O_RDWR | O_CREAT | O_AT_END)) {
    sd.errorHalt("ouverture test.txt pour echec ecriture");
  }
  // si le fichier ouvert OK, y écrire:
  Serial.print("Ecriture du texte.txt...");
  myFile.println("Je fonctionne parfaitement");

  // fermez le fichier:
  myFile.close();
  Serial.println("Test OK.");

  // Rouvrir le dossier pour la lecture:
  if (!myFile.open("test.txt", O_READ)) {
    sd.errorHalt("ouverture test.txt pour echec lecture");
  }
  Serial.println("test.txt:");

  // lire le fichier jusqu'à ce qu'il n'y a rien dedans:
  int data;
  while ((data = myFile.read()) >= 0) {
    Serial.write(data);
  }
  // fermez le fichier:
  myFile.close();
}

void loop() {

Bonjour,je viens de nouveau solliciter la communauté dans le cas ou quelqu'un saurait lire une image BMP stockée sur la carte sd d'un ecran TFT Lcd 2.4" type 9341.
Tout le reste fonctionne et mon but est d'utiliser un ecran d'acceuil et des icones cliquables.
Je suis un peu dépité de ne pas y parvenir.
J'utilise la librairie SdFat en mode Software SPI.
J'ai essaye toute sorte de BMP trouvée sur le web sans succès.
Je me suis basé sur des exemples de la librairie SPFD5408.

bonjour,apres avoir changé d'écan pour un 3.5" avec SD integré,j'ai re-éssayé d'afficher un BMP sur l'écran et j'ai toujours le même probleme.IMPOSSIBLE D'AFFICHER UNE IMAGE DESSUS.!!!!
Tout le reste mache,j'en conclu donc qu'il y a un soucis dans mon Sketch. Aussi je me permet de le poster si quelqu'un peut m'expliquer l'errer, ce serait génial.
Merci d'avance

Début du sketch

#include <Adafruit_GFX.h>    // Bibliothèque graphique principale
#include <MCUFRIEND_kbv.h>
MCUFRIEND_kbv tft;       // Câblé pour les boucliers shields UNO ou Mega2560
//#include <SD.h>
#include <SPI.h>
#include <SdFat.h>

const uint8_t SOFT_MISO_PIN = 12;
const uint8_t SOFT_MOSI_PIN = 11;
const uint8_t SOFT_SCK_PIN  = 13;
//
// La sélection de puce peut être constante ou variable de RAM
const uint8_t SD_CHIP_SELECT_PIN = 10;

SdFatSoftSpi<SOFT_MISO_PIN, SOFT_MOSI_PIN, SOFT_SCK_PIN> sd;

#define LCD_CS A3 // 'Chip Select'sur Analogique 3
#define LCD_CD A2 // 'Command/Data'sur Analogique 2
#define LCD_WR A1 // 'LCD Write'sur Analogique 1
#define LCD_RD A0 // 'LCD Read'sur Analogique 0

// Dans la carte SD, placez des fichiers BMP couleur de 24 bits

Le setup

void setup()
{
  Serial.begin(9600);
  tft.reset();
  tft.begin(0x9481);

  progmemPrint(PSTR("Initialisation carte SD..."));
  if (!sd.begin(SD_CHIP_SELECT_PIN, SPI_HALF_SPEED)) {
    progmemPrintln(PSTR("échoué!"));
    return;
  }
  progmemPrintln(PSTR("Initialisation OK!"));

  bmpDraw("woof.bmp", 0, 0);
  delay(1000);
}

Et enfin la boucle…

void loop()
{
  for(int i = 0; i<4; i++) {
    tft.setRotation(i);
    tft.fillScreen(0);
    for(int j=0; j <= 200; j += 50) {
      bmpDraw("woof.bmp", j, j);
    }
    delay(2000);
  }
}
// Cette fonction ouvre un fichier Bitmap Windows (BMP) et
// l'affiche aux coordonnées données. Il est accéléré
// en lisant beaucoup de pixels en valeur de données à la fois
// (plutôt que pixel par pixel). Augmenter le tampon
// La taille prend plus de place dans la RAM de l'Arduino mais
// Rend le chargement un peu plus rapide. 20 pixels semble un
// Bon équilibre.

#define BUFFPIXEL 20

void bmpDraw(char *filename, int x, int y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // W+H en pixels
  uint8_t  bmpDepth;              // Bit depth (Actuellement doit être 24)
  uint32_t bmpImageoffset;        // Début des données d'image dans le fichier
  uint32_t rowSize;               // Pas toujours = bmpWidth; Peut avoir bourrage
  uint8_t  sdbuffer[3*BUFFPIXEL]; // Pixel dans le tampon (R+G+B par pixel)
  uint16_t lcdbuffer[BUFFPIXEL];  // pixel hors mémoire tampon (16-bit par pixel)
  uint8_t  buffidx = sizeof(sdbuffer); // Position actuelle dans le sdbuffer
  boolean  goodBmp = false;       // Défini sur true quand l'analyse de l'en-tête est valide
  boolean  flip    = true;        // BMP est stocké de bas en haut
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();
  uint8_t  lcdidx = 0;
  boolean  first = true;

  if((x >= tft.width()) || (y >= tft.height())) return;

  Serial.println();
  progmemPrint(PSTR("Chargement image '"));
  Serial.print(filename);
  Serial.println('\'');
  // Open requested file on SD card
  if ((bmpFile = sd.open(filename)) == NULL) {
    progmemPrintln(PSTR("Fichier non trouvé"));
    return;
  }

  // Analyse l'entête BMP
  if(read16(bmpFile) == 0x4D42) { //Signature BMP
    progmemPrint(PSTR("Taille fichier: ")); Serial.println(read32(bmpFile));
    (void)read32(bmpFile); // Lire et ignorer les octets crées
    bmpImageoffset = read32(bmpFile); // Début des données d'image
    progmemPrint(PSTR("Decalage Image: ")); Serial.println(bmpImageoffset, DEC);
    // Read DIB header
    progmemPrint(PSTR("Taille de l'en-tete: ")); Serial.println(read32(bmpFile));
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if(read16(bmpFile) == 1) { // # Niveau -- doit être '1'
      bmpDepth = read16(bmpFile); // bits par pixel
      progmemPrint(PSTR("Profondeur Bit: ")); Serial.println(bmpDepth);
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = Non compressé

        goodBmp = true; // Format BMP pris en charge - procéder!
        progmemPrint(PSTR("Taille de l'image: "));
        Serial.print(bmpWidth);
        Serial.print('x');
        Serial.println(bmpHeight);

        // Les lignes BMP sont bourrées (si nécessaire) à une limite de 4 octets
        rowSize = (bmpWidth * 3 + 3) & ~3;

        // Si la hauteur du BMP est négative, l'image est dans l'ordre descendant.
        if(bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }

        // Recadrer la zone à charger
        w = bmpWidth;
        h = bmpHeight;
        if((x+w-1) >= tft.width())  w = tft.width()  - x;
        if((y+h-1) >= tft.height()) h = tft.height() - y;

        // Définir la fenêtre d'adresse TFT sur les limites d'image découpées
        tft.setAddrWindow(x, y, x+w-1, y+h-1);

        for (row=0; row<h; row++) { // Pour chaque ligne de balayage ...
          // Recherchez le début de la ligne de balayage. ça peut être laborieux
          // // Intensif à faire sur chaque ligne, mais cette
          //  méthode couvre beaucoup de petits détails comme le recadrage
          // et le remplissage de balayage. En outre, la recherche ne prend de
          // la place que si la position du fichier doit réellement changer
          //  (Évite un grand nombre de grappes mathématiques dans la bibliothèque SD).
          if(flip) // Le bitmap est stocké de bas en haut 
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else     // Bitmap est stocké de haut en bas
            pos = bmpImageoffset + row * rowSize;
          if(bmpFile.position() != pos) { // Besoin de chercher?
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer); // Forcer le rechargement du tampon
          }

          for (col=0; col<w; col++) { // Pour chaque colonne ...
            // Du temps pour lire plus de données de pixels?
            if (buffidx >= sizeof(sdbuffer)) { // En effet
              // Appuyer d'abord sur le tampon LCD vers l'écran
              if(lcdidx > 0) {
                tft.pushColors(lcdbuffer, lcdidx, first);
                lcdidx = 0;
                first  = false;
              }
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Définir l'index au début
            }

            // Convertir des pixels du format BMP au format TFT
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            lcdbuffer[lcdidx++] = tft.color565(r,g,b);
          } // Fin pixel
        } // Fin scanline
        // Ecrire les données restantes sur l'écran LCD
        if(lcdidx > 0) {
          tft.pushColors(lcdbuffer, lcdidx, first);
        } 
        progmemPrint(PSTR("Chargé dans "));
        Serial.print(millis() - startTime);
        Serial.println(" ms");
      } // endFin goodBmp
    }
  }

  bmpFile.close();
  if(!goodBmp) progmemPrintln(PSTR("Format .BMP non reconnu."));
}

// Ceux-ci lisent 16- et 32 bits types à partir du fichier de la carte SD.
// Les données BMP sont stockées little-endian, Arduino est little-endian aussi.
// Peut-être besoin d'inverser l'ordre subscript si le portage est ailleurs.

uint16_t read16(File f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read(); // MSB
  return result;
}

uint32_t read32(File f) {
  uint32_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read();
  ((uint8_t *)&result)[2] = f.read();
  ((uint8_t *)&result)[3] = f.read(); // MSB
  return result;
}

// Copier la chaîne du flash au port série
// La chaîne source DOIT être à l'intérieur d'une déclaration PSTR ()!
void progmemPrint(const char *str) {
  char c;
  while(c = pgm_read_byte(str++)) Serial.print(c);
}

// Même que ci-dessus, avec une ligne de fuite
void progmemPrintln(const char *str) {
  progmemPrint(str);
  Serial.println();
}