Having trouble with HX8357_t3 library

Status
Not open for further replies.

willyman

Member
I'm using a teensy 3.6, on an adafruit feather adapter board, which is then mounted on the adafruit featherwing 3.5" tft display. This hardware combination works and is currently functional when used with the adafruit HX8357 library.

I bought the teensy for improved screen refresh rate of the 3.5" display, and there is much improvement over the feather m0, but there is still visible "flashing".
(And yes I am only doing "intelligent" redraws of the screen-only drawing parts that have changed)

So far I have been using the adafruit HX8357 library, and I'm hoping I can get one of the optimized teensy 3.x libraries to work. I spent all day yesterday and today trying to get the HX8357_t3 library to work: https://github.com/Asaurus1/HX8357_t3 . Also the screen buffer library looks like it would be awesome if it could be made to work: https://github.com/FrankBoesing/ILI9341_t3DMA/blob/master/ILI9341_t3DMA.cpp
So far no cigar on either library and I'm hoping someone with better software skills than I can help.

The SPI library optimized for teensy has been installed. To make sure It is being used, I put a debug print statement in the begin() function of the teensy optimized spi library:
Code:
void SPIClass::begin()
{
	volatile uint32_t *reg;

    Serial.println("TEENSY SPI BEING USED");
	hardware().clock_gate_register |= hardware().clock_gate_mask;
	port().MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F);
	port().CTAR0 = SPI_CTAR_FMSZ(7) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1);
	port().CTAR1 = SPI_CTAR_FMSZ(15) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1);
	port().MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F);
	reg = portConfigRegister(hardware().mosi_pin[mosi_pin_index]);
	*reg = hardware().mosi_mux[mosi_pin_index];
	reg = portConfigRegister(hardware().miso_pin[miso_pin_index]);
	*reg= hardware().miso_mux[miso_pin_index];
	reg = portConfigRegister(hardware().sck_pin[sck_pin_index]);
	*reg = hardware().sck_mux[sck_pin_index];
}

The adafruit library works. Here is the start of the sketch:
Code:
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_HX8357.h"
#ifdef ESP8266
   #define STMPE_CS 16
   #define TFT_CS   0
   #define TFT_DC   15
   #define SD_CS    2
#endif
#ifdef ESP32
   #define STMPE_CS 32
   #define TFT_CS   15
   #define TFT_DC   33
   #define SD_CS    14
#endif
#ifdef __AVR_ATmega32U4__
   #define STMPE_CS 6
   #define TFT_CS   9
   #define TFT_DC   10
   #define SD_CS    5
#endif
#ifdef ARDUINO_SAMD_FEATHER_M0
   #define STMPE_CS 6
   #define TFT_CS   9
   #define TFT_DC   10
   #define SD_CS    5
#endif
#ifdef TEENSYDUINO
   #define TFT_DC   10
   #define TFT_CS   4
   #define STMPE_CS 3
   #define SD_CS    8
#endif
#ifdef ARDUINO_STM32_FEATHER
   #define TFT_DC   PB4
   #define TFT_CS   PA15
   #define STMPE_CS PC7
   #define SD_CS    PC5
#endif
#ifdef ARDUINO_FEATHER52
   #define STMPE_CS 30
   #define TFT_CS   13
   #define TFT_DC   11
   #define SD_CS    27
#endif

#define TFT_RST -1

Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC, TFT_RST);

void setup() {
  Serial.begin(115200);
  while (!Serial) ;
  tft.begin(HX8357D);
  
  Serial.print("CS: ");
  Serial.println(TFT_CS);
  Serial.print("DC: ");
  Serial.println(TFT_DC);

 uint8_t x = tft.readcommand8(HX8357_RDPOWMODE);
  Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(HX8357_RDMADCTL);
  Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(HX8357_RDCOLMOD);
  Serial.print("Pixel Format: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(HX8357_RDDIM);
  Serial.print("Image Format: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(HX8357_RDDSDR);
  Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX); 
}

Here's output from serial monitor with adafruit library:
Code:
TEENSY SPI BEING USED
CS: 4
DC: 10
Display Power Mode: 0x9C
MADCTL Mode: 0x60
Pixel Format: 0x5
Image Format: 0x0
Self Diagnostic: 0xC0


The HX8357_t3 library doesnt get the same serial diagnostic info, and it seems the SPI library doesnt like the pins chosen for CS and DC. This is evident by the some print statements put in the HX8357_t3 begin function. Here's the print statements added:
Code:
void HX8357_t3::begin(uint8_t type)
{
    // verify SPI pins are valid;
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
    if ((_mosi == 11 || _mosi == 7 || _mosi == 28) && (_miso == 12 || _miso == 8 || _miso == 39) 
    		&& (_sclk == 13 || _sclk == 14 || _sclk == 27)) {
	#else
    if ((_mosi == 11 || _mosi == 7) && (_miso == 12 || _miso == 8) && (_sclk == 13 || _sclk == 14)) {
    #endif	
        SPI.setMOSI(_mosi);
        SPI.setMISO(_miso);
        SPI.setSCK(_sclk);

    Serial.println("The SPI pins were setup in begin");
    Serial.print("CS: ");
    Serial.println(_cs);
    Serial.print("DC: ");
    Serial.println(_dc);
    Serial.print("mosi: ");
    Serial.println(_mosi);
    Serial.print("miso: ");
    Serial.println(_miso);
    Serial.print("sclk: ");
    Serial.println(_sclk);

	} else
        return; // not valid pins...

    SPI.begin();

    if (SPI.pinIsChipSelect(_cs, _dc)) {
		pcs_data = SPI.setCS(_cs);
		pcs_command = pcs_data | SPI.setCS(_dc);
        Serial.println("Chip select is valid");
	} else {
		pcs_data = 0;
		pcs_command = 0;
        Serial.println("Pins Not Valid");
		//return;
	}
    Serial.print("pcs_data: ");
    Serial.println(pcs_data);
    Serial.print("pcs_command: ");
    Serial.println(pcs_command);

Here's the sketch used with the HX8357_t3 library:
Code:
#include "SPI.h"
#include "HX8357_t3.h"

// For the Adafruit shield, these are the default.
#define TFT_DC  10
#define TFT_CS 4

void setup() {
  Serial.begin(115200);
  while (!Serial) ; // wait for Arduino Serial Monitor

  HX8357_t3 tft = HX8357_t3(TFT_CS, TFT_DC);   // put inside setup to see debug info when instantiating

  tft.begin(HX8357D);

  // read diagnostics (optional but can help debug problems)
  uint8_t x = tft.readcommand8(HX8357_RDMODE);
  Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(HX8357_RDMADCTL);
  Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(HX8357_RDPIXFMT);
  Serial.print("Pixel Format: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(HX8357_RDIMGFMT);
  Serial.print("Image Format: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(HX8357_RDSELFDIAG);
  Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX); 
}

And here's the output:
Code:
The SPI pins were setup in begin
CS: 4
DC: 10
mosi: 11
miso: 12
sclk: 13
TEENSY SPI BEING USED
Pins Not Valid
pcs_data: 0
pcs_command: 0
type == HX8357D
Display Power Mode: 0xFF
MADCTL Mode: 0xFF
Pixel Format: 0xFF
Image Format: 0xFF
Self Diagnostic: 0xFF

Supposing the SPI.pinIsChipSelect(_cs, _dc) check is bypassed, it still doesnt fix things. I'm starting to get to the limit of my coding skills, can anyone offer some pointers? Thanks so much
 
Yes if this code is based off of the original ili9341_t3 library, than it won't work as pin 4 is not a valid hardware chip select pin.

So the code bails as it can not properly encode the chip select into the PUSHR register for the SPI...

Couple of options:

a) try to move it off of 4 to some other pin.

The information on valid chip select pins from the SPI class data:
Code:
	10, 2, 9, 6, 20, 23, 21, 22, 15, 26, 45,
	PORT_PCR_MUX(2),  PORT_PCR_MUX(2), PORT_PCR_MUX(2),  PORT_PCR_MUX(2),  PORT_PCR_MUX(2),  PORT_PCR_MUX(2),  PORT_PCR_MUX(2),  PORT_PCR_MUX(2),  PORT_PCR_MUX(2),   PORT_PCR_MUX(2),   PORT_PCR_MUX(3),
	0x1, 0x1, 0x2, 0x2, 0x4, 0x4, 0x8, 0x8, 0x10, 0x1, 0x20
}
Disregard the MUX line, the first line is the different valid pins for a chip select on SPI. The last line I show is also important. This is the actual physical pin (actually mask) for what that pin maps to. An important thing here is you can not use two of them that map to the same value. That is you can not use both 10 and 2...

b) Slightly harder coding change... With my version of the ili9341_t3n (github kurte) library, I made it such that the CS pin being on a hardware CS pin is optional (preferred), but would work almost as well otherwise. The DC pin still must be a hardware CS pin. I did this as I also supported SPI1 and SPI2, and on SPI1 there was only one valid SPI cs pin...

The changes are not terribly difficult, but slightly tedious. That is the Init code needs to be updated to detect this case and then it hold onto a port/mask for the CS... Then the main changes has to do when
the beginTransaction/endTransaction calls are made. At these locations IF the CS is not directly handled by the SPI hardware, we need to set or clear the CS pin at the right time...
In my code I have some inline functions for the begin/end that handle this... Again not hard...

Good luck
 
Hi Kurt,
Thank you so much for your response. I was completely confused by which pins are valid chip select pins. What I hear you saying is that the two pins used must be valid hardware chip select pins according to {10, 2, 9, 6, 20, 23, 21, 22, 15, 26, 45} , and they must also not share the same mask.

For me, DC has to stay at pin 10 but I can re-route CS. So valid pins for CS could be one of {9, 6, 20, 23, 21, 22, 15, 45} right?

I haven't done this sort of register level programming before so I'm slightly intimidated. I don't completely understand what all the entries in that list represent. Initializing the instantiation spi0_hardware to some values?
Also I'm familiar with bitwise operations, but what is the pin mask for in this case?
Code:
const SPIClass::SPI_Hardware_t SPIClass::spi0_hardware = {
	SIM_SCGC6, SIM_SCGC6_SPI0, 4, IRQ_SPI0,
	32767, DMAMUX_SOURCE_SPI0_TX, DMAMUX_SOURCE_SPI0_RX,
	_spi_dma_rxISR0,
	12, 8, 39, 255,
	PORT_PCR_MUX(2), PORT_PCR_MUX(2), PORT_PCR_MUX(2), 0,
	11, 7, 28, 255,
	PORT_PCR_MUX(2), PORT_PCR_MUX(2), PORT_PCR_MUX(2), 0,
	13, 14, 27,
	PORT_PCR_MUX(2), PORT_PCR_MUX(2), PORT_PCR_MUX(2),
	10, 2, 9, 6, 20, 23, 21, 22, 15, 26, 45,
	PORT_PCR_MUX(2),  PORT_PCR_MUX(2), PORT_PCR_MUX(2),  PORT_PCR_MUX(2),  PORT_PCR_MUX(2),  PORT_PCR_MUX(2),  PORT_PCR_MUX(2),  PORT_PCR_MUX(2),  PORT_PCR_MUX(2),   PORT_PCR_MUX(2),   PORT_PCR_MUX(3),
	0x1, 0x1, 0x2, 0x2, 0x4, 0x4, 0x8, 0x8, 0x10, 0x1, 0x20
};

I was trying to understand what the heck is going on in the transfer command. Looking at part of it:
Code:
void SPIClass::transfer(const void * buf, void * retbuf, size_t count)
{

	if (count == 0) return;
	if (!(port().CTAR0 & SPI_CTAR_LSBFE)) {
		// We are doing the standard MSB order
		const uint8_t *p_write = (const uint8_t *)buf;
		uint8_t *p_read = (uint8_t *)retbuf;
		size_t count_read = count;

		// Lets clear the reader queue
		port().MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F);

		uint32_t sr;

		// Now lets loop while we still have data to output
		if (count & 1) {
		    if (p_write) {
				if (count > 1)
					port().PUSHR = *p_write++ | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0);
				else
					port().PUSHR = *p_write++ | SPI_PUSHR_CTAS(0);
			} else {
				if (count > 1)
					port().PUSHR = _transferWriteFill | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0);
				else
					port().PUSHR = _transferWriteFill | SPI_PUSHR_CTAS(0);
			}
			count--;
		}

	    uint16_t w =  (uint16_t)(_transferWriteFill << 8) | _transferWriteFill;

		while (count > 0) {
			// Push out the next byte; 
		    if (p_write) {
		    	w = (*p_write++) << 8;
				w |= *p_write++;
		    }
		    uint16_t queue_full_status_mask = (hardware().queue_size-1) << 12;
			if (count == 2)
				port().PUSHR = w | SPI_PUSHR_CTAS(1);
			else	
				port().PUSHR = w | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1);
			count -= 2; // how many bytes to output.
			// Make sure queue is not full before pushing next byte out
			do {
				sr = port().SR;
				if (sr & 0xF0)  {
					uint16_t w = port().POPR;  // Read any pending RX bytes in
					if (count_read & 1) {
						if (p_read) {
							*p_read++ = w;  // Read any pending RX bytes in
						} 
						count_read--;
					} else {
						if (p_read) {
							*p_read++ = w >> 8;
							*p_read++ = (w & 0xff);
						}
						count_read -= 2;
					}
				}
			} while ((sr & (15 << 12)) > queue_full_status_mask);

		}

		// now lets wait for all of the read bytes to be returned...
		while (count_read) {
			sr = port().SR;
			if (sr & 0xF0)  {
				uint16_t w = port().POPR;  // Read any pending RX bytes in
				if (count_read & 1) {
					if (p_read)
						*p_read++ = w;  // Read any pending RX bytes in
					count_read--;
				} else {
					if (p_read) {
						*p_read++ = w >> 8;
						*p_read++ = (w & 0xff);
					}
					count_read -= 2;
				}
			}
		}
	}

So when the port().PUSHR command happens, that is directly accessing a register is that right?

I may be in over my head trying to modify the code, I think the easiest option might just be for me to move to a different pin!
 
As I mentioned, the easiest solution is to move the CS pin... And yes those are the valid pins to move to.


But to change the code to allow only needing DC requiring CS pin... Maybe the easiest thing would be to look at example of the code changes needed. Example the big Pull Request I still have outstanding to the main ili9341_t3 library (https://github.com/PaulStoffregen/ILI9341_t3/pull/41) has this change in it, along with several other things, like: clip rectangle, offset, Opaque font drawing text... So it is not all related... The Pull request has all of the changes as a set of diffs. Or you can look at my Fork/Branch for the Pull request which shows the code with the changes...

Things like: You need to define variables in the class to hold CS information. Like in the header file:
Code:
	// add support to allow only one hardware CS (used for dc)
    uint8_t _cspinmask;
    volatile uint8_t *_csport;

Added inline functions to use for begin/end transaction:
Code:
	void beginSPITransaction(uint32_t clock = ILI9341_SPICLOCK) __attribute__((always_inline)) {
		SPI.beginTransaction(SPISettings(clock, MSBFIRST, SPI_MODE0));
		if (_csport)
			*_csport  &= ~_cspinmask;
	}
	void endSPITransaction() __attribute__((always_inline)) {
		if (_csport)
			*_csport |= _cspinmask;
		SPI.endTransaction();
	}

Then everywhere in the source (.cpp file) that called SPI.beginTransaction and SPI.endtTransaction, I insteadd called those two new inline functions: So
Code:
void ILI9341_t3::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
	SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
	setAddr(x0, y0, x1, y1);
	writecommand_last(ILI9341_RAMWR); // write to RAM
	SPI.endTransaction();
}
Became:
Code:
void ILI9341_t3::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
	beginSPITransaction();;
	setAddr(x0, y0, x1, y1);
	writecommand_last(ILI9341_RAMWR); // write to RAM
	endSPITransaction();
}
Note: I did these with a couple of global search and replace functions in editor...

In the constructor, needed to initialize the two new variables;

Code:
    _cspinmask = 0;
    _csport = NULL;

Then the rest of the changes were in the ::begin method to detect that the CS was not a valid CS... Note: my version also allows you to not specify the MISO pin and still work, except you can no longer to input...
The updated code looks something like:
Code:
void ILI9341_t3::begin(void)
{
    // verify SPI pins are valid;
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
    // Allow to work with mimimum of MOSI and SCK
    if ((_mosi == 255 || _mosi == 11 || _mosi == 7 || _mosi == 28)  && (_sclk == 255 || _sclk == 13 || _sclk == 14 || _sclk == 27)) 
	#else
    if ((_mosi == 255 || _mosi == 11 || _mosi == 7) && (_sclk == 255 || _sclk == 13 || _sclk == 14)) 
    #endif	
    {
        
		if (_mosi != 255) SPI.setMOSI(_mosi);
        if (_sclk != 255) SPI.setSCK(_sclk);

        // Now see if valid MISO
	    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
	    if (_miso == 12 || _miso == 8 || _miso == 39)
		#else
	    if (_miso == 12 || _miso == 8)
	    #endif
		{	
        	SPI.setMISO(_miso);
    	} else {
			_miso = 0xff;	// set miso to 255 as flag it is bad
		}
	} else {
        return; // not valid pins...
	}

	SPI.begin();
	if (SPI.pinIsChipSelect(_cs, _dc)) {
		pcs_data = SPI.setCS(_cs);
		pcs_command = pcs_data | SPI.setCS(_dc);
	[COLOR="#FF0000"]} else {
		// See if at least DC is on chipselect pin, if so try to limp along...
		if (SPI.pinIsChipSelect(_dc)) {
			pcs_data = 0;
			pcs_command = pcs_data | SPI.setCS(_dc);
			pinMode(_cs, OUTPUT);
			_csport    = portOutputRegister(digitalPinToPort(_cs));
			_cspinmask = digitalPinToBitMask(_cs);


		} else {
			pcs_data = 0;

		}
	}[/COLOR]
	// toggle RST low to reset
	if (_rst < 255) {
		pinMode(_rst, OUTPUT);
		digitalWrite(_rst, HIGH);
		delay(5);
		digitalWrite(_rst, LOW);
		delay(20);
		digitalWrite(_rst, HIGH);
		delay(150);
	}
	/*
	uint8_t x = readcommand8(ILI9341_RDMODE);
	Serial.print("\nDisplay Power Mode: 0x"); Serial.println(x, HEX);
	x = readcommand8(ILI9341_RDMADCTL);
	Serial.print("\nMADCTL Mode: 0x"); Serial.println(x, HEX);
	x = readcommand8(ILI9341_RDPIXFMT);
	Serial.print("\nPixel Format: 0x"); Serial.println(x, HEX);
	x = readcommand8(ILI9341_RDIMGFMT);
	Serial.print("\nImage Format: 0x"); Serial.println(x, HEX);
	x = readcommand8(ILI9341_RDSELFDIAG);
	Serial.print("\nSelf Diagnostic: 0x"); Serial.println(x, HEX);
	*/
	beginSPITransaction();;
	const uint8_t *addr = init_commands;
	while (1) {
		uint8_t count = *addr++;
		if (count-- == 0) break;
		writecommand_cont(*addr++);
		while (count-- > 0) {
			writedata8_cont(*addr++);
		}
	}
	writecommand_last(ILI9341_SLPOUT);    // Exit Sleep
	endSPITransaction();

	delay(120); 		
	beginSPITransaction();;
	writecommand_last(ILI9341_DISPON);    // Display on
	endSPITransaction();
}
Note: the part in red is the main part for working with CS on other pin... Again other changes were for also making MISO optional...


In regards to the PUSHR stuff mentioned, yes this stuff is confusing. But the interesting stuff is with most of code you will see things like, to output a normal data byte:
Code:
KINETISK_SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0);
The interesting thing here is the variable used pcs_data -> This is used for data bytes, which normally has CS asserted and DC not asserted.

For commands: it looks more like:
Code:
KINETISK_SPI0.PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
Here we are using pcs_command which should have CS asserted AND DC asserted.

The speedup in the code is by encoding this data as part of the queue, you don't have to wait for the data to totally output (queue empty), then have your code assert or deassert the pins... But in truth this was only important with the DC pin as we only really changed the CS pin when we were typically beginning or ending a transaction, and for these we waited until the queue was empty...

Hope that helps.
 
Hi Kurt,
Thank you for your generous help, it is much appreciated. I ran jumper wires and moved CS from pin 4 to pin 9. This helped, and I was able to communicate with the display. The correct debug values weren't being returned though. So I dug through the library as best I could to figure out what was going on. I compared the adafruit library to the HX8357_t3 library.

Interestingly, the adafruit lib doesn't use the SPI.setMOSI, setMISO, setSCK, or setCS commands. The adafruit lib uses pin masks as you were talking about:
Code:
    csport    = portOutputRegister(digitalPinToPort(_cs));
    cspinmask = digitalPinToBitMask(_cs);
    dcport    = portOutputRegister(digitalPinToPort(_dc));
    dcpinmask = digitalPinToBitMask(_dc);

In the begin command for HX8357_t3 I commented out the setSCK and setMISO statements:
Code:
void HX8357_t3::begin(uint8_t type)
{
    // verify SPI pins are valid;
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
    if ((_mosi == 11 || _mosi == 7 || _mosi == 28) && (_miso == 12 || _miso == 8 || _miso == 39) 
    		&& (_sclk == 13 || _sclk == 14 || _sclk == 27)) {
	#else
    if ((_mosi == 11 || _mosi == 7) && (_miso == 12 || _miso == 8) && (_sclk == 13 || _sclk == 14)) {
    #endif	
        SPI.setMOSI(_mosi);
        //SPI.setMISO(_miso);
        //SPI.setSCK(_sclk);
	}

After that change the correct values were returned in the self test debug output. Also for some reason my display didnt like the 30 Mhz clock. It may be due to the fact that Im using jumper wires though. The display wouldnt initialize over 28 Mhz. I turned the clock down to 20 Mhz and the 'graphicstest' runs nicely.

To compare the speed of 'graphicstest' in Adafruit vs HX8357_t3, I turned the Adafruit clock up to 20 Mhz to match. Both demos were run, and the HX8357_t3 library is 20-60% faster!

Awesome!! It works!

Now back to my project, the ADC display. I'm using custom fonts that were generated for the adafruit gfx library. Unfortunately the HX8357_t3 library doesnt like the format and I'm getting compile errors. Here's what the error looks like:
Code:
/Arduino/libraries/HX8357_t3/HX8357_t3.h:289:7: note:   no known conversion for argument 1 from 'const GFXfont' to 'const HX8357_t3_font_t&'
SmartHead:730: error: no matching function for call to 'HX8357_t3::setFont(const GFXfont*)'
   tft.setFont(&Roboto_Medium15pt7b);
                                   ^
In file included from /Arduino/SmartHead/SmartHead.ino:31:0:
/Arduino/libraries/HX8357_t3/HX8357_t3.h:289:7: note: candidate: void HX8357_t3::setFont(const HX8357_t3_font_t&)
  void setFont(const HX8357_t3_font_t &f) { font = &f; }

Do you by chance know what the conversion is between adafruit fonts and the xx_t3 font? What are the possibilities to use the fonts I already customized?
 
Sorry I don't know what the differences are. It looks like the library you are using has it's own font conversion stuff, that may be different than the other ones like that was created for the ili9341_t3 library. Maybe someone else does.

As for commenting out the calls like setSCK and setMISO, these calls simply tell the system which of the pins you are using for the clock pin and MISO pin and I believe you were using the defaults, so I think most of the code checks and if you are setting it to what it already is, should not change anything... So curious what the difference is? Maybe it is not passing 11, 12 and 13 as the default SPI pins?
 
Well I have some good news to report: the fonts are the same as the ILI9341_t3 library. So for anyone else who wants to use an HX8357, the HX8357_t3 library works with teensy 3.6. Also the fonts are the same as the ILI341_t3 large repository of fonts. All you have to do is change 'ILI9341' to 'HX8357' in file names and within the font files. Success!!

Now for the slight difference in the HX8357 begin() function:
If SPI.setSCK(_sclk) & SPI.setMISO(_miso) are commented out, the serial monitor output is as follows:

HX8357 Test!
Display Power Mode: 0x9C
MADCTL Mode: 0xC0
Pixel Format: 0x5
Image Format: 0x0
Self Diagnostic: 0xC0
Benchmark Time (microseconds)
Screen fill 641261
Text 105938
Lines 165196
Horiz/Vert Lines 52078
Rectangles (outline) 28971
Rectangles (filled) 1550202
Circles (filled) 193088
Circles (outline) 143100
Triangles (outline) 35728
Triangles (filled) 501506
Rounded rects (outline) 60917
Rounded rects (filled) 1682371
Done!

If the setSCK and setMiso are left in, the serial output is:
HX8357 Test!
Display Power Mode: 0x8C
MADCTL Mode: 0xC0
Pixel Format: 0x5
Image Format: 0x0
Self Diagnostic: 0xC0
Benchmark Time (microseconds)
Screen fill 641261
Text 105940
Lines 165197
Horiz/Vert Lines 52077
Rectangles (outline) 28971
Rectangles (filled) 1550209
Circles (filled) 193093
Circles (outline) 143106
Triangles (outline) 35734
Triangles (filled) 501503
Rounded rects (outline) 60920
Rounded rects (filled) 1682382
Done!

The only difference being the display power mode. I have no idea what any of those hex codes mean, I just noted there is a difference. There were larger differences when I left the clock at 30 Mhz. I had to slow the clock down to 20 Mhz for my setup.

The times seem slightly faster too with the set statements commented out. It works either way though! I'm stoked!
 
Heya Kurt,
Quick question- what are the requirements to run the ILI9341_t3 double buffered DMA transfer library? I'm wondering, since the HX8357 was able to be modified from the ILI9341, if the same concept could be made to work with the DMA transfer library?

And is there much of a performance difference? Not needed for my current project, but thinking about it for the future!
 
The portion to be buffered has to fit in RAM.

2 bytes color per pixel and 153,600 pixels would be 307,200 for full screen buffer, larger that the 262,144 bytes of RAM on T_3.6 or similar amount on the soon to be updated T_3.5 RAM map.

With half the pixels the 320x480 ILI9341 fit in 153,600 bytes, and left some decent RAM for the whole screen and program variables.
 
Yes that is what he was saying. That on the ili9341 display we have enough memory to store the 16 bits per pixel. (2*320*240) = 153600 bytes for the frame buffer.

This display at 16 bits (2*320*480) = 307200, which as defragster mentioned does not fit well in the 256K memory of the T3.6 which is the largest memory of the current teensy line.

I wondered at times if there was a way to do it when your apps are more like mine that may only use a limited number of colors. In many cases might be less than 16, and almost for sure less than 256... So that maybe build a version that simply stores the color index (like some bitmaps are encoded).

What I don't know if it is possible with DMA is can I setup a transfer where Each pixel goes through look up table. That is:
(Next pixel - index color pallet) ---> (grab pixel from Pallet) ----> (Output actual 16 bit color to SPI)

I know I can do this programmatically as that is what the function:
void ILI9341_t3n::writeRect8BPP(int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *pixels, const uint16_t * palette )

does. If one wanted to use a frame buffer as a way to do quick full updates of data and then update the display when completed and it was OK that the update was synchronous, then it would not be difficult to update the library, to have all of the drawing functions either pass in index or have look up function that converts the color to index and then update the frame buffer which has elements or 8 bits or 4 bits... Then the call like: updateScreen would do the same thing (maybe just call the above writeRect...)

However if you want this to work Asynch then someone would need to understand how to do the DMA through look up table.
 
So I had a thought today regarding the memory limitation- what about adding some of the new spi interfaced FRAM? I've never used it but it seems interesting, and cheap. With the extra spi buses on the 3.6, would that be a viable option to increasing memory for buffering the screen?

And another thought- I dont know a whole lot about how the buffering works, but would it be possible to buffer only parts of a frame that change?
 
Regarding buffering only parts that change - that does indeed speed up rendering because only part of the screen needs to redraw, however it does not lessen the memory requirement of the buffer. Because you don't know how big the area is that changes is at any one time (it may be a tiny part of the display, or it may be the entire display) you still need to reserve a buffer that is your "worst case" - which is a buffer the same size as the display.
 
As some background - a buffer is basically a duplicate of the display, pixel by pixel, within dynamic memory. Usually it is faster to manipulate pixels in the memory buffer and then send the buffer (or the part that has changed) to the display, then it is to manipulate the pixels in the display directly. There are other advantages too, but that is the basic concept.
 
Actually it depends. In a generic case, yes any or all of the screen could change.

However if you are designing a specific app, that for example is only updating something like a smaller scrolling region of text, that you know exactly where that is on your screen and that is the only thing you want updated for a specific screen update. In these types of cases you could work with a smaller backing screen buffer. You would set it up where there was a logical clipping rectangle set to that region and everything updated outside that region was ignored and when you are done you would tell it to update that region... But as @Projectitis mentioned if you try to do it in the generic case it may case you have the same memory constraints. In theory you could do something like have buffer half the size, setup to do the top half, update, setup to do bottom half update... But I think it would look sort of screwy.

But again as I mentioned above if you don't need a full 65K colors but could live with 256 unique colors than could go with a palletted screen update, where you have the display setup as one byte per pixel, which would look up color in 256x2 byte pallet to update the screen. This should fit...

@Projectitis - An other advantage is it is easier to write the update code to avoid things like flashing. you can simply erase the old stuff, draw the new stuff, which may have pixels redrawn 5 times and when done you say update me now, so the pixels on the screen only update once.
 
Yes, there are tons of advantages, Kurt :) I just wanted to highlight the main one for Teensy. Other advantages include:
  • Faster to draw to frame buffer in dynamic memory than directly to display through a bandwidth-limited interface like SPI
  • Manipulate pixels many times (e.g. drawing all your controls to screen) before simply sending all the changes at once to the display
  • It is simpler and cheaper to read the pixels (than from hardware), so alpha blending is easier/faster
  • A framebuffer that is larger than the screen area is better for scrolling - you can build the part of the map off the screen and have it scroll in nicely without redrawing
  • Double-buffering is also possible, which eliminates flicker if you can align it with v-sync/h-sync :)
 
Status
Not open for further replies.
Back
Top