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 }