[erledigt] 2.4" TFT-Shield zeigt invertierte Farben

Auf meinem Schreibtisch liegt ein Display, das ich nach der Beschreibung Touch Screen TFT Shield for Arduino Uno und IDE 1.6.5 versuche zu betreiben. Als Test nehme ich eine Mischung aus Adafruit_TFTLCD\examples\tftbmp\tftbmp.pde und Arduin-o-Phone Sketch. Mit freundlicher Unterstützung durch @stern ist es mir gelungen, das Bild eines Hundes (bmp-Datei) und eine Tastatur abzubilden:


Leider werden alle Farben invertiert dargestellt, siehe links. Wenn ich die bmp-Datei auf dem PC invertiere, hat der Hund ein braunes Fell (Mitte). Auch wenn ich alle Farbdefinitionen bitweise invertiere, werden gemalte Farben richtig dargestellt.

Desweiteren liefert tft.readID() immer den Wert Null.

Nun grüble ich, wo der Fehler liegen könnte. Als Controller habe ich ILI9341 angegeben, was aus den Möglichkeiten der Adafruit-Bbliothek das beste Ergebnis brachte. Auch bei HX8357D wird was angezeigt, aber beim invertierten Bild ist das Fell braun (richtig) mit hellen Augen (falsch).

Ich hoffe auf sachdienliche Hinweise zur Ergreifung des Fehlers :slight_smile:

Mein Testsketch Teil 1:

// Shield und UNO
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_TFTLCD.h> // Hardware-specific library
#include <TouchScreen.h>
#include <SD.h>
#include <SPI.h> 

#define SD_CS 10     // Set the chip select line to whatever you use (10 doesnt conflict with the library) 

#define YP A2  // must be an analog pin, use "An" notation!
#define XM A1  // must be an analog pin, use "An" notation!
#define YM 6   // can be a digital pin
#define XP 7   // can be a digital pin

#define TS_MINX 130
#define TS_MINY  90
#define TS_MAXX 900
#define TS_MAXY 910

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

#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
// optional
#define LCD_RESET A4

// Assign human-readable names to some common 16-bit color values:
#define BLACK   0xFFFF
#define BLUE    0xFFE0
#define RED     0x07FF
#define GREEN   0xF81F
#define CYAN    0xF800
#define MAGENTA 0x07E0
#define YELLOW  0x001F
#define WHITE   0x0000

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

// Color definitions
#define ILI9341_BLACK       0xFFFF
#define ILI9341_NAVY        0xFFF0
#define ILI9341_DARKGREEN   0xFC1F
#define ILI9341_DARKCYAN    0xFC10
#define ILI9341_MAROON      0x87FF
#define ILI9341_PURPLE      0x87F0
#define ILI9341_OLIVE       0x841F
#define ILI9341_LIGHTGREY   0x39E7
#define ILI9341_DARKGREY    0x8410
#define ILI9341_BLUE        0xFFE0
#define ILI9341_GREEN       0xF81F
#define ILI9341_CYAN        0xF800
#define ILI9341_RED         0x07FF
#define ILI9341_MAGENTA     0x07E0
#define ILI9341_YELLOW      0x001F
#define ILI9341_WHITE       0x0000
#define ILI9341_ORANGE      0x02DF
#define ILI9341_GREENYELLOW 0x501A
#define ILI9341_PINK        0x07E0

/******************* UI details */
#define BUTTON_X 40
#define BUTTON_Y 100
#define BUTTON_W 60
#define BUTTON_H 30
#define BUTTON_SPACING_X 20
#define BUTTON_SPACING_Y 20
#define BUTTON_TEXTSIZE 2

// text box where numbers go
#define TEXT_X 10
#define TEXT_Y 10
#define TEXT_W 220
#define TEXT_H 50
#define TEXT_TSIZE 3
#define TEXT_TCOLOR ILI9341_MAGENTA
// the data (phone #) we store in the textfield
#define TEXT_LEN 12
char textfield[TEXT_LEN + 1] = "";
uint8_t textfield_i = 0;

// We have a status line for like, is FONA working
#define STATUS_X 10
#define STATUS_Y 65



Adafruit_GFX_Button buttons[15];
/* create 15 buttons, in classic candybar phone style */
char buttonlabels[15][5] = {"Send", "Clr", "End", "1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "0", "#" };
uint16_t buttoncolors[15] = {ILI9341_DARKGREEN, ILI9341_DARKGREY, ILI9341_RED,
                             ILI9341_BLUE, ILI9341_BLUE, ILI9341_BLUE,
                             ILI9341_BLUE, ILI9341_BLUE, ILI9341_BLUE,
                             ILI9341_BLUE, ILI9341_BLUE, ILI9341_BLUE,
                             ILI9341_ORANGE, ILI9341_BLUE, ILI9341_ORANGE
                            };

void setup(void) {
  Serial.begin(9600);
  Serial.println(F("TFT LCD test"));

  tft.reset();

  uint16_t identifier = 0x9341;
  Serial.println(F("Using 0x9341 LCD driver"));
  tft.begin(identifier);

  Serial.print(F("Initializing SD card..."));
  if (!SD.begin(SD_CS)) {
    Serial.println(F("failed!"));
    return;
  }
  Serial.println(F("OK!"));

  tft.setRotation(2);
  bmpDraw("woof.bmp", 0, 0);
  delay(5000); 
  bmpDraw("woof_neg.bmp", 0, 0);
  delay(5000); 
  
  // tft.fillScreen(BLACK);

  // create buttons
  for (uint8_t row = 0; row < 5; row++) {
    for (uint8_t col = 0; col < 3; col++) {
      buttons[col + row * 3].initButton(&tft, BUTTON_X + col * (BUTTON_W + BUTTON_SPACING_X),
                                        BUTTON_Y + row * (BUTTON_H + BUTTON_SPACING_Y), // x, y, w, h, outline, fill, text
                                        BUTTON_W, BUTTON_H, ILI9341_WHITE, buttoncolors[col + row * 3], ILI9341_WHITE,
                                        buttonlabels[col + row * 3], BUTTON_TEXTSIZE);
      buttons[col + row * 3].drawButton();
    }
  }

  // create 'text field'
  tft.drawRect(TEXT_X, TEXT_Y, TEXT_W, TEXT_H, ILI9341_WHITE);


}
// Print something in the mini status bar with either flashstring
void status(const __FlashStringHelper *msg) {
  tft.fillRect(STATUS_X, STATUS_Y, 240, 8, ILI9341_BLACK);
  tft.setCursor(STATUS_X, STATUS_Y);
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(1);
  tft.print(msg);
}
// or charstring
void status(char *msg) {
  tft.fillRect(STATUS_X, STATUS_Y, 240, 8, ILI9341_BLACK);
  tft.setCursor(STATUS_X, STATUS_Y);
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(1);
  tft.print(msg);
}

Testsketch Teil 2:

#define MINPRESSURE 10
#define MAXPRESSURE 1000

void loop(void) {
  digitalWrite(13, HIGH);
  TSPoint p = ts.getPoint();
  digitalWrite(13, LOW);

  // if sharing pins, you'll need to fix the directions of the touchscreen pins
  //pinMode(XP, OUTPUT);
  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT);
  //pinMode(YM, OUTPUT);

  // we have some minimum pressure we consider 'valid'
  // pressure of 0 means no pressing!

  // p = ts.getPoint();
  /*
  if (ts.bufferSize()) {

  } else {
    // this is our way of tracking touch 'release'!
    p.x = p.y = p.z = -1;
  }*/

  // Scale from ~0->4000 to tft.width using the calibration #'s
  /*
  if (p.z != -1) {
    p.x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width());
    p.y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height());
    Serial.print("("); Serial.print(p.x); Serial.print(", ");
    Serial.print(p.y); Serial.print(", ");
    Serial.print(p.z); Serial.println(") ");
  }*/
  if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
    // scale from 0->1023 to tft.width
    p.x = (tft.width() - map(p.x, TS_MINX, TS_MAXX, tft.width(), 0));
    p.y = (tft.height() - map(p.y, TS_MINY, TS_MAXY, tft.height(), 0));
  }

  // go thru all the buttons, checking if they were pressed
  for (uint8_t b = 0; b < 15; b++) {
    if (buttons[b].contains(p.x, p.y)) {
      //Serial.print("Pressing: "); Serial.println(b);
      buttons[b].press(true);  // tell the button it is pressed
    } else {
      buttons[b].press(false);  // tell the button it is NOT pressed
    }
  }

  // now we can ask the buttons if their state has changed
  for (uint8_t b = 0; b < 15; b++) {
    if (buttons[b].justReleased()) {
      // Serial.print("Released: "); Serial.println(b);
      buttons[b].drawButton();  // draw normal
    }

    if (buttons[b].justPressed()) {
      buttons[b].drawButton(true);  // draw invert!

      // if a numberpad button, append the relevant # to the textfield
      if (b >= 3) {
        if (textfield_i < TEXT_LEN) {
          textfield[textfield_i] = buttonlabels[b][0];
          textfield_i++;
          textfield[textfield_i] = 0; // zero terminate

          // fona.playDTMF(buttonlabels[b][0]);
        }
      }

      // clr button! delete char
      if (b == 1) {

        textfield[textfield_i] = 0;
        if (textfield > 0) {
          textfield_i--;
          textfield[textfield_i] = ' ';
        }
      }

      // update the current text field
      Serial.println(textfield);
      tft.setCursor(TEXT_X + 2, TEXT_Y + 10);
      tft.setTextColor(TEXT_TCOLOR, ILI9341_BLACK);
      tft.setTextSize(TEXT_TSIZE);
      tft.print(textfield);

      // its always OK to just hang up
      if (b == 2) {
        status(F("Hanging up"));
        //fona.hangUp();
      }
      // we dont really check that the text field makes sense
      // just try to call
      if (b == 0) {
        status(F("Calling"));
        Serial.print("Calling "); Serial.print(textfield);

        //fona.callPhone(textfield);
      }

      delay(100); // UI debouncing
    }
  }
}

// This function opens a Windows Bitmap (BMP) file and
// displays it at the given coordinates.  It's sped up
// by reading many pixels worth of data at a time
// (rather than pixel by pixel).  Increasing the buffer
// size takes more of the Arduino's precious RAM but
// makes loading a little faster.  20 pixels seems a
// good balance.

#define BUFFPIXEL 20

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

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // W+H in pixels
  uint8_t  bmpDepth;              // Bit depth (currently must be 24)
  uint32_t bmpImageoffset;        // Start of image data in file
  uint32_t rowSize;               // Not always = bmpWidth; may have padding
  uint8_t  sdbuffer[3*BUFFPIXEL]; // pixel in buffer (R+G+B per pixel)
  uint16_t lcdbuffer[BUFFPIXEL];  // pixel out buffer (16-bit per pixel)
  uint8_t  buffidx = sizeof(sdbuffer); // Current position in sdbuffer
  boolean  goodBmp = false;       // Set to true on valid header parse
  boolean  flip    = true;        // BMP is stored bottom-to-top
  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();
  Serial.print(F("Loading image '"));
  Serial.print(filename);
  Serial.println('\'');
  // Open requested file on SD card
  if ((bmpFile = SD.open(filename)) == NULL) {
    Serial.println(F("File not found"));
    return;
  }

  // Parse BMP header
  if(read16(bmpFile) == 0x4D42) { // BMP signature
    Serial.println(F("File size: ")); Serial.println(read32(bmpFile));
    (void)read32(bmpFile); // Read & ignore creator bytes
    bmpImageoffset = read32(bmpFile); // Start of image data
    Serial.print(F("Image Offset: ")); Serial.println(bmpImageoffset, DEC);
    // Read DIB header
    Serial.print(F("Header size: ")); Serial.println(read32(bmpFile));
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if(read16(bmpFile) == 1) { // # planes -- must be '1'
      bmpDepth = read16(bmpFile); // bits per pixel
      Serial.print(F("Bit Depth: ")); Serial.println(bmpDepth);
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed

        goodBmp = true; // Supported BMP format -- proceed!
        Serial.print(F("Image size: "));
        Serial.print(bmpWidth);
        Serial.print('x');
        Serial.println(bmpHeight);

        // BMP rows are padded (if needed) to 4-byte boundary
        rowSize = (bmpWidth * 3 + 3) & ~3;

        // If bmpHeight is negative, image is in top-down order.
        // This is not canon but has been observed in the wild.
        if(bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }

        // Crop area to be loaded
        w = bmpWidth;
        h = bmpHeight;
        if((x+w-1) >= tft.width())  w = tft.width()  - x;
        if((y+h-1) >= tft.height()) h = tft.height() - y;

        // Set TFT address window to clipped image bounds
        tft.setAddrWindow(x, y, x+w-1, y+h-1);

        for (row=0; row<h; row++) { // For each scanline...
          // Seek to start of scan line.  It might seem labor-
          // intensive to be doing this on every line, but this
          // method covers a lot of gritty details like cropping
          // and scanline padding.  Also, the seek only takes
          // place if the file position actually needs to change
          // (avoids a lot of cluster math in SD library).
          if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else     // Bitmap is stored top-to-bottom
            pos = bmpImageoffset + row * rowSize;
          if(bmpFile.position() != pos) { // Need seek?
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer); // Force buffer reload
          }

          for (col=0; col<w; col++) { // For each column...
            // Time to read more pixel data?
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              // Push LCD buffer to the display first
              if(lcdidx > 0) {
                tft.pushColors(lcdbuffer, lcdidx, first);
                lcdidx = 0;
                first  = false;
              }
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
            }

            // Convert pixel from BMP to TFT format
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            lcdbuffer[lcdidx++] = tft.color565(r,g,b);
          } // end pixel
        } // end scanline
        // Write any remaining data to LCD
        if(lcdidx > 0) {
          tft.pushColors(lcdbuffer, lcdidx, first);
        } 
        Serial.print(F("Loaded in "));
        Serial.print(millis() - startTime);
        Serial.println(" ms");
      } // end goodBmp
    }
  }

  bmpFile.close();
  if(!goodBmp) Serial.println(F("BMP format not recognized."));
}

// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.

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;
}

Bei fehlenden Informationen bitte nachfragen!

http://www.moleandroid.com/arduino-tutorial-2-how-to-setup-mcufriend-2-4-lcd-tft-libraries-and-avoid-white-screen/

@Whandall: Danke für die Links. Beim Video bin ich gestern eingenickt, da war ich wohl zu müde. Aber der erste Link führt mich zu LCD_ID_Reader, womit ich alle Register ausgelesen habe. Gelistet sind die Register größer Null:

Register 0x0A : 0x0808
Register 0x0C : 0x6666
Register 0x2E : 0xFC94
Register 0x3E : 0x9494
Register 0xF2 : 0x2894
Register 0xF3 : 0x0100
Register 0xF4 : 0x0B00
Register 0xF5 : 0x004D
Register 0xF6 : 0x0101
Register 0xF7 : 0x0080
Register 0xF8 : 0x3300
Register 0xF9 : 0x0400
Register 0xFA : 0x000C
Register 0xFE : 0x0420

Verbirgt sich da irgendwo die Kennung vom Controller?

Da ILI9341 ganz gut funktioniert, habe ich mal im Datenblatt geblättert und ein Register zum Invertieren der Anzeige gefunden. Daher habe ich

    writeRegister8(ILI9341_INVERTON, 0);

in der Bibliothek ergänzt, was zum gewünschten Ergebnis führt.

Nun werde ich überlegen, die Adafruit-Bibliothek zu ergänzen oder eine abgespeckte Variante zu erstellen. Die Warnung "Wenig Speicher verfügbar" führt meine Gedanken in diese Richtung.

Die Identifikation sollte über Adresse/Kommando 4 lesbar sein, aber ich habe mit den Internals nicht rumgespielt,
bei mir geht Adafruit_ILI9341 so wie es ist.

Die Größe der Library ist allerdings ein Problem, eine abgespeckte Version wäre wirklich wünschenswert.

Aus einem anderen Thema:

ElEspanol:
Ja, dich meinte ich. Weil ich hab 2 mit 9335 verbaut, die Lib ist Mist, und Touch geht nicht. Die was ich noch daliegen habe, sollen ein 9341 haben. Was muss ich da anpassen in welcher Lib?

Das Shield funktioniert auf dem UNO, aber dann sind gerade mal noch 3 Pins frei und auch der Speicher wird knapp. Daher habe ich das Shield nun als Platine mit dem Mega2560 frei verdrahtet.

Die Beispiele von Adafruit habe ich zusammenkopiert und leicht angepaßt zum Testen, siehe Anhang.

Ergänzungen in Adafruit_TFTLCD.cpp:

...
// Ergaenzt, um die Displayanzeige invertieren zu können. Getestet fuer ILI9341.
void Adafruit_TFTLCD::invert(void) {
    driver = ID_9341;
    CS_ACTIVE;
    writeRegister8(ILI9341_SOFTRESET, 0);
    delay(50);
    writeRegister8(ILI9341_DISPLAYOFF, 0);

    writeRegister8(ILI9341_INVERTON, 0);

    writeRegister8(ILI9341_SLEEPOUT, 0);
    delay(150);
    writeRegister8(ILI9341_DISPLAYON, 0);
    delay(500);
    setAddrWindow(0, 0, TFTWIDTH-1, TFTHEIGHT-1);
}
...

Ergänzungen in Adafruit_TFTLCD.h:

...
void     invert(void); // Ergaenzt, um die Displayanzeige invertieren zu können. Getestet fuer ILI9341.
...

Ergebnis:

bbd06b6340be3ca9e4b01a6f0fe50390370e1286.png

Test_TFTLCD.ino (13.8 KB)