acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

Bouncy_Circles.ino (6625B)

      1 // This sketch is for the RP2040 and ILI9341 TFT display.
      2 // Other processors may work if they have sufficient RAM for
      3 // a full screen buffer (240 x 320 x 2 = 153,600 bytes).
      4 
      5 // In this example 2 sprites are used to create DMA toggle
      6 // buffers. Each sprite is half the screen size, this allows
      7 // graphics to be rendered in one sprite at the same time
      8 // as the other sprite is being sent to the screen.
      9 
     10 // RP2040 typically runs at 45-48 fps
     11 
     12 // Created by Bodmer 20/04/2021 as an example for:
     13 // https://github.com/Bodmer/TFT_eSPI
     14 
     15 // Number of circles to draw
     16 #define CNUMBER 42
     17 
     18 // Define the width and height according to the TFT and the
     19 // available memory. The sprites will require:
     20 //     DWIDTH * DHEIGHT * 2 bytes of RAM
     21 // Note: for a 240 * 320 area this is 150 Kbytes!
     22 #define DWIDTH  240
     23 #define DHEIGHT 320
     24 
     25 #include <TFT_eSPI.h>
     26 
     27 // Library instance
     28 TFT_eSPI    tft = TFT_eSPI();
     29 
     30 // Create two sprites for a DMA toggle buffer
     31 TFT_eSprite spr[2] = {TFT_eSprite(&tft), TFT_eSprite(&tft)};
     32 
     33 // Pointers to start of Sprites in RAM (these are then "image" pointers)
     34 uint16_t* sprPtr[2];
     35 
     36 // Used for fps measuring
     37 uint16_t counter = 0;
     38 int32_t startMillis = millis();
     39 uint16_t interval = 100;
     40 String fps = "xx.xx fps";
     41 
     42 // Structure to hold circle plotting parameters
     43 typedef struct circle_t {
     44   int16_t   cx[CNUMBER] = { 0 }; // x coordinate of centre
     45   int16_t   cy[CNUMBER] = { 0 }; // y coordinate of centre
     46   int16_t   cr[CNUMBER] = { 0 }; // radius
     47   uint16_t col[CNUMBER] = { 0 }; // colour
     48   int16_t   dx[CNUMBER] = { 0 }; // x movement & direction
     49   int16_t   dy[CNUMBER] = { 0 }; // y movement & direction
     50 } circle_param;
     51 
     52 // Create the structure and get a pointer to it
     53 circle_t *circle = new circle_param;
     54 
     55 // #########################################################################
     56 // Setup
     57 // #########################################################################
     58 void setup() {
     59   Serial.begin(115200);
     60 
     61   tft.init();
     62   tft.initDMA();
     63   tft.fillScreen(TFT_BLACK);
     64 
     65   // Create the 2 sprites, each is half the size of the screen
     66   sprPtr[0] = (uint16_t*)spr[0].createSprite(DWIDTH, DHEIGHT / 2);
     67   sprPtr[1] = (uint16_t*)spr[1].createSprite(DWIDTH, DHEIGHT / 2);
     68 
     69   // Move the sprite 1 coordinate datum upwards half the screen height
     70   // so from coordinate point of view it occupies the bottom of screen
     71   spr[1].setViewport(0, -DHEIGHT / 2, DWIDTH, DHEIGHT);
     72 
     73   // Define text datum for each Sprite
     74   spr[0].setTextDatum(MC_DATUM);
     75   spr[1].setTextDatum(MC_DATUM);
     76 
     77   // Seed the random number generator
     78   randomSeed(analogRead(A0));
     79 
     80   // Initialise circle parameters
     81   for (uint16_t i = 0; i < CNUMBER; i++) {
     82     circle->cr[i] = random(12, 24);
     83     circle->cx[i] = random(circle->cr[i], DWIDTH - circle->cr[i]);
     84     circle->cy[i] = random(circle->cr[i], DHEIGHT - circle->cr[i]);
     85     
     86     circle->col[i] = rainbow(4 * i);
     87     circle->dx[i] = random(1, 5);
     88     if (random(2)) circle->dx[i] = -circle->dx[i];
     89     circle->dy[i] = random(1, 5);
     90     if (random(2)) circle->dy[i] = -circle->dy[i];
     91   }
     92 
     93   tft.startWrite(); // TFT chip select held low permanently
     94 
     95   startMillis = millis();
     96 }
     97 
     98 // #########################################################################
     99 // Loop
    100 // #########################################################################
    101 void loop() {
    102   drawUpdate(0); // Update top half
    103   drawUpdate(1); // Update bottom half
    104 
    105   // Calculate the fps every <interval> iterations.
    106   counter++;  
    107   if (counter % interval == 0) {
    108     long millisSinceUpdate = millis() - startMillis;
    109     fps = String((interval * 1000.0 / (millisSinceUpdate))) + " fps";
    110     Serial.println(fps);
    111     startMillis = millis();
    112   }
    113 }
    114 
    115 // #########################################################################
    116 // Render circles to sprite 0 or 1 and initiate DMA
    117 // #########################################################################
    118 void drawUpdate (bool sel) {
    119   spr[sel].fillSprite(TFT_BLACK);
    120   for (uint16_t i = 0; i < CNUMBER; i++) {
    121     // Draw (Note sprite 1 datum was moved, so coordinates do not need to be adjusted
    122     spr[sel].fillCircle(circle->cx[i], circle->cy[i], circle->cr[i], circle->col[i]);
    123     spr[sel].drawCircle(circle->cx[i], circle->cy[i], circle->cr[i], TFT_WHITE);
    124     spr[sel].setTextColor(TFT_BLACK, circle->col[i]);
    125     spr[sel].drawNumber(i + 1, 1 + circle->cx[i], circle->cy[i], 2);
    126   }
    127 
    128   tft.pushImageDMA(0, sel * DHEIGHT / 2, DWIDTH, DHEIGHT / 2, sprPtr[sel]);
    129 
    130   // Update circle positions after bottom half has been drawn
    131   if (sel) {
    132     for (uint16_t i = 0; i < CNUMBER; i++) {
    133       circle->cx[i] += circle->dx[i];
    134       circle->cy[i] += circle->dy[i];
    135       if (circle->cx[i] <= circle->cr[i]) {
    136         circle->cx[i] = circle->cr[i];
    137         circle->dx[i] = -circle->dx[i];
    138       }
    139       else if (circle->cx[i] + circle->cr[i] >= DWIDTH - 1) {
    140         circle->cx[i] = DWIDTH - circle->cr[i] - 1;
    141         circle->dx[i] = -circle->dx[i];
    142       }
    143       if (circle->cy[i] <= circle->cr[i]) {
    144         circle->cy[i] = circle->cr[i];
    145         circle->dy[i] = -circle->dy[i];
    146       }
    147       else if (circle->cy[i] + circle->cr[i] >= DHEIGHT - 1) {
    148         circle->cy[i] = DHEIGHT - circle->cr[i] - 1;
    149         circle->dy[i] = -circle->dy[i];
    150       }
    151     }
    152   }
    153 }
    154 
    155 // #########################################################################
    156 // Return a 16 bit rainbow colour
    157 // #########################################################################
    158 uint16_t rainbow(byte value)
    159 {
    160   // If 'value' is in the range 0-159 it is converted to a spectrum colour
    161   // from 0 = red through to 127 = blue to 159 = violet
    162   // Extending the range to 0-191 adds a further violet to red band
    163 
    164   value = value % 192;
    165 
    166   byte red   = 0; // Red is the top 5 bits of a 16 bit colour value
    167   byte green = 0; // Green is the middle 6 bits, but only top 5 bits used here
    168   byte blue  = 0; // Blue is the bottom 5 bits
    169 
    170   byte sector = value >> 5;
    171   byte amplit = value & 0x1F;
    172 
    173   switch (sector)
    174   {
    175     case 0:
    176       red   = 0x1F;
    177       green = amplit; // Green ramps up
    178       blue  = 0;
    179       break;
    180     case 1:
    181       red   = 0x1F - amplit; // Red ramps down
    182       green = 0x1F;
    183       blue  = 0;
    184       break;
    185     case 2:
    186       red   = 0;
    187       green = 0x1F;
    188       blue  = amplit; // Blue ramps up
    189       break;
    190     case 3:
    191       red   = 0;
    192       green = 0x1F - amplit; // Green ramps down
    193       blue  = 0x1F;
    194       break;
    195     case 4:
    196       red   = amplit; // Red ramps up
    197       green = 0;
    198       blue  = 0x1F;
    199       break;
    200     case 5:
    201       red   = 0x1F;
    202       green = 0;
    203       blue  = 0x1F - amplit; // Blue ramps down
    204       break;
    205   }
    206 
    207   return red << 11 | green << 6 | blue;
    208 }