stm32/boards/LEGO_HUB_NO6: Add helper scripts to update app firmware.

Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
Damien George 2022-06-02 14:09:08 +10:00
parent 87ca431f3f
commit 340872cfdd
5 changed files with 148 additions and 0 deletions

View File

@ -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.

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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