Yet another highly optimzed ILI9341 library: for T4/4.1 with diff updates and vsync !

If you are curious I thought I would show the differences in Logic Analyzer differences of running your library with your 99luft... example as the logic analyzer screen on top and running with my code in ILI9341_t3n, but again with software run DC...

Your code is showing a reasonable progression of numbers, Mine so far is not...
They both output the 0x45 have about a 3us gap then output two 0s... The one difference I see is yours is holding the clock line high between the 0x45 and the first 0 output... They both are asking for CONT bit. But maybe difference for DC... Still playing.
View attachment 22705

Isn't you code also holding the DC pin low during the 3us wait ?
Maybe that is the problem as the 0x45 command is only registered once the dc pin goes high again and that is when the wait should begin no ?
 
Isn't you code also holding the DC pin low during the 3us wait ?
Maybe that is the problem as the 0x45 command is only registered once the dc pin goes high again and that is when the wait should begin no ?

Thanks @vindar,

Actually if you look at both analyzers in the picture, the top is from your 99... sketch and it also has DC..

But: the main issue was I was shooting myself in the foot. As I mentioned in the OV7670 thread, the test board with the camera has all of the pins wired for the display and I did not set the CS pin for the touch screen high, so it was screwing around with the MISO pin... I found my code was working on test sketch on the breadboard that I setup with your wiring and code. Also worked when I moved DC to a none CS pin, and then I wired up another breadboard with the SPI pins I was using on OV... board and it worked... Then the Oops throught came to mind about the CS pins... Should have put on PD resistors on all of the CS pins...

Yesterday in a fork of the ILI9341_t3n library I added simple:
Code:
void ILI9341_t3n::setFrameRateControl(uint8_t mode) {
  // Do simple version
  beginSPITransaction(_SPI_CLOCK);
  writecommand_cont(ILI9341_FRMCTR1);
  writedata8_cont((mode >> 4) & 0x3); // Output DIVA setting (0-3)
  writedata8_cont(0x10 + (mode & 0xf)); // Output RTNA
  endSPITransaction();
}

The OV7670 CSI code sketch I added similar functions like you have in your library to measure the frame rate:
Code:
void sampleRefreshRate()
{
#ifdef USE_ILI9488
  Serial.println("sampleRefreshRate: only supported in ILI9341_t3n");
#else    
  Serial.printf("sampleRefreshRate start");
  static const uint32_t NB_SAMPLE_FRAMES = 10;
  static const uint32_t MAX_SAMPLE_LOOP_COUNT = 10000; 
  elapsedMicros em;
  uint32_t sum = 0;
  uint32_t loop_count = 0;
  uint32_t count_frame_samples = 0;
  for (count_frame_samples = 0; count_frame_samples < NB_SAMPLE_FRAMES; count_frame_samples++)
  {
    delayMicroseconds(5000); // must be less than 200 FPS so wait at least 5ms
    loop_count=0;
    while ((tft.readScanLine() != 0) && (++loop_count < MAX_SAMPLE_LOOP_COUNT)); // wait to reach scanline 0      
    if (loop_count >= MAX_SAMPLE_LOOP_COUNT) break;

    loop_count=0;
    while ((tft.readScanLine() != 1) && (++loop_count < MAX_SAMPLE_LOOP_COUNT)); // wait to reach scanline 1      
    if (loop_count >= MAX_SAMPLE_LOOP_COUNT) break;

    em = 0; // start counter at begining of scanline 1
    delayMicroseconds(5000); // must be less than 200 FPS so wait at least 5ms
    loop_count=0;
    while ((tft.readScanLine() != 0) && (++loop_count < MAX_SAMPLE_LOOP_COUNT)); // wait to reach scanline 0      
    if (loop_count >= MAX_SAMPLE_LOOP_COUNT) break;

    loop_count=0;
    while ((tft.readScanLine() != 1) && (++loop_count < MAX_SAMPLE_LOOP_COUNT)); // wait to reach scanline 1      
    if (loop_count >= MAX_SAMPLE_LOOP_COUNT) break;
    sum += em; // stop counter and add to current sum.
  }

  if (loop_count >= MAX_SAMPLE_LOOP_COUNT) {
    Serial.printf("sampleRefreshRate: loop_index:%u\n", count_frame_samples );

  } else {
    uint32_t _period = (uint32_t)round(((double)sum) / NB_SAMPLE_FRAMES);
    Serial.printf("sum:%lu count:%lu period:%lu frames per second:%u\n", sum, NB_SAMPLE_FRAMES, _period,
        (uint32_t)(1000000l / _period));
  }
#endif 
}
I did add timeouts and the like as if something goes wrong like CS pins screw up, won't have the code completely hang.

Then added simple F <number> command in the Serial input check code, that called off to:

Code:
void set_frame_rate_control() {
  #ifdef USE_ILI9488
    Serial.println("frame rate control: only supported in ILI9341_t3n");
  #else    
  uint8_t diva_rtna = Serial.parseInt();
  Serial.printf("Setting Frame Rate control to %x\n", diva_rtna);
  tft.setFrameRateControl(diva_rtna);

  // get frame rate info.
  sampleRefreshRate();

  #endif
}
The thing I notice is the image on the screen changes when I change the Frame Rate register.. Like less detail more grain? ... Back to playing
 
Thanks @vindar,

Actually if you look at both analyzers in the picture, the top is from your 99... sketch and it also has DC..

But: the main issue was I was shooting myself in the foot. As I mentioned in the OV7670 thread, the test board with the camera has all of the pins wired for the display and I did not set the CS pin for the touch screen high, so it was screwing around with the MISO pin... I found my code was working on test sketch on the breadboard that I setup with your wiring and code. Also worked when I moved DC to a none CS pin, and then I wired up another breadboard with the SPI pins I was using on OV... board and it worked... Then the Oops throught came to mind about the CS pins... Should have put on PD resistors on all of the CS pins...

Yesterday in a fork of the ILI9341_t3n library I added simple:
Code:
void ILI9341_t3n::setFrameRateControl(uint8_t mode) {
  // Do simple version
  beginSPITransaction(_SPI_CLOCK);
  writecommand_cont(ILI9341_FRMCTR1);
  writedata8_cont((mode >> 4) & 0x3); // Output DIVA setting (0-3)
  writedata8_cont(0x10 + (mode & 0xf)); // Output RTNA
  endSPITransaction();
}

The OV7670 CSI code sketch I added similar functions like you have in your library to measure the frame rate:
Code:
void sampleRefreshRate()
{
#ifdef USE_ILI9488
  Serial.println("sampleRefreshRate: only supported in ILI9341_t3n");
#else    
  Serial.printf("sampleRefreshRate start");
  static const uint32_t NB_SAMPLE_FRAMES = 10;
  static const uint32_t MAX_SAMPLE_LOOP_COUNT = 10000; 
  elapsedMicros em;
  uint32_t sum = 0;
  uint32_t loop_count = 0;
  uint32_t count_frame_samples = 0;
  for (count_frame_samples = 0; count_frame_samples < NB_SAMPLE_FRAMES; count_frame_samples++)
  {
    delayMicroseconds(5000); // must be less than 200 FPS so wait at least 5ms
    loop_count=0;
    while ((tft.readScanLine() != 0) && (++loop_count < MAX_SAMPLE_LOOP_COUNT)); // wait to reach scanline 0      
    if (loop_count >= MAX_SAMPLE_LOOP_COUNT) break;

    loop_count=0;
    while ((tft.readScanLine() != 1) && (++loop_count < MAX_SAMPLE_LOOP_COUNT)); // wait to reach scanline 1      
    if (loop_count >= MAX_SAMPLE_LOOP_COUNT) break;

    em = 0; // start counter at begining of scanline 1
    delayMicroseconds(5000); // must be less than 200 FPS so wait at least 5ms
    loop_count=0;
    while ((tft.readScanLine() != 0) && (++loop_count < MAX_SAMPLE_LOOP_COUNT)); // wait to reach scanline 0      
    if (loop_count >= MAX_SAMPLE_LOOP_COUNT) break;

    loop_count=0;
    while ((tft.readScanLine() != 1) && (++loop_count < MAX_SAMPLE_LOOP_COUNT)); // wait to reach scanline 1      
    if (loop_count >= MAX_SAMPLE_LOOP_COUNT) break;
    sum += em; // stop counter and add to current sum.
  }

  if (loop_count >= MAX_SAMPLE_LOOP_COUNT) {
    Serial.printf("sampleRefreshRate: loop_index:%u\n", count_frame_samples );

  } else {
    uint32_t _period = (uint32_t)round(((double)sum) / NB_SAMPLE_FRAMES);
    Serial.printf("sum:%lu count:%lu period:%lu frames per second:%u\n", sum, NB_SAMPLE_FRAMES, _period,
        (uint32_t)(1000000l / _period));
  }
#endif 
}
I did add timeouts and the like as if something goes wrong like CS pins screw up, won't have the code completely hang.

Then added simple F <number> command in the Serial input check code, that called off to:

Code:
void set_frame_rate_control() {
  #ifdef USE_ILI9488
    Serial.println("frame rate control: only supported in ILI9341_t3n");
  #else    
  uint8_t diva_rtna = Serial.parseInt();
  Serial.printf("Setting Frame Rate control to %x\n", diva_rtna);
  tft.setFrameRateControl(diva_rtna);

  // get frame rate info.
  sampleRefreshRate();

  #endif
}
The thing I notice is the image on the screen changes when I change the Frame Rate register.. Like less detail more grain? ... Back to playing

Ahah, I completely sympathize. I made the exact same mistake last week and spent 4 hours pulling my hair and wondering why my screen was behaving erratically. I had just moved from a breadboard to a pcb where the touchscreen shares the spi bus with the screen and had left the cs touch pin floating in my code :)

As for changing the frame rate. I agree it changes the aspect somewhat but I cannot really tell if it is better or worse. I guess it depends.

Maybe for a camera with a noisy image, having a faster framerate and and no syncing may amount, somehow, to performing some temporal averaging, similar as adding a random blur that smoothes the image a bit ?
 
The stuff I am trying to understand with tearing and knowing the scan line, is in cases like you wish to update the whole display, how much does it help?

That is: if my quick elapsedMicros output around a full screen writeRect takes about:
WriteRect time: 41051 so little over 41ms so you can get about: 24 frames per second at 30mhz.
And running at 40mhz it looks like: 30809 so about 32 frames per second...
50mhz 25687 or almost 39
60... 20571 or 48.6...

Which actually worked...

The question is if the time to refresh the frame is < time to output new frame, the output will overlap the scanline? So does it buy anything visually to break up the writeRect itnto multiple pieces that don't collide with the scan line? Again I am no expert here, just wondering how that works. In both cases you will see frames with partial data updated.
 
The stuff I am trying to understand with tearing and knowing the scan line, is in cases like you wish to update the whole display, how much does it help?

That is: if my quick elapsedMicros output around a full screen writeRect takes about:
WriteRect time: 41051 so little over 41ms so you can get about: 24 frames per second at 30mhz.
And running at 40mhz it looks like: 30809 so about 32 frames per second...
50mhz 25687 or almost 39
60... 20571 or 48.6...

Which actually worked...

The question is if the time to refresh the frame is < time to output new frame, the output will overlap the scanline? So does it buy anything visually to break up the writeRect into multiple pieces that don't collide with the scan line? Again I am no expert here, just wondering how that works. In both cases you will see frames with partial data updated.

Hi KurtE

I think the main point to notice is that you do not need the duration of a frame upload to be smaller than a refresh period but only to be smaller than (a little less than) 2 times the refresh period, which is much better !

Indeed, imagine that you start drawing just behind the scanline when it starts displaying a frame (so the scanline is a line 0). And imagine also that the scanline is moving 50% faster than your upload does, then when the screen refresh terminate (i.e. when the scanline reached 320), you will have only drawn 2/3 of the screen and the scanline will immediately start again at 0 displaying the new frame but that is OK because you will finish your upload when the scanline will be around the middle of the screen. So, for this refresh, the scanline will only 'see' the new frame! Then, you just need to wait until the scanline goes back to 0 and repeat the operation, uploading a new frame behind the scanline while it displays your previously uploaded frame a second time... With this stategy, there is never any screen tear since each refresh displays exactly one frame (and each frame uploaded is displayed twice). This is the purpose of my setVsyncSpacing() method which locks the framerate to be an integer divide of the refresh rate (but there is not advantage to setting it to any value larger than 2).

Sorry, I think I am doing a bad job at explaining this. My point is that, if you can upload full frame at more than 45 FPS (say 48FPS to be sure), then you can get tear free display with a refresh rate of 90Hz and obtain a stable framerate of 45FPS...

To illustrate this, you can try the code below but first you will need to pull the latest version of my library on github as I made some breaking changes recently. In this code, the whole framebuffer is redrawn every time as it completely changes color. Setting SPI at 60Mhz gives a frame upload speed around 48 fps which is enough to get a stable 45FPS without screen tear when setting a refresh rate around 90Hz. You can try it and then compare to what happens when you set tft.setVSyncSpacing(0) in the code (this disables vsync).

Better yet, if you screen can manage it, set the SPI at 80Mhz and use tft.setRefreshRate(120) and you will get 'tear free' full frames at 60FPS, almost like a real computer screen :)
Of course, setting the SPI that high may not be safe (I do not know really) and that is one reason I wanted to add differential updates to the library in order to increase the upload rate whenever possible.

Code:
#include "ILI9341Driver.h"

#define PIN_SCK			27
#define PIN_MISO		1
#define PIN_MOSI		26
#define PIN_DC			0
#define PIN_RESET		29
#define PIN_CS			30
#define PIN_BACKLIGHT   28  // 255 if not connected to MCU. 
#define PIN_TOUCH_IRQ	32  // 255 if not used (or not on the same spi bus)
#define PIN_TOUCH_CS	31  // 255 if not used (or not on the same spi bus)


#define SPI_SPEED		60000000


// the screen driver object
ILI9341_T4::ILI9341Driver tft(PIN_CS, PIN_DC, PIN_SCK, PIN_MOSI, PIN_MISO, PIN_RESET, PIN_TOUCH_CS, PIN_TOUCH_IRQ);


uint16_t fb[320*240];     // memory framebuffer.


void setup()
    {
    while (!Serial);

    if (!tft.begin(SPI_SPEED)) Serial.println("Oops! I did it again..");

    pinMode(PIN_BACKLIGHT, OUTPUT);
    digitalWrite(PIN_BACKLIGHT, HIGH);

    tft.setRotation(0); // portrait mode 240 x320 is the fastest mode!
    tft.setRefreshRate(90); // can be set to 120 with 80Mhz SPI
    tft.setVSyncSpacing(2); // framerate = refreshrate/2. Setting this to 0 disables vsync.
    }


int nbf = 0; // counts the number of frames drawn. 

const uint16_t RED = 31 << 11;
const uint16_t BLUE = 31;

void loop()
    {
    // draw the framebuffer full blue for even nbf and full red for odd ones. 
    for (int i = 0; i < 320 * 240; i++) fb[i] = (nbf & 1) ? RED : BLUE;

    tft.update(fb); // push the framebuffer to the screen

    if (++nbf % 1000 == 200) tft.printStats();
    }
 
Last edited:
Thanks,

I understand, and remembered that from before... That is with the turtle and the hare going around the track. We just need the turtle to advance fast enough that it does not get totally lapped ;)

I am also not sure if 60mhz is totally safe and reliable. Obviously probably not if running over jumper wires.

I probably have enough stuff in my main library to try it out and see if my psuedo video mode looks better, with a simple startup sync... Will again probably start simple and experiment.
 
Thanks,

I understand, and remembered that from before... That is with the turtle and the hare going around the track. We just need the turtle to advance fast enough that it does not get totally lapped ;)

Yes exactly :)

I am also not sure if 60mhz is totally safe and reliable. Obviously probably not if running over jumper wires.

I probably have enough stuff in my main library to try it out and see if my psuedo video mode looks better, with a simple startup sync... Will again probably start simple and experiment.

Yes, I think it should work in your case since there is no way your will upload rate will be faster than the refresh rate. In my case, differential updates can be way faster than the refresh rate so I must check regularly and slow down if needed (this is what the IntervalTimer is for, so that there is no busy wait).
Also, you will only be able to use this strategy for for orientation mode 0. Other mode will still require multiple sync and ordering correctly the uploaded pixels. But anyway, it does not work as well for other orientation mode so maybe having it for mode 0 is good enough....
 
Hi,

Just bumping this thread to say that I made quite a few optimizations to the library.
It now performs almost identically in any orientation mode. CPU usage has been significantly reduced and asynchronous updates now make use of the SPI FIFO to increase the upload rate.

@KurtE. I ported the Demosauce example from your library to see how differential updates would help there. Indeed, it gives a nice speed bump. This is especially noticeable at lower SPI speed (e.g. apart from 'plasmacloud' the demo is rather watchable with SPI@15Mhz).
 
@vindar thank you very much for this library!! I'm easily getting 60 fps with diff buffers and vsync. I put together a proof of concept with the same code that runs using your library, Kurt's ILI9341_t3n, and an opengl version here: https://pigweed-review.googlesource.com/c/pigweed/sample_project/+/28320

On device:
test2.gif

On host machine:
Peek 2020-12-30 21-00.gif
 

Attachments

  • test.jpg
    test.jpg
    13.9 KB · Views: 82
I've been using this driver for some time now for all my projects and I'm really addicted to it ;o)

Before I used the ILI9341_t3n from KurtE.
There I miss the differential updates and vSync, or I haven't found or understood them yet. Clipping areas are costly and asynchronous updates brought significant problems in my use cases - but maybe I am not smart enough to use this method.
Kurt, please don't misunderstand, your ILI9341_t3n is wonderful, but Vindar's driver is more workable for me. I don't want to question your work in any way.

Meanwhile I play with the ILI9488 Diaplay and would be very happy if I could use Arvind's driver.
I often read that porting from ILI9341 to ILI9488 is relatively easy - is that true?
But my knowledge is far from sufficient for this.

I realize that Arvind's driver would use tons of RAM for the buffers on a 320x480 display, but it still fits on the Teensy 4.x. Arvind is very busy at the moment, so I wanted to ask if someone can tell me if a port to ILI9488 can be done, even if you don't have that much experience. Thanks!
 
From the ili9488 datasheet looks like it does not support RGB565 data formatting over SPI. It uses RGB666 which would take up 3bytes per pixel (one extra byte). Some work would be needed to support that. That would make transfers slower and take up more ram.

RGB565 is supported on the 8 and 16-bit parallel interfaces though.
 
Hi,

I am bumping this old thread to mention that I made several improvements to my ILI9341 screen driver: https://github.com/vindar/ILI9341_T4.

Most importantly, thanks to user feedback, several subtle bugs have been discovered and solved. The code has now been tested quite extensively is very stable (for instance, I have been running demos on two ILI9341_T4 screens simultaneously with SPI@60Mhz continuously for 3 days without any hiccups... ).

Some new features:

  • The driver now works with screens that do not have the CS pin broken out.
  • DC and CS can now be chosen to be any pin (but still using a hardware chip select pin for dc will increase the framerate while decreasing the CPU load).
  • Full optimization: no more busy wait, maximum use of the SPI FIFO for short burst transfer and SPI interrupt / DMA for longer one. I tested transfer up to 80Mhz SPI (thanks to my new L.A. yeh :)) and the driver does not miss a beat !
  • Reduced CPU load (practically none when DC is hardware CS).
  • Reduced code size (removed useless triple buffering and put slow code in progmem).
  • Added an (optional) FPS counter.
  • Improved statistics.
  • Improved XPT2046 touchscreen calibration routine.
  • Added new examples, more detailed documentation, etc...

The driver is quite specialized and does not aim to compete with the great 'general purpose' ILI9341_t3 and ILI9341_t3n libraries. It remains very 'memory hungry' as it requires two full framebuffers (300K) in order to enable all its fancy features (but the memory can be anywhere in DMAMEM, EXTMEM... and 200K will suffice when paired with LVGL). Yet, it you can afford that memory cost, then you will likely get performance unmatched by the other ILI9341 SPI drivers...
 
Is it possible something in HardwareSerial is messing with the interrupts? The driver crashes when i send 400 characters over Serial1. It hangs in _waitUpdateAsyncComplete() indicating that somehow an interrupt is missed and _dma_state isn't set back to ILI9341_T4_DMA_IDLE.

I put up an issue https://github.com/vindar/ILI9341_T4/issues/10

Since the problem can be related to HardwareSerial, I put it here also (maybe an issue with core library?)
 
Is it possible something in HardwareSerial is messing with the interrupts? The driver crashes when i send 400 characters over Serial1. It hangs in _waitUpdateAsyncComplete() indicating that somehow an interrupt is missed and _dma_state isn't set back to ILI9341_T4_DMA_IDLE.

I put up an issue https://github.com/vindar/ILI9341_T4/issues/10

Since the problem can be related to HardwareSerial, I put it here also (maybe an issue with core library?)

Hardware Serial should not impact the driver, unless there is some form of timing issue where the display driver expects some code to be called within some period of time:

That is if you do something like: Serial1.write(my_buffer, 400);
This call will not return until all 400 characters have been at least put into the software queue for Serial1.
By default the software queue is 40 characters, plus however many have been stuffed into hardware FIFO, you are left with your code waiting in a loop
for the time it takes to output about 350+ characters over Serial1, before the call returns.

If you have additional memory left you can always add another buffer to Serial1:
Serial1.addMemoryForWrite(buffer, size);
(Note make sure the buffer is not on the stack... )
And you only need to call this once. (Last call wins)
 
I have an esp8266 connected to the teensy and i telnet into it. And if i paste over 400 characters the driver crashes. The teensy uses Serial1 to read/write to the esp.
https://github.com/MaltWhiskey/Mega-Cube/blob/master/Software/LED Display/src/main.cpp

void setup() {
// Start with clearing blue leds asap
Animation::begin();
// Serial output to usb for console display
Serial.begin(115200);
// ESP8266 UART baudrate on Hardware Serial1
Serial1.begin(460800);
// Prevents RX buffer overflow if not reading fast enough
static char read_buffer[4096];
Serial1.addMemoryForRead(read_buffer, sizeof(read_buffer));
// Prevents TX buffer overflow and blocking the program
static char write_buffer[1024];
Serial1.addMemoryForWrite(write_buffer, sizeof(write_buffer));
// Safety delay in case of code crash
delay(2000);
// Request time from Internet, the UART or Internet might fail
ESP8266::request_time();
delay(3000);
setup_lcd();
}

I have 4K buffer, so that should not fill up the read buffer…

So the way i see it, the lcd driver uses interrupts and the serial uses interrupt and they don’t play nice together…
 
Maybe... Or maybe not...

It is also unclear here from your comment about 4k buffer is that you have 4K buffer for Reads so stuff that comes in on Serial1.read().
You have a 1K for stuff that goes out...

So unclear which direction is stuff going on Serial1? Now if you receive and you are processing using ESP8266::Loop, and you are receiving 400 bytes
maybe a problem of the static String message where maybe the string class has problems with appending 400 characters...

Does the program crash? you might add in at start:

Code:
while (!Serial && millis() < 4000) ; // wait up to 4 seconds for Serial to be valid object.
if(CrashReport) Serial.print(CrashReport);

Note: I have not looked at the ILI9341_t4 code, so not sure how many interrupts he does... With ILI9341_t3n, I do most of the ASYNC stuff using DMA. I might set interrupt on frame complete and frame half complete, but again usually not much overhead there.
 
The string is not the problem (i know it doesn’t crash tested extensively with much larger data before).
But just to eliminate the “noise” i replaced the yield with just read write. So for sure the Serial Interrupt
Must be interfering. I’m not writing anything over Serial1, just reading (empty the buffer) nothing more.

void loop() {
// Print FPS once every x seconds
static Timer print_interval = 1.0f;

//Animation::animate();
loop_lcd();

if (print_interval.update()) {
static char fps[20];
sprintf(fps, "FPS=%1.2f", Animation::fps());
Serial.println(fps);
}
}

// Check custom serial events (yield gets called in ILI9341_T4Driver)
void yield() {
while (Serial1.available()) Serial1.read();
// ESP8266::loop();
}
 
Hi,

The problem was (obviously) with my code: the interrupt flag was getting clear too late when another interrupt with higher priority was delaying to SPI interrupt code, resulting in the next interrupt never getting called... The bug should now be fixed.

On a related note, I just added a new `setIRQPriority()' method which allows choosing the priority at which all the ILI9341_T4 driver interrupts will run (DMA, Timer and SPI). Default value is 128 but one can increase this value (i.e. lower the priority) to insure that some other code/device always get served first. On the other hand, increasing the priority can possibly improve/stabilize the framerate in some particular cases...
 
Hi,

I am resurrecting this old thread to mention some improvements to the ILI9341_T4 library. It can now work with any pin configuration (no more special requirements for DC) and it can also work with display without a CS or a MISO pin (or both missing!). Of course, if MISO is missing, vsync will be disabled but all other features such as DMA upload and differential updates will still work.

On a related note, I adapted the library to work with ILI9342(C). The link is here: https://github.com/vindar/ILI9342_T4

It has the all the same features as the original library but uses half-duplex SPI communication (the ILI9342 controller uses a SDA line instead of the usual MISO/MISO for bidirectional communication). That is one less wire to connect ;)
 
Back
Top