diff --git a/misc/gpu/Makefile b/misc/gpu/Makefile index d72c1eb2..14dbe589 100644 --- a/misc/gpu/Makefile +++ b/misc/gpu/Makefile @@ -74,7 +74,7 @@ ASFLAGS += -Wa,--defsym,GPU_SRAM_BASE=$(GPU_SRAM_BASE) -Wa,--defsym,GPU_SRAM_SIZ TARGET_ELF = $(TARGET_NAME).elf -TARGETS = $(TARGET_NAME).lss $(TARGET_NAME).bin $(TARGET_NAME).sym +TARGETS = $(TARGET_NAME).lss $(TARGET_NAME).bin $(TARGET_NAME).sym gpu_binary.py all: $(TARGETS) @@ -120,10 +120,16 @@ DELIVERABLES = $(TARGET_NAME).bin $(TARGET_NAME).lss gpu_binary.py # package the binary into a mpy file to be frozen/included into main micro code gpu_binary.py: version.txt $(TARGET_NAME).bin ./repackage.py `cat version.txt` $(TARGET_NAME).bin > $@ + wc -c $(TARGET_NAME).bin checksums.txt: $(DELIVERABLES) shasum -a 256 $(DELIVERABLES) > $@ +lcd.o: barcode.h +#barcode.h: version.h make_barcode.py Makefile +barcode.h: make_barcode.py Makefile + python3 make_barcode.py + # Track released versions .PHONY: capture capture: version.txt version-full.txt $(DELIVERABLES) checksums.txt diff --git a/misc/gpu/README.md b/misc/gpu/README.md index 4afef572..65b5818c 100644 --- a/misc/gpu/README.md +++ b/misc/gpu/README.md @@ -37,47 +37,21 @@ Useful commands: flash erase_sector 0 0 last +Set EMPTY bit, so goes into BL: + + > mdw 0x40022000 + 0x40022000: 00040600 + > mmw 0x40022000 0x10000 0 + > mdw 0x40022000 + 0x40022000: 00050600 + + ## In-Circuit Programming - AN4221 describes the protocol used to load the flash - timing is sensitive, but more important is where the i2c start/stops fall: -```python ->>> from machine import I2C, Pin ->>> i2c = I2C(1, freq=400000) - ->>> r = Pin('G_RESET') ->>> r -Pin(Pin.cpu.E6, mode=Pin.OUT) ->>> r.init(mode=Pin.OPEN_DRAIN, pull=Pin.PULL_UP) ->>> r() -0 ->>> r(1) ->>> r() -1 ->>> [hex(i) for i in i2c.scan()] -['0x2d', '0x51', '0x53', '0x55', '0x57', '0x64'] - -# for SWI to work, also: ->>> gg=Pin('G_SWCLK_B0') ->>> gg.init(mode=Pin.IN) - - -# Get - reveals bootloader version, commands (v1.2) ->>> i2c.writeto(0x64, b'\x00\xff'); i2c.readfrom(0x64, 1); i2c.readfrom(0x64, 20); i2c.readfrom(0x64, 1); -2 -b'y' -b'\x12\x11\x00\x01\x02\x11!1Dcs\x82\x922Edt\x83\x93\xa1' -b'y' - -# Get ID command ->>> i2c.writeto(0x64, b'\x02\xfd'); i2c.readfrom(0x64, 1); [hex(i) for i in i2c.readfrom(0x64, 3)]; i2c.readfrom(2x64, 1); -b'y' -['0x1', '0x4', '0x43'] -b'y' -``` - ## First Time Boot - on a fresh device, there is an `EMPTY` bit set on power-up (only) if flash looks empty @@ -85,6 +59,7 @@ b'y' - so must clear bit 16 of `FLASH_ACR` after loading image: @ `0x40022000` - also, main micro has control over `BOOT0` (PE2) which stop main flash from running too - and the reset line on E6 +- we use this flag to get into boot mode from working code ## Getting BOOT0 to work @@ -93,6 +68,7 @@ b'y' - `NRST_MODE` should be 0b01 (input only) not default (0b11 = bidirectional) - register `FLASH_OPTR` at 0x40022020 => found as 0xfffffeaa - loads from 0x1FFF7800 at power up +- TODO XXX still need this! ## AN4221 / Bootloader Bugs @@ -101,6 +77,7 @@ b'y' - command 0x01 - 'getversion' ... return 1 byte, and doesn't include length prefix byte - memory read only works from flash, some parts of SRAM... not IO registers - undocumented need for N-1 as length in read/write commands +- flash writes need to be 256-aligned, or else they do nothing and don't fail ## Resource Sharing diff --git a/misc/gpu/barcode.h b/misc/gpu/barcode.h new file mode 100644 index 00000000..f1db32a6 --- /dev/null +++ b/misc/gpu/barcode.h @@ -0,0 +1,8 @@ +// autogen file, see make_barcode.py + +// in python: b'\x00\x00\x00\x00\x00\x00\x00?\x1c\x0e\x00\x1f\x8e\x00\xe0\x0f\xf8\xff\x8f\xc7\xe3\xfe?\xe3\xf1\xf8\xff\xf1\xf8\x03\xfe8\xfc\x00\x00\x00\x00\x00\x00\x00' +static const uint8_t test_barcode[40] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x1c, 0x0e, 0x00, 0x1f, 0x8e, 0x00, 0xe0, 0x0f, 0xf8, 0xff, 0x8f, 0xc7, 0xe3, 0xfe, 0x3f, 0xe3, 0xf1, 0xf8, 0xff, 0xf1, 0xf8, 0x03, 0xfe, 0x38, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +// EOF \ No newline at end of file diff --git a/misc/gpu/gpu_binary.py b/misc/gpu/gpu_binary.py index 6dcb655e..b4b221e6 100644 --- a/misc/gpu/gpu_binary.py +++ b/misc/gpu/gpu_binary.py @@ -4,107 +4,130 @@ # # see misc/gpu for source # -VERSION = '1.0.0' +VERSION = '1.1.0' -LENGTH = const(3140) # bytes +LENGTH = const(3500) # bytes -BINARY = (b'\x00\x18\x00 \t\x01\x00\x08\x99\x06\x00\x08\x9b\x06\x00\x08\x00\x00\x00\x00' +BINARY = (b'\x00\x18\x00 \t\x01\x00\x08\x19\t\x00\x08\x1b\t\x00\x08\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00\x9d\x06\x00\x08\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x9f\x06\x00\x08\xa1\x06\x00\x08U\x01\x00\x08' - b'\x00\x00\x00\x00U\x01\x00\x08U\x01\x00\x08U\x01\x00\x08U\x01\x00\x08' - b'U\x01\x00\x08U\x01\x00\x08\x00\x00\x00\x00U\x01\x00\x08U\x01\x00\x08' - b'U\x01\x00\x08U\x01\x00\x08U\x01\x00\x08U\x01\x00\x08\x00\x00\x00\x00' - b'U\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00U\x01\x00\x08\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x1d\t\x00\x08\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x1f\t\x00\x08!\t\x00\x08U\x01\x00\x08\x00\x00\x00\x00' + b'U\x01\x00\x08U\x01\x00\x08U\x01\x00\x08U\x01\x00\x08U\x01\x00\x08' + b'U\x01\x00\x08\x00\x00\x00\x00U\x01\x00\x08U\x01\x00\x08U\x01\x00\x08' b'U\x01\x00\x08U\x01\x00\x08U\x01\x00\x08\x00\x00\x00\x00U\x01\x00\x08' - b'\x00\x00\x00\x00U\x01\x00\x08U\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x10\xb5\x06L#x\x00+\x07\xd1\x05K\x00+\x02\xd0\x04H\x00\xe0' - b'\x00\xbf\x01##p\x10\xbd\x0c\x00\x00 \x00\x00\x00\x004\x0c\x00\x08' - b'\x04K\x10\xb5\x00+\x03\xd0\x03I\x04H\x00\xe0\x00\xbf\x10\xbd\xc0F' - b'\x00\x00\x00\x00\x10\x00\x00 4\x0c\x00\x08\x0cH\x85F\x00\xf0T\xf9' - b'\x00!\x03\xe0\nK[XCP\x041\tH\nKB\x18\x9aB\xf6\xd3\tJ\x02\xe0\x00#\x13`\x042' - b'\x07K\x9aB\xf9\xd3\x00\xf0\x83\xf9\xfe\xe7\x00\x18\x00 \x04\x00\x00 ' - b'\x00\x00\x00 \x0c\x00\x00 \x0c\x00\x00 @\x00\x00 \xfe\xe7\x00\x00' - b'\x00\xb5\x89\xb0\x14"\x00!\x03\xa8\x00\xf0)\xfd\x01 \tJQk\x01CQcSk\x02\xa9' - b'\x03@\x01\x93\x01\x9b?#\x9f0\x02\x93\xc0\x05=;\x04\x93\x00\xf0\x8c\xfa\t\xb0' - b'\x00\xbd\xc0F\x00\x10\x02@\x10\xb5\x02$ K!IZm\x88\xb0\n@ZeZk\x00!"CZc[k\x0c"' - b'#@\x00\x93\x03\xa8\x00\x9b\x00\xf0\xfe\xfc\xc0#\x01\x93\xbf;\x04\x93' - b'\x01\xa9\x053\x16H\x06\x93\x02\x94\x00\xf0h\xfa\x1c"\x00!\x01\xa8' - b'\x00\xf0\xee\xfc\x12K\x13L\x02\x93\xca# \x00\x01\xa9\x05\x93\x00\xf0' - b'\xeb\xfb\x80#bh\x9b\x04\x13Cc`\xe3h\rJ\x13@\xe3`\xe3h\x0cJ\x13@\xe3`#h\x0bJ' - b'\x13@#`#h\nJ\x13@#`\x08\xb0\x10\xbd\x00\x10\x02@\xff\xcf\xff\xff' - b'\x00\x04\x00P\x13\x04\x10\x00\x00T\x00@\x01\xf8\xff\xff\xff\x7f\xff\xff' - b'\xff\xff\xf7\xff\xff\xff\xfd\xff\x80#\x03J[\x02\x11h\x0bC\x13`pG\xc0F' - b'\x00 \x02@\x08#\xf7\xb5KLLJ\xa1i\x19B\x10\xd0\xe1i\x0bC\x80!\xe3a\xa3i' - b'I\x02\x0b@\x19\x00H\x1e\x81A\x11p\x00+!\xd1DI\x0bpDI\x0bp\x13x\xa1i' - b'\x01\x93AK\x1dxAK\x1bx\x00\x93 #\x19B(\xd0\xe2i\x13C\xe3a\x01\x9b\x00+' - b'"\xd1\xd1\x90@ 3\x00(\x02\xd0\x00\xf0\xd7\xf9\xc3\xb2' - b'\x03"[\x00\x9a@ \x00\x97C3\x00\x1f"\x00+5\xd1\x90@ 3\x00(\x02\xd0\x00\xf0' - b'\xc7\xf9\xc3\xb2\x05\x9a[\x00\x9a@\x04\x9b\x17C\xef`\x02+_\xd1 \x003\x00' - b'\x1f"/\xe0\x01!@\x00\x19@\x08C\x01:[\x08\x85\xe7\x01!@\x00\x19@\x08C\x01:' - b'[\x08\x8e\xe7\x01!@\x00\x19@\x08C\x01:[\x08\xa0\xe7\x01!@\x00\x19@\x08C\x01:' - b'[\x08\xa9\xe7\x01!@\x00\x19@\x08C\x01:[\x08\xb7\xe7\x01!@\x00\x19@\x08C\x01:' - b'[\x08\xc0\xe7\x01!@\x00\x19@\x08C\x01:[\x08\x00+\xf7\xd1\x02\x9b\x90@' - b'[i\x04\x935\xd0\x00\xf0\x81\xf9\x07(1\xdc \x003\x00\x1f"/j\x00+\x1d\xd1\x90@' - b' 3\x00(\x02\xd0\x00\xf0s\xf9\xc3\xb2\x0f"\x9b\x00\x9a@\x1f#\x97C\x00.' - b'\x16\xd1 \x00\x98@ #\x00(\x02\xd0\x00\xf0d\xf9\xc3\xb2\x04\x9a\x9b\x00\x9a@' - b'\x17C/b\x03\x9b\x013\x00\xe7\x01!@\x00\x19@\x08C\x01:[\x08\xd8\xe7\x01 d\x00' - b'0@\x04C\x01;v\x08\xdf\xe7&\nd\n0\x00#\x00\x1f"oj\x00+\x1b\xd1\x90@ 3\x00(' - b'\x02\xd0\x00\xf0?\xf9\xc3\xb2\x0f"\x9b\x00\x9a@\x1f#\x97C\x00,\x14\xd10\x00' - b'\x98@ #\x00(\x02\xd0\x00\xf00\xf9\xc3\xb2\x04\x9a\x9b\x00\x9a@\x17Cob' + b'\x00\x00\x00\x00\x00\x00\x00\x00U\x01\x00\x08\x00\x00\x00\x00U\x01\x00\x08' + b'U\x01\x00\x08U\x01\x00\x08\x00\x00\x00\x00U\x01\x00\x08\x00\x00\x00\x00' + b'U\x01\x00\x08U\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x10\xb5\x06L#x\x00+\x07\xd1\x05K\x00+\x02\xd0\x04H\x00\xe0\x00\xbf\x01#' + b'#p\x10\xbd\x08\x00\x00 \x00\x00\x00\x00\xa0\r\x00\x08\x04K\x10\xb5' + b'\x00+\x03\xd0\x03I\x04H\x00\xe0\x00\xbf\x10\xbd\xc0F\x00\x00\x00\x00' + b'\x0c\x00\x00 \xa0\r\x00\x08\x0cH\x85F\x00\xf0\x96\xf9\x00!\x03\xe0\nK[X' + b'CP\x041\tH\nKB\x18\x9aB\xf6\xd3\tJ\x02\xe0\x00#\x13`\x042\x07K\x9aB' + b'\xf9\xd3\x00\xf0\xc7\xf9\xfe\xe7\x00\x18\x00 \x00\x00\x00 \x00\x00\x00 ' + b'\x08\x00\x00 \x08\x00\x00 D\x00\x00 \xfe\xe7\x00\x00\x00\xb5\x89\xb0' + b'\x14"\x00!\x03\xa8\x00\xf0\xc7\xfd\x01 \tJQk\x01CQcSk\x02\xa9\x03@\x01\x93' + b'\x01\x9b?#\x9f0\x02\x93\xc0\x05=;\x04\x93\x00\xf0\xcc\xfb\t\xb0\x00\xbd\xc0F' + b'\x00\x10\x02@\x10\xb5\x02$ K!IZm\x88\xb0\n@ZeZk\x00!"CZc[k\x0c"#@\x00\x93' + b'\x03\xa8\x00\x9b\x00\xf0\x9c\xfd\xc0#\x01\x93\xbf;\x04\x93\x01\xa9\x053' + b'\x16H\x06\x93\x02\x94\x00\xf0\xa8\xfb\x1c"\x00!\x01\xa8\x00\xf0\x8c\xfd' + b'\x12K\x13L\x02\x93\xca# \x00\x01\xa9\x05\x93\x00\xf0+\xfd\x80#bh\x9b\x04' + b'\x13Cc`\xe3h\rJ\x13@\xe3`\xe3h\x0cJ\x13@\xe3`#h\x0bJ\x13@#`#h\nJ\x13@#`' + b'\x08\xb0\x10\xbd\x00\x10\x02@\xff\xcf\xff\xff\x00\x04\x00P\x13\x04\x10\x00' + b'\x00T\x00@\x01\xf8\xff\xff\xff\x7f\xff\xff\xff\xff\xf7\xff\xff\xff\xfd\xff' + b'\x80#\x03J[\x02\x11h\x0bC\x13`pG\xc0F\x00 \x02@\x08#\xf7\xb5jLkJ\xa1i\x19B' + b'\x10\xd0\xe1i\x0bC\x80!\xe3a\xa3iI\x02\x0b@\x19\x00H\x1e\x81A\x11p\x00+$\xd1' + b'cI\x0bpcI\x0bp\x13x\xa1i\x01\x93`K\x1fx`K\x1dx #\x19B4\xd0\xe2i\x13C' + b'\xe3a\x01\x9b\x00+.\xd1[N3pc/[\xd0\x10\xd8a/\x00\xd1r\xe0b/L\xd0V/\x15\xd0' + b'VKWJ\x1a`\x08#C\xe0#hUI\x0b@#`\xd9\xe7t/\x00\xd1l\xe0PKv/3\xd0p/\xed\xd1PJ5p' + b'\x1a`\x08\xe0\x00-h\xd1NHJK\x18`\x00\xf0\x07\xfd\x0100p\x01#\xa2i\x13C' + b'\xa3a\x00"\x04#\x11\x00\x9cF\x01 fF\xa3i3BY\xd1\x00)\x01\xd0\r\x00\x08\x80"\x1aK' + b'\x12\x05\x9a`\x80"\x19KR\x00\x19h\nC\x1a`\x80"\xd2\x00\x19h\x11B\xfc\xd0Zh' + b'\x14I\x11@\x80"\xd2\x01\nCZ`\x1ah\x12I\x11@\x80"R\x01\nC\x1a`\x9ah\x0fI\n@' + b'\x07!\x9a`\x9ah\x8aC\x9a`8"\x99h\x08\x00\x10@\x11B\xfa\xd1\x9ah\tI\n@' + b'\x9a`\tK\tJZ`\x05"\x98`\x1a`pG\x00\xed\x00\xe0\x00\x10\x02@\xff\x80\xff\xff' + b'\xff\xc7\xff\xff\xff\xf0\xff\xff\xff\x8f\xff\xff\x10\xe0\x00\xe0' + b'\xdf.\x00\x00\x18Ks\xb5\x19l\x18H\xa0$\x01C\x19d\x81!\x01&\x1alI\x05\x02@' + b'\x01\x92\x01\x9a\xdak\xe4\x05\nC\xdac\xdbk\x0b@\x00\x93\x00\x9b\xff\xf70\xfe' + b'\x00\xf0\xbc\xf8\xff\xf7J\xfe\x0cJ\rI\x13h\x0b@\x13`\xff\xf7\xa3\xfe#i' + b'3B\xfa\xd1 %#i+B\xf6\xd0\x00\xf0\x92\xf9#i+B\xf1\xd0\xff\xf7\x95\xfe\xf9\xe7' + b'\x00\x10\x02@\x01\x10\x00\x00\x00 \x02@\xff\xff\xfe\xff\xa0#\x10"' + b'\x08!\xdb\x05\x10\xb5\x9aa\x99b\x9ab\xc0#[\x01\x1c\x00\x08J\x91h\x19@' + b'\xa1B\xfb\xd0\x10s\x91h\x19B\xfc\xd1\xa0#\x10"\xdb\x05\x9aa\x08:\x9aa' + b'\x10\xbd\xc0F\x000\x01@\xa0#0\xb5\x10"\x08$\xdb\x05\x9aa\x9ca\x9ab\xc0"\x00#' + b'\tLR\x01\x98B\x07\xdc\xa3h\x13B\xfc\xd1\xa0#\x10"\xdb\x05\x9aa0\xbd' + b'\xa5h\x15@\x95B\xfb\xd0\xcd\\\x013%s\xed\xe7\x000\x01@s\xb5\x0e\x00' + b'\x1d\x00\x01:\x83\xb2\x9a\x18\x01\xac\x92\xb2\x1b\n#p`p\x13\n* \xa3p' + b'\xe2p\xff\xf7\xb1\xff!\x00\x04 \xff\xf7\xcb\xff\x01=\xb2\xb2U\x19' + b'\xad\xb2\x12\n"p+ *\n\xa2pfp\xe5p\xff\xf7\xa0\xff!\x00\x04 \xff\xf7\xba\xff' + b', \xff\xf7\x99\xffs\xbd\xa0#p\xb5\x10"\x08$\xdb\x05\x9aa\x9ca\x9ab' + b'\xc0#\xce\xb2\xe1@\x00$\x0bJ[\x01\xa0B\x07\xdc\x91h\x19B\xfc\xd1\xa0#' + b'\x10"\xdb\x05\x9aap\xbd\x95h\x1d@\x9dB\xfb\xd0\x16s\x95h\x1d@\x9dB' + b'\xfb\xd0\x11s\x014\xe9\xe7\x000\x01@p\xb5\x1c\x00\x04\xab\x1e\x88' + b'#\x00\x15\x00\xff\xf7\xa8\xff \x001\x00hC\xff\xf7\xcd\xffp\xbd' + b'\x10\xb5\x8a\xb0\x1c"\x00!\x03\xa8\x00\xf09\xfb\xc0#\x1b\x02\x00\x93' + b'\x82#[\x00\x01\x93\xe0#\xdb\x00\x02\x93\x80#\tLiF\x9b\x00 \x00\x05\x93' + b'\x00\xf0\x92\xfa@#"h\x13C#`\x01#\x04J\x11x\x0bC\x13p\n\xb0\x10\xbd\xc0F' + b'\x000\x01@<\x00\x00 \xf7\xb5\x16\x00\x08\xaa\x12x\x94F!(0\xdc\t).\xdc\x16%' + b'\t"MCPC)\x00bF\x12$\xc7\x1d\x0f1\x00*\x00\xd1\t<\x00+.\xd1\x00.' + b'\x1f\xd0\x1e\x00\x01#"\x008\x00\x00\x96\xff\xf7\xaa\xff)\x00\x01#"\x008\x00' + b'$1\x105\x00\x96\xff\xf7\xa1\xff\x14#\x01")\x008\x00\x00\x96\xff\xf7\x9a\xff' + b'\x14#\x01")\x008\x19\x018\x00\x96\xff\xf7\x92\xff\xf7\xbd\x08K"\x00\x00\x93' + b'8\x00\x16#\xf6\xe7)\x00\x14#\xa2\x1e\x101\x080\x00\x96\xef\xe7\x00.\xf6\xd0' + b'\x00N\xcf\xe7\xfd`\x00\x00\xf7\xb5\x1dKR!\x00\xaf\x9dDnF\x00"\x06$\x1aH\xff1' + b'\x13\x00#@]BkA\x18M[B\x03@[\x19U\x00\x012sS\x8aB\xf2\xd1\x03#\xa0"\xed!' + b'\x00 [BR\x00\xff\xf7\x0f\xff\x03$\x10M\xa0 +h\x80\x00{`\x0f#zh\x01<' + b'\x99\x1aI\x00q\x18\xff\xf7\xe1\xfe\x00,\xf0\xd1{h\tJ\x013\x13@\x04\xd5' + b'\x10"\x01;RB\x13C\x013\xbdF+`\xf7\xbdX\xfd\xff\xff\x03\x9f\xff\xff' + b'\xfd`\x00\x00@\x00\x00 \x0f\x00\x00\x80\xf8\xb5\x14K\x00\xaf\x9dDlF\x80 ' + b'"\x00\x00!\x06\x00\x11K[\\\x03@\x1d\x00kBkA[B\x13\x80@\x08\x14\xd1\x011' + b'()\x10\xd1\xa0"x#\xa0&R\x00\xff\xf7\xcc\xfex%\xb6\x00!\x000\x00\x01=\xff\xf7' + b'\xa5\xfe\x00-\xf8\xd1\xbdF\xf8\xbd0\x00\x022\xde\xe7\x80\xfd\xff\xff' + b'H\r\x00\x08\xf0\xb5\x89\xb0\x0c"\x00!\x05\xa8\x00\xf0Q\xfa\xa0 \x06#\x03%' + b'\x02$\x02\xa9\xc0\x05\x02\x93\x03\x94\x04\x95\x00\xf0]\xf8\x18#\xa0 ' + b'\x02\x93\x17;\x03\x93\x00#\x02\xa9\xc0\x05\x06\x93\x05\x93\x07\x93\x04\x95' + b'\x00\xf0O\xf8!N3x#B\x04\xd0\xff\xf7\xac\xff3x\xa3C3p3x\xdb\x07\x01\xd5' + b'\xff\xf7^\xff\x0c#7x\x1fB"\xd0\x19M,h\x00,\x14\xd1\xbb\x06:\x07' + b'\xff\x06\xff\x0f\xb1xpx\xdb\x0f\xd2\x0f\x00\x97\xff\xf7\xff\xfe\x01"' + b'3x\x99\x06\xc9\x0f\x8aC !R\x01\x8bC\x13C3p\x0eJc\x1c\x13@\x04\xd5 "\x01;RB' + b'\x13C\x013+`\x14"\x00!\x03\xa8\x00\xf0\xfe\xf9\xa0 \x1e#\x02\xa9\xc0\x05' + b'\x02\x93\x00\xf0\x0e\xf8\t\xb0\xf0\xbd\xc0F<\x00\x00 8\x00\x00 ' + b'\x1f\x00\x00\x80\xfe\xe7\xfe\xe7pGpGpG\xf0\xb5\x87\xb0\x0bh\x02\x91\x1f"' + b'\x01!\x01\x93\x05\x00\x01\x98[\x08\x00+\x1e\xd1\x90@ 3\x00(\x02\xd0\x00\xf0' + b'\xb9\xf9\xc3\xb2\x03\x93\x02\x9b\x03\x9a[h\x04\x93\x01\x9b\xd3@\x16\xd1' + b'\x04\x9b\x01;\x01+\x08\xd8kh\x01\x9a\x01\x99\x93C\x02\x9a\xd2hJC\x13Ck`\x00 ' + b'\x07\xb0\xf0\xbd\x1c\x00@\x00\x0c@ C\x01:[\x08\xd7\xe7\x01#\x03\x9a\x01\x9c' + b'\x93@\x01\x9a\x1c@\x1aB\x00\xd1\xd3\xe0f\x08 \x003\x00\x1f"/h\x00+p\xd1\x90@' + b' 3\x00(\x02\xd0\x00\xf0\x83\xf9\xc3\xb2\x03"[\x00\x9a@ \x00\x97C3\x00' + b'\x1f"\x00+g\xd1\x90@ 3\x00(\x02\xd0\x00\xf0s\xf9\xc3\xb2\x04\x9a[\x00' + b'\x9a@\x04\x9b\x17C\x01;/`\x01+$\xd8\x02\x9b \x00\x9bh\x1f"\x05\x933\x00\xafh' + b'\x00+U\xd1\x90@ 3\x00(\x02\xd0\x00\xf0Z\xf9\xc3\xb2\x03"[\x00\x9a@ \x00\x97C' + b'3\x00\x1f"\x00+L\xd1\x90@ 3\x00(\x02\xd0\x00\xf0J\xf9\xc3\xb2\x05\x9a' + b'[\x00\x9a@\x17C\xaf`\x02\x9b \x00\x1bi\x1f"\x05\x933\x00\xefh\x00+>\xd1\x90@' + b' 3\x00(\x02\xd0\x00\xf05\xf9\xc3\xb2\x03"[\x00\x9a@ \x00\x97C3\x00\x1f"\x00+' + b'5\xd1\x90@ 3\x00(\x02\xd0\x00\xf0%\xf9\xc3\xb2\x05\x9a[\x00\x9a@\x04\x9b' + b'\x17C\xef`\x02+_\xd1 \x003\x00\x1f"/\xe0\x01!@\x00\x19@\x08C\x01:[\x08' + b'\x85\xe7\x01!@\x00\x19@\x08C\x01:[\x08\x8e\xe7\x01!@\x00\x19@\x08C\x01:[\x08' + b'\xa0\xe7\x01!@\x00\x19@\x08C\x01:[\x08\xa9\xe7\x01!@\x00\x19@\x08C\x01:[\x08' + b'\xb7\xe7\x01!@\x00\x19@\x08C\x01:[\x08\xc0\xe7\x01!@\x00\x19@\x08C\x01:[\x08' + b'\x00+\xf7\xd1\x02\x9b\x90@[i\x04\x935\xd0\x00\xf0\xdf\xf8\x07(1\xdc \x00' + b'3\x00\x1f"/j\x00+\x1d\xd1\x90@ 3\x00(\x02\xd0\x00\xf0\xd1\xf8\xc3\xb2' + b'\x0f"\x9b\x00\x9a@\x1f#\x97C\x00.\x16\xd1 \x00\x98@ #\x00(\x02\xd0' + b'\x00\xf0\xc2\xf8\xc3\xb2\x04\x9a\x9b\x00\x9a@\x17C/b\x03\x9b\x013' + b'\x00\xe7\x01!@\x00\x19@\x08C\x01:[\x08\xd8\xe7\x01 d\x000@\x04C\x01;v\x08' + b'\xdf\xe7&\nd\n0\x00#\x00\x1f"oj\x00+\x1b\xd1\x90@ 3\x00(\x02\xd0\x00\xf0' + b'\x9d\xf8\xc3\xb2\x0f"\x9b\x00\x9a@\x1f#\x97C\x00,\x14\xd10\x00\x98@ #' + b'\x00(\x02\xd0\x00\xf0\x8e\xf8\xc3\xb2\x04\x9a\x9b\x00\x9a@\x17Cob' b'\xca\xe7\x01!@\x00\x19@\x08C\x01:[\x08\xda\xe7\x01 v\x00 @\x06C\x01;d\x08' b'\xe1\xe7\x00\x00\x03h\x02\x00p\xb5\x01 [\x06,\xd4Nh\x0bhHi3C\xceh\x15h' b'3C\x0ei\x0cj3C\x8ei\x03C3C\xcei\x00\x0c3C\x10N#C5@+C\x13`Sh\x8dh\x0eN(C3@' @@ -114,27 +137,15 @@ BINARY = (b'\x00\x18\x00 \t\x01\x00\x08\x99\x06\x00\x08\x9b\x06\x00\x08\x00\x00\ b'\x04h+C\x14M,@#C\x03`Kh\x13L\x03a\x03h\ri\x1aC\x02`\x83h\x8ai#@\x83`\x83h' b'*C\xdb\n\xdb\x02\x1aC\x82`\x00-\x04\xd0\x80#\x82h\x1b\x02\x13C\x83`' b'\x02h\x08K\x1a@\x0bh\x1aC\x02`ChJi#@\x13CC`\x00 0\xbd\xc0F\xff\xe0\xff\xff' - b'\xff\x7f\xff\xff\xff\xff\xcf\xff\xfa!\x10\xb5\x89\x00\x00\xf0\x11\xf8\x00"' - b'\x03K\x018X`\x9a`\x052\x1a`\x10\xbd\xc0F\x10\xe0\x00\xe0\x01K\x18`pG\xc0F' - b'\x00\x00\x00 \x00"C\x08\x8bBt\xd3\x03\t\x8bB_\xd3\x03\n\x8bBD\xd3' - b'\x03\x0b\x8bB(\xd3\x03\x0c\x8bB\r\xd3\xff"\t\x02\x12\xba\x03\x0c' - b'\x8bB\x02\xd3\x12\x12\t\x02e\xd0\x03\x0b\x8bB\x19\xd3\x00\xe0\t\n' - b'\xc3\x0b\x8bB\x01\xd3\xcb\x03\xc0\x1aRA\x83\x0b\x8bB\x01\xd3\x8b\x03' - b'\xc0\x1aRAC\x0b\x8bB\x01\xd3K\x03\xc0\x1aRA\x03\x0b\x8bB\x01\xd3\x0b\x03' - b'\xc0\x1aRA\xc3\n\x8bB\x01\xd3\xcb\x02\xc0\x1aRA\x83\n\x8bB\x01\xd3\x8b\x02' - b'\xc0\x1aRAC\n\x8bB\x01\xd3K\x02\xc0\x1aRA\x03\n\x8bB\x01\xd3\x0b\x02' - b'\xc0\x1aRA\xcd\xd2\xc3\t\x8bB\x01\xd3\xcb\x01\xc0\x1aRA\x83\t\x8bB\x01\xd3' - b'\x8b\x01\xc0\x1aRAC\t\x8bB\x01\xd3K\x01\xc0\x1aRA\x03\t\x8bB\x01\xd3' - b'\x0b\x01\xc0\x1aRA\xc3\x08\x8bB\x01\xd3\xcb\x00\xc0\x1aRA\x83\x08' - b'\x8bB\x01\xd3\x8b\x00\xc0\x1aRAC\x08\x8bB\x01\xd3K\x00\xc0\x1aRAA\x1a' - b'\x00\xd2\x01FRA\x10FpG\xff\xe7\x01\xb5\x00 \x00\xf0\x06\xf8\x02\xbd\xc0F' - b'\x00)\xf7\xd0v\xe7pGpG\xc0F\x1c!\x01#\x1b\x04\x98B\x01\xd3\x00\x0c' + b'\xff\x7f\xff\xff\xff\xff\xcf\xff\x1c!\x01#\x1b\x04\x98B\x01\xd3\x00\x0c' b'\x109\x1b\n\x98B\x01\xd3\x00\n\x089\x1b\t\x98B\x01\xd3\x00\t\x049\x02\xa2' b'\x10\\@\x18pG\xc0F\x04\x03\x02\x02\x01\x01\x01\x01\x00\x00\x00\x00' b'\x00\x00\x00\x00\x03\x00\x82\x18\x93B\x00\xd1pG\x19p\x013\xf9\xe7\x00#\xc2\\' b'\x013\x00*\xfb\xd1X\x1epG\x00\x00\xf8\xb5\xc0F\xf8\xbc\x08\xbc\x9eFpG' - b'\xf8\xb5\xc0F\xf8\xbc\x08\xbc\x9eFpG1.0.0\x00OK\x00Bad cmd?\x001.0.0 time' - b'=20230728.120528 git=q1@d86d521 DEV=1\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x1b\xb7\x00\xe9\x00\x00\x08\xc1\x00\x00\x08') + b'\xf8\xb5\xc0F\xf8\xbc\x08\xbc\x9eFpG1.1.0\x00OK\x00Bad cmd?\x00Bad args?\x00' + b'\x00\x00\x00\x00\x00\x00\x00?\x1c\x0e\x00\x1f\x8e\x00\xe0\x0f' + b'\xf8\xff\x8f\xc7\xe3\xfe?\xe3\xf1\xf8\xff\xf1\xf8\x03\xfe8\xfc\x00\x00\x00' + b'\x00\x00\x00\x001.1.0 time=20230801.083144 git=q1@f6b50e1 DEV=1\x00' + b'\x00\x00\x00\x00\xe9\x00\x00\x08\xc1\x00\x00\x08') # EOF diff --git a/misc/gpu/lcd.c b/misc/gpu/lcd.c index 6e67006c..c181a87f 100644 --- a/misc/gpu/lcd.c +++ b/misc/gpu/lcd.c @@ -10,6 +10,7 @@ #include "lcd.h" #include #include "stm32c0xx_hal_gpio_ex.h" +#include "barcode.h" lcd_state_t lcd_state; @@ -20,7 +21,8 @@ const int NUM_PIXELS = (LCD_WIDTH*LCD_HEIGHT); // doing RGB565, but swab16 const uint16_t COL_BLACK = 0; const uint16_t COL_WHITE = ~0; -const uint16_t COL_FOREGROUND = 0x60fd; //SWAB16(0xfd60); // orange +const uint16_t COL_RED = 0x00f8; //SWAP16(0xf800); +const uint16_t COL_FOREGROUND = 0x60fd; //SWAB16(0xfd60); // brand orange // progress bar specs const uint16_t PROG_HEIGHT = 3; @@ -28,9 +30,7 @@ const uint16_t PROG_Y = LCD_HEIGHT - PROG_HEIGHT; static const int NUM_PHASES = 16; -// forward refs -void lcd_write_rows(int y, int num_rows, uint16_t *pixels); - +#if 0 // memset2() // static inline void @@ -52,6 +52,7 @@ static inline void wait_vsync(void) { } //puts("TEAR timeout"); } +#endif // write_byte() // @@ -87,6 +88,31 @@ write_bytes(int len, const uint8_t *buf) } } +// write_uint16() +// + static inline void +write_uint16(int count, uint16_t val) +{ + uint8_t a = val & 0xff; + uint8_t b = val >> 8; + + for(int n=0; n 2*CELL_W); // for dbl_wide case + // no error reporting.. but dont die either + if(char_x >= CHARS_W) return; + if(char_y >= CHARS_H) return; // top left corner, just on edge of character cell int x = LEFT_MARGIN + (char_x * CELL_W); @@ -274,18 +315,16 @@ cursor_draw(int char_x, int char_y, bool outline, bool phase, bool dbl_wide) int cell_w = CELL_W + (dbl_wide?CELL_W:0); // make some pixels big enough for either vert or horz lines - uint16_t colour = phase ? COL_FOREGROUND : COL_BLACK; - uint16_t row[CELL_H]; - memset2(row, colour, sizeof(row)); + uint16_t colour = phase ? COL_FOREGROUND : COL_BLACK; if(outline) { // horz - send_window(x,y, cell_w, 1, &row); - send_window(x,y+CELL_H-1, cell_w, 1, &row); + send_solid(x,y, cell_w, 1, colour); + send_solid(x,y+CELL_H-1, cell_w, 1, colour); // vert - send_window(x, y+1, 1, CELL_H-2, &row); - send_window(x+cell_w-1, y+1, 1, CELL_H-2, &row); + send_solid(x, y+1, 1, CELL_H-2, colour); + send_solid(x+cell_w-1, y+1, 1, CELL_H-2, colour); } else { if(!phase) { // solid fill -- draw first time @@ -297,31 +336,14 @@ cursor_draw(int char_x, int char_y, bool outline, bool phase, bool dbl_wide) } } -// lcd_show_raw() -// -// No decompression. Just used for factory show. 1k bytes -// - void -lcd_show_raw(uint32_t len, const uint8_t *pixels) -{ - // 1024 / 2 = 512 / 320 = 1.6 => just one row! - lcd_write_rows(LCD_HEIGHT-3, 1, (uint16_t *)pixels); - lcd_write_rows(LCD_HEIGHT-2, 1, (uint16_t *)pixels); -} - // lcd_fill_solid() // void lcd_fill_solid(uint16_t pattern) { + // whole screen send_window(0, 0, LCD_WIDTH, LCD_HEIGHT, NULL); - - uint16_t row[LCD_WIDTH]; - memset2(row, pattern, sizeof(row)); - - for(int y=0; y>= 1) { + row[x++] = (test_barcode[i] & m) ? COL_BLACK : COL_WHITE; + } + } + + const int y = 40, h = 120; + send_window(0, y, LCD_WIDTH, h, NULL); + + for(int i=0; i -// LL/HAL internal vars +// LL/CMSIS internal vars uint32_t SystemCoreClock = 12000000UL; #define MY_I2C_ADDR 0x65 @@ -97,7 +97,7 @@ i2c_setup(void) void enter_bootloader(void) { - // prepare enter bootloader on next reset + // Force entry into bootloader on next reset SET_BIT(FLASH->ACR, FLASH_ACR_PROGEMPTY); } @@ -136,13 +136,16 @@ i2c_poll(void) // - sending strings as zero-terminated // - for other responses, master will need to know true length of response if(!isRead) { + respLen = 0; switch(cmd) { case 'V': // full version + if(argLen != 0) goto bad_args; resp = version_string; respLen = strlen(version_string)+1; break; case 'v': // short version + if(argLen != 0) goto bad_args; resp = RELEASE_VERSION; respLen = strlen(RELEASE_VERSION)+1; break; @@ -152,17 +155,46 @@ i2c_poll(void) respLen = argLen; break; - case 'b': // enter bootloader + case 'b': // enter bootloader (follow w/ hard reset) + if(argLen != 0) goto bad_args; enter_bootloader(); resp = "OK"; respLen = 3; break; + case 'c': // enable cursor: args=x,y,outline(else solid),dbl_wide + if(argLen != 4) goto bad_args; + lcd_state.activity_bar = false; + lcd_state.cursor_x = args[0]; + lcd_state.cursor_y = args[1]; + lcd_state.outline_cursor = !args[2]; + lcd_state.solid_cursor = !!args[2]; + lcd_state.dbl_wide = !!args[3]; + lcd_state.cur_flash = false; + break; + + case 'a': // disable cursor (implied: enable activity bar) + if(argLen != 0) goto bad_args; + lcd_state.activity_bar = true; + lcd_state.outline_cursor = false; + lcd_state.solid_cursor = false; + break; + + case 't': // test feature: draw a single test pattern + if(argLen != 0) goto bad_args; + lcd_state.test_pattern = true; + break; + case 0: default: resp = "Bad cmd?"; respLen = strlen(resp); break; + + bad_args: + resp = "Bad args?"; + respLen = strlen(resp); + break; } // critical: flush old data @@ -232,10 +264,16 @@ clock_setup(void) // Set APB1 prescaler LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); - LL_Init1msTick(12000000); - // Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) - LL_SetSystemCoreClock(12000000); + // WAS: + // LL_Init1msTick(12000000); + // LL_SetSystemCoreClock(12000000); + // but, this saves 296-324 bytes because it avoids a division that pulls in a math helper + //SysTick->LOAD = (uint32_t)((12000000 / 1000) - 1UL); // set reload register + SysTick->LOAD = 11999; + SysTick->VAL = 0; // Load the SysTick Counter Value + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_ENABLE_Msk; // Enable the Systick Timer } // mainloop() @@ -249,7 +287,6 @@ mainloop(void) LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG | LL_APB2_GRP1_PERIPH_SPI1); LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR | LL_APB1_GRP1_PERIPH_I2C1); - // Our setup code. gpio_setup(); lcd_setup(); @@ -262,7 +299,7 @@ mainloop(void) while(1) { i2c_poll(); - // G_CTRL must be low, and TEAR high, and if so we do progress bar + // G_CTRL must be low, and TEAR high, and if so we can write to LCD. if(!LL_GPIO_IsInputPinSet(GPIOA, PIN_G_CTRL) && LL_GPIO_IsInputPinSet(GPIOA, PIN_TEAR) ) { @@ -274,8 +311,6 @@ mainloop(void) } } } - - //return 0; } // fatal_error() diff --git a/misc/gpu/make_barcode.py b/misc/gpu/make_barcode.py new file mode 100644 index 00000000..a7e5cc68 --- /dev/null +++ b/misc/gpu/make_barcode.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# +# Render a little barcode we need for selftest process. +# +# - packed bytes +import barcode +from io import BytesIO +from barcode import Code128 +from barcode.writer import ImageWriter + + +class Packer(barcode.writer.BaseWriter): + # api in <../../ENV/lib/python3.10/site-packages/barcode/writer.py> + def __init__(self): + super().__init__(initialize=self.do_init, + paint_module=self.paint, paint_text=self.do_text, finish=self.do_fin) + + def do_init(self, code): + # the answer I want is given to init function: binary for black/white sections + assert len(code) == 1, 'not a list?' + code = code[0] + + if len(code) % 2: + code += '0' + while (len(code) % 8) != 0: + code = f'0{code}0' + + #code = code.replace('0', '00').replace('1', '11') # double it up + code = code.replace('0', '000').replace('1', '111') # 3X + #code = code.replace('0', '0000').replace('1', '1111') # 4X + + # pad to 320 pixels (div 8) (centered) + while len(code) < 320: + code = f'0000{code}0000' + + # convert to bytes + self.result = int(code, 2).to_bytes(len(code)//8, 'big') + + def do_text(self, *unused): + pass + + def paint(self, xpos, ypos, width, color): + #print(f'paint: pos={xpos},{ypos} w={width} c={color}') + pass + + def do_fin(self): + return self.result + +def doit(ofile='barcode.h'): + + # contents of barcode + if 0: + # works, but overkill and reads better if simpler + version = None + with open('version.h') as fd: + for ln in fd: + if 'RELEASE_VERSION' in ln: + version = eval(ln.split()[-1]) + break + assert version + msg = f'GPU={version}' + msg = f'GPU' + bc = Code128(msg, writer=Packer()) + rv = bc.render() + + #bc2 = Code128(msg, writer=ImageWriter()) + #bc2.write('check.png') + + #print(f'Result: {rv.hex()} len={len(rv)}') + + assert len(rv) * 8 <= 320, 'too wide to fit on screen' + assert len(rv) == 40, 'expected 320 pixels' + + enc = rv.hex(' ', 1).replace(' ', ', 0x') + + with open(ofile, 'wt') as fd: + fd.write(f'''// autogen file, see make_barcode.py + +// in python: {repr(rv)} +static const uint8_t test_barcode[{len(rv)}] = {{ + 0x{enc} +}}; + +// EOF''') + + print(f"Updated: {ofile}") + +if __name__ == '__main__': + doit() + +# EOF diff --git a/misc/gpu/requirements.txt b/misc/gpu/requirements.txt new file mode 100644 index 00000000..3b0214de --- /dev/null +++ b/misc/gpu/requirements.txt @@ -0,0 +1,2 @@ +# optional, unless you want to run make_barcode.py +python-barcode==0.15.1 diff --git a/misc/gpu/version.h b/misc/gpu/version.h index 50731e78..8a1ee5ac 100644 --- a/misc/gpu/version.h +++ b/misc/gpu/version.h @@ -5,7 +5,7 @@ #include // Public version number for humans. Lots more version data added by Makefile. -#define RELEASE_VERSION "1.0.0" +#define RELEASE_VERSION "1.1.1" extern const char version_string[];