Highly optimized ILI9341 (320x240 TFT color display) library

I didn't get into what went wrong as note. But I did put in a proper :: while ( !Serial && (millis() < 2000)) ; Although I did not see your infinite loop version. I did have Serial active - if it was connecting as I thought it was but was not running right last night! { this is a recurring issue Win 10? IDE 1.6.6? )

UPDATE: It does seem to be working today! - mostly

Regularly and reliably on my T_3.1 (using PJRC's OSH adapter)!

<EDIT>
The problem is apparently in my hand wired adapter board (w/rtc battery and weeks on handling hard wired proto board)! Moved display and swapped 3.1 and 3.2 and the 3.2 working fine on PJRC OSH board with other display. Time to build out more PJRC OSH boards now that I have DigiKey parts for power.

For some reason my T_3.2 unit - hand wired differently but acts the same on my other work::
Test loop #1 - after printing Line #3 it blacks the area above then scrolls the text in just the bottom row starting with line #4
Test loop #2 - after printing Line #14 it blacks the area above (except some of the pixels on the left edge)then scrolls the text in just the bottom row starting with line #15
<edit this unit has the SD adapter wired - I pulled those wires and the T_3.2 board bad behavior persists>
 
Last edited:
I think the added functionality is worth the code space! RAM hit was nothing. Having a way to scroll data off reliably - especially in a windowed area looks useful.
 
@vitormhenrique
I was wondering if it would work having multiple active scrollers on the screen at once and static text as well.

I crafted this functional example that shows what I mean: View attachment scrollTest.ino

To do this I needed to rejoin a scroller in place it needed to not re-fill the rectangle so I created: resetScrollBackgroundColor().

That change to ILI9341.cpp without fillRect() is:
Code:
void ILI9341_t3::resetScrollBackgroundColor(uint16_t color){
	scrollbgcolor=color;
}

And .h:
Code:
	void resetScrollBackgroundColor(uint16_t color);

Running this works fine while exercising some of the code well.

Updated sketch to abuse all 'scroll' functions.
 
Last edited:
@vitormhenrique
I was wondering if it would work having multiple active scrollers on the screen at once and static text as well.

Cool! I did not think of that! :)

Did you figure it out the issue with Teensy 3.2? I do not have one to test. Can you post a picture of the issue? Thanks!
 
Cool! I did not think of that! :)
Thanks!

You did good with what you implemented as far as I can tell. I'm thinking of BUTTON GUI type usage and revisiting the scroller seemed critical.

The issue was not Teensy specific - nor the display. I'm running 100% perfect for uploads and operation with the code posted above - right now using the T_3.2. Left it running the past 8+ hours exercising all the xxSCROLLxx() functions I saw and it is GOOD!

The problem is somewhere in the one sided PCB I soldered up about 6 weeks ago and have picked up and put down 100's of times and triggered the Teensy Button dozens+ of times and used for the TOUCH display for thousands of presses. I put on the touch display - wired up to the SD card slot and soldered on an RTC battery. There must be some unsure connection or some wire effect.
 
Last edited:
From my prior sample I functionalized the process of writing to a scroll window. Updated samples attached. it looks like this:
Code:
struct TFT_SCROLL {
  int16_t xx, yy, ww, hh;
  uint16_t fgc, bgc;
  int16_t  cxx, cyy;
  uint8_t textsize;
};
TFT_SCROLL sc1 = { 30, 20, 120, 210, ILI9341_BLACK, ILI9341_GREEN, 30, 120, 1 };

setup() {
  scrollInit( sc1 );
}
loop () {
  scrollBegin( sc1 );
  tft.print("this is line "); tft.println(ii);
  scrollEnd( sc1 );
}

Code above from simple sample:
View attachment MinScroll_dec03a.ino

First sample updated:
View attachment scrollTest2.ino
 
@vitor - I added two more scrolls to my sample and started getting :: Serial.println(cursor_x);
In ili9341.cpp it is line #861.

Running without flaw or problem on 4 simultaneous scroll sub windows! [ with other active prints ]
View attachment scrollTest2b.ino

<edit>: here is a single file sketch - dropped extra font - runs 4 scrolling windows at the same time [odd lines, even lines, millis, delay value wait between updates] , with static text of millis written as well, a busy char rotator.
> it will need Victor's lib version with my added tft.resetScrollBackgroundColor function to compile and run
 
Last edited:
@victor - have you run all the other samples? I just ran the Original GraphicsTest and it runs okay - but maybe slower than 4/24/15 linked bottom - but that goes for both builds I did.
If you update your code per my notes I'll start using it as I go back to my Touch buttons - I like what it offers.

<edit> : Found Frank's post 8/28/15 and these numbers are at or under - so slower than the 4/24 - but don't see that the SCROLL checking hurt anything.

@FrankB - did your speedup get pulled in?

This was with the 1.26 ILI9341_t3
ILI9341 Test!
Display Power Mode: 0xDE
MADCTL Mode: 0x6C
Pixel Format: 0x7
Image Format: 0x0
Self Diagnostic: 0xE0
Benchmark Time (microseconds)
Screen fill 281179
Text 17577
Lines 73217
Horiz/Vert Lines 23059
Rectangles (outline) 14628
Rectangles (filled) 584892
Circles (filled) 91850
Circles (outline) 76485
Triangles (outline) 17743
Triangles (filled) 194804
Rounded rects (outline) 34328
Rounded rects (filled) 639590
Done!

This was with the MODIFIED 1.26 ILI9341_t3
Benchmark Time (microseconds)
Screen fill 281180
Text 17578
Lines 73217
Horiz/Vert Lines 23060
Rectangles (outline) 14628
Rectangles (filled) 584879
Circles (filled) 91849
Circles (outline) 76484
Triangles (outline) 17743
Triangles (filled) 194802
Rounded rects (outline) 34320
Rounded rects (filled) 639593
Done!

The results shown in this 4/24/15 post though look faster?
Benchmark Time (microseconds)
Screen fill 224084
Text 13680
Lines 58589
Horiz/Vert Lines 18374
Rectangles (outline) 11686
Rectangles (filled) 465324
Circles (filled) 69758
Circles (outline) 57633
Triangles (outline) 14183
Triangles (filled) 153957
Rounded rects (outline) 26517
Rounded rects (filled) 507952
Done!
 
Last edited:
@victor - have you run all the other samples? I just ran the Original GraphicsTest and it runs okay - but maybe slower than 4/24/15 linked bottom - but that goes for both builds I did.
If you update your code per my notes I'll start using it as I go back to my Touch buttons - I like what it offers.

Updated the github with resetScrollBackgroundColor, removed the println that I forgot. Thank you defragster!

No idea why the library got slower, but for me the difference was acceptable.

Should I push it to Paul's github?
 
Thank you defragster!

No idea why the library got slower, but for me the difference was acceptable.

Should I push it to Paul's github?

:) - Glad to see this working. I think Paul should give it a look and consider a pull. If you watch the movie it shows how it might make for fast easy and compact data presentation. I've done semi-non-trivial usage of it and it looks good and valuable. Would be nice to have a report from another user who counts on the ILI9341 to say it doesn't break anything for them.

Scroller Movie on OneDrive
<edit: I had to watch the movie in Firefox and it worked - my IE has been adjusted to not do some active content>

The Slowdown is sometime before August - and happily I don't see any slowdown from the scroll additions. And the movie shows it runs pretty quickly. The lower right corner shows the delay time between each iteration of the "for (int ii = 0; ii < 40; ii++)". It has been running overnight so the lower left ms has naturally jumped to two lines.

Is there any general value in having this scrollTextArea() take a signed 16 bit number for up or down scrolling - or by some other means scrollTextAreaDown()?
 
Last edited:
@vitor : got your latest https://github.com/vitormhenrique/ILI9341_t3

My sample compiles and runs with that in place of my edits. I added another pair of scroll's tracking the time to do display updates - it shows 1ms without big scrolls and up to 60ms when the one does a double line scrolls - so now there are 6 active on screen plus 3 other dynamic text elements.
View attachment scrollTest2b.ino

20151204_144443_HDR.jpg
 
Last edited:
For the foreground color - if set uniquely on entry the text will show in that color to point out 'special values' in the scrolling list!

<edit> the background color change was an unsupportable use case. The background only changes to fill the vacated scroll area and works perfectly well, but not to highlight an entry on demand.
 
Last edited:
Just FYI, after running the sample code the transparencies and fonts are messed up on my Ubuntu laptop running KDE. I had to reboot to make things work again. I'm using a Teensy 3.1. I'm new to this library and am trying to get some code working so I'll see if I can isolate what's triggering this problem while I do so.

I'm experiencing something else weird too. Perhaps someone can help me out. I'm using the ILI9341_t3 library but it doesn't seem to be seeing getCursorX() and getCursorY(); Both of these are inline code in ILI9341_t3.h. I tried changing the calls to conventional methods but continued to get the build errors. It seemed to me like my changes are being ignored in the Arduino environment so I put a garbage string in the header which should have thrown an error but there was no effect.

I figured that the problem has something to do with caching so I did some digging and found some articles that say cached files are in /tmp/build*. I deleted those directories, rebooted and the behavior remains unchanged.

I'm using 1.0.6 on Ubuntu 14.04. I've also got 1.6 installed but it looks like it's not supported by teensy yet and I hate to install yet another version if I don't need to.

Suggestions anyone?
 

Attachments

  • cover shot3.png
    cover shot3.png
    258.2 KB · Views: 287
Last edited:
That is an impressive looking screen on the 320x240 display?

Teensy 1.26 supports IDE 1.6.6 > supported 1.6.0 some days after it was released. You may have an old library being picked up - 1.6.6 will tell you about that, and 1.26 will make sure you have the latest sources.
 
Moving to the new version of the Teensy software seems to have fixed the problem with my laptop's display getting messed up too. I have no idea how that was happening.
 
I am having a problem with the latest version of arduino 1.66 and the latest teensyduino. I downloaded the latest ILI9341 libs and when I try to compile the graphicstest program in the examples, I get a ton of errors. The Adafruit version works fine with my LC, so I know everything's connected right.

Here are the errors: I may be missing something boneheaded, but given the success with the Adafruit, I'm stumped.

Compiling 'graphicstest' for 'Teensy LC'
ILI9341_t3.cpp:In file included from
ILI9341_t3.h:In member function 'void ILI9341_t3::waitFifoNotFull()
ILI9341_t3.h:216:9: error: 'KINETISK_SPI0' was not declared in this scope
:sr = KINETISK_SPI0.SR
ILI9341_t3.h:In member function 'void ILI9341_t3::waitFifoEmpty()
ILI9341_t3.h:224:9: error: 'KINETISK_SPI0' was not declared in this scope
:sr = KINETISK_SPI0.SR
ILI9341_t3.h:In member function 'void ILI9341_t3::waitTransmitComplete()
ILI9341_t3.h:230:12: error: 'KINETISK_SPI0' was not declared in this scope
:while (!(KINETISK_SPI0.SR & SPI_SR_TCF)) ; \\ wait until final output done
ILI9341_t3.h:230:31: error: 'SPI_SR_TCF' was not declared in this scope
:while (!(KINETISK_SPI0.SR & SPI_SR_TCF)) ; \\ wait until final output done
ILI9341_t3.h:231:9: error: 'KINETISK_SPI0' was not declared in this scope
:tmp = KINETISK_SPI0.POPR; \\ drain the final RX FIFO word
ILI9341_t3.h:In member function 'void ILI9341_t3::waitTransmitComplete(uint32_t)
ILI9341_t3.h:236:18: error: 'KINETISK_SPI0' was not declared in this scope
:uint32_t sr = KINETISK_SPI0.SR
ILI9341_t3.h:237:13: error: 'SPI_SR_EOQF' was not declared in this scope
:if (sr & SPI_SR_EOQF) break; \\ wait for last transmit
ILI9341_t3.h:240:3: error: 'KINETISK_SPI0' was not declared in this scope
:KINETISK_SPI0.SR = SPI_SR_EOQF
ILI9341_t3.h:240:22: error: 'SPI_SR_EOQF' was not declared in this scope
:KINETISK_SPI0.SR = SPI_SR_EOQF
ILI9341_t3.h:241:3: error: 'SPI0_MCR' was not declared in this scope
:SPI0_MCR = mcr
ILI9341_t3.h:In member function 'void ILI9341_t3::writecommand_cont(uint8_t)
ILI9341_t3.h:247:3: error: 'KINETISK_SPI0' was not declared in this scope
:KINETISK_SPI0.PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT
ILI9341_t3.h:247:67: error: 'SPI_PUSHR_CTAS' was not declared in this scope
:KINETISK_SPI0.PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT
ILI9341_t3.h:247:71: error: 'SPI_PUSHR_CONT' was not declared in this scope
:KINETISK_SPI0.PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT
ILI9341_t3.h:In member function 'void ILI9341_t3::writedata8_cont(uint8_t)
ILI9341_t3.h:251:3: error: 'KINETISK_SPI0' was not declared in this scope
:KINETISK_SPI0.PUSHR = c | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT
ILI9341_t3.h:251:64: error: 'SPI_PUSHR_CTAS' was not declared in this scope
:KINETISK_SPI0.PUSHR = c | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT
ILI9341_t3.h:251:68: error: 'SPI_PUSHR_CONT' was not declared in this scope
:KINETISK_SPI0.PUSHR = c | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT
ILI9341_t3.h:In member function 'void ILI9341_t3::writedata16_cont(uint16_t)
ILI9341_t3.h:255:3: error: 'KINETISK_SPI0' was not declared in this scope
:KINETISK_SPI0.PUSHR = d | (pcs_data << 16) | SPI_PUSHR_CTAS(1) | SPI_PUSHR_CONT
ILI9341_t3.h:255:64: error: 'SPI_PUSHR_CTAS' was not declared in this scope
:KINETISK_SPI0.PUSHR = d | (pcs_data << 16) | SPI_PUSHR_CTAS(1) | SPI_PUSHR_CONT
ILI9341_t3.h:255:68: error: 'SPI_PUSHR_CONT' was not declared in this scope
:KINETISK_SPI0.PUSHR = d | (pcs_data << 16) | SPI_PUSHR_CTAS(1) | SPI_PUSHR_CONT
ILI9341_t3.h:In member function 'void ILI9341_t3::writecommand_last(uint8_t)
ILI9341_t3.h:259:18: error: 'SPI0_MCR' was not declared in this scope
:uint32_t mcr = SPI0_MCR
ILI9341_t3.h:260:3: error: 'KINETISK_SPI0' was not declared in this scope
:KINETISK_SPI0.PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ
ILI9341_t3.h:260:67: error: 'SPI_PUSHR_CTAS' was not declared in this scope
:KINETISK_SPI0.PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ
ILI9341_t3.h:260:71: error: 'SPI_PUSHR_EOQ' was not declared in this scope
:KINETISK_SPI0.PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ
ILI9341_t3.h:In member function 'void ILI9341_t3::writedata8_last(uint8_t)
ILI9341_t3.h:264:18: error: 'SPI0_MCR' was not declared in this scope
:uint32_t mcr = SPI0_MCR
ILI9341_t3.h:265:3: error: 'KINETISK_SPI0' was not declared in this scope
:KINETISK_SPI0.PUSHR = c | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ
ILI9341_t3.h:265:64: error: 'SPI_PUSHR_CTAS' was not declared in this scope
:KINETISK_SPI0.PUSHR = c | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ
ILI9341_t3.h:265:68: error: 'SPI_PUSHR_EOQ' was not declared in this scope
:KINETISK_SPI0.PUSHR = c | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ
ILI9341_t3.h:In member function 'void ILI9341_t3::writedata16_last(uint16_t)
ILI9341_t3.h:269:18: error: 'SPI0_MCR' was not declared in this scope
:uint32_t mcr = SPI0_MCR
ILI9341_t3.h:270:3: error: 'KINETISK_SPI0' was not declared in this scope
:KINETISK_SPI0.PUSHR = d | (pcs_data << 16) | SPI_PUSHR_CTAS(1) | SPI_PUSHR_EOQ
ILI9341_t3.h:270:64: error: 'SPI_PUSHR_CTAS' was not declared in this scope
:KINETISK_SPI0.PUSHR = d | (pcs_data << 16) | SPI_PUSHR_CTAS(1) | SPI_PUSHR_EOQ
ILI9341_t3.h:270:68: error: 'SPI_PUSHR_EOQ' was not declared in this scope
:KINETISK_SPI0.PUSHR = d | (pcs_data << 16) | SPI_PUSHR_CTAS(1) | SPI_PUSHR_EOQ
ILI9341_t3.cpp:In member function 'uint8_t ILI9341_t3::readcommand8(uint8_t, uint8_t)
ILI9341_t3.cpp:222:14: error: 'KINETISK_SPI0' was not declared in this scope
:while (((KINETISK_SPI0.SR) & (15 << 12)) && (--wTimeout)) ; \\ wait until empty
ILI9341_t3.cpp:225:5: error: 'KINETISK_SPI0' was not declared in this scope
:KINETISK_SPI0.SR = SPI_SR_TCF; \\ dlear it out
ILI9341_t3.cpp:225:24: error: 'SPI_SR_TCF' was not declared in this scope
:KINETISK_SPI0.SR = SPI_SR_TCF; \\ dlear it out
ILI9341_t3.cpp:236:69: error: 'SPI_PUSHR_CTAS' was not declared in this scope
:KINETISK_SPI0.PUSHR = 0xD9 | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT
ILI9341_t3.cpp:236:73: error: 'SPI_PUSHR_CONT' was not declared in this scope
:KINETISK_SPI0.PUSHR = 0xD9 | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT
ILI9341_t3.cpp:In member function 'uint16_t ILI9341_t3::readPixel(int16_t, int16_t)
ILI9341_t3.cpp:283:2: error: 'KINETISK_SPI0' was not declared in this scope
:KINETISK_SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_CONT
ILI9341_t3.cpp:283:63: error: 'SPI_PUSHR_CTAS' was not declared in this scope
:KINETISK_SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_CONT
ILI9341_t3.cpp:283:66: error: 'SPI_PUSHR_CONT' was not declared in this scope
:KINETISK_SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_CONT
ILI9341_t3.cpp:288:66: error: 'SPI_PUSHR_EOQ' was not declared in this scope
:KINETISK_SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_EOQ
ILI9341_t3.cpp:291:29: error: 'SPI_SR_EOQF' was not declared in this scope
:while ((KINETISK_SPI0.SR & SPI_SR_EOQF) == 0)
ILI9341_t3.cpp:292:21: error: 'SPI_SR_EOQF' was not declared in this scope
:KINETISK_SPI0.SR = SPI_SR_EOQF; \\ make sure it is clear
ILI9341_t3.cpp:In member function 'void ILI9341_t3::readRect(int16_t, int16_t, int16_t, int16_t, uint16_t*)
ILI9341_t3.cpp:316:2: error: 'KINETISK_SPI0' was not declared in this scope
:KINETISK_SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_CONT | SPI_PUSHR_EOQ
ILI9341_t3.cpp:316:63: error: 'SPI_PUSHR_CTAS' was not declared in this scope
:KINETISK_SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_CONT | SPI_PUSHR_EOQ
ILI9341_t3.cpp:316:66: error: 'SPI_PUSHR_CONT' was not declared in this scope
:KINETISK_SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_CONT | SPI_PUSHR_EOQ
ILI9341_t3.cpp:316:83: error: 'SPI_PUSHR_EOQ' was not declared in this scope
:KINETISK_SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_CONT | SPI_PUSHR_EOQ
ILI9341_t3.cpp:317:29: error: 'SPI_SR_EOQF' was not declared in this scope
:while ((KINETISK_SPI0.SR & SPI_SR_EOQF) == 0)
ILI9341_t3.cpp:318:21: error: 'SPI_SR_EOQF' was not declared in this scope
:KINETISK_SPI0.SR = SPI_SR_EOQF; \\ make sure it is clear
Error compiling libraries
 
I was seeing screen burn under Navy and Blue - so while updating my SCROLL example I started shifting the colors across the 6 active scrollers. I enter and leave the scrollers often and quickly and with adafruit or PJRC font and the only oddity I found worked around using the PJRC font and its height not being hard coded math like the Adafruit one showed an overwrite anomaly that doens't appear when using the scroll without exiting.

So here is my spastic sample with 6 flying scrollers and 4 other added active onscreen elements working well - you need the PJRC ComicSans include as in Vitor's first sample. To test I cycle and grow each of the 6 scrollers after 40 prints for 18 passes to cross common font height boundaries - then I rotate the fgc/bgc through to reduce screen burn. I also cycle the font used and I see a small problem - one pixel high on one scroller with ComicSans - I emailed Vitor about. Also sometimes the one text string 'cnt=' outside a scroller doesn't show.

View attachment scrollTest2e.ino

PS: I really like the looks of the scrolling code.
 
I'm playing with the fast library and discovered something surprising. Essentially I'm trying to develop the equivalent of an artificial horizon for aircraft. I'm displaying the yaw, pitch and roll as level 3 text, the basic horizon as a two part circle with a 100 pixel radius and then drawing some lines on top of the circle.

I actually get the best text performance (~8.5 ms) by redrawing the text in the background color to clear the old value. If I simply clear the region behind the text by drawing an appropriate sized rectangle the time jumps to 13 ms.

Code:
static float lastYaw = 0, lastPitch = 0, lastRoll = 0;
  int x, y;
  tft.setTextSize(3);
  tft.setCursor(0, 0);
  tft.setTextColor(ILI9341_BLACK);
  tft.print("Y:");
  tft.println(lastYaw);
  tft.setCursor(0,0);
  tft.setTextColor(ILI9341_WHITE);
  tft.print("Y:");
  tft.println(lastYaw = TO_DEG(yaw));
  x = tft.getCursorX();
  y = tft.getCursorY();
  tft.setTextColor(ILI9341_BLACK);
  tft.print("P:");
  tft.println(lastPitch);
  tft.setCursor(x,y);
  tft.setTextColor(ILI9341_WHITE);
  tft.print("P:");
  tft.println(lastPitch = TO_DEG(pitch));
  x = tft.getCursorX();
  y = tft.getCursorY();
  tft.setTextColor(ILI9341_BLACK);
  tft.print("R:");
  tft.println(lastRoll);
  tft.setCursor(x,y);
  tft.setTextColor(ILI9341_WHITE);
  tft.print("R:");
  tft.println(lastRoll = TO_DEG(roll));

Drawing the basic split circle takes about 50 ms. That seems pretty fast considering the number of pixels being drawn. Here's the artificial horizon code that I've got so far:

Code:
float slope = 0;
float slopeIntersect = 0;

void drawArtificialHorizon(int16_t x0, int16_t y0, int16_t r, uint16_t color1, uint16_t color2) {
  tft.drawFastVLine(x0, y0-r, 2*r+1, color1);
  artificialHorizonHelper(x0, y0, r, 3, 0, color1, color2);
}

// Used to do circles and roundrects
void artificialHorizonHelper(int16_t x0, int16_t y0, int16_t r,
   uint8_t cornername, int16_t delta, uint16_t color1, uint16_t color2) {

  int16_t f     = 1 - r;
  int16_t ddF_x = 1;
  int16_t ddF_y = -2 * r;
  int16_t x     = 0;
  int16_t y     = r;

  slope = tan(roll);
  slopeIntersect = r + TO_DEG(pitch);

  while (x<y) {
    if (f >= 0) {
      y--;
      ddF_y += 2;
      f     += ddF_y;
    }
    x++;
    ddF_x += 2;
    f     += ddF_x;

    if (cornername & 0x1) {
      drawFastVLine(x0+x, y0-y, 2*y+1+delta, r, color1, color2);
      drawFastVLine(x0+y, y0-x, 2*x+1+delta, r, color1, color2);
    }
    if (cornername & 0x2) {
      drawFastVLine(x0-x, y0-y, 2*y+1+delta, r, color1, color2);
      drawFastVLine(x0-y, y0-x, 2*x+1+delta, r, color1, color2);
    }

#if 0
    tft.fillRect( x0 - r * .8, y0 - 2, r * .5, 4, ILI9341_YELLOW);
    tft.fillRect( x0 - r * .3, y0 - 2, 4, 10, ILI9341_YELLOW);
    tft.fillRect( x0 + r * .3, y0 - 2, r * .5, 4, ILI9341_YELLOW);
    tft.fillRect( x0 + r * .3, y0 - 2, 4, 10, ILI9341_YELLOW);
#endif
#if 0
    tft.fillCircle(x0, y0, 4, ILI9341_YELLOW);
#endif
  }
}

void drawFastVLine(int16_t x, int16_t y, int16_t length, int16_t r, int16_t color1, int16_t color2) {
  int intercept = getIntercept(x - r);
  if (y < intercept) {
    if (y + length > intercept) {
      tft.drawFastVLine(x, y, intercept - y, color1);
      tft.drawFastVLine(x, intercept, length - (intercept - y), color2);
    } else {
      tft.drawFastVLine(x, y, length, color1);
    }
  } else {
      tft.drawFastVLine(x, y, length, color2);
  }
}

int getIntercept(int x) {
  return x * slope + slopeIntersect;
}

What surprises me is the performance hit I'm seeing for the four fillRect operations. Enabling the first #if block increased the time by 42 ms which seems like a lot since it's a simple operation with a limited number of pixels being drawn. Drawing the small inner circle (#if block 2) increases the time by about 13 ms. As an experiment I tried drawing the blocks as a series of drawFastHLine operations but that didn't seem to help.

Code:
   for (int i = 0; i < 4; i++) {
      tft.drawFastHLine(x0 - r * .8, y0 - 2 + i, r * .5,  ILI9341_YELLOW);
      tft.drawFastHLine(x0 + r * .3, y0 - 2 + i, r * .5,  ILI9341_YELLOW);
    }

Are there any tricks that I can use to speed up the line/block drawing operations? The text drawing operations are much faster and looking at the code it appears that drawFontBits() is mostly a series of drawFastHLine operations too and it's a much more complex operation. I'm running this code on a Teensy 3.1 at 72mhz.
 
Last edited:
The bottleneck ist the interface of the display.

72MHz is suboptimal - not because of the CPU-Speed (mainly), but the internal F_BUS in the CPU does not run at the best speed for the display. The best is overclocking to 120MHz, which results in 30MHz SPI-Transfers to the display.
 
The display has to be fed each pixel and coordinate in some fashion to make it change. There are no primitives in the controller. Even a fillScreen is just a rectangle write - that writes the whole screen - all that takes bytes over SPI.
 
These displays require 9 bytes to set up a rectangular drawing region, and then 2 bytes per pixel. Drawing diagonal line and curves requires a lot of extra overhead, because many small rectangular regions have to set up.

While quite a lot of work has gone into this library to minimize those setup overheads, not every part is necessarily optimized as much as it could be. If you've found a function that seems slow, I need you to post a small benchmark sketch which uses only that function. I know you've described this above, but if you want me to look into it more deeply, you must post a small but complete program which can be copied into Arduino and run on a Teensy 3.2 to show the issue.
 
These displays require 9 bytes to set up a rectangular drawing region, and then 2 bytes per pixel. Drawing diagonal line and curves requires a lot of extra overhead, because many small rectangular regions have to set up.

While quite a lot of work has gone into this library to minimize those setup overheads, not every part is necessarily optimized as much as it could be. If you've found a function that seems slow, I need you to post a small benchmark sketch which uses only that function. I know you've described this above, but if you want me to look into it more deeply, you must post a small but complete program which can be copied into Arduino and run on a Teensy 3.2 to show the issue.

Thanks but I know you're busy. I'll dig deeper.
 
@vitor - I found a useful sketch adjustment to get my reentrant post#369 scroll abuse working on the PJRC fonts as well as the Adafruit default font. Have you put it forward for a pull request to PJRC? I saw it was not in the TD_1.27b1 update so I have to revert to my private copy. That code is awesome fun for a very useful UI feature. It can be used nicely in a proper GUI way as I've seen it - as well as a quick and dirty way to output spewing numbers (I haven't implemented yet) like one would have from a set of (I2C) 9 DOF readings. It'll be days until I get back to that but with my touch screen buttons I'm planning to start with four screens one combined 9DOF and one for the three component parts unique x,y,z that will be driven by touch to change the screen - the scroll area will provide for usably dense - easy to update - history display. With multiple scrollers screen update was as much as 60 millis with area scrolling.

I see my touch sample that doesn't use the scroll grow by 2144 bytes using the altered library. From 51,940 to 54,084 and as noted before RAM usage only grows 16 bytes. [ That is 'OPTIMIZED' ]

Non optimized the same sketch looks like this with only 1,296 bytes in code growth:
NO SCROLL:
Sketch uses 27,704 bytes (10%) of program storage space. Maximum is 262,144 bytes.

WITH SCROLL:
Sketch uses 29,000 bytes (11%) of program storage space. Maximum is 262,144 bytes.
 
youTube vid of current demo - I shortened the run loop printing 5x for action - 4 of 6 scrolls are updated in each loop top left and right get even or odd counts [lower corner shows ms update time that pass]. Odd/Even toggles font used in scroll panes. Panes grow a pixel with increasing count to catch the rounding error bug in scrolling return with PJRC font. Scroll background color changed on completed pass when height reset. [@vitor?] One small error noted above still shows where 't & l' ascend over the scroll top on one font? Also a spinner and 3 numeric areas each loop, plus the odd/even.
 
Back
Top