8 Bit TFT Library Issue

Working libraries, libraries being ported and related hardware
mkengineering
Posts: 12
Joined: Thu Apr 30, 2020 8:35 pm

8 Bit TFT Library Issue

Post by mkengineering »

Hello All,

I recently ported Steve's 8 Bit Parallel TFT library to the new ST core here.
The library seems to work well (I'm currently using it for a 320x480 4" ILI9486 display, using the ILI9341 init) but, for some reason, in order for the display to properly initialize it requires me to enable USB Support (even though USB is not used anywhere in the sketch or the library. I am using STLink to upload.

Image

The current sketch I'm using is just displaying Hello World at the top of the screen:

Code: Select all

#include <Adafruit_TFTLCD_8bit_STM32.h>

Adafruit_TFTLCD_8bit_STM32 tft;

void setup(void) {
  tft.begin(0x9341);
  tft.setRotation(1);
  tft.fillScreen(BLACK);
  tft.setCursor(0, 0);
  tft.setTextColor(WHITE);  tft.setTextSize(6);
  tft.println("Hello World!");
}
void loop(void) {
}
Basically, whenever I set USB Support (if available) to "None", the sketch compiles fine with no errors but the display remains in its default state with a white screen, similar to if it hadn't been properly initialized.
Is this due to some errors in the compilation process?

Here are the terminal outputs for the two different settings:

USB Support (if available): "CDC (generic 'Serial' supercede U(S)ART)":

Code: Select all

Sketch uses 23796 bytes (36%) of program storage space. Maximum is 65536 bytes.
Global variables use 3732 bytes (18%) of dynamic memory, leaving 16748 bytes for local variables. Maximum is 20480 bytes.
C:\Users\Mike\AppData\Local\Arduino15\packages\STM32\tools\STM32Tools\1.3.2/tools/win/stm32CubeProg.bat 0 C:\Users\Mike\AppData\Local\Temp\arduino_build_4549/test_ili9486.ino.bin -g 
      -------------------------------------------------------------------
                       STM32CubeProgrammer v2.4.0                  
      -------------------------------------------------------------------

ST-LINK SN  : 53FF6B064885525553372187
ST-LINK FW  : V2J35S7
Voltage     : 3.23V
SWD freq    : 4000 KHz
Connect mode: Under Reset
Reset mode  : Hardware reset
Device ID   : 0x410
Device name : STM32F101/F102/F103 Medium-density
Flash size  : 64 KBytes
Device type : MCU
Device CPU  : Cortex-M3



Memory Programming ...
Opening and parsing file: test_ili9486.ino.bin
  File          : test_ili9486.ino.bin
  Size          : 24092 Bytes
  Address       : 0x08000000 


Erasing memory corresponding to segment 0:
Erasing internal memory sectors [0 23]
Download in Progress:


File download complete
Time elapsed during download operation: 00:00:01.734

RUNNING Program ... 
  Address:      : 0x8000000
Application is running
Start operation achieved successfully
USB Support (if available): "None":

Code: Select all

Sketch uses 14288 bytes (21%) of program storage space. Maximum is 65536 bytes.
Global variables use 788 bytes (3%) of dynamic memory, leaving 19692 bytes for local variables. Maximum is 20480 bytes.
C:\Users\Mike\AppData\Local\Arduino15\packages\STM32\tools\STM32Tools\1.3.2/tools/win/stm32CubeProg.bat 0 C:\Users\Mike\AppData\Local\Temp\arduino_build_4549/test_ili9486.ino.bin -g 
      -------------------------------------------------------------------
                       STM32CubeProgrammer v2.4.0                  
      -------------------------------------------------------------------

ST-LINK SN  : 53FF6B064885525553372187
ST-LINK FW  : V2J35S7
Voltage     : 3.23V
SWD freq    : 4000 KHz
Connect mode: Under Reset
Reset mode  : Hardware reset
Device ID   : 0x410
Device name : STM32F101/F102/F103 Medium-density
Flash size  : 64 KBytes
Device type : MCU
Device CPU  : Cortex-M3



Memory Programming ...
Opening and parsing file: test_ili9486.ino.bin
  File          : test_ili9486.ino.bin
  Size          : 14584 Bytes
  Address       : 0x08000000 


Erasing memory corresponding to segment 0:
Erasing internal memory sectors [0 14]
Download in Progress:


File download complete
Time elapsed during download operation: 00:00:01.185

RUNNING Program ... 
  Address:      : 0x8000000
Application is running
Start operation achieved successfully
Any help I could get on this would be great. I assume it's something simple that I'm overlooking
mkengineering
Posts: 12
Joined: Thu Apr 30, 2020 8:35 pm

Re: 8 Bit TFT Library Issue

Post by mkengineering »

Update:
I went ahead and swapped the display out with another 3.5" ILI9486 display I had lying around, same issue. I also swapped out the blue pill I was using, same issue. I then tried updating my version of the STM32duino Core, same issue. I also spent some time messing with other upload options and the display will also init fine with U(S)ART enabled in the upload settings and a Serial.begin() in your sketch, for some reason.

Upload Settings:

Image

Sketch:

Code: Select all

#include <Arduino.h>

#include <Adafruit_TFTLCD_8bit_STM32.h> // Hardware-specific library

Adafruit_TFTLCD_8bit_STM32 tft;

void setup(void) {
  Serial.begin(115200); //for some reason this is needed?
  tft.begin(0x9341);
}

void loop(void) {
  tft.fillScreen(BLACK);
  delay(1000);
  tft.fillScreen(RED);
  delay(1000);
}
I'm pretty much stumped at this point :(
GonzoG
Posts: 403
Joined: Wed Jan 15, 2020 11:30 am
Answers: 27
Location: Prudnik, Poland

Re: 8 Bit TFT Library Issue

Post by GonzoG »

Probably you try to access "Serial" in your library which isn't initialized and program hangs.

To see if program hangs I use simple test with LED: turn LED on before tft.begin(), turn it off after.

If you use Serial to output any data, use it in "if"

Code: Select all

if(Serial)
	Serial.print("...");
this way, it won't try to access non-existing object.
mkengineering
Posts: 12
Joined: Thu Apr 30, 2020 8:35 pm

Re: 8 Bit TFT Library Issue

Post by mkengineering »

I went through the entire library and I was only able to find some commented out Serial.print() from earlier debugging, which I decided to delete.
Here are the two main files:
CPP:

Code: Select all

// Graphics library by ladyada/adafruit with init code from Rossum
// MIT license

#include "Adafruit_TFTLCD_8bit_STM32.h"

#include "ili932x.h"
#include "ili9341.h"
#include "hx8347g.h"
#include "hx8357x.h"

GPIO_TypeDef * cntrlRegs; //HAL method
GPIO_TypeDef * dataRegs;

/*****************************************************************************/
// Constructor
/*****************************************************************************/
Adafruit_TFTLCD_8bit_STM32 :: Adafruit_TFTLCD_8bit_STM32(void)
: Adafruit_GFX(TFTWIDTH, TFTHEIGHT)
{
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::begin(uint16_t id)
{
	reset();

	if ((id == 0x9325) || (id == 0x9328)) {
		driver = ID_932X;
		ili932x_begin();
	} else if (id == 0x9341) {
		driver = ID_9341;
		ili9341_begin();
	} else if (id == 0x8357) {
		// HX8357D
		driver = ID_HX8357D;
		hx8357x_begin();
	} else if (id == 0x9338) {
		// ILI9338?
		driver = ID_9338;
		hx8357x_begin();
	} else if(id == 0x7575) {
		driver = ID_7575;
		hx8347g_begin();
	} else {
		driver = ID_UNKNOWN;
	}
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::reset(void)
{
	cntrlRegs = TFT_CNTRL_PORT; //HAL method
	dataRegs = TFT_DATA_PORT;
	pinMode(TFT_RD, OUTPUT);
	pinMode(TFT_WR, OUTPUT);
	pinMode(TFT_RS, OUTPUT);
	pinMode(TFT_CS, OUTPUT);
	CS_IDLE; // Set all control bits to HIGH (idle)
	CD_DATA; // Signals are ACTIVE LOW
	WR_IDLE;
	RD_IDLE;
	//set up 8 bit parallel port to write mode.
	setWriteDir();

	// toggle RST low to reset
	if (TFT_RST > 0) {
		pinMode(TFT_RST, OUTPUT);
		digitalWrite(TFT_RST, HIGH);
		delay(100);
		digitalWrite(TFT_RST, LOW);
		delay(100);
		digitalWrite(TFT_RST, HIGH);
		delay(100);
	}
}

/*****************************************************************************/
// Sets the LCD address window (and address counter, on 932X).
// Relevant to rect/screen fills and H/V lines.  Input coordinates are
// assumed pre-sorted (e.g. x2 >= x1).
/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::setAddrWindow(int16_t x1, int16_t y1, int16_t x2, int16_t y2)
{
	if(driver == ID_932X) {
		ili932x_setAddrWindow(x1, y1, x2, y2);
	} else if(driver == ID_7575) {
		hx8347g_setAddrWindow(x1, y1, x2, y2);
	} else if ((driver == ID_9341) || (driver == ID_HX8357D) || (driver == ID_9338)){
		ili9341_setAddrWindow(x1, y1, x2, y2);
	}
}

/*****************************************************************************/
// Fast block fill operation for fillScreen, fillRect, H/V line, etc.
// Requires setAddrWindow() has previously been called to set the fill
// bounds.  'len' is inclusive, MUST be >= 1.
/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::flood(uint16_t color, uint32_t len)
{
  uint16_t blocks;
  uint8_t  i, hi = color >> 8,
              lo = color;

	CS_ACTIVE_CD_COMMAND;
  if (driver == ID_9341) {
    writeCommand(ILI9341_MEMORYWRITE); // 16 bit
  } else if (driver == ID_932X) {
    writeCommand(ILI932X_RW_GRAM); // 16 bit
  } else if (driver == ID_HX8357D || driver == ID_9338) {
    write8(HX8357_RAMWR);
  } else {
    write8(0x22); // Write data to GRAM
  }

  // Write first pixel normally, decrement counter by 1
  CD_DATA;
  write8(hi);
  write8(lo);
  len--;

  blocks = (uint16_t)(len / 64); // 64 pixels/block
  if(hi == lo) {
    // High and low bytes are identical.  Leave prior data
    // on the port(s) and just toggle the write strobe.
    while(blocks--) {
      i = 16; // 64 pixels/block / 4 pixels/pass
      do {
        WR_STROBE; WR_STROBE; WR_STROBE; WR_STROBE; // 2 bytes/pixel
        WR_STROBE; WR_STROBE; WR_STROBE; WR_STROBE; // x 4 pixels
      } while(--i);
    }
    // Fill any remaining pixels (1 to 64)
	i = len & 63;
    while (i--) {
		WR_STROBE; WR_STROBE;
	}
  } else {
    while(blocks--) {
      i = 16; // 64 pixels/block / 4 pixels/pass
      do {
        write8(hi); write8(lo); write8(hi); write8(lo);
        write8(hi); write8(lo); write8(hi); write8(lo);
      } while(--i);
    }
	i = len & 63;
    while (i--) { // write here the remaining data
      write8(hi); write8(lo);
    }
  }
  CS_IDLE;
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::drawFastHLine(int16_t x, int16_t y, int16_t length, uint16_t color)
{
  int16_t x2;

  // Initial off-screen clipping
  if((length <= 0     ) ||
     (y      <  0     ) || ( y                  >= _height) ||
     (x      >= _width) || ((x2 = (x+length-1)) <  0      )) return;

  if(x < 0) {        // Clip left
    length += x;
    x       = 0;
  }
  if(x2 >= _width) { // Clip right
    x2      = _width - 1;
    length  = x2 - x + 1;
  }

  setAddrWindow(x, y, x2, y);
  flood(color, length);
  if(driver == ID_932X) setAddrWindow(0, 0, _width - 1, _height - 1);
  else                  hx8347g_setLR();
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::drawFastVLine(int16_t x, int16_t y, int16_t length, uint16_t color)
{
  int16_t y2;

  // Initial off-screen clipping
  if((length <= 0      ) ||
     (x      <  0      ) || ( x                  >= _width) ||
     (y      >= _height) || ((y2 = (y+length-1)) <  0     )) return;
  if(y < 0) {         // Clip top
    length += y;
    y       = 0;
  }
  if(y2 >= _height) { // Clip bottom
    y2      = _height - 1;
    length  = y2 - y + 1;
  }

  setAddrWindow(x, y, x, y2);
  flood(color, length);
  if(driver == ID_932X) setAddrWindow(0, 0, _width - 1, _height - 1);
  else                  hx8347g_setLR();
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::fillRect(int16_t x1, int16_t y1, int16_t w, int16_t h, uint16_t fillcolor)
{

  int16_t  x2, y2;

  // Initial off-screen clipping
  if( (w            <= 0     ) ||  (h             <= 0      ) ||
      (x1           >= _width) ||  (y1            >= _height) ||
     ((x2 = x1+w-1) <  0     ) || ((y2  = y1+h-1) <  0      )) return;
  if(x1 < 0) { // Clip left
    w += x1;
    x1 = 0;
  }
  if(y1 < 0) { // Clip top
    h += y1;
    y1 = 0;
  }
  if(x2 >= _width) { // Clip right
    x2 = _width - 1;
    w  = x2 - x1 + 1;
  }
  if(y2 >= _height) { // Clip bottom
    y2 = _height - 1;
    h  = y2 - y1 + 1;
  }

  setAddrWindow(x1, y1, x2, y2);
  flood(fillcolor, (uint32_t)w * (uint32_t)h);
  if(driver == ID_932X) setAddrWindow(0, 0, _width - 1, _height - 1);
  //else                  hx8347g_setLR();
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::fillScreen(uint16_t color)
{
  if(driver == ID_932X) {
    // For the 932X, a full-screen address window is already the default
    // state, just need to set the address pointer to the top-left corner.
    // Although we could fill in any direction, the code uses the current
    // screen rotation because some users find it disconcerting when a
    // fill does not occur top-to-bottom.
	ili932x_fillScreen(color);
  } else if ((driver == ID_9341) || (driver == ID_7575) || (driver == ID_HX8357D) || (driver == ID_9338)) {
    // For these, there is no settable address pointer, instead the
    // address window must be set for each drawing operation.  However,
    // this display takes rotation into account for the parameters, no
    // need to do extra rotation math here.
    setAddrWindow(0, 0, _width - 1, _height - 1);
  }
  flood(color, (long)TFTWIDTH * (long)TFTHEIGHT);
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::drawPixel(int16_t x, int16_t y, uint16_t color)
{
  // Clip
  if((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) return;

  if(driver == ID_932X) {

	  ili932x_drawPixel(x, y, color);

  } else if(driver == ID_7575) {

    uint8_t hi, lo;
    switch(rotation) {
     default: lo = 0   ; break;
     case 1 : lo = 0x60; break;
     case 2 : lo = 0xc0; break;
     case 3 : lo = 0xa0; break;
    }
    writeRegister8(   HX8347G_MEMACCESS      , lo);
    // Only upper-left is set -- bottom-right is full screen default
    writeRegisterPair(HX8347G_COLADDRSTART_HI, HX8347G_COLADDRSTART_LO, x);
    writeRegisterPair(HX8347G_ROWADDRSTART_HI, HX8347G_ROWADDRSTART_LO, y);
    hi = color >> 8; lo = color;
    CD_COMMAND; write8(0x22); CD_DATA; write8(hi); write8(lo);

  } else if ((driver == ID_9341) || (driver == ID_HX8357D) || (driver == ID_9338)) {

    setAddrWindow(x, y, x+1, y+1);
    writeRegister16(0x2C, color);
  }
}

/*****************************************************************************/
// Draw an image bitmap (16bits per color) at the specified position from the provided buffer.
/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::drawBitmap(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t * bitmap)
{
	if ( x>=0 && (x+w)<_width && y>=0 && (y+h)<=_height ) {
		// all pixel visible, do it in the fast way
		setAddrWindow(x,y,x+w-1,y+h-1);
		pushColors((uint16_t*)bitmap, w*h, true);
	} else {
		// some pixels outside visible area, do it in the classical way to disable off-screen points
		int16_t i, j;
		uint16_t * colorP = (uint16_t*)bitmap;
		for(j=0; j<h; j++) {
			for(i=0; i<w; i++ ) {
				drawPixel(x+i, y+j, *colorP++);
			}
		}
	}
}

/*****************************************************************************/
// Issues 'raw' an array of 16-bit color values to the LCD; used
// externally by BMP examples.  Assumes that setWindowAddr() has
// previously been set to define the bounds.  Max 255 pixels at
// a time (BMP examples read in small chunks due to limited RAM).
/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::pushColors(uint16_t *data, int16_t len, boolean first)
{
  uint16_t color;
  uint8_t  hi, lo;
  CS_ACTIVE;
  if(first == true) { // Issue GRAM write command only on first call
    CD_COMMAND;
    if(driver == ID_932X) write8(0x00);
    if ((driver == ID_9341) || (driver == ID_HX8357D) || (driver == ID_9338)){
       write8(0x2C);
     }  else {
       write8(0x22);
     }
  }
  CD_DATA;
  while(len--) {
    color = *data++;
    hi    = color >> 8; // Don't simplify or merge these
    lo    = color;      // lines, there's macro shenanigans
    write8(hi);         // going on.
    write8(lo);
  }
  CS_IDLE;
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::invertDisplay(boolean i)
{
	if ( driver==ID_932X ) ili932x_invertDisplay(i);
	else if ( driver==ID_9341 ) {
		writeCommand( i ? ILI9341_INVERTON : ILI9341_INVERTOFF);
		CS_IDLE;
	}
}
/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::setRotation(uint8_t x)
{
  // Call parent rotation func first -- sets up rotation flags, etc.
  Adafruit_GFX::setRotation(x);
  // Then perform hardware-specific rotation operations...

  if (driver == ID_932X) {

    ili932x_setRotation(x);

  } else if (driver == ID_7575) {

	hx8347g_setRotation(x);

  } else if (driver == ID_9341) {
   // MEME, HX8357D uses same registers as 9341 but different values
   uint16_t t;

   switch (rotation) {
   case 1:
     t = ILI9341_MADCTL_MX | ILI9341_MADCTL_MY | ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR;
     break;
   case 2:
     t = ILI9341_MADCTL_MX | ILI9341_MADCTL_BGR;
     break;
   case 3:
     t = ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR;
     break;
   case 0:
   default:
    t = ILI9341_MADCTL_MY | ILI9341_MADCTL_BGR;
    break;
  }
   writeRegister8(ILI9341_MADCTL, t ); // MADCTL
   // For 9341, init default full-screen address window:
   //setAddrWindow(0, 0, _width - 1, _height - 1); // CS_IDLE happens here

  } else if (driver == ID_HX8357D) {
    // MEME, HX8357D uses same registers as 9341 but different values
    uint16_t t;

    switch (rotation) {
      case 1:
        t = HX8357B_MADCTL_MY | HX8357B_MADCTL_MV | HX8357B_MADCTL_RGB;
        break;
      case 2:
        t = HX8357B_MADCTL_RGB;
        break;
      case 3:
        t = HX8357B_MADCTL_MX | HX8357B_MADCTL_MV | HX8357B_MADCTL_RGB;
        break;
      case 0:
      default:
        t = HX8357B_MADCTL_MX | HX8357B_MADCTL_MY | HX8357B_MADCTL_RGB;
        break;
    }
    writeRegister8(ILI9341_MADCTL, t ); // MADCTL
    // For 8357, init default full-screen address window:
    setAddrWindow(0, 0, _width - 1, _height - 1); // CS_IDLE happens here
  } else if (driver == ID_9338) {
    // Like HX8357D, but mirrored
    uint16_t t;
    
    switch (rotation) {
      case 1:
        t = HX8357B_MADCTL_MY | HX8357B_MADCTL_MX | HX8357B_MADCTL_MV | HX8357B_MADCTL_RGB;
        break;
      case 2:
        t = HX8357B_MADCTL_MY | HX8357B_MADCTL_RGB;
        break;
      case 3:
        t = HX8357B_MADCTL_MV | HX8357B_MADCTL_RGB;
        break;
      case 0:
      default:
        t = HX8357B_MADCTL_MH | HX8357B_MADCTL_MX | HX8357B_MADCTL_RGB;
        break;
    }
    writeRegister8(ILI9341_MADCTL, t ); // MADCTL
    // For 8357, init default full-screen address window:
    setAddrWindow(0, 0, _width - 1, _height - 1); // CS_IDLE happens here
  }
}

/*****************************************************************************/
uint8_t read8_(void)
{
  RD_ACTIVE;
  delayMicroseconds(10);
  uint8_t temp = ( (dataRegs->IDR>>TFT_DATA_SHIFT) & 0x00FF);
  delayMicroseconds(10);
  RD_IDLE;
  delayMicroseconds(10);
  return temp;
}

/*****************************************************************************/
inline void writeCommand(uint16_t c)
{
	CS_ACTIVE_CD_COMMAND;
	write8(c>>8);
	write8(c);
}

/*****************************************************************************/
// Because this function is used infrequently, it configures the ports for
// the read operation, reads the data, then restores the ports to the write
// configuration.  Write operations happen a LOT, so it's advantageous to
// leave the ports in that state as a default.
/*****************************************************************************/
uint16_t Adafruit_TFTLCD_8bit_STM32::readPixel(int16_t x, int16_t y)
{
  if((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) return 0;

  if(driver == ID_932X) {

    return ili932x_readPixel(x, y);

  } else if(driver == ID_7575) {

    uint8_t r, g, b;
    writeRegisterPair(HX8347G_COLADDRSTART_HI, HX8347G_COLADDRSTART_LO, x);
    writeRegisterPair(HX8347G_ROWADDRSTART_HI, HX8347G_ROWADDRSTART_LO, y);
    writeCommand(0x22); // Read data from GRAM
    setReadDir();  // Set up LCD data port(s) for READ operations
    CD_DATA;
    read8(r);      // First byte back is a dummy read
    read8(r);
    read8(g);
    read8(b);
    setWriteDir(); // Restore LCD data port(s) to WRITE configuration
    CS_IDLE;
    return (((uint16_t)r & B11111000) << 8) |
           (((uint16_t)g & B11111100) << 3) |
           (           b              >> 3);
  } else return 0;
}

/*****************************************************************************/
uint16_t Adafruit_TFTLCD_8bit_STM32::readID(void)
{
  if (readReg32(0x04) == 0x8000) { // eh close enough
    // setc!
    writeRegister24(HX8357D_SETC, 0xFF8357);
    delay(300);
	
    if (readReg32(0xD0) == 0x990000) {
      return 0x8357;
    }
  }

  uint16_t id = readReg32(0xD3);
  if (id != 0x9341 && id != 0x9338) {
    id = readReg(0);
  }

  return id;
}

/*****************************************************************************/
uint32_t readReg32(uint8_t r)
{
  uint32_t id;
  uint8_t x;

  // try reading register #4
  writeCommand(r);
  setReadDir();  // Set up LCD data port(s) for READ operations
  CD_DATA;
  delayMicroseconds(50);
  read8(x);
  id = x;          // Do not merge or otherwise simplify
  id <<= 8;              // these lines.  It's an unfortunate
  read8(x);
  id  |= x;        // shenanigans that are going on.
  id <<= 8;              // these lines.  It's an unfortunate
  read8(x);
  id  |= x;        // shenanigans that are going on.
  id <<= 8;              // these lines.  It's an unfortunate
  read8(x);
  id  |= x;        // shenanigans that are going on.
  CS_IDLE;
  setWriteDir();  // Restore LCD data port(s) to WRITE configuration
  return id;
}
/*****************************************************************************/
uint16_t readReg(uint8_t r)
{
  uint16_t id;
  uint8_t x;

  writeCommand(r);
  setReadDir();  // Set up LCD data port(s) for READ operations
  CD_DATA;
  delayMicroseconds(10);
  read8(x);
  id = x;          // Do not merge or otherwise simplify
  id <<= 8;              // these lines.  It's an unfortunate
  read8(x);
  id |= x;        // shenanigans that are going on.
  CS_IDLE;
  setWriteDir();  // Restore LCD data port(s) to WRITE configuration

  return id;
}

/*****************************************************************************/
void writeRegister8(uint16_t a, uint8_t d)
{
  writeCommand(a);
  CD_DATA;
  write8(d);
  CS_IDLE;
}

/*****************************************************************************/
void writeRegister16(uint16_t a, uint16_t d)
{
  writeCommand(a);
  CD_DATA;
  write8(d>>8);
  write8(d);
  CS_IDLE;
}

/*****************************************************************************/
void writeRegisterPair(uint16_t aH, uint16_t aL, uint16_t d)
{
  writeRegister8(aH, d>>8);
  writeRegister8(aL, d);
}

/*****************************************************************************/
void writeRegister24(uint16_t r, uint32_t d)
{
  writeCommand(r); // includes CS_ACTIVE
  CD_DATA;
  write8(d >> 16);
  write8(d >> 8);
  write8(d);
  CS_IDLE;
}

/*****************************************************************************/
void writeRegister32(uint16_t r, uint32_t d)
{
  writeCommand(r);
  CD_DATA;
  write8(d >> 24);
  write8(d >> 16);
  write8(d >> 8);
  write8(d);
  CS_IDLE;
}
//Adafruit_TFTLCD_8bit_STM32 tft;
Header:

Code: Select all

// Graphics library by ladyada/adafruit with init code from Rossum
// MIT license

#ifndef _ADAFRUIT_TFTLCD_8BIT_STM32_H_
#define _ADAFRUIT_TFTLCD_8BIT_STM32_H_

#include <Adafruit_GFX.h>

/*****************************************************************************/
// Define pins and Output Data Registers
/*****************************************************************************/
// Data port
//For my project I used PA0-PA7 -> DB0-DB7
#define TFT_DATA_PORT	GPIOA 
// Data bits/pins
#define TFT_DATA_SHIFT 0 // take the lower bits/pins 0..7
//#define TFT_DATA_SHIFT 8 // take the higher bits/pins 8..15

//Control pins |RD |WR |RS |CS |RST|
#define TFT_CNTRL_PORT	GPIOB
#define TFT_RD			PB9
#define TFT_WR			PB8
#define TFT_RS			PB7
#define TFT_CS			PB6
//Masks - Example: PB5 -> GPIO_PIN_5
//Note: using digitalPinToBitMask(PIN) slows display writes down substantially 
#define TFT_RD_MASK		GPIO_PIN_9 // HAL mask for RD pin (PB8 in example)
#define TFT_WR_MASK		GPIO_PIN_8
#define TFT_RS_MASK		GPIO_PIN_7
#define TFT_CS_MASK		GPIO_PIN_6

#define TFT_RST			PB5 //can be any pin (doesn't need to be same port as cntrl pins)

#define SLOW_WRITE 0   // set to 1 for legacy slow write (using individual digitalWrite()s

/*****************************************************************************/
// LCD controller chip identifiers
#define ID_932X    0
#define ID_7575    1
#define ID_9341    2
#define ID_HX8357D    3
#define ID_9338    4
#define ID_UNKNOWN 0xFF

/*****************************************************************************/
#define TFTWIDTH   320
#define TFTHEIGHT  480

// Initialization command tables for different LCD controllers
#define TFTLCD_DELAY 0xFF

// For compatibility with sketches written for older versions of library.
// Color function name was changed to 'color565' for parity with 2.2" LCD
// library.
#define Color565 color565

/*****************************************************************************/
#define	BLACK   0x0000
#define	BLUE    0x001F
#define	RED     0xF800
#define	GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF


#define RD_ACTIVE    digitalWrite(TFT_RD, LOW)
#define RD_IDLE      digitalWrite(TFT_RD, HIGH)
	// use fast bit toggling, very fast speed!
//extern gpio_reg_map * cntrlRegs; //Libmaple method
extern GPIO_TypeDef * cntrlRegs; //HAL method

#define WR_ACTIVE				{ cntrlRegs->BRR  = TFT_WR_MASK; }
#define WR_IDLE					{ cntrlRegs->BSRR = TFT_WR_MASK; }
#define CD_COMMAND				{ cntrlRegs->BRR  = TFT_RS_MASK; }
#define CD_DATA					{ cntrlRegs->BSRR = TFT_RS_MASK; }
#define CS_ACTIVE				{ cntrlRegs->BRR  = TFT_CS_MASK; }
#define CS_IDLE					{ cntrlRegs->BSRR = TFT_CS_MASK; }
#define CS_ACTIVE_CD_COMMAND	{ cntrlRegs->BRR  = (TFT_CS_MASK|TFT_RS_MASK); }
#define WR_STROBE { WR_ACTIVE; WR_IDLE; }

extern uint8_t read8_(void);
#define read8(x) ( x = read8_() )

//extern gpio_reg_map * dataRegs;
extern GPIO_TypeDef * dataRegs;

// set the pins to input mode
#define setReadDir() ( dataRegs->CRL = 0x88888888 )	// set the lower 8 bits as input
// set the pins to output mode
#define setWriteDir() ( dataRegs->CRL = 0x33333333 )	// set the lower 8 bits as output

// set pins to output the 8 bit value

#define write8(c) { dataRegs->BSRR = (uint32_t)(0x00FF0000 + ((c)&0xFF)); WR_STROBE; }

/*****************************************************************************/

#define swap(a, b) { int16_t t = a; a = b; b = t; }

/*****************************************************************************/
class Adafruit_TFTLCD_8bit_STM32 : public Adafruit_GFX {

 public:

  Adafruit_TFTLCD_8bit_STM32(void);

  void     begin(uint16_t id = 0x9341);
  void     drawPixel(int16_t x, int16_t y, uint16_t color);
  void     drawFastHLine(int16_t x0, int16_t y0, int16_t w, uint16_t color);
  void     drawFastVLine(int16_t x0, int16_t y0, int16_t h, uint16_t color);
  void     fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t c);
  void     fillScreen(uint16_t color);
  void     reset(void);
  void     setRegisters8(uint8_t *ptr, uint8_t n);
  void     setRegisters16(uint16_t *ptr, uint8_t n);
  void     setRotation(uint8_t x);
       // These methods are public in order for BMP examples to work:
  void     setAddrWindow(int16_t x1, int16_t y1, int16_t x2, int16_t y2);
  void     invertDisplay(boolean i),
			pushColors(uint16_t *data, int16_t len, boolean first),
           drawBitmap(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t * bitmap);
  uint16_t readPixel(int16_t x, int16_t y), readID(void);
/*****************************************************************************/
// Pass 8-bit (each) R,G,B, get back 16-bit packed color
// color coding on bits:
// high byte sill be sent first
// bit nr: 		15	14	13	12	11	 10	09	08		07	06	05	 04	03	02	01	00
// color/bit:	R5	R4	R3	R2	R1 | G5	G4	G3		G2	G1	G0 | B5	B4	B3	B2	B1
// 								R0=R5											B0=B5
/*****************************************************************************/
  uint16_t inline color565(uint8_t r, uint8_t g, uint8_t b) { return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); }

 private:
  void     init(), flood(uint16_t color, uint32_t len);
  uint8_t  driver;
};

extern uint16_t readReg(uint8_t r);
extern uint32_t readReg32(uint8_t r);
extern void writeCommand(uint16_t c);
extern void writeRegister8(uint16_t a, uint8_t d);
extern void writeRegister16(uint16_t a, uint16_t d);
extern void writeRegister24(uint16_t a, uint32_t d);
extern void writeRegister32(uint16_t a, uint32_t d);
extern void writeRegisterPair(uint16_t aH, uint16_t aL, uint16_t d);

extern Adafruit_TFTLCD_8bit_STM32 tft;

#endif
I also tried the LED trick, it seems that the sketch runs through completely but the display just doesn't properly init. Here is the revised sketch.

Code: Select all

#include <Arduino.h>
#include <Adafruit_TFTLCD_8bit_STM32.h>

Adafruit_TFTLCD_8bit_STM32 tft;

void setup(void) {
  //Serial.begin(9600);
  pinMode(PC13, OUTPUT);
  digitalWrite(PC13, HIGH); //turn led off
  tft.begin(0x9341);
  tft.fillScreen(BLACK);
  tft.setRotation(1);
  tft.setCursor(10,10);
  tft.setTextColor(WHITE);
  tft.setTextSize(6);
  tft.println("Hello World!");
  digitalWrite(PC13, LOW); //turn led on
}
void loop(void) {
}
The LED is on when PC13 is Low; when I upload it the led is on all the time(it gets to the end of setup, no hangup).

Could this be a result of a compiler error? Or maybe something to do with the Adafruit GFX library?
stevestrong
Posts: 502
Joined: Fri Dec 27, 2019 4:53 pm
Answers: 8
Location: Munich, Germany
Contact:

Re: 8 Bit TFT Library Issue

Post by stevestrong »

You can try to include Adafruit.h instead of Adafruit_GFX.h in the header file. For that you have to download the original Adafruit library.

I think you have to check the HAL correspondence of the defines used in the header file for GPIO access.
User avatar
fpiSTM
Posts: 1738
Joined: Wed Dec 11, 2019 7:11 pm
Answers: 91
Location: Le Mans
Contact:

Re: 8 Bit TFT Library Issue

Post by fpiSTM »

Why do you want port the library from Steve ?
The original should work as the STM32 core follows the Arduino API.
https://github.com/adafruit/TFTLCD-Library

Did you try it?

/Edit
OK I' saw the pin_magic.h ... so not basically not compatible... :?
mkengineering
Posts: 12
Joined: Thu Apr 30, 2020 8:35 pm

Re: 8 Bit TFT Library Issue

Post by mkengineering »

I went ahead and revised the gpio access method a little to match some of the HAL based STM32 projects I've seen in the past.
Header:

Code: Select all

// Graphics library by ladyada/adafruit with init code from Rossum
// MIT license

#ifndef _ADAFRUIT_TFTLCD_8BIT_STM32_H_
#define _ADAFRUIT_TFTLCD_8BIT_STM32_H_

#include "Arduino.h"
#include <Adafruit_GFX.h>

/*****************************************************************************/
// Define pins and Output Data Registers
/*****************************************************************************/
// Data port
//For my project I used PA0-PA7 -> DB0-DB7
 
// Data bits/pins
#define TFT_DATA_SHIFT 0 // take the lower bits/pins 0..7
//#define TFT_DATA_SHIFT 8 // take the higher bits/pins 8..15

//Control pins |RD |WR |RS |CS |RST|
#define TFT_DATA_PORT	GPIOA
#define TFT_CNTRL_PORT	GPIOB

#define TFT_RD			PB9
#define TFT_WR			PB8
#define TFT_RS			PB7
#define TFT_CS			PB6
//Masks - Example: PB5 -> GPIO_PIN_5
//Note: using digitalPinToBitMask(PIN) slows display writes down substantially 
#define TFT_RD_MASK		GPIO_PIN_9 // HAL mask for RD pin (PB8 in example)
#define TFT_WR_MASK		GPIO_PIN_8
#define TFT_RS_MASK		GPIO_PIN_7
#define TFT_CS_MASK		GPIO_PIN_6

#define TFT_RST			PB5 //can be any pin (doesn't need to be same port as cntrl pins)

#define SLOW_WRITE 0   // set to 1 for legacy slow write (using individual digitalWrite()s

/*****************************************************************************/
// LCD controller chip identifiers
#define ID_932X    0
#define ID_7575    1
#define ID_9341    2
#define ID_HX8357D    3
#define ID_9338    4
#define ID_UNKNOWN 0xFF

/*****************************************************************************/
#define TFTWIDTH   320
#define TFTHEIGHT  480

// Initialization command tables for different LCD controllers
#define TFTLCD_DELAY 0xFF

// For compatibility with sketches written for older versions of library.
// Color function name was changed to 'color565' for parity with 2.2" LCD
// library.
#define Color565 color565

/*****************************************************************************/
#define	BLACK   0x0000
#define	BLUE    0x001F
#define	RED     0xF800
#define	GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF


// use fast bit toggling, very fast speed!
//extern gpio_reg_map * cntrlRegs; //Libmaple method
// extern GPIO_TypeDef * TFT_CNTRL_PORT;
// extern GPIO_TypeDef * TFT_DATA_PORT; //HAL method

#define RD_ACTIVE               { TFT_CNTRL_PORT->BRR  = TFT_RD_MASK; }
#define RD_IDLE                 { TFT_CNTRL_PORT->BSRR = TFT_RD_MASK; }
#define WR_ACTIVE				{ TFT_CNTRL_PORT->BRR  = TFT_WR_MASK; }
#define WR_IDLE					{ TFT_CNTRL_PORT->BSRR = TFT_WR_MASK; }
#define CD_COMMAND				{ TFT_CNTRL_PORT->BRR  = TFT_RS_MASK; }
#define CD_DATA					{ TFT_CNTRL_PORT->BSRR = TFT_RS_MASK; }
#define CS_ACTIVE				{ TFT_CNTRL_PORT->BRR  = TFT_CS_MASK; }
#define CS_IDLE					{ TFT_CNTRL_PORT->BSRR = TFT_CS_MASK; }
#define CS_ACTIVE_CD_COMMAND	{ TFT_CNTRL_PORT->BRR  = (TFT_CS_MASK|TFT_RS_MASK); }
#define WR_STROBE { WR_ACTIVE; WR_IDLE; }

extern uint8_t read8_(void);
#define read8(x) ( x = read8_() )

//extern gpio_reg_map * dataRegs;
// extern GPIO_TypeDef * dataRegs;

// set the pins to input mode
#define setReadDir() ( TFT_DATA_PORT->CRL = 0x88888888 )	// set the lower 8 bits as input
// set the pins to output mode
#define setWriteDir() ( TFT_DATA_PORT->CRL = 0x33333333 )	// set the lower 8 bits as output

// set pins to output the 8 bit value

#define write8(c) { TFT_DATA_PORT->BSRR = (uint32_t)(0x00FF0000 + ((c)&0xFF)); WR_STROBE; }

/*****************************************************************************/

#define swap(a, b) { int16_t t = a; a = b; b = t; }

/*****************************************************************************/
class Adafruit_TFTLCD_8bit_STM32 : public Adafruit_GFX {

 public:

  Adafruit_TFTLCD_8bit_STM32(void);

  void     begin(uint16_t id = 0x9341);
  void     drawPixel(int16_t x, int16_t y, uint16_t color);
  void     drawFastHLine(int16_t x0, int16_t y0, int16_t w, uint16_t color);
  void     drawFastVLine(int16_t x0, int16_t y0, int16_t h, uint16_t color);
  void     fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t c);
  void     fillScreen(uint16_t color);
  void     reset(void);
  void     setRegisters8(uint8_t *ptr, uint8_t n);
  void     setRegisters16(uint16_t *ptr, uint8_t n);
  void     setRotation(uint8_t x);
       // These methods are public in order for BMP examples to work:
  void     setAddrWindow(int16_t x1, int16_t y1, int16_t x2, int16_t y2);
  void     invertDisplay(boolean i),
			pushColors(uint16_t *data, int16_t len, boolean first),
           drawBitmap(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t * bitmap);
  uint16_t readPixel(int16_t x, int16_t y), readID(void);
/*****************************************************************************/
// Pass 8-bit (each) R,G,B, get back 16-bit packed color
// color coding on bits:
// high byte sill be sent first
// bit nr: 		15	14	13	12	11	 10	09	08		07	06	05	 04	03	02	01	00
// color/bit:	R5	R4	R3	R2	R1 | G5	G4	G3		G2	G1	G0 | B5	B4	B3	B2	B1
// 								R0=R5											B0=B5
/*****************************************************************************/
  uint16_t inline color565(uint8_t r, uint8_t g, uint8_t b) { return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); }

 private:
  void     init(), flood(uint16_t color, uint32_t len);
  uint8_t  driver;
};

extern uint16_t readReg(uint8_t r);
extern uint32_t readReg32(uint8_t r);
extern void writeCommand(uint16_t c);
extern void writeRegister8(uint16_t a, uint8_t d);
extern void writeRegister16(uint16_t a, uint16_t d);
extern void writeRegister24(uint16_t a, uint32_t d);
extern void writeRegister32(uint16_t a, uint32_t d);
extern void writeRegisterPair(uint16_t aH, uint16_t aL, uint16_t d);

extern Adafruit_TFTLCD_8bit_STM32 tft;

#endif
C++:

Code: Select all

// Graphics library by ladyada/adafruit with init code from Rossum
// MIT license

#include "Adafruit_TFTLCD_8bit_STM32.h"

#include "ili932x.h"
#include "ili9341.h"
#include "hx8347g.h"
#include "hx8357x.h"

// GPIO_TypeDef * TFT_CNTRL_PORT; //HAL method
// GPIO_TypeDef * TFT_DATA_PORT;

/*****************************************************************************/
// Constructor
/*****************************************************************************/
Adafruit_TFTLCD_8bit_STM32 :: Adafruit_TFTLCD_8bit_STM32(void)
: Adafruit_GFX(TFTWIDTH, TFTHEIGHT)
{
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::begin(uint16_t id)
{
	reset();

	if ((id == 0x9325) || (id == 0x9328)) {
		driver = ID_932X;
		ili932x_begin();
	} else if (id == 0x9341) {
		driver = ID_9341;
		ili9341_begin();
	} else if (id == 0x8357) {
		// HX8357D
		driver = ID_HX8357D;
		hx8357x_begin();
	} else if (id == 0x9338) {
		// ILI9338?
		driver = ID_9338;
		hx8357x_begin();
	} else if(id == 0x7575) {
		driver = ID_7575;
		hx8347g_begin();
	} else {
		driver = ID_UNKNOWN;
	}
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::reset(void)
{
	// cntrlRegs = TFT_CNTRL_PORT; //HAL method
	// dataRegs = TFT_DATA_PORT;
	pinMode(TFT_RD, OUTPUT);
	pinMode(TFT_WR, OUTPUT);
	pinMode(TFT_RS, OUTPUT);
	pinMode(TFT_CS, OUTPUT);
	CS_IDLE; // Set all control bits to HIGH (idle)
	CD_DATA; // Signals are ACTIVE LOW
	WR_IDLE;
	RD_IDLE;
	//set up 8 bit parallel port to write mode.
	setWriteDir();

	// toggle RST low to reset
	if (TFT_RST > 0) {
		pinMode(TFT_RST, OUTPUT);
		digitalWrite(TFT_RST, HIGH);
		delay(100);
		digitalWrite(TFT_RST, LOW);
		delay(100);
		digitalWrite(TFT_RST, HIGH);
		delay(100);
	}
}

/*****************************************************************************/
// Sets the LCD address window (and address counter, on 932X).
// Relevant to rect/screen fills and H/V lines.  Input coordinates are
// assumed pre-sorted (e.g. x2 >= x1).
/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::setAddrWindow(int16_t x1, int16_t y1, int16_t x2, int16_t y2)
{
	if(driver == ID_932X) {
		ili932x_setAddrWindow(x1, y1, x2, y2);
	} else if(driver == ID_7575) {
		hx8347g_setAddrWindow(x1, y1, x2, y2);
	} else if ((driver == ID_9341) || (driver == ID_HX8357D) || (driver == ID_9338)){
		ili9341_setAddrWindow(x1, y1, x2, y2);
	}
}

/*****************************************************************************/
// Fast block fill operation for fillScreen, fillRect, H/V line, etc.
// Requires setAddrWindow() has previously been called to set the fill
// bounds.  'len' is inclusive, MUST be >= 1.
/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::flood(uint16_t color, uint32_t len)
{
  uint16_t blocks;
  uint8_t  i, hi = color >> 8,
              lo = color;

	CS_ACTIVE_CD_COMMAND;
  if (driver == ID_9341) {
    writeCommand(ILI9341_MEMORYWRITE); // 16 bit
  } else if (driver == ID_932X) {
    writeCommand(ILI932X_RW_GRAM); // 16 bit
  } else if (driver == ID_HX8357D || driver == ID_9338) {
    write8(HX8357_RAMWR);
  } else {
    write8(0x22); // Write data to GRAM
  }

  // Write first pixel normally, decrement counter by 1
  CD_DATA;
  write8(hi);
  write8(lo);
  len--;

  blocks = (uint16_t)(len / 64); // 64 pixels/block
  if(hi == lo) {
    // High and low bytes are identical.  Leave prior data
    // on the port(s) and just toggle the write strobe.
    while(blocks--) {
      i = 16; // 64 pixels/block / 4 pixels/pass
      do {
        WR_STROBE; WR_STROBE; WR_STROBE; WR_STROBE; // 2 bytes/pixel
        WR_STROBE; WR_STROBE; WR_STROBE; WR_STROBE; // x 4 pixels
      } while(--i);
    }
    // Fill any remaining pixels (1 to 64)
	i = len & 63;
    while (i--) {
		WR_STROBE; WR_STROBE;
	}
  } else {
    while(blocks--) {
      i = 16; // 64 pixels/block / 4 pixels/pass
      do {
        write8(hi); write8(lo); write8(hi); write8(lo);
        write8(hi); write8(lo); write8(hi); write8(lo);
      } while(--i);
    }
	i = len & 63;
    while (i--) { // write here the remaining data
      write8(hi); write8(lo);
    }
  }
  CS_IDLE;
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::drawFastHLine(int16_t x, int16_t y, int16_t length, uint16_t color)
{
  int16_t x2;

  // Initial off-screen clipping
  if((length <= 0     ) ||
     (y      <  0     ) || ( y                  >= _height) ||
     (x      >= _width) || ((x2 = (x+length-1)) <  0      )) return;

  if(x < 0) {        // Clip left
    length += x;
    x       = 0;
  }
  if(x2 >= _width) { // Clip right
    x2      = _width - 1;
    length  = x2 - x + 1;
  }

  setAddrWindow(x, y, x2, y);
  flood(color, length);
  if(driver == ID_932X) setAddrWindow(0, 0, _width - 1, _height - 1);
  else                  hx8347g_setLR();
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::drawFastVLine(int16_t x, int16_t y, int16_t length, uint16_t color)
{
  int16_t y2;

  // Initial off-screen clipping
  if((length <= 0      ) ||
     (x      <  0      ) || ( x                  >= _width) ||
     (y      >= _height) || ((y2 = (y+length-1)) <  0     )) return;
  if(y < 0) {         // Clip top
    length += y;
    y       = 0;
  }
  if(y2 >= _height) { // Clip bottom
    y2      = _height - 1;
    length  = y2 - y + 1;
  }

  setAddrWindow(x, y, x, y2);
  flood(color, length);
  if(driver == ID_932X) setAddrWindow(0, 0, _width - 1, _height - 1);
  else                  hx8347g_setLR();
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::fillRect(int16_t x1, int16_t y1, int16_t w, int16_t h, uint16_t fillcolor)
{

  int16_t  x2, y2;

  // Initial off-screen clipping
  if( (w            <= 0     ) ||  (h             <= 0      ) ||
      (x1           >= _width) ||  (y1            >= _height) ||
     ((x2 = x1+w-1) <  0     ) || ((y2  = y1+h-1) <  0      )) return;
  if(x1 < 0) { // Clip left
    w += x1;
    x1 = 0;
  }
  if(y1 < 0) { // Clip top
    h += y1;
    y1 = 0;
  }
  if(x2 >= _width) { // Clip right
    x2 = _width - 1;
    w  = x2 - x1 + 1;
  }
  if(y2 >= _height) { // Clip bottom
    y2 = _height - 1;
    h  = y2 - y1 + 1;
  }

  setAddrWindow(x1, y1, x2, y2);
  flood(fillcolor, (uint32_t)w * (uint32_t)h);
  if(driver == ID_932X) setAddrWindow(0, 0, _width - 1, _height - 1);
  //else                  hx8347g_setLR();
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::fillScreen(uint16_t color)
{
  if(driver == ID_932X) {
    // For the 932X, a full-screen address window is already the default
    // state, just need to set the address pointer to the top-left corner.
    // Although we could fill in any direction, the code uses the current
    // screen rotation because some users find it disconcerting when a
    // fill does not occur top-to-bottom.
	ili932x_fillScreen(color);
  } else if ((driver == ID_9341) || (driver == ID_7575) || (driver == ID_HX8357D) || (driver == ID_9338)) {
    // For these, there is no settable address pointer, instead the
    // address window must be set for each drawing operation.  However,
    // this display takes rotation into account for the parameters, no
    // need to do extra rotation math here.
    setAddrWindow(0, 0, _width - 1, _height - 1);
  }
  flood(color, (long)TFTWIDTH * (long)TFTHEIGHT);
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::drawPixel(int16_t x, int16_t y, uint16_t color)
{
  // Clip
  if((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) return;

  if(driver == ID_932X) {

	  ili932x_drawPixel(x, y, color);

  } else if(driver == ID_7575) {

    uint8_t hi, lo;
    switch(rotation) {
     default: lo = 0   ; break;
     case 1 : lo = 0x60; break;
     case 2 : lo = 0xc0; break;
     case 3 : lo = 0xa0; break;
    }
    writeRegister8(   HX8347G_MEMACCESS      , lo);
    // Only upper-left is set -- bottom-right is full screen default
    writeRegisterPair(HX8347G_COLADDRSTART_HI, HX8347G_COLADDRSTART_LO, x);
    writeRegisterPair(HX8347G_ROWADDRSTART_HI, HX8347G_ROWADDRSTART_LO, y);
    hi = color >> 8; lo = color;
    CD_COMMAND; write8(0x22); CD_DATA; write8(hi); write8(lo);

  } else if ((driver == ID_9341) || (driver == ID_HX8357D) || (driver == ID_9338)) {

    setAddrWindow(x, y, x+1, y+1);
    writeRegister16(0x2C, color);
  }
}

/*****************************************************************************/
// Draw an image bitmap (16bits per color) at the specified position from the provided buffer.
/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::drawBitmap(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t * bitmap)
{
	if ( x>=0 && (x+w)<_width && y>=0 && (y+h)<=_height ) {
		// all pixel visible, do it in the fast way
		setAddrWindow(x,y,x+w-1,y+h-1);
		pushColors((uint16_t*)bitmap, w*h, true);
	} else {
		// some pixels outside visible area, do it in the classical way to disable off-screen points
		int16_t i, j;
		uint16_t * colorP = (uint16_t*)bitmap;
		for(j=0; j<h; j++) {
			for(i=0; i<w; i++ ) {
				drawPixel(x+i, y+j, *colorP++);
			}
		}
	}
}

/*****************************************************************************/
// Issues 'raw' an array of 16-bit color values to the LCD; used
// externally by BMP examples.  Assumes that setWindowAddr() has
// previously been set to define the bounds.  Max 255 pixels at
// a time (BMP examples read in small chunks due to limited RAM).
/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::pushColors(uint16_t *data, int16_t len, boolean first)
{
  uint16_t color;
  uint8_t  hi, lo;
  CS_ACTIVE;
  if(first == true) { // Issue GRAM write command only on first call
    CD_COMMAND;
    if(driver == ID_932X) write8(0x00);
    if ((driver == ID_9341) || (driver == ID_HX8357D) || (driver == ID_9338)){
       write8(0x2C);
     }  else {
       write8(0x22);
     }
  }
  CD_DATA;
  while(len--) {
    color = *data++;
    hi    = color >> 8; // Don't simplify or merge these
    lo    = color;      // lines, there's macro shenanigans
    write8(hi);         // going on.
    write8(lo);
  }
  CS_IDLE;
}

/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::invertDisplay(boolean i)
{
	if ( driver==ID_932X ) ili932x_invertDisplay(i);
	else if ( driver==ID_9341 ) {
		writeCommand( i ? ILI9341_INVERTON : ILI9341_INVERTOFF);
		CS_IDLE;
	}
}
/*****************************************************************************/
void Adafruit_TFTLCD_8bit_STM32::setRotation(uint8_t x)
{
  // Call parent rotation func first -- sets up rotation flags, etc.
  Adafruit_GFX::setRotation(x);
  // Then perform hardware-specific rotation operations...

  if (driver == ID_932X) {

    ili932x_setRotation(x);

  } else if (driver == ID_7575) {

	hx8347g_setRotation(x);

  } else if (driver == ID_9341) {
   // MEME, HX8357D uses same registers as 9341 but different values
   uint16_t t;

   switch (rotation) {
   case 1:
     t = ILI9341_MADCTL_MX | ILI9341_MADCTL_MY | ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR;
     break;
   case 2:
     t = ILI9341_MADCTL_MX | ILI9341_MADCTL_BGR;
     break;
   case 3:
     t = ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR;
     break;
   case 0:
   default:
    t = ILI9341_MADCTL_MY | ILI9341_MADCTL_BGR;
    break;
  }
   writeRegister8(ILI9341_MADCTL, t ); // MADCTL
   // For 9341, init default full-screen address window:
   //setAddrWindow(0, 0, _width - 1, _height - 1); // CS_IDLE happens here

  } else if (driver == ID_HX8357D) {
    // MEME, HX8357D uses same registers as 9341 but different values
    uint16_t t;

    switch (rotation) {
      case 1:
        t = HX8357B_MADCTL_MY | HX8357B_MADCTL_MV | HX8357B_MADCTL_RGB;
        break;
      case 2:
        t = HX8357B_MADCTL_RGB;
        break;
      case 3:
        t = HX8357B_MADCTL_MX | HX8357B_MADCTL_MV | HX8357B_MADCTL_RGB;
        break;
      case 0:
      default:
        t = HX8357B_MADCTL_MX | HX8357B_MADCTL_MY | HX8357B_MADCTL_RGB;
        break;
    }
    writeRegister8(ILI9341_MADCTL, t ); // MADCTL
    // For 8357, init default full-screen address window:
    setAddrWindow(0, 0, _width - 1, _height - 1); // CS_IDLE happens here
  } else if (driver == ID_9338) {
    // Like HX8357D, but mirrored
    uint16_t t;
    
    switch (rotation) {
      case 1:
        t = HX8357B_MADCTL_MY | HX8357B_MADCTL_MX | HX8357B_MADCTL_MV | HX8357B_MADCTL_RGB;
        break;
      case 2:
        t = HX8357B_MADCTL_MY | HX8357B_MADCTL_RGB;
        break;
      case 3:
        t = HX8357B_MADCTL_MV | HX8357B_MADCTL_RGB;
        break;
      case 0:
      default:
        t = HX8357B_MADCTL_MH | HX8357B_MADCTL_MX | HX8357B_MADCTL_RGB;
        break;
    }
    writeRegister8(ILI9341_MADCTL, t ); // MADCTL
    // For 8357, init default full-screen address window:
    setAddrWindow(0, 0, _width - 1, _height - 1); // CS_IDLE happens here
  }
}

/*****************************************************************************/
uint8_t read8_(void)
{
  RD_ACTIVE;
  delayMicroseconds(10);
  uint8_t temp = (TFT_DATA_PORT->IDR & 0x00FF);
  delayMicroseconds(10);
  RD_IDLE;
  delayMicroseconds(10);
  return temp;
}

/*****************************************************************************/
inline void writeCommand(uint16_t c)
{
	CS_ACTIVE_CD_COMMAND;
	write8(c>>8);
	write8(c);
}

/*****************************************************************************/
// Because this function is used infrequently, it configures the ports for
// the read operation, reads the data, then restores the ports to the write
// configuration.  Write operations happen a LOT, so it's advantageous to
// leave the ports in that state as a default.
/*****************************************************************************/
uint16_t Adafruit_TFTLCD_8bit_STM32::readPixel(int16_t x, int16_t y)
{
  if((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) return 0;

  if(driver == ID_932X) {

    return ili932x_readPixel(x, y);

  } else if(driver == ID_7575) {

    uint8_t r, g, b;
    writeRegisterPair(HX8347G_COLADDRSTART_HI, HX8347G_COLADDRSTART_LO, x);
    writeRegisterPair(HX8347G_ROWADDRSTART_HI, HX8347G_ROWADDRSTART_LO, y);
    writeCommand(0x22); // Read data from GRAM
    setReadDir();  // Set up LCD data port(s) for READ operations
    CD_DATA;
    read8(r);      // First byte back is a dummy read
    read8(r);
    read8(g);
    read8(b);
    setWriteDir(); // Restore LCD data port(s) to WRITE configuration
    CS_IDLE;
    return (((uint16_t)r & B11111000) << 8) |
           (((uint16_t)g & B11111100) << 3) |
           (           b              >> 3);
  } else return 0;
}

/*****************************************************************************/
uint16_t Adafruit_TFTLCD_8bit_STM32::readID(void)
{
  if (readReg32(0x04) == 0x8000) { // eh close enough
    // setc!
    writeRegister24(HX8357D_SETC, 0xFF8357);
    delay(300);
	
    if (readReg32(0xD0) == 0x990000) {
      return 0x8357;
    }
  }

  uint16_t id = readReg32(0xD3);
  if (id != 0x9341 && id != 0x9338) {
    id = readReg(0);
  }

  return id;
}

/*****************************************************************************/
uint32_t readReg32(uint8_t r)
{
  uint32_t id;
  uint8_t x;

  // try reading register #4
  writeCommand(r);
  setReadDir();  // Set up LCD data port(s) for READ operations
  CD_DATA;
  delayMicroseconds(50);
  read8(x);
  id = x;          // Do not merge or otherwise simplify
  id <<= 8;              // these lines.  It's an unfortunate
  read8(x);
  id  |= x;        // shenanigans that are going on.
  id <<= 8;              // these lines.  It's an unfortunate
  read8(x);
  id  |= x;        // shenanigans that are going on.
  id <<= 8;              // these lines.  It's an unfortunate
  read8(x);
  id  |= x;        // shenanigans that are going on.
  CS_IDLE;
  setWriteDir();  // Restore LCD data port(s) to WRITE configuration
  return id;
}
/*****************************************************************************/
uint16_t readReg(uint8_t r)
{
  uint16_t id;
  uint8_t x;

  writeCommand(r);
  setReadDir();  // Set up LCD data port(s) for READ operations
  CD_DATA;
  delayMicroseconds(10);
  read8(x);
  id = x;          // Do not merge or otherwise simplify
  id <<= 8;              // these lines.  It's an unfortunate
  read8(x);
  id |= x;        // shenanigans that are going on.
  CS_IDLE;
  setWriteDir();  // Restore LCD data port(s) to WRITE configuration

  return id;
}

/*****************************************************************************/
void writeRegister8(uint16_t a, uint8_t d)
{
  writeCommand(a);
  CD_DATA;
  write8(d);
  CS_IDLE;
}

/*****************************************************************************/
void writeRegister16(uint16_t a, uint16_t d)
{
  writeCommand(a);
  CD_DATA;
  write8(d>>8);
  write8(d);
  CS_IDLE;
}

/*****************************************************************************/
void writeRegisterPair(uint16_t aH, uint16_t aL, uint16_t d)
{
  writeRegister8(aH, d>>8);
  writeRegister8(aL, d);
}

/*****************************************************************************/
void writeRegister24(uint16_t r, uint32_t d)
{
  writeCommand(r); // includes CS_ACTIVE
  CD_DATA;
  write8(d >> 16);
  write8(d >> 8);
  write8(d);
  CS_IDLE;
}

/*****************************************************************************/
void writeRegister32(uint16_t r, uint32_t d)
{
  writeCommand(r);
  CD_DATA;
  write8(d >> 24);
  write8(d >> 16);
  write8(d >> 8);
  write8(d);
  CS_IDLE;
}
//Adafruit_TFTLCD_8bit_STM32 tft;
Unfortunately I'm still having the same issue :x
Does my GPIO access method look alright, Frederic?
mkengineering
Posts: 12
Joined: Thu Apr 30, 2020 8:35 pm

Re: 8 Bit TFT Library Issue

Post by mkengineering »

I'm still just kind of dumbfounded at how it works perfectly fine as long as U(S)ART is enabled and there's a Serial.begin() in the sketch. Is there anything else setup / changed when that peripheral is enabled?
stevestrong
Posts: 502
Joined: Fri Dec 27, 2019 4:53 pm
Answers: 8
Location: Munich, Germany
Contact:

Re: 8 Bit TFT Library Issue

Post by stevestrong »

Have you tried SLOW_WRITE with 1?
mkengineering
Posts: 12
Joined: Thu Apr 30, 2020 8:35 pm

Re: 8 Bit TFT Library Issue

Post by mkengineering »

stevestrong wrote: Fri Aug 21, 2020 8:59 pm Have you tried SLOW_WRITE with 1?
I modified write8 for the SLOW_WRITE option using digitalWrite, same issue. So then it isn't the GPIO access method?

Revised header file:

Code: Select all

// Graphics library by ladyada/adafruit with init code from Rossum
// MIT license

#ifndef _ADAFRUIT_TFTLCD_8BIT_STM32_H_
#define _ADAFRUIT_TFTLCD_8BIT_STM32_H_

#include "Arduino.h"
#include <Adafruit_GFX.h>

/*****************************************************************************/
// Define pins and Output Data Registers
/*****************************************************************************/
// Data port
//For my project I used PA0-PA7 -> DB0-DB7
 
// Data bits/pins
#define TFT_DATA_SHIFT 0 // take the lower bits/pins 0..7
//#define TFT_DATA_SHIFT 8 // take the higher bits/pins 8..15

//Control pins |RD |WR |RS |CS |RST|
#define TFT_DATA_PORT	GPIOA
#define TFT_CNTRL_PORT	GPIOB

#define TFT_RD			PB9
#define TFT_WR			PB8
#define TFT_RS			PB7
#define TFT_CS			PB6
//Masks - Example: PB5 -> GPIO_PIN_5
//Note: using digitalPinToBitMask(PIN) slows display writes down substantially 
#define TFT_RD_MASK		GPIO_PIN_9 // HAL mask for RD pin (PB8 in example)
#define TFT_WR_MASK		GPIO_PIN_8
#define TFT_RS_MASK		GPIO_PIN_7
#define TFT_CS_MASK		GPIO_PIN_6

#define TFT_RST			PB5 //can be any pin (doesn't need to be same port as cntrl pins)

#define SLOW_WRITE 0   // set to 1 for legacy slow write (using individual digitalWrite()s

/*****************************************************************************/
// LCD controller chip identifiers
#define ID_932X    0
#define ID_7575    1
#define ID_9341    2
#define ID_HX8357D    3
#define ID_9338    4
#define ID_UNKNOWN 0xFF

/*****************************************************************************/
#define TFTWIDTH   320
#define TFTHEIGHT  480

// Initialization command tables for different LCD controllers
#define TFTLCD_DELAY 0xFF

// For compatibility with sketches written for older versions of library.
// Color function name was changed to 'color565' for parity with 2.2" LCD
// library.
#define Color565 color565

/*****************************************************************************/
#define	BLACK   0x0000
#define	BLUE    0x001F
#define	RED     0xF800
#define	GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF


// use fast bit toggling, very fast speed!
//extern gpio_reg_map * cntrlRegs; //Libmaple method
// extern GPIO_TypeDef * TFT_CNTRL_PORT;
// extern GPIO_TypeDef * TFT_DATA_PORT; //HAL method

#define RD_ACTIVE               { TFT_CNTRL_PORT->BRR  = TFT_RD_MASK; }
#define RD_IDLE                 { TFT_CNTRL_PORT->BSRR = TFT_RD_MASK; }
#define WR_ACTIVE				{ TFT_CNTRL_PORT->BRR  = TFT_WR_MASK; }
#define WR_IDLE					{ TFT_CNTRL_PORT->BSRR = TFT_WR_MASK; }
#define CD_COMMAND				{ TFT_CNTRL_PORT->BRR  = TFT_RS_MASK; }
#define CD_DATA					{ TFT_CNTRL_PORT->BSRR = TFT_RS_MASK; }
#define CS_ACTIVE				{ TFT_CNTRL_PORT->BRR  = TFT_CS_MASK; }
#define CS_IDLE					{ TFT_CNTRL_PORT->BSRR = TFT_CS_MASK; }
#define CS_ACTIVE_CD_COMMAND	{ TFT_CNTRL_PORT->BRR  = (TFT_CS_MASK|TFT_RS_MASK); }
#define WR_STROBE { WR_ACTIVE; WR_IDLE; }

extern uint8_t read8_(void);
#define read8(x) ( x = read8_() )

//extern gpio_reg_map * dataRegs;
// extern GPIO_TypeDef * dataRegs;

// set the pins to input mode
#define setReadDir() ( TFT_DATA_PORT->CRL = 0x88888888 )	// set the lower 8 bits as input
// set the pins to output mode
#define setWriteDir() ( TFT_DATA_PORT->CRL = 0x33333333 )	// set the lower 8 bits as output

// set pins to output the 8 bit value

// #define write8(c) { TFT_DATA_PORT->BSRR = (uint32_t)(0x00FF0000 + ((c)&0xFF)); WR_STROBE; }

inline void write8(uint8_t c) { /*Serial.print(" write8: "); Serial.print(c,HEX); Serial.write(',');*/
	digitalWrite(PA0, (c & 0b00000001));
	digitalWrite(PA1, (c & 0b00000010));
	digitalWrite(PA2, (c & 0b00000100));
	digitalWrite(PA3, (c & 0b00001000));
	digitalWrite(PA4, (c & 0b00010000));
	digitalWrite(PA5, (c & 0b00100000));
	digitalWrite(PA6, (c & 0b01000000));
	digitalWrite(PA7, (c & 0b10000000));
	WR_STROBE; }

/*****************************************************************************/

#define swap(a, b) { int16_t t = a; a = b; b = t; }

/*****************************************************************************/
class Adafruit_TFTLCD_8bit_STM32 : public Adafruit_GFX {

 public:

  Adafruit_TFTLCD_8bit_STM32(void);

  void     begin(uint16_t id = 0x9341);
  void     drawPixel(int16_t x, int16_t y, uint16_t color);
  void     drawFastHLine(int16_t x0, int16_t y0, int16_t w, uint16_t color);
  void     drawFastVLine(int16_t x0, int16_t y0, int16_t h, uint16_t color);
  void     fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t c);
  void     fillScreen(uint16_t color);
  void     reset(void);
  void     setRegisters8(uint8_t *ptr, uint8_t n);
  void     setRegisters16(uint16_t *ptr, uint8_t n);
  void     setRotation(uint8_t x);
       // These methods are public in order for BMP examples to work:
  void     setAddrWindow(int16_t x1, int16_t y1, int16_t x2, int16_t y2);
  void     invertDisplay(boolean i),
			pushColors(uint16_t *data, int16_t len, boolean first),
           drawBitmap(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t * bitmap);
  uint16_t readPixel(int16_t x, int16_t y), readID(void);
/*****************************************************************************/
// Pass 8-bit (each) R,G,B, get back 16-bit packed color
// color coding on bits:
// high byte sill be sent first
// bit nr: 		15	14	13	12	11	 10	09	08		07	06	05	 04	03	02	01	00
// color/bit:	R5	R4	R3	R2	R1 | G5	G4	G3		G2	G1	G0 | B5	B4	B3	B2	B1
// 								R0=R5											B0=B5
/*****************************************************************************/
  uint16_t inline color565(uint8_t r, uint8_t g, uint8_t b) { return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); }

 private:
  void     init(), flood(uint16_t color, uint32_t len);
  uint8_t  driver;
};

extern uint16_t readReg(uint8_t r);
extern uint32_t readReg32(uint8_t r);
extern void writeCommand(uint16_t c);
extern void writeRegister8(uint16_t a, uint8_t d);
extern void writeRegister16(uint16_t a, uint16_t d);
extern void writeRegister24(uint16_t a, uint32_t d);
extern void writeRegister32(uint16_t a, uint32_t d);
extern void writeRegisterPair(uint16_t aH, uint16_t aL, uint16_t d);

extern Adafruit_TFTLCD_8bit_STM32 tft;

#endif
Post Reply

Return to “Libraries & Hardware”