RA8876LiteTeensy For Teensy T36 and T40

So far my quick 5 minute look hopefully makes it easier to find things. So I would suggest going for it!

At some point will be interesting to see how much we can speed things up. Looks like everything is going through SPI.transfer or SPI.transfer16. It does look like it supports the
DMA version of the SPI Transfer, but we don't have any support for 16 writes that way...

So will be interesting to see how well the SPI buss is kept busy. Looks like gaos after each group. Not too bad... But also it is running at 5mhz... Runs at 30... Did not at 50...

But at 30 you can see some pretty good gaps between outputs...

screenshot.jpg

Now to go relax for the rest of the night.
 
@KurtE
Pushed the changes but GitHub was acting funny for the merges so it there is a problem may have repush the changes.

Anyway fixed one problem with setactiveWindow so now the backfills and screen fills are correct for anti-alias font. Not sure yet why the characters are not printing - that's next I guess.

EDIT: Just pushed the fix so anti-alias fonts will display. CENTERING is whats left :)
 
Last edited:
Morning:

If you wish to try out the font test 4... I have a version up in a soon to be deleted branch Will migrate into the main WIP branch:
Code:
#include <Adafruit_GFX.h>

#include <SPI.h>
#include "RA8876_t3.h"
#define RA8876_CS 10
#define RA8876_RESET 9
#define BACKLITE 7 //External backlight control connected to this Arduino pin

#include "font_Arial.h"
#include "font_ArialBold.h"
#include "font_ComicSansMS.h"
#include "font_OpenSans.h"
#include "font_DroidSans.h"
#include "font_Michroma.h"
#include "font_Crystal.h"
#include "font_ChanceryItalic.h"

// maybe a few GFX FOnts?
#include <Fonts/FreeMonoBoldOblique12pt7b.h>
#include <Fonts/FreeSerif12pt7b.h>

typedef struct {
  const ILI9341_t3_font_t *ili_font;
  const GFXfont       *gfx_font;
  const char          *font_name;
  uint16_t            font_fg_color;
  uint16_t            font_bg_color;
} ili_fonts_test_t;

const uint16_t  PINK       = 0xFCFF; // M.Sandercock
const uint16_t  PURPLE       = 0x8017; // M.Sandercock

const ili_fonts_test_t font_test_list[] = {
  {nullptr, nullptr,  "Internal Font", RED, YELLOW},
  {&Arial_14, nullptr,  "Arial_14", WHITE, WHITE},
  {&Arial_14_Bold, nullptr,  "ArialBold 14", YELLOW, YELLOW},
  {&ComicSansMS_14, nullptr,  "ComicSansMS 14", GREEN, GREEN},
  {&DroidSans_14, nullptr,  "DroidSans_14", WHITE, WHITE},
  {&Michroma_14, nullptr,  "Michroma_14", YELLOW, YELLOW},
  {&Crystal_24_Italic, nullptr,  "CRYSTAL_24", BLACK, YELLOW},
  {&Chancery_24_Italic, nullptr,  "Chancery_24_Italic", GREEN, GREEN},
  {&OpenSans24, nullptr,  "OpenSans 18", RED, YELLOW},
  {nullptr, &FreeMonoBoldOblique12pt7b,  "GFX FreeMonoBoldOblique12pt7b", WHITE, WHITE},
  {nullptr, &FreeMonoBoldOblique12pt7b,  "GFX FreeMonoBoldOblique12pt7b", RED, YELLOW},
  {nullptr, &FreeSerif12pt7b,  "GFX FreeSerif12pt7b", WHITE, WHITE},
  {nullptr, &FreeSerif12pt7b,  "GFX FreeSerif12pt7b", RED, YELLOW},

} ;

extern void displayStuff(const char *font_name);


RA8876_t3 tft = RA8876_t3(RA8876_CS, RA8876_RESET); //Using standard SPI pins

uint8_t test_screen_rotation = 0;


void setup() {
  Serial.begin(38400);
  long unsigned debug_start = millis ();
  while (!Serial && ((millis () - debug_start) <= 5000)) ;
  Serial.println("Setup");
  tft.begin();
  pinMode(BACKLITE, OUTPUT);
  digitalWrite(BACKLITE, HIGH);


//  tft.setRotation(4);
  tft.fillScreen(BLACK);
  tft.setTextColor(WHITE);
  tft.setFont(Arial_14);
  displayStuff("Arial_14");

  tft.setTextColor(YELLOW);
  tft.setFont(Arial_14_Bold);
  displayStuff("ArialBold 14");

  tft.setTextColor(GREEN);
  tft.setFont(ComicSansMS_14);
  displayStuff("ComicSansMS 14");

  nextPage();
  tft.setTextColor(WHITE);
  tft.setFont(DroidSans_14);
  displayStuff("DroidSans_14");

  tft.setTextColor(YELLOW);
  tft.setFont(Michroma_14);
  displayStuff("Michroma_14");
  stepThrough();

  tft.setTextColor(BLACK, YELLOW);
  tft.setFont(Crystal_24_Italic);
  displayStuff("CRYSTAL_24");

  nextPage();

  tft.setTextColor(GREEN);
  tft.setFont(Chancery_24_Italic);
  displayStuff("Chancery_24_Italic");

  //anti-alias font OpenSans
  tft.setTextColor(RED, YELLOW);
  tft.setFont(OpenSans24);
  displayStuff("OpenSans24");

  Serial.println("Basic Font Display Complete");
  Serial.println("Loop test for alt colors + font");
}

void loop()
{
  Serial.printf("\nRotation: %d\n", test_screen_rotation);
  //tft.setRotation(test_screen_rotation);
  tft.fillScreen(RED);
  tft.setCursor(tft.width()/2, tft.height()/2/*, true*/);
  tft.printf("Rotation: %d", test_screen_rotation);
  test_screen_rotation = (test_screen_rotation + 1) & 0x3;
  tft.setCursor(200, 300);
  Serial.printf("  Set cursor(200, 300), retrieved(%d %d)",
                tft.getCursorX(), tft.getCursorY());
  tft.setCursor(50, 50);
  tft.write('0');
  tft.setCursor(tft.width() - 50, 50);
  tft.write('1');
  tft.setCursor(50, tft.height() - 50);
  tft.write('2');
  tft.setCursor(tft.width() - 50, tft.height() - 50);
  tft.write('3');

  for (uint8_t font_index = 0; font_index < (sizeof(font_test_list) / sizeof(font_test_list[0])); font_index++) {
    nextPage();
    if (font_test_list[font_index].font_fg_color != font_test_list[font_index].font_bg_color)
      tft.setTextColor(font_test_list[font_index].font_fg_color, font_test_list[font_index].font_bg_color);
    else
      tft.setTextColor(font_test_list[font_index].font_fg_color);
    if (font_test_list[font_index].ili_font) tft.setFont(*font_test_list[font_index].ili_font);
    else if (font_test_list[font_index].gfx_font)  tft.setFont(font_test_list[font_index].gfx_font);
    else tft.setFontDef();
    tft.println(font_test_list[font_index].font_name);
    displayStuff1();
  }
  nextPage();
}

void displayStuff(const char *font_name)
{
  elapsedMillis elapsed_time = 0;
  tft.println(font_name);
  tft.println("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
//  stepThrough();
  tft.println("abcdefghijklmnopqrstuvwxyz");
//  stepThrough();
  tft.println("0123456789");
//  stepThrough();
  tft.println("!@#$%^ &*()-");
//  stepThrough();
  tft.println(); tft.println();
//  stepThrough();
  Serial.printf("%s: %dms\n", font_name, (uint32_t)elapsed_time);
}

uint32_t displayStuff1()
{
  elapsedMillis elapsed_time = 0;
  tft.println("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
  tft.println("abcdefghijklmnopqrstuvwxyz");
  tft.println("0123456789");
  tft.println("!@#$%^ &*()-");

  int16_t cursorX = tft.getCursorX();
  int16_t cursorY = tft.getCursorY();

  uint16_t width = tft.width();
  uint16_t height = tft.height();
  Serial.printf("DS1 (%d,%d) %d %d\n", cursorX, cursorY, width, height);
  uint16_t rect_x = width / 2 - 50;
  uint16_t rect_y = height - 75;
  tft.drawRect(rect_x, rect_y, 100, 50, WHITE);
  for (uint16_t y = rect_y + 5; y < rect_y + 50; y += 5)
    tft.drawFastHLine(rect_x + 1, y, 98, PINK);
  for (uint16_t x = rect_x + 5; x < rect_x + 100; x += 5)
    tft.drawFastVLine(x, rect_y+1, 48, PINK);
  tft.setCursor(width / 2, height - 50/*, true*/);
  tft.print("Center");

  // Lets try again with CENTER X keyword.
  rect_y -= 100;
  tft.drawRect(rect_x, rect_y, 100, 50, PINK);
  for (uint16_t y = rect_y + 5; y < rect_y + 50; y += 5)
    tft.drawFastHLine(rect_x + 1, y, 98, CYAN);
  for (uint16_t x = rect_x + 5; x < rect_x + 100; x += 5)
    tft.drawFastVLine(x, rect_y+1, 48, CYAN);
//  tft.setCursor(CENTER, rect_y);
  tft.print("XCENTR");

  // Lets try again with CENTER Y keyword.
  rect_x = 50;
  rect_y = tft.height()/2 -25;
  tft.drawRect(rect_x, rect_y, 100, 50, CYAN);
  for (uint16_t y = rect_y + 5; y < rect_y + 50; y += 5)
    tft.drawFastHLine(rect_x + 1, y, 98, PINK);
  for (uint16_t x = rect_x + 5; x < rect_x + 100; x += 5)
//  tft.setCursor(50, CENTER);
  tft.print("YCENTR");
  
  // Lets see how close the getTextBounds gets the bounds of the text
  rect_x = 200;
  rect_y += 25; //center 
  static const char rectText[] = "RectText";
  int16_t xT, yT;
  uint16_t wT, hT;
  tft.getTextBounds(rectText, rect_x, rect_y, &xT, &yT, &wT, &hT);
  Serial.printf("getTextBounds: (%d, %d): %d %d %d %d\n", rect_x, rect_y, xT, yT, wT, hT);
  tft.setCursor(rect_x, rect_y);
  tft.print(rectText);
  tft.drawRect(xT, yT, wT, hT, CYAN);

  tft.setCursor(cursorX, cursorY);
  static const char alternating_text[] = "AbCdEfGhIjKlMnOpQrStUvWxYz\raBcDeFgHiJkLmNoPqRsTuVwXyZ";

  for (uint8_t i = 0; i < sizeof(alternating_text); i++) {
    if (i & 1) tft.setTextColor(WHITE, RED);
    else tft.setTextColor(YELLOW, BLUE);
    tft.write(alternating_text[i]);
  }

  tft.println(); tft.println();



  return (uint32_t) elapsed_time;
}

void nextPage()
{
  Serial.println("Press anykey to continue");
  while (Serial.read() == -1) ;
  while (Serial.read() != -1) ;

  tft.fillScreen(BLACK);
  tft.setCursor(0, 0);
}
void stepThrough()
{
  Serial.println("Press anykey to continue");
  while (Serial.read() == -1) ;
  while (Serial.read() != -1) ;
}

Not sure which route to take next... Continue here to figure out some of the things missing.

See about maybe adding some of the things like PWM backlight So don't need IO pin, Or SPI speedup...
Or back to Robot ;)


: I updated the above sketch to Serial.printf the name of font and how long the display of it took in the initial displayStuff, as a way to then see how much it can be sped up.

Example run:
Code:
Setup

Arial_14: 89ms
ArialBold 14: 89ms
ComicSansMS 14: 109ms
Press anykey to continue

DroidSans_14: 95ms
Michroma_14: 112ms
Press anykey to continue

CRYSTAL_24: 451ms
Press anykey to continue

Chancery_24_Italic: 228ms
OpenSans24: 1473ms
Basic Font Display Complete

Loop test for alt colors + font

EDIT2 - changed from 5mhz to 30...
Code:
Setup

Arial_14: 22ms
ArialBold 14: 22ms
ComicSansMS 14: 27ms
Press anykey to continue

DroidSans_14: 23ms
Michroma_14: 28ms
Press anykey to continue

CRYSTAL_24: 110ms
Press anykey to continue

Chancery_24_Italic: 56ms
OpenSans24: 368ms
Basic Font Display Complete

Loop test for alt colors + font
 
Last edited:
@KurtE
Cool - going to have to copy that one in the test sketch I have running (the fonttest4 sketch you have in your copy from yesterday :) ).

Oh by the way have CENTERING working for the most part, will push the changes up shortly. BTW don't need to do the define in the sketch anymore.

EDIT: Not working yet for default font.
 
Glad you have centering mostly working :D

Note from the stuff before to get the font working. I did not put in the code to offset the rectangle by the current offset. Needless to say most all other graphic primitives have the offset/clip code in yet.

Right now taking quick look at SPI speed ups.
 
@mjs513 - Doing a first pass at SPI enhancments/speedups...

Things like: hopefully now you can specify pins for SPI1 or SPI2 and it will work.

Also failing when you ask if the CS pins is a hardware Serial pin and it is not, is not valid here as we are not using the hardware SPI CS support. Just digital writes... I did convert these over to grab the registers and directly set/clear...

I also updated the begin method to allow you to pass in SPI speed..

It runs... But no real speed change yet.

Edit forgot to mention if anyone wants to play along: it is up in the branch: GIFW-HSPI
 
@KurtE

You lost me on this one. A little distracted right now watching SpaceX:
Note from the stuff before to get the font working. I did not put in the code to offset the rectangle by the current offset. Needless to say most all other graphic primitives have the offset/clip code in yet.
 
@KurtE

You lost me on this one. A little distracted right now watching SpaceX:
I keep meaning to watch... Have been busy burying the potato plants...
What I mean that is needed is:

Code:
void RA8876_t3::fillRect(int16_t x, int16_t y, int16_t w, int16_t h,uint16_t color) {
	[COLOR="#FF0000"]x += _originx;
	y += _originy;[/COLOR]
	int16_t x_end = x+w-1;
	int16_t y_end = y+h-1;
	if((x >= _displayclipx2)   || // Clip right
		 (y >= _displayclipy2) || // Clip bottom
		 (x_end < _displayclipx1)    || // Clip left
		 (y_end < _displayclipy1))  	// Clip top 
	{
		// outside the clip rectangle
		return;
	}
	if (x < _displayclipx1) x = _displayclipx1;
	if (y < _displayclipy1) y = _displayclipy1;
	if (x_end > _displayclipx2) x_end = _displayclipx2;
	if (y_end > _displayclipy2) y_end = _displayclipy2;
	drawSquareFill(x, y, x_end, y_end, color);
}
And then all of the other graphic primitives need something similar...
 
Quick update: I believe that the RA8876(and maybe 5) are much more picky on SPI state. I tried hacking larger area of code to queue up the queue of data and the data did not show up correctly. (like more or less not at all).

I have bypassed it and did a quick dirty change to see if it made a difference:
Code:
void RA8876_t3::lcdRegDataWrite(ru8 reg, ru8 data, bool finalize)
{
  //write the register we wish to write to, then send the data
  //don't need to release _CS between the two transfers
  //ru16 _reg = (RA8876_SPI_CMDWRITE16 | reg);
  //ru16 _data = (RA8876_SPI_DATAWRITE16 | data);
  uint8_t buf[4] = {RA8876_SPI_CMDWRITE, reg, RA8876_SPI_DATAWRITE, data };
  startSend();
  //_pspi->transfer16(_reg);
  //_pspi->transfer16(_data);
  _pspi->transfer(buf, nullptr, 4);
  endSend(finalize);
}
It works and at 30mhz, Timing shows.

Code:
Setup

Arial_14: 19ms
ArialBold 14: 20ms
ComicSansMS 14: 24ms
Press anykey to continue

DroidSans_14: 20ms
Michroma_14: 25ms
Press anykey to continue

CRYSTAL_24: 98ms
Press anykey to continue

Chancery_24_Italic: 50ms
OpenSans24: 345ms
So it cut down some of the SPI gaps...
 
I keep meaning to watch... Have been busy burying the potato plants...
What I mean that is needed is:
…..
And then all of the other graphic primitives need something similar...
Got it. Also just looked at the RA8875 code and Sumotoy got sneaky with how he deals with clipping. He created functions like _rect_helper, _trangle_helper, etc that has the clipping so if you call drawTriangle or fillTriangle:
Code:
void RA8875::drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color)
{
	_triangle_helper(x0, y0, x1, y1, x2, y2, color, false);   //true for fill
}

The triangle helper has:
Code:
	if (x0 >= _width || x1 >= _width || x2 >= _width) return;
	if (y0 >= _height || y1 >= _height || y2 >= _height) return;	
	if (_portrait) {swapvals(x0,y0); swapvals(x1,y1); swapvals(x2,y2);}

So going to be fun to add in clipping and origin shifting.
 
Quick update: I believe that the RA8876(and maybe 5) are much more picky on SPI state. I tried hacking larger area of code to queue up the queue of data and the data did not show up correctly. (like more or less not at all).

I have bypassed it and did a quick dirty change to see if it made a difference:
Code:
void RA8876_t3::lcdRegDataWrite(ru8 reg, ru8 data, bool finalize)
{
  //write the register we wish to write to, then send the data
  //don't need to release _CS between the two transfers
  //ru16 _reg = (RA8876_SPI_CMDWRITE16 | reg);
  //ru16 _data = (RA8876_SPI_DATAWRITE16 | data);
  uint8_t buf[4] = {RA8876_SPI_CMDWRITE, reg, RA8876_SPI_DATAWRITE, data };
  startSend();
  //_pspi->transfer16(_reg);
  //_pspi->transfer16(_data);
  _pspi->transfer(buf, nullptr, 4);
  endSend(finalize);
}
It works and at 30mhz, Timing shows.

Code:
Setup

Arial_14: 19ms
ArialBold 14: 20ms
ComicSansMS 14: 24ms
Press anykey to continue

DroidSans_14: 20ms
Michroma_14: 25ms
Press anykey to continue

CRYSTAL_24: 98ms
Press anykey to continue

Chancery_24_Italic: 50ms
OpenSans24: 345ms
So it cut down some of the SPI gaps...

Very cool. Right now I am running at 50Mhz and got the same numbers as you did for the FontTest4 sketch.
 
I may quit worrying about trying to speed up some of the SPI communications. I sort of now remember doing some of this exercise with RA8875 and punted...

That is If I capture with Logic Analyzer the display of one of our font pages ...

It looks like:
screenshot.jpg

screenshot2.jpg

The first one gives sort of an overview look at the page. The part at the very start of the trace, is I believe the code that sets up to do a Page erase (fill Screen black)....
The Red colored marked area to the right of it, is us doing a query loop waiting for the command to complete. Which I believe is something like 11.3ms

The second one shows part of the data sent to initiate the update. Again one might be able to optimize the code especially on T3.x (or T4 with CS on PIN 9), to change the CS using the FIFO and remove those minor gaps... But compared to the wait... Not sure how much it is worth it.
 
@KurtE
I agree with you and the SPI speed now seems to make a difference when I change it. Before running at 50Mhz didn't seem to do anything even though it accepted it.

Just pushed some more changes up to GITHUB and almost got centering working for default font. missing something though. Off a bit. Also copied over a bunch of clipping the way RA8875 did the graphic primitives except for a couple that I have to think about.
 
@mjs513 - I think we can speed up a few of the SPI things a bit more, without needing to go down to SPI register level. Sort of like how I packed the two words into 4 bytes and did one transfer.

But I before packing too many of these, may need to see in what circumstances the display requires the CS pin to change...

Right now I am trying to play around with the PWM0 stuff. There was a test app (for some 8080 board) up on BuyDisplay.com, which supposedly sets up the PWM for the display, so I am trying to emulate this, first within test sketch... So far not working. So investigating.

With these displays, keep wondering if we should try to make rotations of the display work? I don't think the registers for controlling reversing of bit orders is in both directions. Nor do I think they have the swap X, Y stuff like some of our other displays do. So if it is something should do at the interface level... I know it would be Royal PIA. Could probably do it with all of our higher level primitives, but probably not with built in fonts and the like.

EDIT: Backlight control - Probably would help to look at PDF file for display.. There are hardware jumpers controlling this... Looks like setup for external.
 
Last edited:
@KurtE

SPI is always challenging. PWM never played with it with these displays :)

Next on my list was the screen rotation. Remember it being a challenge with the HDMI shield I was playing with back in the T4 beta days. Never could get it working right but maybe now is the time so off to that. But you know me I like doing coding from between 6-12 then I am off doing other stuff :)
 
@mjs513 and @ KurtE - I need a little direction here. I am so easily confused:) Who's branch of Ra8876Teeny should I be pulling from to keep up with you guy's? I have to take a break from MSC for a while. I am finally starting to understand and play with queuing, linked lists and such. Tonight I was trying out some of of the example sketches for Ra8876Teensy and realized I have no idea where you guy's are at with it. I did find a problem with drawing rectangles and round rectangles both filled and unfilled that I was able to figure out. But it's late and I have to be to work in four hours. i will post the updated code later:)
 
@mjs513 - Just did PR to you for adding backlight support. I no longer need the jumper to get the display to work...

But had to change jumpers on the board.
 
@KurtE
Just did the merge. Not too complicated but a lot of work to get it working right.

Just checked by display and it has the same config as yours :) but going to leave mine for awhile.
 
@wwatson
Sounds like you are having as much fun with MSC as we are with the RA8876 :)

Anyway the most current changes are at: https://github.com/mjs513/Ra8876LiteTeensy/tree/GFX-ILI-Font-WIP. Interesting with the rectangle and rectangle fill. Both use drawSquare so let me know what the issue is?

Thank you for the link. Everything you guys have done so far seems to be working on my display as well at what appears to be 50 MHz. Cool:)

I am running 'grahpics.ino' and anything to do with drawing rectangles or round rectangles is going completely off of the screen. Out of bounds so to speak. I was looking at 'drawRect(...)' in ILI9341_t3. I see the the way it is using 'drawFastHLine()' and 'drawFastVLine()' functions to draw a rectangle. Is this because there is no builtin draw square function? Anyway What I did to make it work was this:
Code:
// Draw a rectangle. Note: damages text color register
void RA8876_t3::drawRect(int16_t x, int16_t y, int16_t w, int16_t h,uint16_t color) {

	drawSquare(x, y, x+w-1, y+h-1, color);
}

// Draw a filled rectangle. Note: damages text color register
void RA8876_t3::fillRect(int16_t x, int16_t y, int16_t w, int16_t h,uint16_t color) {

	drawSquareFill(x, y, x+w-1, y+h-1, color);
}

// Draw a round rectangle. 
void RA8876_t3::drawRoundRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t xr, uint16_t yr, uint16_t color) {
	drawCircleSquare(x, y, x+w-1, y+h-1, xr, yr, color);
}

// Draw a filed round rectangle.
void RA8876_t3::fillRoundRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t xr, uint16_t yr, uint16_t color) {
	
	
	drawCircleSquareFill(x, y, x+w-1, y+h-1, xr, yr, color);
}

To this:
Code:
// Draw a rectangle. Note: damages text color register
void RA8876_t3::drawRect(int16_t x, int16_t y, int16_t w, int16_t h,uint16_t color) {

	drawSquare(x, y, w-1, h-1, color);
}

// Draw a filled rectangle. Note: damages text color register
void RA8876_t3::fillRect(int16_t x, int16_t y, int16_t w, int16_t h,uint16_t color) {

	drawSquareFill(x, y, w-1, h-1, color);
}

// Draw a round rectangle. 
void RA8876_t3::drawRoundRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t xr, uint16_t yr, uint16_t color) {
	drawCircleSquare(x, y, w-1, h-1, xr, yr, color);
}

// Draw a filed round rectangle.
void RA8876_t3::fillRoundRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t xr, uint16_t yr, uint16_t color) {
	
	
	drawCircleSquareFill(x, y, w-1, h-1, xr, yr, color);
}

This eliminated the addition of x+w and y+h which if x = 1023 and w = 1023 would equal 2048 and is obviously beyond the screen width of 1023. Same with y+h.

Here is the 'graphics.ino' sketch I am using. Only change is commenting out the '#include "Ra8876_Lite.h"' line:
Code:
/*************************************************************** 
 * graphics.ino 
 * 
 * Basic graphics test for RA8876 based display
 ***************************************************************/
#include "Arduino.h"
//#include "Ra8876_Lite.h"
#include "RA8876_t3.h"
#define RA8876_CS 10
#define RA8876_RESET 8
#define BACKLITE 7 //External backlight control connected to this Arduino pin
RA8876_t3 tft = RA8876_t3(RA8876_CS, RA8876_RESET); //Using standard SPI pins

// Array of Simple RA8876 Basic Colors
PROGMEM uint16_t myColors[] = {
	0x0000,
	0xffff,
	0xf800,
	0xfc10,
	0x8000,
	0x07e0,
	0x87f0,
	0x0400,
	0x001f,
	0x051f,
	0x841f,
	0x0010,
	0xffe0,
	0xfff0,
	0x8400,
	0x07ff,
	0x87ff,
	0x0410,
	0xf81f,
	0xfc1f,
	0x8010,
	0xA145
};

int interations = 0;

// Draw random unfilled rectangle boxes
void rectangles(uint16_t thickness) {
	uint16_t x0, y0, x1, y1, c;
	uint16_t j;

	for(int i=0; i < interations; i++) {
		x0 = (uint16_t)random(1,512);
		y0 = (uint16_t)random(1,288);
		x1 = (uint16_t)random(512,1023);
		y1 = (uint16_t)random(288,575);
		c = (uint16_t)random(21);
		if(x0 > tft.width()) x0 = tft.width();
		if(y0 > tft.height()) y0 = tft.height();
		if(x1 > tft.width()) x1 = tft.width();
		if(y1 > tft.height()) y1 = tft.height();
		if(thickness > 0) {
			for(j = 1; j <= thickness; j++) {
				tft.drawRect(x0,y0,x1,y1,myColors[c]);
				if(x0 <= tft.width())
					x0++;
				if(y0 <= tft.height())
					y0++;
				if(x1 > 0)
					x1--;
				if(y1 > 0)
					y1--;
			}
		} else {
			tft.drawRect(x0,y0,x1,y1,myColors[c]);
		}
	}
	tft.fillStatusLine(myColors[11]);
}

// Draw random filled rectangle boxes
void filledRectangles(void) {
	uint16_t x0, y0, x1, y1, c;
	for(int i=0; i< interations; i++) {
		x0 = (uint16_t)random(1023);
		y0 = (uint16_t)random(575);
		x1 = (uint16_t)random(1023);
		y1 = (uint16_t)random(575);
		c = (uint16_t)random(21);
		tft.fillRect(x0,y0,x1,y1,myColors[c+1]);
	}
	tft.fillStatusLine(myColors[11]);
}

// Draw random round rectangle boxes
void rRectangles(uint16_t thickness) {
	uint16_t x0, y0, x1, y1, xr, yr, c;
	uint16_t j;

	for(int i=0; i < interations; i++) {
		x0 = (uint16_t)random(1,512);
		y0 = (uint16_t)random(1,288);
		x1 = (uint16_t)random(512,1023);
		y1 = (uint16_t)random(288,575);
		xr = 20; // Major Radius - Line Thickness must be less than
		yr = 20; // Minor Radius - Major and Minor radiuses by at
				 //				 - least half of xr and yr.
		c = (uint16_t)random(21);
		if(x0 > tft.width()) x0 = tft.width();
		if(y0 > tft.height()) y0 = tft.height();
		if(x1 > tft.width()) x1 = tft.width();
		if(y1 > tft.height()) y1 = tft.height();
		// Make sure major radius (xr) is less than x1 - x0
		// Must be xr * 2 + 1 less than x1 - x0
		// RA8876.pdf section 12.6 page 62
		if((xr * 2 + 1) >= (x1 - x0))
			xr = (x1 - x0) / 2 - 1;
		// Same for minor radius (yr)
		if((yr * 2 + 1) >= (y1 - y0))
			yr = (y1 - y0) / 2 - 1;
		if(thickness > 0) {
			for(j = 1; j <= thickness; j++) {
				tft.drawRoundRect(x0,y0,x1,y1,xr,yr,myColors[c]);
				if(x0 <= tft.width())
					x0++;
				if(y0 <= tft.height())
					y0++;
				if(x1 > 0)
					x1--;
				if(y1 > 0 )
					y1--;
				if(xr > 0)
					xr--;
				if(yr > 0)
					yr--;
			}
		} else {
			tft.drawRoundRect(x0,y0,x1,y1,xr,yr,myColors[c]);
		}
	}
	tft.fillStatusLine(myColors[11]);
}

// Draw random filled round rectangle boxes
void filledRRectangles(void) {
	uint16_t x0, y0, x1, y1, xr, yr, c;

	for(int i=0; i < interations; i++) {
		x0 = (uint16_t)random(1,512);
		y0 = (uint16_t)random(1,288);
		x1 = (uint16_t)random(512,1023);
		y1 = (uint16_t)random(288,575);
		xr = 20; // Major Radius
		yr = 20; // Minor Radius
		c = (uint16_t)random(21);

		// Keep x,y within 1024x576 boundries
		if(x0 > tft.width()) x0 = tft.width();
		if(y0 > tft.height()) y0 = tft.height();
		if(x1 > tft.width()) x1 = tft.width();
		if(y1 > tft.height()) y1 = tft.height();
		
		// Make sure major radius (xr) is less than x1 - x0
		// Must be xr * 2 + 1 less than x1 - x0
		// RA8876.pdf section 12.6 page 62
		if((xr * 2 + 1) >= (x1 - x0))
			xr = (x1 - x0) / 2 - 1;
		// Same for minor radius (yr)
		if((yr * 2 + 1) >= (y1 - y0))
			yr = (y1 - y0) / 2 - 1;
		tft.fillRoundRect(x0, y0, x1, y1, xr, yr, myColors[c]);
	}
	tft.fillStatusLine(myColors[11]);
}

// Draw random circles
void drawcircles(uint16_t thickness) {
	uint16_t x0, y0, r, c;
	int j;
	for(int i=0; i < interations; i++) {
		x0 = (uint16_t)random(1023);
		y0 = (uint16_t)random(575);
		r = (uint16_t)random(239);
		c = (uint16_t)random(21);
		if(x0-r <= 0)
			x0 += (uint16_t)r;
		if(y0-r <= 0)
			y0 += (uint16_t)r;
		if(x0+r >=  tft.width())
			x0 = (uint16_t)(tft.width() - r);
		if(y0+r >= tft.height())
			y0 = (uint16_t)(tft.height() - r);
		if(thickness > 0) {
			for(j = 1; j <= thickness; j++) {
				tft.drawCircle(x0, y0, r, myColors[c]);
				if(r > 0)
					r--;
			}
		} else {
		tft.drawCircle(x0, y0, r, myColors[c]);
		}
		tft.drawCircle(x0, y0, r, myColors[c]);
	}
	tft.fillStatusLine(myColors[11]);
}

// Draw random filled circles
void fillcircles(void) {
	uint16_t x0, y0, r, c;
	for(int i=0; i< interations; i++) {
		x0 = (uint16_t)random(1023);
		y0 = (uint16_t)random(575);
		r = (uint16_t)random(239);
		c = (uint16_t)random(21);
		if(x0-r <= 0)
			x0 += (uint16_t)r;
		if(y0-r <= 0)
			y0 += (uint16_t)r;
		if(x0+r >=  tft.width())
			x0 = (uint16_t)(tft.width() - r);
		if(y0+r >= tft.height())
			y0 = (uint16_t)(tft.height() - r);
		tft.fillCircle(x0, y0, r, myColors[c]);
	}
	tft.fillStatusLine(myColors[11]);
}

// Draw random unfilled tritangles
void drawTriangles(void) {
	uint16_t x0, y0, x1, y1, x2, y2, c;
	for(int i=0; i < interations; i++) {
		x0 = (uint16_t)random(1023);
		y0 = (uint16_t)random(575);
		x1 = (uint16_t)random(1023);
		y1 = (uint16_t)random(575);
		x2 = (uint16_t)random(1023);
		y2 = (uint16_t)random(575);
		c = (uint16_t)random(21);
		tft.drawTriangle(x0,y0,x1,y1,x2,y2,myColors[c+1]);
	}
	tft.fillStatusLine(myColors[11]);
}

// Draw random filled triangles
void fillTriangles(void) {
	uint16_t x0, y0, x1, y1, x2, y2, c;
	for(int i=0; i < interations; i++) {
		x0 = (uint16_t)random(1023);
		y0 = (uint16_t)random(575);
		x1 = (uint16_t)random(1023);
		y1 = (uint16_t)random(575);
		x2 = (uint16_t)random(1023);
		y2 = (uint16_t)random(575);
		c = (uint16_t)random(21);
		tft.fillTriangle(x0,y0,x1,y1,x2,y2,myColors[c+1]);
	}
	tft.fillStatusLine(myColors[11]);
}

// Draw random unfilled ellipses
void drawEllipses(void)
{
	int16_t  x0, y0, xr, yr;
	uint16_t color;
	for(int i=0; i < interations; i++) {
		x0 = (uint16_t)random(1023);
		y0 = (uint16_t)random(575);
		xr = (uint16_t)random(239);
		yr = (uint16_t)random(239);
		color = (uint16_t)random(21);
		if(x0-xr <= 0)
			x0 += (uint16_t)xr;
		if(y0-yr <= 0)
			y0 += (uint16_t)yr;
		if(x0+xr >=  tft.width())
			x0 = (uint16_t)(tft.width() - xr);
		if(y0+yr >= tft.height())
			y0 = (uint16_t)(tft.height() - yr);
		tft.drawEllipse(x0, y0, xr, yr, myColors[color]);
	}
	tft.fillStatusLine(myColors[11]);
}

// Draw random filled ellipses
void fillEllipses(void)
{
	int16_t  x0, y0, xr, yr;
	uint16_t color;
	for(int i=0; i < interations; i++) {
		x0 = (uint16_t)random(1023);
		y0 = (uint16_t)random(575);
		xr = (uint16_t)random(239);
		yr = (uint16_t)random(239);
		color = (uint16_t)random(21);
		if(x0-xr <= 0)
			x0 += (uint16_t)xr;
		if(y0-yr <= 0)
			y0 += (uint16_t)yr;
		if(x0+xr >=  tft.width())
			x0 = (uint16_t)(tft.width() - xr);
		if(y0+yr >= tft.height())
			y0 = (uint16_t)(tft.height() - yr);
		tft.fillEllipse(x0, y0, xr, yr, myColors[color]);
	}
	tft.fillStatusLine(myColors[11]);
	//timeOn(); // Turn on time/date display on status line
}

// Draw random Lines
void drawlines(void) {
	uint16_t x0, y0, x1, y1, c;

	for(int i=0; i < interations; i++) {
		x0 = (uint16_t)random(1,1023);
		y0 = (uint16_t)random(1,575);
		x1 = (uint16_t)random(1,1023);
		y1 = (uint16_t)random(1,575);
		c = (uint16_t)random(21);
		if(x0 > tft.width()) x0 = tft.width();
		if(y0 > tft.height()) y0 = tft.height();
		if(x1 > tft.width()) x1 = tft.width();
		if(y1 > tft.height()) y1 = tft.height();
		tft.drawLine(x0,y0,x1,y1,myColors[c]);
	}
	tft.fillStatusLine(myColors[11]);
}

int i = 0;
void setup() {
  //I'm guessing most copies of this display are using external PWM
  //backlight control instead of the internal RA8876 PWM.
  //Connect a Teensy pin to pin 14 on the display.
  //Can use analogWrite() but I suggest you increase the PWM frequency first so it doesn't sing.
  pinMode(BACKLITE, OUTPUT);
  digitalWrite(BACKLITE, HIGH);
    
	tft.begin();
	//initVT100();
  tft.setTextCursor(0,0);
	tft.fillScreen(myColors[11]);
	tft.setFontSize(1,false);
}

void loop() {
	tft.printStatusLine(0,myColors[1],myColors[11],"Rectangles");
	interations = 30000;
	rectangles(0);
	tft.fillScreen(myColors[11]);
	delay(10);
	tft.printStatusLine(0,myColors[1],myColors[11],"Rectangles 10 pixel line thickness");
	interations = 4000;
	rectangles(10);
	tft.fillScreen(myColors[11]);
	delay(10);
	tft.printStatusLine(0,myColors[1],myColors[11],"Filled Rectangles");
	interations = 4000;
	filledRectangles();
	tft.fillScreen(myColors[11]);
	delay(10);
	tft.printStatusLine(0,myColors[1],myColors[11],"Round Rectangles");
	interations = 30000;
	rRectangles(0);
	tft.fillScreen(myColors[11]);
	delay(10);
	tft.printStatusLine(0,myColors[1],myColors[11],"Round Rectangles 10 pixel line thickness");
	interations = 4000;
	rRectangles(10);
	tft.fillScreen(myColors[11]);
	delay(10);
	tft.printStatusLine(0,myColors[1],myColors[11],"Filled Round Rectangles");
	interations = 4000;
	filledRRectangles();
	tft.fillScreen(myColors[11]);
	delay(10);
	tft.printStatusLine(0,myColors[1],myColors[11],"Circles");
	interations = 30000;
	drawcircles(0);
	tft.fillScreen(myColors[11]);
	delay(10);
	tft.printStatusLine(0,myColors[1],myColors[11],"Circles 10 pixel circle thickness");
	interations = 4000;
	drawcircles(10);
	tft.fillScreen(myColors[11]);
	delay(10);
	tft.printStatusLine(0,myColors[1],myColors[11],"Filled Circles");
	interations = 4000;
	fillcircles();
	tft.fillScreen(myColors[11]);
	delay(10);
	tft.printStatusLine(0,myColors[1],myColors[11],"Triangles");
	interations = 30000;
	drawTriangles();
	tft.fillScreen(myColors[11]);
	delay(10);
	tft.printStatusLine(0,myColors[1],myColors[11],"Filled Triangles");
	interations = 4000;
	fillTriangles();
	tft.fillScreen(myColors[11]);
	delay(10);
	interations = 30000;
	tft.printStatusLine(0,myColors[1],myColors[11],"Ellipses");
	drawEllipses();
	tft.fillScreen(myColors[11]);
	delay(10);
	tft.printStatusLine(0,myColors[1],myColors[11],"Filled Ellipses");
	interations = 4000;
	fillEllipses();
	tft.fillScreen(myColors[11]);
	delay(10);
	tft.printStatusLine(0,myColors[1],myColors[11],"Lines");
	interations = 100000;
	drawlines();
	tft.fillScreen(myColors[11]);
	delay(10);
}

Hopefully I did not miss something:)
 
Last edited:
The original code expects to send screen coordinates for top-left and bottom-right corners to drawSquare().

If drawSquare() is expecting top-left and width&height then the maths is wrong. You don't need to subtract one from W & H.
 
The original code expects to send screen coordinates for top-left and bottom-right corners to drawSquare().

If drawSquare() is expecting top-left and width&height then the maths is wrong. You don't need to subtract one from W & H.

I did miss that. If choosing a random number between 0 and 1023 for width you would not need to subtract 1 from the width. The other thing that pop's into my mind is if the random(...) function would actually return a 0 value for a minimum number to get the full range of 1024 pixels.
 
I did miss that. If choosing a random number between 0 and 1023 for width you would not need to subtract 1 from the width. The other thing that pop's into my mind is if the random(...) function would actually return a 0 value for a minimum number to get the full range of 1024 pixels.

Think that was my fault. I was adding some clipping tests and changing things around. Think I messed up and doing the restructuring. Going to put it back to w, h and fix the test in drawSquare etc.
 
@wwatson - @MorganS -@KurtE

Just pushed some changes up to the font wip branch: https://github.com/mjs513/Ra8876LiteTeensy/tree/GFX-ILI-Font-WIP

1. Has @KurtE's PWM Backlite code incorporated
2. Fixed clipping for rectangle calls
3. Fixed graphics.ino: the current sketch graphics.ino sketch uses absolute coordinates for drawing rectangles as opposed to x,y,w,h. That is now fixed in the sketch
4. added a Rotate() function to rotate the screen 90degs ccw. However, its not working. It appears when I write to MACR register it doesn't retain the changed value so its always using the default value. Not sure whats going on. The code is based on an example I got from RAIO when I asked them a question on screen rotation.
5. I also added a simple test sketch for rotation.
 
Back
Top