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 |
boing_ball.ino (6727B)
1 // 'Boing' ball demo 2 3 // STM32F767 55MHz SPI 170 fps without DMA 4 // STM32F767 55MHz SPI 227 fps with DMA 5 // STM32F446 55MHz SPI 110 fps without DMA 6 // STM32F446 55MHz SPI 187 fps with DMA 7 // STM32F401 55MHz SPI 56 fps without DMA 8 // STM32F401 55MHz SPI 120 fps with DMA 9 10 // STM32F767 27MHz SPI 99 fps without DMA 11 // STM32F767 27MHz SPI 120 fps with DMA 12 // STM32F446 27MHz SPI 73 fps without DMA 13 // STM32F446 27MHz SPI 97 fps with DMA 14 // STM32F401 27MHz SPI 51 fps without DMA 15 // STM32F401 27MHz SPI 90 fps with DMA 16 17 // Blue Pill - 36MHz SPI *no* DMA 36 fps 18 // Blue Pill - 36MHz SPI with DMA 67 fps 19 // Blue Pill overclocked to 128MHz *no* DMA - 32MHz SPI 64 fps 20 // Blue Pill overclocked to 128MHz with DMA - 32MHz SPI 116 fps 21 22 // ESP32 - 8 bit parallel 110 fps (no DMA) 23 // ESP32 - 40MHz SPI *no* DMA 93 fps 24 // ESP32 - 40MHz SPI with DMA 112 fps 25 26 #define SCREENWIDTH 320 27 #define SCREENHEIGHT 240 28 29 #include "graphic.h" 30 31 #include <TFT_eSPI.h> // Hardware-specific library 32 33 TFT_eSPI tft = TFT_eSPI(); // Invoke custom library 34 35 #define BGCOLOR 0xAD75 36 #define GRIDCOLOR 0xA815 37 #define BGSHADOW 0x5285 38 #define GRIDSHADOW 0x600C 39 #define RED 0xF800 40 #define WHITE 0xFFFF 41 42 #define YBOTTOM 123 // Ball Y coordinate at bottom 43 #define YBOUNCE -3.5 // Upward velocity on ball bounce 44 45 // Ball coordinates are stored floating-point because screen refresh 46 // is so quick, whole-pixel movements are just too fast! 47 float ballx = 20.0, bally = YBOTTOM, // Current ball position 48 ballvx = 0.8, ballvy = YBOUNCE, // Ball velocity 49 ballframe = 3; // Ball animation frame # 50 int balloldx = ballx, balloldy = bally; // Prior ball position 51 52 // Working buffer for ball rendering...2 scan lines that alternate, 53 // one is rendered while the other is transferred via DMA. 54 uint16_t renderbuf[2][SCREENWIDTH]; 55 56 uint16_t palette[16]; // Color table for ball rotation effect 57 58 uint32_t startTime, frame = 0; // For frames-per-second estimate 59 60 void setup() { 61 Serial.begin(115200); 62 // while(!Serial); 63 64 tft.begin(); 65 tft.setRotation(3); // Landscape orientation, USB at bottom right 66 tft.setSwapBytes(false); 67 // Draw initial frame buffer contents: 68 //tft.setBitmapColor(GRIDCOLOR, BGCOLOR); 69 tft.fillScreen(BGCOLOR); 70 71 tft.initDMA(); 72 73 tft.drawBitmap(0, 0, (const uint8_t *)background, SCREENWIDTH, SCREENHEIGHT, GRIDCOLOR); 74 75 startTime = millis(); 76 } 77 78 void loop() { 79 80 balloldx = (int16_t)ballx; // Save prior position 81 balloldy = (int16_t)bally; 82 ballx += ballvx; // Update position 83 bally += ballvy; 84 ballvy += 0.06; // Update Y velocity 85 if((ballx <= 15) || (ballx >= SCREENWIDTH - BALLWIDTH)) 86 ballvx *= -1; // Left/right bounce 87 if(bally >= YBOTTOM) { // Hit ground? 88 bally = YBOTTOM; // Clip and 89 ballvy = YBOUNCE; // bounce up 90 } 91 92 // Determine screen area to update. This is the bounds of the ball's 93 // prior and current positions, so the old ball is fully erased and new 94 // ball is fully drawn. 95 int16_t minx, miny, maxx, maxy, width, height; 96 // Determine bounds of prior and new positions 97 minx = ballx; 98 if(balloldx < minx) minx = balloldx; 99 miny = bally; 100 if(balloldy < miny) miny = balloldy; 101 maxx = ballx + BALLWIDTH - 1; 102 if((balloldx + BALLWIDTH - 1) > maxx) maxx = balloldx + BALLWIDTH - 1; 103 maxy = bally + BALLHEIGHT - 1; 104 if((balloldy + BALLHEIGHT - 1) > maxy) maxy = balloldy + BALLHEIGHT - 1; 105 106 width = maxx - minx + 1; 107 height = maxy - miny + 1; 108 109 // Ball animation frame # is incremented opposite the ball's X velocity 110 ballframe -= ballvx * 0.5; 111 if(ballframe < 0) ballframe += 14; // Constrain from 0 to 13 112 else if(ballframe >= 14) ballframe -= 14; 113 114 // Set 7 palette entries to white, 7 to red, based on frame number. 115 // This makes the ball spin 116 for(uint8_t i=0; i<14; i++) { 117 palette[i+2] = ((((int)ballframe + i) % 14) < 7) ? WHITE : RED; 118 // Palette entries 0 and 1 aren't used (clear and shadow, respectively) 119 } 120 121 // Only the changed rectangle is drawn into the 'renderbuf' array... 122 uint16_t c, *destPtr; 123 int16_t bx = minx - (int)ballx, // X relative to ball bitmap (can be negative) 124 by = miny - (int)bally, // Y relative to ball bitmap (can be negative) 125 bgx = minx, // X relative to background bitmap (>= 0) 126 bgy = miny, // Y relative to background bitmap (>= 0) 127 x, y, bx1, bgx1; // Loop counters and working vars 128 uint8_t p; // 'packed' value of 2 ball pixels 129 int8_t bufIdx = 0; 130 131 // Start SPI transaction and drop TFT_CS - avoids transaction overhead in loop 132 tft.startWrite(); 133 134 // Set window area to pour pixels into 135 tft.setAddrWindow(minx, miny, width, height); 136 137 // Draw line by line loop 138 for(y=0; y<height; y++) { // For each row... 139 destPtr = &renderbuf[bufIdx][0]; 140 bx1 = bx; // Need to keep the original bx and bgx values, 141 bgx1 = bgx; // so copies of them are made here (and changed in loop below) 142 for(x=0; x<width; x++) { 143 if((bx1 >= 0) && (bx1 < BALLWIDTH) && // Is current pixel row/column 144 (by >= 0) && (by < BALLHEIGHT)) { // inside the ball bitmap area? 145 // Yes, do ball compositing math... 146 p = ball[by][bx1 / 2]; // Get packed value (2 pixels) 147 c = (bx1 & 1) ? (p & 0xF) : (p >> 4); // Unpack high or low nibble 148 if(c == 0) { // Outside ball - just draw grid 149 c = background[bgy][bgx1 / 8] & (0x80 >> (bgx1 & 7)) ? GRIDCOLOR : BGCOLOR; 150 } else if(c > 1) { // In ball area... 151 c = palette[c]; 152 } else { // In shadow area... 153 c = background[bgy][bgx1 / 8] & (0x80 >> (bgx1 & 7)) ? GRIDSHADOW : BGSHADOW; 154 } 155 } else { // Outside ball bitmap, just draw background bitmap... 156 c = background[bgy][bgx1 / 8] & (0x80 >> (bgx1 & 7)) ? GRIDCOLOR : BGCOLOR; 157 } 158 *destPtr++ = c<<8 | c>>8; // Store pixel colour 159 bx1++; // Increment bitmap position counters (X axis) 160 bgx1++; 161 } 162 163 tft.pushPixelsDMA(&renderbuf[bufIdx][0], width); // Push line to screen 164 165 // Push line to screen (swap bytes false for STM/ESP32) 166 //tft.pushPixels(&renderbuf[bufIdx][0], width); 167 168 bufIdx = 1 - bufIdx; 169 by++; // Increment bitmap position counters (Y axis) 170 bgy++; 171 } 172 //if (random(100) == 1) delay(2000); 173 tft.endWrite(); 174 //delay(5); 175 // Show approximate frame rate 176 if(!(++frame & 255)) { // Every 256 frames... 177 uint32_t elapsed = (millis() - startTime) / 1000; // Seconds 178 if(elapsed) { 179 Serial.print(frame / elapsed); 180 Serial.println(" fps"); 181 } 182 } 183 }