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:
parent
87ca431f3f
commit
340872cfdd
|
@ -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.
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue