NT35510_t4x_p - a parallel display driver for Teensy 4.x boards

KurtE

Senior Member+
Sort of place holder to be edited...

Decided that probably should split this off from the the thread:
Thought I would mention that @mjs513 and I have made some progress on the NT35510:

Current stuff is up at: https://github.com/KurtE/NT35510_t4x_p

At the moment it only supports 8 bit Parallel, using 16 bit color (RGB565)

I am currently testing it using the Dev Board 4.5 and I believe that @mjs513 is
testing it using a Teensy 4.1. We are both using the BuyDisplay board mentioned above.

I currently have another display ordered on Ebay that is configured for 16 bit transfers.
This will probably take a couple of weeks to arrive.

Here is the clip and the like test, showing in this case update using DMA. The Frame buffer is located in SDRAM as
it is larger than can fit into DTCM or DMAMem.
View attachment 35320

Here is showing the MTP TFT viewer of the image. Again using SDRAM to upload the whole image at once...

View attachment 35321
Will probably do likewise for HX8357 and not should if we should for ILI948x as well?

The current WIP code is up at:


Like these other drivers mentioned it use the library:


Again WIP
 
Currently I am experimenting with 24 bit color as well as using either 8 bit or 16 bit buss_width.

The current code has so far only been tested on some BuyDisplay displays that were configured for 8 bit bus:
I believe it is this one:

I ordered another one on Ebay for a 16 bit version, which was delivered to my mailbox in town.

As mentioned in the other thread, I have some limited support for 24 bit. Most of the code should hopefully
map the 16 bit apis to 24 bit. And there are a couple of new APIS that allow you to output in 24 bit mode (writeRect, fillRect).
You can see some of the output difference on the other thread:
I hear you... I was curious if one might see some differences or not, so did a quick and dirty sketch, that draws all of the color ranges
top half using color565 and bottom half 888.
C++:
#include <Teensy_Parallel_GFX.h>
#include "NT35510_t4x_p.h"

#define NT35510X NT35510
#define NT35510X_SPEED_MHZ 20

#ifdef ARDUINO_TEENSY41
#define TFT_DC 10
#define TFT_CS 8
#define TFT_RST 9
#elif defined(ARDUINO_TEENSY40)
#define TFT_DC 0
#define TFT_CS 1
#define TFT_RST 2
#elif defined(ARDUINO_TEENSY_DEVBRD4) || defined(ARDUINO_TEENSY_DEVBRD5)
extern "C" bool sdram_begin(uint8_t external_sdram_size, uint8_t clock, uint8_t useDQS);
#define TFT_DC 10
#define TFT_CS 11
#define TFT_RST 12
#else  // micromod?
#define TFT_DC 4
#define TFT_CS 5
#define TFT_RST 3
#endif
NT35510_t4x_p tft = NT35510_t4x_p(TFT_DC, TFT_CS, TFT_RST);  //(dc, cs, rst)


#ifndef BLUE
#define BLUE 0x001F
#define BLACK 0x0000
#define WHITE 0xFFFF
#define GREEN 0x07E0
#define RED 0xf800
#endif

void setup(void) {
    while (!Serial && millis() < 3000)
        ;

    Serial.println("*** start up NT35510 ***");
    tft.begin(NT35510X, NT35510X_SPEED_MHZ);
    tft.setBitDepth(24);
    tft.setRotation(1);

    tft.fillScreen(RED);
    delay(500);
    tft.fillScreen(GREEN);
    delay(500);
    tft.fillScreen(BLUE);
    delay(500);
}

void fillScreenOneColorRange(uint32_t color_start, uint32_t color_end) {
    uint8_t r, g, b;
    uint8_t rs, gs, bs, re, ge, be;
    tft.color888toRGB(color_start, rs, gs, bs);
    tft.color888toRGB(color_end, re, ge, be);
    for (uint16_t i = 0; i < 256; i++) {
        r = rs + (((uint32_t)(re - rs)) * i) / 256;
        g = gs + (((uint32_t)(ge - gs)) * i) / 256;
        b = bs + (((uint32_t)(be - bs)) * i) / 256;
        tft.fillRect(i * 3 + 16, 1, 3, 239, tft.color565(r, g, b));
        tft.fillRect24BPP(i * 3 + 16, 240, 3, 239, tft.color888(r, g, b));
    }
}

uint32_t colors[] = { tft.color888(255, 0, 0), tft.color888(0, 255, 0), tft.color888(0, 0, 255),
                      tft.color888(255, 255, 0), tft.color888(255, 0, 255), tft.color888(0, 255, 255), tft.color888(255, 255, 255) };
uint8_t color_index = 0xff;

void loop() {
    Serial.println("Press any key to continue");
    while (Serial.read() == -1) {}
    while (Serial.read() != -1) {}
    color_index++;
    if (color_index == (sizeof(colors) / sizeof(colors[0]))) color_index = 0;
    fillScreenOneColorRange(tft.color888(0, 0, 0), colors[color_index]);
}
View attachment 35382

I also just pushed up some initial changes for me to start trying it on 16 bit bus.

I have not tested any of it yet, except did rerun the sketch above with current code and it still looks like it works the same.

Also I have not yet made any of the changes to support the Async code. Will first debug some of the basic things, like can I draw anything? Both in RGB565 mode and in RGB8888 mode.

Once that works, I will most likely then try to get async to work first with Interrupts, as currently I believe the only shipping Teensy that could support this is T4.1 and that is on FlexIO3 which does not support Interrupts.

Once that works, I will then move it to one of the Dev Boards and try to get DMA to work, assuming that no one else beats me to it.
 
Sorry, I know,mostly talking to myself 😀

i now have the 16 bit bus display, connected to T41, it is on left, the BuyDisplay one on right (8 bit),
IMG_0697.jpeg

color band 565 vs 888 on both running 24 bit
IMG_0698.jpeg

running my clip… test 16 bit color shows still some stuff to fix before async stuff
IMG_0699.jpeg
 
Very glad to see this thread and work done so far!
Have you made any 16bit bus DMA progress? Would really like to test it out next week
 
I have not done much yet with the 16 bit DMA. The 16 bit one I have now is hooked up to T4.1... I have a couple more Buydisplay ones ordered and shipped (one 16 bit the other 18 bit) waiting to transfer to USPS... How many days it will take this time???

I have been working on 32 bit frame buffer and now trying to work the async code with IRQ on Flexio3... Hopefully in the next few days...
 
Right not I am contemplating if the 32 bit frame buffer is the right way to go for 24 bit color, or if I would be better off with packing it to 24 bits per pixel... Probably would make it easier to support DMA and IRQ support.

For example, when you output pixels to the display with a 16 bit data bus,
The data output is packed like: R1G1 B1R2 G2B2 R3G3.... So not sure if there is an easy way to tell it to ignore the high byte of each 32 bit pixel...
Likewise for simple 8 bit data bus, where when you setup the multi-beat, again it would be setup more or less with the same data, like
FlexIO buffer 0 with r1g1b1r2 next one with g2b2r3g3 ....
 
Quick update: My two new NT35510 displays from BuyDisplay arrived yesterday.
One is configured with 16 bit bus and the other with 18 bit bus.

I now have the 16 bit running on the DB5. I have a different brand 16 bit one that was earlier running on T41
On the DB5, I have now done some hacking on the FlexIO DMA code and have it at least limping along in 4 different
configurations:
8 bit bus, RGB565 and RGB888
16 bit bus, RGB565 and RGB888

Which I have pushed up to my fork/branch...

Probably lots of things to fix...

Now when I get brave, I will try to setup 18 bit mode, hoping that maybe it can mostly be handled by the 24 bit mode, where I keep 3 bytes
per pixel. However the FlexIO stuff might be interesting when I define D16 and D17 as then only one pixel per flexio buffer, it may turn out in this case having 32 bits of ram per pixel might be easier. ...
 
@KurtE This is awesome to see! It's been a couple of years since I played around with the BuyDisplay ER-TFTM040-1 and my 8/16 bit driver code for the NT35510 controllers, but one little useful hardware hack is to wire a switch to flip J1 and J5 between open and short, so you can use one screen in either 8 or 16 bit mode at the flip of a switch (and re-compile/re-boot). As you progress, I might dust off my Teensy/screen/code and give it a whirl!
 
Thanks,

One thing I am trying to figure out is, how does the Buy display device work in 18 bit mode?
Sorry in advance, I am simply thinking out loud.

That is: with the Novatek Data Sheet, probably the most complete one I have found is up at:

The interface pins mention: P17
24-bit bi-directional data bus for 80-series MPU I/F and 24-bit input data bus for RGB I/F.
For 8080-series MPU I/F:
8-bit interface: D[7:0] are used, D[23:8] should be connected to VSSI
16-bit interface: D[15:0] are used, D[23:16] should be connected to VSSI
18-bit interface: D[17:0] are used, D[23:18] should be connected to VSSI
24-bit interface: D[23:0] are used
These pins are not used for 16-bit SPI, I2C, MIPI or MDDI I/F, please connect to VSSI these
pins.

But then P28 mentions:
1725572377322.png


No mention of 18 bit parallel.

The actual output format is: set by COLMOD
1725572691740.png


Wonder... Looking at the BuyDisplay
Yep maybe, looking at the Jumpers:
1725572888143.png

Same Jumpers for 18 bit and 24 bit interface...
Will try it...
 
I have a Novatech Datsheet v0.8, dated 10/28/2011, that was the latest I could find, plus another sister document "NT35510-Application Notes" v0.07 which was helpful for setup commands, I recall. The relevent sections you copied don't appear to differ.

The documentation for 18 bit modes is confusing, possibly due to translation? In some places it seems to indicate the parallel interface can use 18 bit data lines (but doesn't document an 18 bit parallel interface, only how to send 18 bit color data over 8/16/24 bit data lines), in other places it seems to indicate 18 bit data lines is for the RGB interface, and in others it is indeterminate if they are refering to 18 bit color data or an 18 bit interface! It hurts my head, and brings back bad memories trying to write this with no prior NT35510 art 3 years ago :)
 
I have a Novatech Datsheet v0.8, dated 10/28/2011,
That is the same version I downloaded from Waveshare...

18 bit color == PIA ;)

I Found I did not make allowances in the DB5 shield I made for > 16 data lines, and used the next ones D16 and D17 for the CS pin and RD pin...

So I hacked it up this morning by stacking in a connector where I bent out those two pins and jumpered those to other IO pin and then also added the jumpers for 16 and 17

I have also done some updates (not checked in) to library to handle 18 bits in many of the places and have some things working:
1725642993031.png


Currently, I am trying to figure out how to get reading pixels to work.
I believe it is trying to return the pixels with three bytes of data, like it does with different data buses.
However, there are only 18 data pins, so I think I am getting the full G and B bytes but only the low 2 bits of Red.
Plus maybe some other issues with it as well.

I may try some hack to see what it will do. That is what if I change the color mode to 565, will it return the pixels in multiple buffers?

Or I may defer it and try to get the DMA update to work.
 
Cannot recall from memory, but looking at the docs, which states "D[23:0] is read back from the frame memory" and my own (working) code to read the display memory:

C++:
#if (TFT_BUSWIDTH == 8)
      uint8_t r = readBus();
      uint8_t g = readBus();
      uint8_t b = readBus();
#else //16 bit bus
      uint16_t c1 = readBus();
      uint16_t c2 = readBus();
      uint8_t r = (uint8_t)(c1 >> 8);
      uint8_t g = (uint8_t)(c1 & 0xFF);
      uint8_t b = (uint8_t)(c2 >> 8);
#endif

then you may have to do two bus reads to get 18 + 6 bits and derive the r,g,b from those? I also don't think color mode affects the read format, I have no special handling based on color mode and the docs don't indicate that.
 
Last edited:
Thanks, I have similar code that does as you mentioned. It either takes 3 reads or two...

But I believe the issue is that as far as the display is concerned it has 24 bits to work with as it has the
same hardware Jumper settings as their 24 bit version and so it tries to output the whole color in one write...
 
Thanks, I have similar code that does as you mentioned. It either takes 3 reads or two...

But I believe the issue is that as far as the display is concerned it has 24 bits to work with as it has the
same hardware Jumper settings as their 24 bit version and so it tries to output the whole color in one write...
Not quite the same configuration in 18 vs 24 bit.....in 8, 16 and 18 bit mode R40-R45 are shorted (0R) but in 24 bit mode are open.....not entirely sure what those resistors do though, but if you map out the ones set to 0R vs the bitdepth in the doc (and exclude the ones that are set in each instance parallel 8/16/18/24 bits), you get:


Screenshot 2024-09-08 at 1.18.39 PM.png


Which gives a clear picture, I think the 0R resistors pulls the unused data lines to VSSI, per the docs..., so maybe you need to short R40-R45 too (or open R1 and R2, not sure what config you purchased)?
 
I purchased the 18 bit one which has those shorted out... I assume they specify being to VSSI as to maybe limit noise
coming in through them... Being 0k, I am assuming no signal and not just Pulled down...
 
Quick update: I think I sort of confirmed with BuyDisplay, that that NT35510 configured for 18 bits, will not allow you to
read the pixel data back. Although unclear if they understood what I was asking.

That is I mentioned, that, I was able to output to the display with COLMOD=66 (18 bits), but that the Reading of memory always
returns the pixels colors as 24 bits.
And hardware (Jumpers) wise there is only 3 variations:
8 bit (hardware BUS), 16 bit hardware, and 24 bit hardware...
The 8 bit returns: data as three reads
The 16 bit returns the data in two reads
The 24 returns the pixel in one read.

There is no set of hardware jumpers for 18 bits, so they actually are setup for 24 bit. So...

For myself, not sure I will take the 18 bits much farther. That is on the T4.1 we can setup for 18 bits RGB666, but
not sure having one extra bit for R and B is worth the issues involved.

Edit: Changed G to B. G has 6 bits
 
Last edited:
The specs for writing to the display in 24 bit parallel interface mode for COLMOD = 666 is to send data on D0-D17, so I think it's fair to say, based on your research and experiments, there is no specific 18 bit parallel interface mode, but you can write to the display in 24 bit parallel interface mode configuration using 18 bit color on 18 data lines.....

The 1 extra bit for R and B boosts color counts to 262k from 65k, so there would be use cases where this is advantageous. Based on your research, when I get around to revisiting my 'kitchen sink' device project, you've inspired me to juggle my pin usage and reconfigure/remake my boards to free up D16 and D17 from FlexIO3 to divert to the screen!
 
The 1 extra bit for R and B boosts color counts to 262k from 65k, so there would be use cases where this is advantageous. Based on your research, when I get around to revisiting my 'kitchen sink' device project, you've inspired me to juggle my pin usage and reconfigure/remake my boards to free up D16 and D17 from FlexIO3 to divert to the screen!
Mine is also a Kitchen sink!

Today I got up and thought it might be interesting to try out the capacitive touch screen, that I purchased on the 8bit one, did not have include one on the 16 or 18... But I also believe it is the same touch controller on the ILI9488 displays I purchased and I think I have it on both 8 and 16...
(FT6236).

So I decided to hack up a touch paint. sketch. Started off with the example sketch from the
Adafruit_FT6036 library, and edited it up to use this library instead, plus scale it up, as the example sketch was for ILI9431 (320x240)
and we are 800x480.

IMG_0705.jpeg

However I ran into some issues, like while the Adafruit library says it is compatible with the FT6236, the begin method, looks for
a specific vendor ID as well as a specific chip ID
Code:
#define FT62XX_VENDID 0x11  //!< FocalTech's panel ID
#define FT6206_CHIPID 0x06  //!< Chip selecting
#define FT6236_CHIPID 0x36  //!< Chip selecting
Which does not match:
Code:
Vend ID: 0x1A
Chip ID: 0x64
Firm V: 3

For now I commented out the check and it works. I may raise an issue with Adafruit, but we do have other options.
Like: Back in the RA8875 integration, we ran into similar issues with another FT5... that SumoToy had his own library for.
There were some issues with that which we fixed.... Also I believe we integrated some optional calls into the RA8875 library.

Question is, wondering which approach we should take here?
 
Still experimenting with the touch library... Worst case scenario, I could simply use an existing library, but ...

Currently if you specify an Interrupt pin, I will return false if I have not received an interrupt. However once I receive one, I will continue to
then check with asking Wire object for data, until one returns that I am no longer touching, in which case I clear the interrupt happened flag...
Also I left the ISR active..

But believe that maybe I should disable interrupt, and again wait for next interrupt before asking (Wire...) for the new data.
Actually, I am thinking of doing a hybrid version, where once I get an Interrupt I will disable the Interrupt, do the Wire transfer. re-enable ISR
If calling code calls again in this state, I will continue to return the old data, for maybe up to some TO or new Interrupt. New interrupt ask for
data. TO, return false, fully clear state.

Why? Spec does not give a very precise description of how many individual reports it will return per second:
1726937011619.png

Running one of the ILI9486 ones from Buy display, I see about 89 hz:
1726937080402.png

Question to self (and others) is, if I were to ask multiple times for data in between these interrupts, would it always still return the
same data...

Guess easiest answer is to experiment...
 
Back
Top