summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien George <damien@micropython.org>2022-06-02 14:09:08 +1000
committerDamien George <damien@micropython.org>2022-06-02 15:22:16 +1000
commit340872cfdd724b7acc6768a22d90a84834b0e7b5 (patch)
tree3dc36c8b8fdbd25f1b9209cb42e558e12698a99b
parent87ca431f3fa3fc87094526e33b77c50d6f968100 (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.md29
-rw-r--r--ports/stm32/boards/LEGO_HUB_NO6/appupdate.py37
-rw-r--r--ports/stm32/boards/LEGO_HUB_NO6/manifest.py5
-rw-r--r--ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.mk3
-rw-r--r--ports/stm32/boards/LEGO_HUB_NO6/spiflash.py74
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