Skip to content

Instantly share code, notes, and snippets.

@Volcano339
Last active June 20, 2023 09:37
Show Gist options
  • Save Volcano339/55a048626da193856df4910a7909ce7d to your computer and use it in GitHub Desktop.
Save Volcano339/55a048626da193856df4910a7909ce7d to your computer and use it in GitHub Desktop.
Visual artifacts updated demo
/********************************************************************
*
* ILI9341_T4 library example: Conway's game of life
*
* Thanks to Malt Whiskey for the improvements !
********************************************************************/
#include <Arduino.h>
#include <ILI9341_T4.h>
//
// DEFAULT WIRING USING SPI 0 ON TEENSY 4/4.1
//
//#define PIN_SCK 13 // mandatory
//#define PIN_MISO 12 // mandatory (if the display has no MISO line, set this to 255 but then VSync will be disabled)
//#define PIN_MOSI 11 // mandatory
//#define PIN_DC 10 // mandatory, can be any pin but using pin 10 (or 36 or 37 on T4.1) provides greater performance
#define PIN_CS 9 // optional (but recommended), can be any pin.
#define PIN_RESET 6 // optional (but recommended), can be any pin.
#define PIN_BACKLIGHT 2 // optional, set this only if the screen LED pin is connected directly to the Teensy.
#define PIN_TOUCH_IRQ 255 // 28 // optional, set this only if the touchscreen is connected on the same SPI bus
#define PIN_TOUCH_CS 4 // optional, set this only if the touchscreen is connected on the same spi bus
//
// ALTERNATE WIRING USING SPI 1 ON TEENSY 4/4.1
//
#define PIN_SCK 27 // mandatory
#define PIN_MISO 1 // mandatory (if the display has no MISO line, set this to 255 but then VSync will be disabled)
#define PIN_MOSI 26 // mandatory
#define PIN_DC 0 // mandatory, can be any pin but using pin 0 (or 38 on T4.1) provides greater performance
//#define PIN_CS 30 // optional (but recommended), can be any pin.
//#define PIN_RESET 29 // optional (but recommended), can be any pin.
//#define PIN_BACKLIGHT 255 // optional, set this only if the screen LED pin is connected directly to the Teensy.
//#define PIN_TOUCH_IRQ 255 // optional, set this only if the touchscreen is connected on the same SPI bus
//#define PIN_TOUCH_CS 255 // optional, set this only if the touchscreen is connected on the same spi bus
// 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);
// 2 diff buffers with 8K memory each
ILI9341_T4::DiffBuffStatic<8192> diff1;
ILI9341_T4::DiffBuffStatic<8192> diff2;
// Screen size in landscape mode
#define LX 320
#define LY 240
// 30MHz SPI. can do better with short wires
#define SPI_SPEED 10000000
// the framebuffers
DMAMEM uint16_t internal_fb[LX * LY]; // used by the library for buffering (in DMAMEM)
uint16_t fb[LX * LY]; // the main framebuffer we draw onto.
// size of a cell in pixels
#define CELL_LX 2 // possible value: 1, 2, 4, 8
#define CELL_LY 2 // possible values: 1, 2, 4, 8
// size of the 'world'
#define WORLD_LX (LX / CELL_LX)
#define WORLD_LY (LY / CELL_LY)
// two world buffers
uint8_t worldA[WORLD_LX * WORLD_LY];
uint8_t worldB[WORLD_LX * WORLD_LY];
// pointers to the 'current' and the 'previous' world
uint8_t* oldworld = worldA;
uint8_t* curworld = worldB;
// swap the two worlds pointers.
void swap_worlds()
{
auto tmp = oldworld;
oldworld = curworld;
curworld = tmp;
}
// draw a filled rectangle in the framebuffer
void draw_rect(int x, int y, int lx, int ly, uint16_t color)
{
uint16_t* p = fb + x + (LX * y);
for (int j = 0; j < ly; j++)
{
for (int i = 0; i < lx; i++) p[i] = color;
p += LX;
}
}
// create a random initial world with given density in [0.0f,1.0f]
void random_world(uint8_t* world, float density)
{
memset(world, 0, WORLD_LX * WORLD_LY);
const int thr = density * 1024;
for (int j = 1; j < WORLD_LY - 1; j++)
for (int i = 1; i < WORLD_LX - 1; i++)
world[i + (j*WORLD_LX)] = (random(0, 1024) < thr) ? 1 : 0;
}
// draw a world on the framebuffer
void draw_world(uint8_t* world, uint16_t color, uint16_t bkcolor)
{
for (int j = 0; j < WORLD_LY; j++)
for (int i = 0; i < WORLD_LX; i++)
draw_rect(i * CELL_LX, j * CELL_LY, CELL_LX, CELL_LY, world[(j*WORLD_LX) + i] ? color : bkcolor);
}
// number of neighbours of a cell
inline int nb_neighbours(int x, int y, uint8_t* world)
{
const int ind = x + (WORLD_LX * y);
return world[ind - WORLD_LX - 1] + world[ind - WORLD_LX] + world[ind - WORLD_LX + 1] +
world[ind -1] + world[ind + 1] +
world[ind + WORLD_LX - 1] + world[ind + WORLD_LX] + world[ind + WORLD_LX + 1];
}
const int HASH_LIST_SIZE = 128; // max number of previous hash to store
static uint32_t hash_list[HASH_LIST_SIZE]; // array of previou hashes
int hash_nr = 0; // number of hashed currently stored
// check if the current hash appears multiple times among the
// previous ones. If so we have reached a periodic config so
// and recreate a anew world.
void check_world(uint32_t hash)
{
uint16_t matches = 0;
for (uint16_t i = 0; i < hash_nr; i++)
{
if (hash_list[i] == hash) matches++;
}
if (matches >= 2)
{
hash_nr = 0;
random_world(oldworld, random(1,1024) / 1024.0f);
return;
}
if (hash_nr == HASH_LIST_SIZE) hash_nr = 0;
hash_list[hash_nr++] = hash;
}
// compute the world at the next generation.
uint32_t compute(uint8_t* cur_world, uint8_t* new_world)
{
uint32_t hash = 0;
for (int j = 1; j < WORLD_LY - 1; j++)
{
for (int i = 1; i < WORLD_LX - 1; i++)
{
const int ind = i + (WORLD_LX * j);
const int nbn = nb_neighbours(i, j, cur_world);
hash += nbn * (j * 3 + i * 5);
if (nbn == 3) new_world[ind] = 1;
else if ((nbn < 2) || (nbn > 3)) new_world[ind] = 0;
else new_world[ind] = cur_world[ind];
}
}
return hash;
}
void setup()
{
Serial.begin(9600);
pinMode(28, INPUT_PULLUP);
tft.output(&Serial); // output debug infos to serial port.
while (!tft.begin(SPI_SPEED)); // initialize the display
tft.setRotation(3); // landscape mode 320x240
tft.setFramebuffer(internal_fb); // set the internal framebuffer (enables 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 (useful because cells are small...)
tft.setRefreshRate(120); // Set the refresh rate to around 120Hz
tft.setVSyncSpacing(1); // set framerate = refreshrate (we must draw the fast for this to works: ok in our case).
if (PIN_BACKLIGHT != 255)
{ // make sure backlight is on
pinMode(PIN_BACKLIGHT, OUTPUT);
digitalWrite(PIN_BACKLIGHT, HIGH);
}
random_world(curworld, 0.7f); // compute initial world ditribution
}
int nbf = 0; // count the number of frames drawn.
int calib[4];
void loop()
{
uint32_t hash = compute(curworld, oldworld); // compute the next generation
check_world(hash); // check if we should recreate a new world
swap_worlds(); //swap between old and new worlds
draw_world(curworld, ILI9341_T4_COLOR_GREEN, ILI9341_T4_COLOR_BLACK); // draw onto the framebuffer
while(!calib[0])
tft.calibrateTouch(calib);
tft.overlayFPS(fb); // draw the FPS counter on the top right corner of the framebuffer
int tx, ty, tz;
bool touched = tft.readTouch(tx, ty, tz);;
char hud_buffer[128];
sprintf(hud_buffer, "Touch: %d x: %d y: %d z: %d", touched, tx, ty, tz);
tft.overlayText(fb, hud_buffer, 2, 0, 14, ILI9341_T4_COLOR_WHITE, 1.0f, ILI9341_T4_COLOR_RED, 0.3f, 1); // draw text
tft.update(fb); // push to screen (asynchronously).
if (++nbf % 1000 == 500)
{ // output some stats on Serial every 1000 frames.
tft.printStats(); // stats about the driver
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment