8 Bit TFT Library Issue

Working libraries, libraries being ported and related hardware
stevestrong
Posts: 339
Joined: Fri Dec 27, 2019 4:53 pm
Answers: 7
Location: Munich, Germany
Contact:

Re: 8 Bit TFT Library Issue

Post by stevestrong »

I am not sure that the TFT library is wrong, I rather suspect the core, something at the initialization it may go wrong if you enable USB serial but you do not use it.
But I cannot help you there due to not available knowledge, sorry.

User avatar
fpiSTM
Posts: 777
Joined: Wed Dec 11, 2019 7:11 pm
Answers: 30
Location: Le Mans
Contact:

Re: 8 Bit TFT Library Issue

Post by fpiSTM »

I did not check all the code anyway did you enabled all the required GPIO clock ?

mkengineering
Posts: 12
Joined: Thu Apr 30, 2020 8:35 pm

Re: 8 Bit TFT Library Issue

Post by mkengineering »

fpiSTM wrote:
Mon Aug 24, 2020 8:51 am
I did not check all the code anyway did you enabled all the required GPIO clock ?
Ahh! That makes much more sense now. I didn't enable the GPIOA / GPIOB clocks in the APB2ENR register anywhere in my code, causing the display not to init properly. When Serial is enabled, it must init APB2 (also used by USART1) somewhere else. Thanks for the help guys! I modified my the reset function to init APB2 for GPIOA and GPIOB according to the following table from the datasheet and it now works as intended:

Image

Modified reset:

Code: Select all

void Adafruit_TFTLCD_8bit_STM32::reset(void)
{
	RCC->APB2ENR |= 0b1100; //set bits 3 and 4 for GPIOA and GPIOB
	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);
	}
}
Is there a cleaner way to do this? It works but would need to be changed if a different GPIO port was used (as modified in the header).

User avatar
fpiSTM
Posts: 777
Joined: Wed Dec 11, 2019 7:11 pm
Answers: 30
Location: Le Mans
Contact:

Re: 8 Bit TFT Library Issue

Post by fpiSTM »

Well pinMode set the clock of the GPIO pin port.

As it calls this function to set the GPIO port clock:
https://github.com/stm32duino/Arduino_C ... c#L67-L152

mkengineering
Posts: 12
Joined: Thu Apr 30, 2020 8:35 pm

Re: 8 Bit TFT Library Issue

Post by mkengineering »

Thanks for the help on this guys!
Here's the final version of my ILI9486 library for STM32duino.
https://github.com/mkengineering/STM32_ILI9486_8_bit
I also ported over some popular examples for the Adafruit TFTs.

Ollie
Posts: 4
Joined: Wed Mar 18, 2020 4:46 am

Re: 8 Bit TFT Library Issue

Post by Ollie »

I like your efficient programming style. Efficient usage of resources, avoidance of the bloatware, and proper level of documentation and naming conventions are hall marks of long term experience. Well done 8-)

mkengineering
Posts: 12
Joined: Thu Apr 30, 2020 8:35 pm

Re: 8 Bit TFT Library Issue

Post by mkengineering »

Hello Everyone,

Recently I started messing with a Robotdyn STM32F303 board for a project which I intended to use with my display library. Though, I seem to be having some issues with the macro definitions again :cry: . The STM32F303 seems to be quite different with regard to gpio access. As a results, I had to change the setCntrlDir, setReadDir, setWriteDir and write8 definitions.

Additionally, for this project, I intended to use the upper 8 pins (PA8-PA15) for the data lines of the display in order to preserve the analog inputs on PA0-PA7 for other uses. This conflicts with the swd pins (PA13, PA14) of the st link for programming. Based on the reference manual for the STM32F303, I'm able to assign an alternate function to these pins after programming by using the AFR register.

Image

I decided to just assign PA13/PA14 to AF2 and ended up with the following code.

Header:

Code: Select all

// Graphics library by ladyada/adafruit with init code from Rossum
// MIT license
// ported to STM32duino ST Core by MKE

//modified for STM32F303

/*****************************************************************************/
// Wiring Settings
/*****************************************************************************/

// By default, the library is designed to use:

// PA0:PA7 	-> 	DB0:DB7
// PB5 		-> 	RESET
// PB6		->	CS
// PB7		->	RS/DC
// PB8		->	WR
// PB9		->	RD

// To deviate from these settings, you must modify:

// 1. "Data and Control Port / Pin Definitions"
//		a. Data and Control Ports
//		b. Bit Masks for Control Pins

// 2. "Macro Definitions"
//		a. setCntrlDir()
//		b. setWriteDir()
//		c. setReadDir()
//		d. write8()

// Note: the control pins can be any 5 pins from a single register (Example: PB5, PB6, PB7, PB8, PB9, etc...)
// Note: the data pins must be 8 CONSECUTIVE pins from a single register (Example: PA0:PA7, PA8:PA15, etc...)

#ifndef _STM32_ILI9486_8_BIT_H_
#define _STM32_ILI9486_8_BIT_H_

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

/*****************************************************************************/
// Data and Control Port / Pin Definitions
/*****************************************************************************/

// Example: PB5-> PORT = GPIOB, MASK = GPIO_PIN_5

// Data and Control Ports

#define TFT_DATA_PORT	GPIOA
#define TFT_CNTRL_PORT	GPIOB

// Masks for Control Pins

#define TFT_RD_MASK		GPIO_PIN_15
#define TFT_WR_MASK		GPIO_PIN_14
#define TFT_RS_MASK		GPIO_PIN_13
#define TFT_CS_MASK		GPIO_PIN_12
#define TFT_RST_MASK	GPIO_PIN_11

/*****************************************************************************/
// TFT Register Names
/*****************************************************************************/

#define ILI9486_SOFTRESET         0x01
#define ILI9486_SLEEPIN           0x10
#define ILI9486_SLEEPOUT          0x11
#define ILI9486_NORMALDISP        0x13
#define ILI9486_INVERTOFF         0x20
#define ILI9486_INVERTON          0x21
#define ILI9486_GAMMASET          0x26
#define ILI9486_DISPLAYOFF        0x28
#define ILI9486_DISPLAYON         0x29
#define ILI9486_COLADDRSET        0x2A
#define ILI9486_PAGEADDRSET       0x2B
#define ILI9486_MEMORYWRITE       0x2C
#define ILI9486_PIXELFORMAT       0x3A
#define ILI9486_FRAMECONTROL      0xB1
#define ILI9486_DISPLAYFUNC       0xB6
#define ILI9486_ENTRYMODE         0xB7
#define ILI9486_POWERCONTROL1     0xC0
#define ILI9486_POWERCONTROL2     0xC1
#define ILI9486_VCOMCONTROL1      0xC5
#define ILI9486_VCOMCONTROL2      0xC7
#define ILI9486_MEMCONTROL        0x36
#define ILI9486_MADCTL			  0x36

#define ILI9486_MADCTL_MY  0x80
#define ILI9486_MADCTL_MX  0x40
#define ILI9486_MADCTL_MV  0x20
#define ILI9486_MADCTL_ML  0x10
#define ILI9486_MADCTL_RGB 0x00
#define ILI9486_MADCTL_BGR 0x08
#define ILI9486_MADCTL_MH  0x04

/*****************************************************************************/
// TFT Size
/*****************************************************************************/

#define TFTWIDTH   320
#define TFTHEIGHT  480

#define TFTLCD_DELAY 0xFF
#define TFTLCD_DELAY8 0xFF

#define Color565 color565

/*****************************************************************************/
// Color Definitions
/*****************************************************************************/

#define BLACK       0x0000
#define NAVY        0x000F
#define DARKGREEN   0x03E0
#define DARKCYAN    0x03EF
#define MAROON      0x7800
#define PURPLE      0x780F
#define OLIVE       0x7BE0
#define LIGHTGREY   0xC618
#define GRAY        0x5AEB
#define DARKGREY    0x7BEF
#define BLUE        0x001F
#define GREEN       0x07E0
#define CYAN        0x07FF
#define RED         0xF800
#define MAGENTA     0xF81F
#define YELLOW      0xFFE0
#define WHITE       0xFFFF
#define ORANGE      0xFD20
#define GREENYELLOW 0xAFE5
#define PINK        0xF81F

/*****************************************************************************/
// Macro Definitions
/*****************************************************************************/

#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 WR_STROBE 				{ WR_ACTIVE; WR_IDLE; }

#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 RST_ACTIVE				{ TFT_CNTRL_PORT->BRR = TFT_RST_MASK; }
#define RST_IDLE				{ TFT_CNTRL_PORT->BSRR = TFT_RST_MASK; }
#define RST_TOGGLE				{ RST_IDLE; RST_ACTIVE; RST_IDLE; }

extern uint8_t read8_(void);

#define read8(x) ( x = read8_() )

// set control pins (PB11-PB15) to output mode and enable ahb clock for gpiob
#define setCntrlDir() { RCC->AHBENR |= (0b0100 << 16); TFT_CNTRL_PORT->MODER = 0x55400000; TFT_CNTRL_PORT->OSPEEDR = 0xFFC00000; TFT_CNTRL_PORT->OTYPER = 0x00000000; }

// set the data pins (PA8-PA15) to input mode and enable ahb clock for gpioa
#define setReadDir() { RCC->AHBENR |= (0b0010 << 16); TFT_DATA_PORT->AFR[1] = 0x2200000; TFT_DATA_PORT->PUPDR = 0x00000000; TFT_DATA_PORT->MODER = 0x00000000; }

// set the data pins (PA8-PA15) to output mode and enable ahb clock for gpioa
#define setWriteDir() { RCC->AHBENR |= (0b0010 << 16); TFT_DATA_PORT->AFR[1] = 0x2200000; TFT_DATA_PORT->OTYPER = 0x00000000; TFT_DATA_PORT->PUPDR = 0x00000000; TFT_DATA_PORT->OSPEEDR = 0xFFFF0000; TFT_DATA_PORT->MODER = 0x55550000; }

// set pins to output the 8 bit value
#define write8(c) { TFT_DATA_PORT->BSRR = (uint32_t)(0xFF000000 + (((c)&0xFF) << 8)); WR_STROBE; }

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

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

/*****************************************************************************/
class STM32_ILI9486_8_bit : public Adafruit_GFX {

 public:

  STM32_ILI9486_8_bit(void);

  void     begin(void);
  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 STM32_ILI9486_8_bit tft;

#endif
C++ file (unmodified from the STM32F103 version):

Code: Select all

// Graphics library by ladyada/adafruit with init code from Rossum
// MIT license
// ported to STM32duino ST Core by MKE

#include "STM32_ILI9486_8_bit.h"

static const uint8_t ILI9486_regValues_ada[] PROGMEM = {
	//from mcufriend lib
	0xC0, 2, 0x0d, 0x0d,        //Power Control 1 [0E 0E]
    0xC1, 2, 0x43, 0x00,        //Power Control 2 [43 00]
    0xC2, 1, 0x00,      //Power Control 3 [33]
    0xC5, 4, 0x00, 0x48, 0x00, 0x48,    //VCOM  Control 1 [00 40 00 40]
    0xB4, 1, 0x00,      //Inversion Control [00]
    0xB6, 3, 0x02, 0x02, 0x3B,  // Display Function Control [02 02 3B]
	0xE0, 15, 0x0F, 0x21, 0x1C, 0x0B, 0x0E, 0x08, 0x49, 0x98, 0x38, 0x09, 0x11, 0x03, 0x14, 0x10, 0x00, //Positive Gamma Correction
	0xE1, 15, 0x0F, 0x2F, 0x2B, 0x0C, 0x0E, 0x06, 0x47, 0x76, 0x37, 0x07, 0x11, 0x04, 0x23, 0x1E, 0x00, //Negative Gamma Correction
};

/*****************************************************************************/
static void WriteCmdParamN(uint16_t cmd, int8_t N, const uint8_t * block)
{
    writeCommand(cmd);
	while (N-- > 0) {
		uint8_t u8 = *block++;
		CD_DATA;
		write8(u8);
	}
    CS_IDLE;
}

/*****************************************************************************/
static void init_table(const uint8_t *table, int16_t size)
{
	while (size > 0) {
		uint8_t cmd = *table++;
		uint8_t len = *table++;
		if (cmd == TFTLCD_DELAY8) {
			delay(len);
			len = 0;
		} else {
			WriteCmdParamN(cmd, len, table);
			table += len;
		}
		size -= len + 2;
	}
}

const uint8_t reset_off[] PROGMEM = {
	0x01, 0,            //Soft Reset
	TFTLCD_DELAY8, 150,  // .kbv will power up with ONLY reset, sleep out, display on
	0x28, 0,            //Display Off
	0x3A, 1, 0x55,      //Pixel read=565, write=565.
};

const uint8_t wake_on[] PROGMEM = {
	0x11, 0,            //Sleep Out
	TFTLCD_DELAY8, 150,
	0x29, 0,            //Display On
	// //additional settings
	ILI9486_INVERTOFF, 0,			// invert off
	0x36, 1, 0x48,      //Memory Access
	0xB0, 1, 0x40,      //RGB Signal [40] RCM=2
};

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

/*****************************************************************************/
void STM32_ILI9486_8_bit::begin(void)
{
	reset();
	init_table(reset_off, sizeof(reset_off));
	init_table(ILI9486_regValues_ada, sizeof(ILI9486_regValues_ada));
	init_table(wake_on, sizeof(wake_on));
}

/*****************************************************************************/
void STM32_ILI9486_8_bit::reset(void)
{
	setCntrlDir();
	CS_IDLE; // Set all control bits to HIGH (idle)
	CD_DATA; // Signals are ACTIVE LOW
	WR_IDLE;
	RD_IDLE;
	setWriteDir();
	RST_TOGGLE;
}

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

void STM32_ILI9486_8_bit::setAddrWindow(int16_t x1, int16_t y1, int16_t x2, int16_t y2)
{
	writeRegister32(ILI9486_COLADDRSET, ((uint32_t)(x1<<16) | x2));  // HX8357D uses same registers!
	writeRegister32(ILI9486_PAGEADDRSET, ((uint32_t)(y1<<16) | y2)); // HX8357D uses same registers!
}

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

void STM32_ILI9486_8_bit::flood(uint16_t color, uint32_t len)
{
	uint16_t blocks;
	uint8_t  i, hi = color >> 8, lo = color;
	CS_ACTIVE_CD_COMMAND;
	writeCommand(ILI9486_MEMORYWRITE);
	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 STM32_ILI9486_8_bit::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);
  writeRegisterPair(0x04, 0x05, TFTWIDTH  - 1);
  writeRegisterPair(0x08, 0x09, TFTHEIGHT - 1);
}

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

void STM32_ILI9486_8_bit::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);
  writeRegisterPair(0x04, 0x05, TFTWIDTH  - 1);
  writeRegisterPair(0x08, 0x09, TFTHEIGHT - 1);
}

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

void STM32_ILI9486_8_bit::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);
}

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

void STM32_ILI9486_8_bit::fillScreen(uint16_t color)
{
	setAddrWindow(0, 0, _width - 1, _height - 1);
	flood(color, (long)TFTWIDTH * (long)TFTHEIGHT);
}

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

void STM32_ILI9486_8_bit::drawPixel(int16_t x, int16_t y, uint16_t color)
{
  // Clip
  if((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) return;
  setAddrWindow(x, y, x+1, y+1);
  writeRegister16(0x2C, color);
}

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

void STM32_ILI9486_8_bit::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++);
			}
		}
	}
}

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

void STM32_ILI9486_8_bit::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;
    write8(0x2C);
  }
  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 STM32_ILI9486_8_bit::invertDisplay(boolean i)
{
	writeCommand( i ? ILI9486_INVERTON : ILI9486_INVERTOFF);
	CS_IDLE;
}

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

void STM32_ILI9486_8_bit::setRotation(uint8_t x)
{
  // Call parent rotation func first -- sets up rotation flags, etc.
  Adafruit_GFX::setRotation(x);
  // Then perform hardware-specific rotation operations...
   uint16_t t;
   switch (rotation) {
   case 1:
     t = ILI9486_MADCTL_MX | ILI9486_MADCTL_MY | ILI9486_MADCTL_MV | ILI9486_MADCTL_BGR;
     break;
   case 2:
     t = ILI9486_MADCTL_MX | ILI9486_MADCTL_BGR;
     break;
   case 3:
     t = ILI9486_MADCTL_MV | ILI9486_MADCTL_BGR;
     break;
   case 0:
   default:
    t = ILI9486_MADCTL_MY | ILI9486_MADCTL_BGR;
    break;
  }
   writeRegister8(ILI9486_MADCTL, t ); // MADCTL
   // For 9341, 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);
}

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

uint16_t STM32_ILI9486_8_bit::readPixel(int16_t x, int16_t y)
{
	if((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) return 0;
	return 0;
}

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

uint16_t STM32_ILI9486_8_bit::readID(void)
{
  uint16_t id = readReg32(0xD3);
  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;
}

//STM32_ILI9486_8_bit tft;
While this compiles fine, I end up with the dreaded white screen on the display. I have used the onboard LED to determine that the sketch completes "tft.begin()" and the fillscreen, but the display must not be getting properly initialized.

Is it possible to repurpose the SWD pins using this method? Am I missing something in my gpio setup?

mkengineering
Posts: 12
Joined: Thu Apr 30, 2020 8:35 pm

Re: 8 Bit TFT Library Issue

Post by mkengineering »

I think I need more sleep...
I just realized I had been updating the header file without it saving to my arduino folder.
It works now! I removed the AFR configuration as well as some of the other setup as it seems to work fine without it.
Here is my revised header file:

Code: Select all

// Graphics library by ladyada/adafruit with init code from Rossum
// MIT license
// ported to STM32duino ST Core by MKE

//modified for STM32F303

/*****************************************************************************/
// Wiring Settings
/*****************************************************************************/

// By default, the library is designed to use:

// PA0:PA7 	-> 	DB0:DB7
// PB5 		-> 	RESET
// PB6		->	CS
// PB7		->	RS/DC
// PB8		->	WR
// PB9		->	RD

// To deviate from these settings, you must modify:

// 1. "Data and Control Port / Pin Definitions"
//		a. Data and Control Ports
//		b. Bit Masks for Control Pins

// 2. "Macro Definitions"
//		a. setCntrlDir()
//		b. setWriteDir()
//		c. setReadDir()
//		d. write8()

// Note: the control pins can be any 5 pins from a single register (Example: PB5, PB6, PB7, PB8, PB9, etc...)
// Note: the data pins must be 8 CONSECUTIVE pins from a single register (Example: PA0:PA7, PA8:PA15, etc...)

#ifndef _STM32_ILI9486_8_BIT_H_
#define _STM32_ILI9486_8_BIT_H_

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

/*****************************************************************************/
// Data and Control Port / Pin Definitions
/*****************************************************************************/

// Example: PB5-> PORT = GPIOB, MASK = GPIO_PIN_5

// Data and Control Ports

#define TFT_DATA_PORT	GPIOA
#define TFT_CNTRL_PORT	GPIOB

// Masks for Control Pins

#define TFT_RD_MASK		GPIO_PIN_15
#define TFT_WR_MASK		GPIO_PIN_14
#define TFT_RS_MASK		GPIO_PIN_13
#define TFT_CS_MASK		GPIO_PIN_12
#define TFT_RST_MASK	GPIO_PIN_11

/*****************************************************************************/
// TFT Register Names
/*****************************************************************************/

#define ILI9486_SOFTRESET         0x01
#define ILI9486_SLEEPIN           0x10
#define ILI9486_SLEEPOUT          0x11
#define ILI9486_NORMALDISP        0x13
#define ILI9486_INVERTOFF         0x20
#define ILI9486_INVERTON          0x21
#define ILI9486_GAMMASET          0x26
#define ILI9486_DISPLAYOFF        0x28
#define ILI9486_DISPLAYON         0x29
#define ILI9486_COLADDRSET        0x2A
#define ILI9486_PAGEADDRSET       0x2B
#define ILI9486_MEMORYWRITE       0x2C
#define ILI9486_PIXELFORMAT       0x3A
#define ILI9486_FRAMECONTROL      0xB1
#define ILI9486_DISPLAYFUNC       0xB6
#define ILI9486_ENTRYMODE         0xB7
#define ILI9486_POWERCONTROL1     0xC0
#define ILI9486_POWERCONTROL2     0xC1
#define ILI9486_VCOMCONTROL1      0xC5
#define ILI9486_VCOMCONTROL2      0xC7
#define ILI9486_MEMCONTROL        0x36
#define ILI9486_MADCTL			  0x36

#define ILI9486_MADCTL_MY  0x80
#define ILI9486_MADCTL_MX  0x40
#define ILI9486_MADCTL_MV  0x20
#define ILI9486_MADCTL_ML  0x10
#define ILI9486_MADCTL_RGB 0x00
#define ILI9486_MADCTL_BGR 0x08
#define ILI9486_MADCTL_MH  0x04

/*****************************************************************************/
// TFT Size
/*****************************************************************************/

#define TFTWIDTH   320
#define TFTHEIGHT  480

#define TFTLCD_DELAY 0xFF
#define TFTLCD_DELAY8 0xFF

#define Color565 color565

/*****************************************************************************/
// Color Definitions
/*****************************************************************************/

#define BLACK       0x0000
#define NAVY        0x000F
#define DARKGREEN   0x03E0
#define DARKCYAN    0x03EF
#define MAROON      0x7800
#define PURPLE      0x780F
#define OLIVE       0x7BE0
#define LIGHTGREY   0xC618
#define GRAY        0x5AEB
#define DARKGREY    0x7BEF
#define BLUE        0x001F
#define GREEN       0x07E0
#define CYAN        0x07FF
#define RED         0xF800
#define MAGENTA     0xF81F
#define YELLOW      0xFFE0
#define WHITE       0xFFFF
#define ORANGE      0xFD20
#define GREENYELLOW 0xAFE5
#define PINK        0xF81F

/*****************************************************************************/
// Macro Definitions
/*****************************************************************************/

#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 WR_STROBE 				{ WR_ACTIVE; WR_IDLE; }

#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 RST_ACTIVE				{ TFT_CNTRL_PORT->BRR = TFT_RST_MASK; }
#define RST_IDLE				{ TFT_CNTRL_PORT->BSRR = TFT_RST_MASK; }
#define RST_TOGGLE				{ RST_IDLE; RST_ACTIVE; RST_IDLE; }

extern uint8_t read8_(void);

#define read8(x) ( x = read8_() )

// set control pins (PB11-PB15) to output mode and enable ahb clock for gpiob
#define setCntrlDir() { RCC->AHBENR |= (0b0100 << 16); TFT_CNTRL_PORT->OSPEEDR = 0xFFC00000; TFT_CNTRL_PORT->MODER = 0x55400000; }

// set the data pins (PA8-PA15) to input mode and enable ahb clock for gpioa
#define setReadDir() { RCC->AHBENR |= (0b0010 << 16); TFT_DATA_PORT->MODER = 0x00000000; }

// set the data pins (PA8-PA15) to output mode and enable ahb clock for gpioa
#define setWriteDir() { RCC->AHBENR |= (0b0010 << 16); TFT_DATA_PORT->OSPEEDR = 0xFFFF0000; TFT_DATA_PORT->MODER = 0x55550000; }

// set pins to output the 8 bit value
#define write8(c) { TFT_DATA_PORT->BSRR = (uint32_t)(0xFF000000 + (((c)&0xFF) << 8)); WR_STROBE; }

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

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

/*****************************************************************************/
class STM32_ILI9486_8_bit : public Adafruit_GFX {

 public:

  STM32_ILI9486_8_bit(void);

  void     begin(void);
  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 STM32_ILI9486_8_bit tft;

#endif
Image

Should I worry about adding the alternate function definitions back in? I assume as I'm not using the serial wire debugging interface it doesn't affect the pins after it starts running my code.

Post Reply

Return to “Libraries & Hardware”