
Originally Posted by
neutron7
That's good to hear, thanks! A simple example set up with t3/t3n with a few 2d primitives and text using alpha blending would be a big help getting people started.
another question, can you mix tgx with ILI9341_t3(n) commands? Im wondering that because someone might want to add it to existing code.
Yes, you can freely mix tgx with any other library since it does not access any peripheral hardware. The only thing tgx does is to write inside a tgx:Image which is basically a just buffer in RAM...
Here is a very basic example combining tgx with Kurte's ILI9341_t3n library. The animation we want to display is first drawn in memory using tgx methods and then pushed to the screen using the ILI9341_t3n lib. Here we use double buffering to prevent flickering.
Code:
#include <SPI.h>
#include <ILI9341_t3n.h>
#include <tgx.h>
#include <font_tgx_Arial_Bold.h>
// we assume that the display is connected on SPI0
#define TFT_CS 10
#define TFT_DC 9
#define TFT_RST 6
#define SPI_SPEED 30000000
ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC, TFT_RST);
// let's use double buffering (each framebuffer occupies 150Kb in DMAMEM)
DMAMEM uint16_t fb1[320 * 240];
DMAMEM uint16_t fb2[320 * 240];
tgx::Image<tgx::RGB565> im(fb1, 320, 240); // the tgx image pointing to the framebuffer we are currently drawing on
// display the content of im and swap the frame buffers (update is done async via DMA).
void displayAndSwapFB()
{
while (tft.asyncUpdateActive()) { yield(); } // wait for previous screen update to complete.
if ((uint16_t *)im.data() == fb1)
{ // we were drawing on fb1
tft.setFrameBuffer(fb1);
im.set(fb2, 320, 240);
}
else
{ // we were drawing on fb2
tft.setFrameBuffer(fb2);
im.set(fb1, 320, 240);
}
tft.updateScreenAsync(false); // start new update;
}
void setup()
{
Serial.begin(9600);
tft.begin();
tft.setRotation(1);
tft.useFrameBuffer(true);
}
elapsedMillis em;
void loop()
{
const float t = (em % 10000) / 10000.0f;
// erase the image
im.fillScreen(tgx::RGB565_White);
// draw a fixed ellipse with solid colors (red outline and green interior)
im.fillEllipse({ 230, 120 }, { 70,120 }, tgx::RGB565_Green, tgx::RGB565_Red);
// draw a moving text
im.drawText("Hello World!", { t * 100, t * 290 }, tgx::RGB565_Orange, font_tgx_Arial_Bold_32, true,0.7f);
// draw a moving gradient triangle
tgx::fVec2 center = { 150, 120 };
const float c = cosf(t * M_PI * 2);
const float s = sinf(t * M_PI * 2);
const tgx::fVec2 P1 = center + tgx::fVec2{180 * c, 180 * s};
const tgx::fVec2 P2 = center + tgx::fVec2{ 80 * c, -80 * s };
const tgx::fVec2 P3 = center + tgx::fVec2{ -110 * c, 110 * s };
im.drawGradientTriangle(P1, P2, P3, tgx::RGB565_Green, tgx::RGB565_Blue, tgx::RGB565_Red, 0.4f);
// display the image on the screen
displayAndSwapFB();
}
With SPI set at 30Mhz, this gives about 24FPS, limited by the time it takes to upload full frames to the screen...
This is not a bad frame rate but let me just (shamelessly) mention that, if you can swap the CS and DC pin and use my ILI9341_T4 library, then the same animation runs smoothly at 120FPS without any screen tearing and with the SPI bus clocked at only 15Mhz using the code below:
Code:
#include <ILI9341_T4.h>
#include <tgx.h>
#include <font_tgx_Arial_Bold.h>
#define SPI_SPEED 15000000
#define PIN_CS 9
#define PIN_DC 10
#define PIN_RESET 6
#define PIN_SCK 13
#define PIN_MISO 12
#define PIN_MOSI 11
// the screen driver object
ILI9341_T4::ILI9341Driver tft(PIN_CS, PIN_DC, PIN_SCK, PIN_MOSI, PIN_MISO, PIN_RESET);
ILI9341_T4::DiffBuffStatic<6000> diff1;
ILI9341_T4::DiffBuffStatic<6000> diff2;
uint16_t internal_fb[320 * 240];
uint16_t fb[320*240];
tgx::Image<tgx::RGB565> im(fb, 320, 240);
void setup()
{
Serial.begin(9600);
tft.output(&Serial);
while (!tft.begin(SPI_SPEED))
{
Serial.println("Initialization error...");
delay(1000);
}
tft.setRotation(3); // portrait mode 240 x320
tft.setFramebuffers(internal_fb); // set 1 internal framebuffer -> activate double buffering.
tft.setDiffBuffers(&diff1, &diff2); // set the 2 diff buffers => activate differential updates.
tft.setDiffGap(4); // use a small gap for the diff buffers
tft.setRefreshRate(120); // around 120hz for the display refresh rate.
tft.setVSyncSpacing(1); // set framerate = refreshrate/2 (and enable vsync at the same time).
}
elapsedMillis em;
void loop()
{
const float t = (em % 10000) / 10000.0f;
// erase the image
im.fillScreen(tgx::RGB565_White);
// draw a fixed ellipse with solid colors (red outline and green interior)
im.fillEllipse({ 230, 120 }, { 70,120 }, tgx::RGB565_Green, tgx::RGB565_Red);
// draw a moving text
im.drawText("Hello World!", { t * 100, t * 290 }, tgx::RGB565_Orange, font_tgx_Arial_Bold_32, true,0.7f);
// draw a moving gradient triangle
tgx::fVec2 center = { 150, 120 };
const float c = cosf(t * M_PI * 2);
const float s = sinf(t * M_PI * 2);
const tgx::fVec2 P1 = center + tgx::fVec2{180 * c, 180 * s};
const tgx::fVec2 P2 = center + tgx::fVec2{ 80 * c, -80 * s };
const tgx::fVec2 P3 = center + tgx::fVec2{ -110 * c, 110 * s };
im.drawGradientTriangle(P1, P2, P3, tgx::RGB565_Green, tgx::RGB565_Blue, tgx::RGB565_Red, 0.4f);
// display the image on the screen
tft.update(fb);
}