Arduino:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | #include <SPI.h> #include <Adafruit_GFX.h> #include <Adafruit_ST7789.h> #define TFT_MOSI 19 // SDA (SPI MOSI) #define TFT_SCLK 18 // SCL (SPI SCK) #define TFT_CS 5 #define TFT_DC 16 #define TFT_RST 23 #define KEY1_PIN 35 // 使用者按鈕 P35 Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST); static inline uint16_t RGB565(uint8_t r, uint8_t g, uint8_t b) { return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); } // ====== 虎嘴繪圖 ====== void drawMuzzleBase(int cx, int cy) { uint16_t furLight = RGB565(235, 228, 210); uint16_t shadow = RGB565(160, 150, 135); uint16_t dotDark = RGB565(70, 70, 70); tft.fillScreen(ST77XX_BLACK); // 口鼻白毛區(只畫嘴周圍,像照片的口鼻墊) tft.fillCircle(cx - 55, cy + 10, 55, furLight); tft.fillCircle(cx + 55, cy + 10, 55, furLight); tft.fillRoundRect(cx - 90, cy - 25, 180, 110, 28, furLight); // 下巴陰影 tft.fillRoundRect(cx - 85, cy + 60, 170, 22, 12, shadow); tft.fillRoundRect(cx - 40, cy + 55, 80, 10, 5, RGB565(120,110,100)); // 鬍鬚點 int dotY1 = cy + 6; int dotY2 = cy + 26; for (int k = 0; k < 3; k++) { tft.fillCircle(cx - 55 - k * 12, dotY1 + k * 2, 2, dotDark); tft.fillCircle(cx - 55 - k * 12, dotY2 + k * 2, 2, dotDark); tft.fillCircle(cx + 55 + k * 12, dotY1 + k * 2, 2, dotDark); tft.fillCircle(cx + 55 + k * 12, dotY2 + k * 2, 2, dotDark); } } void drawMouthClosed(int cx, int cy) { uint16_t lineDark = RGB565(40, 40, 40); // 人中線(嘴巴上方中線) tft.drawLine(cx, cy - 5, cx, cy + 20, lineDark); tft.drawLine(cx - 1, cy - 5, cx - 1, cy + 20, RGB565(80,80,80)); // 上唇弧線 int mouthY = cy + 18; for (int i = 0; i < 34; i++) { int y = mouthY + (i * i) / 95; tft.drawPixel(cx - i, y, lineDark); tft.drawPixel(cx - i, y + 1, lineDark); tft.drawPixel(cx + i, y, lineDark); tft.drawPixel(cx + i, y + 1, lineDark); } // 嘴角 tft.fillCircle(cx - 36, mouthY + 12, 3, lineDark); tft.fillCircle(cx + 36, mouthY + 12, 3, lineDark); // 嘴中間小暗線 tft.fillRoundRect(cx - 10, mouthY + 20, 20, 4, 2, RGB565(90,80,70)); } void drawMouthOpen(int cx, int cy) { uint16_t lineDark = RGB565(40, 40, 40); uint16_t mouthIn = RGB565(15, 15, 15); // 口腔黑 uint16_t tongue = RGB565(210, 120, 120); // 舌頭粉 // 人中線(到上唇) tft.drawLine(cx, cy - 5, cx, cy + 10, lineDark); tft.drawLine(cx - 1, cy - 5, cx - 1, cy + 10, RGB565(80,80,80)); // 上唇弧線(稍微更彎) int mouthY = cy + 16; for (int i = 0; i < 36; i++) { int y = mouthY + (i * i) / 85; tft.drawPixel(cx - i, y, lineDark); tft.drawPixel(cx - i, y + 1, lineDark); tft.drawPixel(cx + i, y, lineDark); tft.drawPixel(cx + i, y + 1, lineDark); } // 嘴角 tft.fillCircle(cx - 38, mouthY + 14, 3, lineDark); tft.fillCircle(cx + 38, mouthY + 14, 3, lineDark); // 口腔 int w = 90, h = 45; int x = cx - w / 2; int y0 = cy + 28; tft.fillRoundRect(x, y0, w, h, 18, mouthIn); tft.fillCircle(cx, y0 + h, 22, mouthIn); // 舌頭 tft.fillRoundRect(cx - 22, y0 + 18, 44, 20, 10, tongue); tft.fillCircle(cx, y0 + 36, 14, tongue); } void drawTigerMouth(bool open) { int cx = tft.width() / 2; int cy = tft.height() / 2; drawMuzzleBase(cx, cy); if (open) drawMouthOpen(cx, cy); else drawMouthClosed(cx, cy); } // ====== 按鍵去彈跳 + 切換 ====== bool mouthOpen = false; // 若你的按鍵「按下讀到 LOW」,用 true;若「按下讀到 HIGH」,用 false const bool ACTIVE_LOW = true; int lastStable = HIGH; int lastRead = HIGH; unsigned long lastDebounceMs = 0; const unsigned long DEBOUNCE_MS = 30; bool isPressedLevel(int level) { return ACTIVE_LOW ? (level == LOW) : (level == HIGH); } void setup() { SPI.begin(TFT_SCLK, -1, TFT_MOSI, TFT_CS); tft.init(240, 320); // 若你確認是 170x320,再改成 tft.init(170, 320) tft.setRotation(1); pinMode(KEY1_PIN, INPUT); // P35 無內建上拉下拉,用板上電阻 drawTigerMouth(mouthOpen); } void loop() { int reading = digitalRead(KEY1_PIN); if (reading != lastRead) { lastDebounceMs = millis(); lastRead = reading; } if (millis() - lastDebounceMs > DEBOUNCE_MS) { if (reading != lastStable) { lastStable = reading; // 只在「按下瞬間」切換一次 if (isPressedLevel(lastStable)) { mouthOpen = !mouthOpen; drawTigerMouth(mouthOpen); } } } } |
沒有留言:
張貼留言