Add UF2 bootloader for USB firmware flashing
Vendored and trimmed from microsoft/uf2-samdx1, adapted to the actual ATSAMD21G17D (128 KiB flash / 16 KiB RAM), fixing the flash/RAM-size mismatch that the previously commented-out SAM-BA bootloader target had (it assumed a 256 KiB SAMD21G18A). Builds as a standalone PlatformIO environment (bootloader/platformio.ini), no python2/make dependency. Compiles clean, fits in the 8 KiB bootloader region (7292/8192 bytes). Not yet flashed/verified on real hardware.
This commit is contained in:
parent
ce5db617a1
commit
de52b57041
143 changed files with 69119 additions and 0 deletions
489
bootloader/src/screen.c
Normal file
489
bootloader/src/screen.c
Normal file
|
|
@ -0,0 +1,489 @@
|
|||
#include "uf2.h"
|
||||
|
||||
#if USE_SCREEN
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#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
|
||||
Loading…
Add table
Add a link
Reference in a new issue