#include "uf2.h" #if USE_SCREEN #include #define DISPLAY_WIDTH 160 #define DISPLAY_HEIGHT 128 // Overlap 4x chars by this much. #define CHAR4_KERNING 2 // Width of a single 4x char, adjusted by kerning #define CHAR4_KERNED_WIDTH (6 * 4 - CHAR4_KERNING) #define ST7735_NOP 0x00 #define ST7735_SWRESET 0x01 #define ST7735_RDDID 0x04 #define ST7735_RDDST 0x09 #define ST7735_SLPIN 0x10 #define ST7735_SLPOUT 0x11 #define ST7735_PTLON 0x12 #define ST7735_NORON 0x13 #define ST7735_INVOFF 0x20 #define ST7735_INVON 0x21 #define ST7735_DISPOFF 0x28 #define ST7735_DISPON 0x29 #define ST7735_CASET 0x2A #define ST7735_RASET 0x2B #define ST7735_RAMWR 0x2C #define ST7735_RAMRD 0x2E #define ST7735_PTLAR 0x30 #define ST7735_COLMOD 0x3A #define ST7735_MADCTL 0x36 #define ST7735_FRMCTR1 0xB1 #define ST7735_FRMCTR2 0xB2 #define ST7735_FRMCTR3 0xB3 #define ST7735_INVCTR 0xB4 #define ST7735_DISSET5 0xB6 #define ST7735_PWCTR1 0xC0 #define ST7735_PWCTR2 0xC1 #define ST7735_PWCTR3 0xC2 #define ST7735_PWCTR4 0xC3 #define ST7735_PWCTR5 0xC4 #define ST7735_VMCTR1 0xC5 #define ST7735_RDID1 0xDA #define ST7735_RDID2 0xDB #define ST7735_RDID3 0xDC #define ST7735_RDID4 0xDD #define ST7735_PWCTR6 0xFC #define ST7735_GMCTRP1 0xE0 #define ST7735_GMCTRN1 0xE1 uint32_t lookupCfg(uint32_t key, uint32_t defl); #define CFG(v) lookupCfg(CFG_##v, 0x42) uint32_t lookupCfg(uint32_t key, uint32_t defl) { const uint32_t *ptr = UF2_BINFO->config_data; if (!ptr || (((uint32_t)ptr) & 3) || *ptr != CFG_MAGIC0) { // no config data! } else { ptr += 4; while (*ptr) { if (*ptr == key) return ptr[1]; ptr += 2; } } if (defl == 0x42) while (1) ; return defl; } void pin_set(int pincfg, int v) { int pin = lookupCfg(pincfg, -1); if (pin < 0) return; if (v) { PINOP(pin, OUTSET); } else { PINOP(pin, OUTCLR); } } void setup_output_pin(int pincfg) { int pin = lookupCfg(pincfg, -1); if (pin < 0) return; PINOP(pin, DIRSET); PINOP(pin, OUTCLR); } #define PINPORT(pin) PORT->Group[(pin) / 32] #define pinmask(pin) (1 << (pin & 0x1f)) void transfer(uint8_t *ptr, uint32_t len) { int mosi = CFG(PIN_DISPLAY_MOSI); int sck = CFG(PIN_DISPLAY_SCK); volatile uint32_t *mosi_set = &PINPORT(mosi).OUTSET.reg; volatile uint32_t *mosi_clr = &PINPORT(mosi).OUTCLR.reg; volatile uint32_t *sck_tgl = &PINPORT(sck).OUTTGL.reg; uint32_t mosi_mask = pinmask(mosi); uint32_t sck_mask = pinmask(sck); PINOP(sck, OUTCLR); uint8_t mask = 0, b; for (;;) { if (!mask) { if (!len--) break; mask = 0x80; b = *ptr++; } if (b & mask) *mosi_set = mosi_mask; else *mosi_clr = mosi_mask; *sck_tgl = sck_mask; mask >>= 1; *sck_tgl = sck_mask; } } #define DELAY 0x80 // clang-format off static const uint8_t initCmds[] = { ST7735_SWRESET, DELAY, // 1: Software reset, 0 args, w/delay 120, // 150 ms delay ST7735_SLPOUT , DELAY, // 2: Out of sleep mode, 0 args, w/delay 120, // 500 ms delay ST7735_INVOFF , 0 , // 13: Don't invert display, no args, no delay ST7735_COLMOD , 1 , // 15: set color mode, 1 arg, no delay: 0x05, // 16-bit color ST7735_GMCTRP1, 16 , // 1: Magical unicorn dust, 16 args, no delay: 0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d, 0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10, ST7735_NORON , DELAY, // 3: Normal display on, no args, w/delay 10, // 10 ms delay ST7735_DISPON , DELAY, // 4: Main screen turn on, no args w/delay 10, 0, 0 // END }; // clang-format on static uint8_t cmdBuf[20]; #define SET_DC(v) pin_set(CFG_PIN_DISPLAY_DC, v) #define SET_CS(v) pin_set(CFG_PIN_DISPLAY_CS, v) static void scr_delay(unsigned msec) { int k = msec * 15000; while (k--) asm("nop"); } static void sendCmd(uint8_t *buf, int len) { // make sure cmd isn't on stack if (buf != cmdBuf) memcpy(cmdBuf, buf, len); buf = cmdBuf; SET_DC(0); SET_CS(0); transfer(buf, 1); SET_DC(1); len--; buf++; if (len > 0) transfer(buf, len); SET_CS(1); } static void sendCmdSeq(const uint8_t *buf) { while (*buf) { cmdBuf[0] = *buf++; int v = *buf++; int len = v & ~DELAY; // note that we have to copy to RAM memcpy(cmdBuf + 1, buf, len); sendCmd(cmdBuf, len + 1); buf += len; if (v & DELAY) { scr_delay(*buf++); } } } static uint32_t palXOR; static void setAddrWindow(int x, int y, int w, int h) { w += x - 1; h += y - 1; uint8_t cmd0[] = {ST7735_RASET, 0, (uint8_t)x, (uint8_t)(w >> 8), (uint8_t)w}; uint8_t cmd1[] = {ST7735_CASET, 0, (uint8_t)y, (uint8_t)(h >> 8), (uint8_t)h}; sendCmd(cmd1, sizeof(cmd1)); sendCmd(cmd0, sizeof(cmd0)); } static void configure(uint8_t madctl, uint32_t frmctr1) { uint8_t cmd0[] = {ST7735_MADCTL, madctl}; uint8_t cmd1[] = {ST7735_FRMCTR1, (uint8_t)(frmctr1 >> 16), (uint8_t)(frmctr1 >> 8), (uint8_t)frmctr1}; sendCmd(cmd0, sizeof(cmd0)); sendCmd(cmd1, cmd1[3] == 0xff ? 3 : 4); } #define COL0(r, g, b) ((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3)) #define COL(c) COL0((c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff) const uint16_t palette[] = { COL(0x000000), // 0 COL(0xffffff), // 1 COL(0xff2121), // 2 COL(0xff93c4), // 3 COL(0xff8135), // 4 COL(0xfff609), // 5 COL(0x249ca3), // 6 COL(0x78dc52), // 7 COL(0x003fad), // 8 COL(0x87f2ff), // 9 COL(0x8e2ec4), // 10 COL(0xa4839f), // 11 COL(0x5c406c), // 12 COL(0xe5cdc4), // 13 COL(0x91463d), // 14 COL(0x000000), // 15 }; uint8_t fb[168 * 128]; extern const uint8_t font8[]; extern const uint8_t fileLogo[]; extern const uint8_t pendriveLogo[]; extern const uint8_t arrowLogo[]; static void printch(int x, int y, int col, const uint8_t *fnt) { for (int i = 0; i < 6; ++i) { uint8_t *p = fb + (x + i) * DISPLAY_HEIGHT + y; uint8_t mask = 0x01; for (int j = 0; j < 8; ++j) { if (*fnt & mask) *p = col; p++; mask <<= 1; } fnt++; } } static void printch4(int x, int y, int col, const uint8_t *fnt) { for (int i = 0; i < 6 * 4; ++i) { uint8_t *p = fb + (x + i) * DISPLAY_HEIGHT + y; uint8_t mask = 0x01; for (int j = 0; j < 8; ++j) { for (int k = 0; k < 4; ++k) { if (*fnt & mask) *p = col; p++; } mask <<= 1; } if ((i & 3) == 3) fnt++; } } void printicon(int x, int y, int col, const uint8_t *icon) { int w = *icon++; int h = *icon++; int sz = *icon++; uint8_t mask = 0x80; int runlen = 0; int runbit = 0; uint8_t lastb = 0x00; for (int i = 0; i < w; ++i) { uint8_t *p = fb + (x + i) * DISPLAY_HEIGHT + y; for (int j = 0; j < h; ++j) { int c = 0; if (mask != 0x80) { if (lastb & mask) c = 1; mask <<= 1; } else if (runlen) { if (runbit) c = 1; runlen--; } else { if (sz-- <= 0) panic(10); lastb = *icon++; if (lastb & 0x80) { runlen = lastb & 63; runbit = lastb & 0x40; } else { mask = 0x01; } --j; continue; // restart } if (c) *p = col; p++; } } } void print(int x, int y, int col, const char *text) { int x0 = x; while (*text) { char c = *text++; if (c == '\r') continue; if (c == '\n') { x = x0; y += 10; continue; } /* if (x + 8 > DISPLAY_WIDTH) { x = x0; y += 10; } */ if (c < ' ') c = '?'; if (c >= 0x7f) c = '?'; c -= ' '; printch(x, y, col, &font8[c * 6]); x += 6; } } void print4(int x, int y, int col, const char *text) { while (*text) { char c = *text++; c -= ' '; printch4(x, y, col, &font8[c * 6]); x += CHAR4_KERNED_WIDTH; if (x + CHAR4_KERNED_WIDTH > DISPLAY_WIDTH) { // Next char won't fit. return; } } } void draw_screen() { if (lookupCfg(CFG_PIN_DISPLAY_SCK, 1000) == 1000) return; cmdBuf[0] = ST7735_RAMWR; sendCmd(cmdBuf, 1); SET_DC(1); SET_CS(0); uint8_t *p = fb; if (lookupCfg(CFG_DISPLAY_TYPE, 7735) == 7735) { for (int i = 0; i < DISPLAY_WIDTH; ++i) { for (int j = 0; j < DISPLAY_HEIGHT; ++j) { uint16_t color = palette[*p++ & 0xf]; uint8_t cc[] = {color >> 8, color & 0xff}; transfer(cc, 2); } } } else { // ILI9341/st7789(320*240*p8b) DISPLAY for (int i = 0; i < DISPLAY_WIDTH; ++i) { for (int j = 0; j < DISPLAY_HEIGHT - 8; j++) { uint16_t color = palette[*(p + j) & 0xf]; uint8_t cc[] = {color >> 8, color & 0xff}; transfer(cc, 2); transfer(cc, 2); } for (int j = 0; j < DISPLAY_HEIGHT - 8; j++) { uint16_t color = palette[*(p + j) & 0xf]; uint8_t cc[] = {color >> 8, color & 0xff}; transfer(cc, 2); transfer(cc, 2); } p += DISPLAY_HEIGHT; } } SET_CS(1); } void drawBar(int y, int h, int c) { for (int x = 0; x < DISPLAY_WIDTH; ++x) { memset(fb + x * DISPLAY_HEIGHT + y, c, h); } } void draw_hf2() { print4(20, 22, 5, "<-->"); print(40, 110, 7, "flashing..."); draw_screen(); } void draw_drag() { drawBar(0, 52, 7); drawBar(52, 55, 8); drawBar(107, 14, 4); // Center PRODUCT_NAME and UF2_VERSION_BASE. int name_x = (DISPLAY_WIDTH - (6 * 4 - CHAR4_KERNING) * (int) strlen(PRODUCT_NAME)) / 2; print4(name_x >= 0 ? name_x : 0, 5, 1, PRODUCT_NAME); int version_x = (DISPLAY_WIDTH - 6 * (int) strlen(UF2_VERSION_BASE)) / 2; print(version_x >= 0 ? version_x : 0, 40, 6, UF2_VERSION_BASE); print(23, 110, 1, "arcade.makecode.com"); #define DRAG 70 #define DRAGX 10 printicon(DRAGX + 20, DRAG + 5, 1, fileLogo); printicon(DRAGX + 66, DRAG, 1, arrowLogo); printicon(DRAGX + 108, DRAG, 1, pendriveLogo); print(10, DRAG - 12, 1, "arcade.uf2"); print(90, DRAG - 12, 1, VOLUME_LABEL); draw_screen(); } void screen_early_init() { setup_output_pin(CFG_PIN_DISPLAY_BL); } void screen_init() { if (lookupCfg(CFG_PIN_DISPLAY_SCK, 1000) == 1000) return; setup_output_pin(CFG_PIN_DISPLAY_SCK); setup_output_pin(CFG_PIN_DISPLAY_MOSI); setup_output_pin(CFG_PIN_DISPLAY_BL); setup_output_pin(CFG_PIN_DISPLAY_DC); setup_output_pin(CFG_PIN_DISPLAY_RST); setup_output_pin(CFG_PIN_DISPLAY_CS); SET_CS(1); SET_DC(1); pin_set(CFG_PIN_DISPLAY_BL, 1); pin_set(CFG_PIN_DISPLAY_RST, 0); scr_delay(20); pin_set(CFG_PIN_DISPLAY_RST, 1); scr_delay(20); sendCmdSeq(initCmds); uint32_t cfg0 = CFG(DISPLAY_CFG0); //uint32_t cfg2 = CFG(DISPLAY_CFG2); uint32_t frmctr1 = CFG(DISPLAY_CFG1); palXOR = (cfg0 & 0x1000000) ? 0xffffff : 0x000000; uint32_t madctl = cfg0 & 0xff; uint32_t offX = (cfg0 >> 8) & 0xff; uint32_t offY = (cfg0 >> 16) & 0xff; //uint32_t freq = (cfg2 & 0xff); //offX = (CFG(DISPLAY_WIDTH) - DISPLAY_WIDTH) / 2; //commented out, breaks ILI9341 compatibility //offY = (CFG(DISPLAY_HEIGHT) - DISPLAY_HEIGHT) / 2; // DMESG("configure screen: FRMCTR1=%p MADCTL=%p SPI at %dMHz", frmctr1, madctl, freq); configure(madctl, frmctr1); setAddrWindow(offX, offY, CFG(DISPLAY_WIDTH), CFG(DISPLAY_HEIGHT)); memset(fb, 0, sizeof(fb)); } #endif