diff options
| author | Damien George <damien@micropython.org> | 2022-06-02 14:09:08 +1000 |
|---|---|---|
| committer | Damien George <damien@micropython.org> | 2022-06-02 15:22:16 +1000 |
| commit | 340872cfdd724b7acc6768a22d90a84834b0e7b5 (patch) | |
| tree | 3dc36c8b8fdbd25f1b9209cb42e558e12698a99b | |
| parent | 87ca431f3fa3fc87094526e33b77c50d6f968100 (diff) | |
stm32/boards/LEGO_HUB_NO6: Add helper scripts to update app firmware.
Signed-off-by: Damien George <damien@micropython.org>
| -rw-r--r-- | ports/stm32/boards/LEGO_HUB_NO6/README.md | 29 | ||||
| -rw-r--r-- | ports/stm32/boards/LEGO_HUB_NO6/appupdate.py | 37 | ||||
| -rw-r--r-- | ports/stm32/boards/LEGO_HUB_NO6/manifest.py | 5 | ||||
| -rw-r--r-- | ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.mk | 3 | ||||
| -rw-r--r-- | ports/stm32/boards/LEGO_HUB_NO6/spiflash.py | 74 |
5 files changed, 148 insertions, 0 deletions
diff --git a/ports/stm32/boards/LEGO_HUB_NO6/README.md b/ports/stm32/boards/LEGO_HUB_NO6/README.md index 0b88cd837..7d5f2a416 100644 --- a/ports/stm32/boards/LEGO_HUB_NO6/README.md +++ b/ports/stm32/boards/LEGO_HUB_NO6/README.md @@ -97,3 +97,32 @@ To scan for BLE devices: >>> ble.gap_scan(2000, 625, 625) Use help("modules") to see available built-in modules. + +Updating MicroPython from the Hub's filesystem +---------------------------------------------- + +You can update the MicroPython application firmware using the instructions above +for installing the firmware for the first time. The Hub also supports updating +the application firmware from within MicroPython itself, using the on-board +filesystem. + +To use this feature, build the firmware (see above for details) then gzip it and +copy the resulting file to the Hub (eg using mpremote): + + $ make BOARD=LEGO_HUB_NO6 + $ gzip boards/LEGO_HUB_NO6/firmware.dfu + $ mpremote cp boards/LEGO_HUB_NO6/firmware.dfu.gz : + +Then get a REPL on the Hub and execute: + + >>> import appupdate + >>> appupdate.update_app("firmware.dfu.gz") + +You can alternatively run this REPL command using mpremote: + + $ mpremote exec --no-follow "import appupdate; appupdate.update_app('firmware.dfu.gz')" + +At that point the Hub should restart and the LED on the central button will flash +different colours. Once the update is complete the LED will stop flashing and the +Hub will appear again as a USB device. The application firmware is now updated +and you can remove the firmware.dfu.gz file if desired. diff --git a/ports/stm32/boards/LEGO_HUB_NO6/appupdate.py b/ports/stm32/boards/LEGO_HUB_NO6/appupdate.py new file mode 100644 index 000000000..65954454a --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/appupdate.py @@ -0,0 +1,37 @@ +# Application firmware update funcion for LEGO_HUB_NO6. +# MIT license; Copyright (c) 2022 Damien P. George + +from micropython import const +import struct, machine, fwupdate, spiflash + +_SPIFLASH_UPDATE_KEY_ADDR = const(1020 * 1024) +_SPIFLASH_UPDATE_KEY_VALUE = const(0x12345678) + +_FILESYSTEM_ADDR = const(0x8000_0000 + 1024 * 1024) +_FILESYSTEM_LEN = const(31 * 1024 * 1024) + + +def update_app(filename): + print(f"Updating application firmware from {filename}") + + # Create the elements for the mboot filesystem-load operation. + elems = fwupdate.update_app_elements(filename, _FILESYSTEM_ADDR, _FILESYSTEM_LEN) + if not elems: + return + + # Create the update key. + key = struct.pack("<I", _SPIFLASH_UPDATE_KEY_VALUE) + + # Create a SPI flash object. + spi = machine.SoftSPI(sck="B13", mosi="C3", miso="C2", baudrate=50_000_000) + cs = machine.Pin("B12", machine.Pin.OUT, value=1) + flash = spiflash.SPIFlash(spi, cs) + + # Write the update key and elements to the SPI flash. + flash.erase_block(_SPIFLASH_UPDATE_KEY_ADDR) + flash.write(_SPIFLASH_UPDATE_KEY_ADDR, key + elems) + + # Enter mboot with a request to do a filesystem-load update. + # If there is a power failure during the update (eg battery removed) then + # mboot will read the SPI flash update key and elements and retry. + machine.bootloader(elems) diff --git a/ports/stm32/boards/LEGO_HUB_NO6/manifest.py b/ports/stm32/boards/LEGO_HUB_NO6/manifest.py new file mode 100644 index 000000000..dc09d04c9 --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/manifest.py @@ -0,0 +1,5 @@ +include("$(PORT_DIR)/boards/manifest.py") + +# Modules for application firmware update. +freeze("$(PORT_DIR)/mboot", "fwupdate.py", opt=3) +freeze("$(BOARD_DIR)", ("spiflash.py", "appupdate.py"), opt=3) diff --git a/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.mk b/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.mk index 8f8695a9c..e80a213e8 100644 --- a/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.mk +++ b/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.mk @@ -13,6 +13,9 @@ MICROPY_BLUETOOTH_NIMBLE ?= 0 MICROPY_BLUETOOTH_BTSTACK ?= 1 MICROPY_VFS_LFS2 ?= 1 +# Board specific frozen modules +FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py + ifneq ($(BUILDING_MBOOT),1) LIB_SRC_C += lib/btstack/chipset/cc256x/btstack_chipset_cc256x.c endif diff --git a/ports/stm32/boards/LEGO_HUB_NO6/spiflash.py b/ports/stm32/boards/LEGO_HUB_NO6/spiflash.py new file mode 100644 index 000000000..e052f7738 --- /dev/null +++ b/ports/stm32/boards/LEGO_HUB_NO6/spiflash.py @@ -0,0 +1,74 @@ +# MicroPython driver for SPI flash +# MIT license; Copyright (c) 2022 Damien P. George + +from micropython import const + +_PAGE_SIZE = const(256) # maximum bytes writable in one SPI transfer +_CMD_RDSR = const(0x05) +_CMD_WREN = const(0x06) +_CMD_WRITE_32 = const(0x12) +_CMD_READ_32 = const(0x13) +_CMD_SEC_ERASE_32 = const(0x21) +_CMD_C4READ_32 = const(0xEC) + + +class SPIFlash: + def __init__(self, spi, cs): + self.spi = spi + self.cs = cs + + def _wait_wel1(self): + # wait WEL=1 + self.cs(0) + self.spi.write(bytearray([_CMD_RDSR])) + buf = bytearray(1) + while True: + self.spi.readinto(buf) + if buf[0] & 2: + break + self.cs(1) + + def _wait_wip0(self): + # wait WIP=0 + self.cs(0) + self.spi.write(bytearray([_CMD_RDSR])) + buf = bytearray(1) + while True: + self.spi.readinto(buf) + if not (buf[0] & 1): + break + self.cs(1) + + def _flash_modify(self, cmd, addr, buf): + self.cs(0) + self.spi.write(bytearray([_CMD_WREN])) + self.cs(1) + self._wait_wel1() + self.cs(0) + self.spi.write(bytearray([cmd, addr >> 24, addr >> 16, addr >> 8, addr])) + if buf: + self.spi.write(buf) + self.cs(1) + self._wait_wip0() + + def erase_block(self, addr): + self._flash_modify(_CMD_SEC_ERASE_32, addr, None) + + def readinto(self, addr, buf): + self.cs(0) + self.spi.write(bytearray([_CMD_READ_32, addr >> 16, addr >> 8, addr])) + self.spi.readinto(buf) + self.cs(1) + + def write(self, addr, buf): + offset = addr & (_PAGE_SIZE - 1) + remain = len(buf) + buf = memoryview(buf) + buf_offset = 0 + while remain: + l = min(_PAGE_SIZE - offset, remain) + self._flash_modify(_CMD_WRITE_32, addr, buf[buf_offset : buf_offset + l]) + remain -= l + addr += l + buf_offset += l + offset = 0 |
