From 3f7e70ca37c8428d227167d3fed63fe345eb8145 Mon Sep 17 00:00:00 2001 From: raphaelcoeffic <1050031+raphaelcoeffic@users.noreply.github.com> Date: Mon, 19 Oct 2026 11:52:05 +0200 Subject: [PATCH 1/2] feat(mpm): flash the MPM v2 (CC2652) multi-module Firmware files with board type 3 in the multi-x signature are MPM v2 (CC2652) images. They are accepted for internal and external modules like STM32 ones, and the module's STK500 device signature must match the file: 1E 55 26 for the MPM v2 boot loader. A file for the other kind of module is refused before anything is written. The MPM v2 boot loader only listens for a short time after power on, so the module starts fast after a radio reboot: for these files the sync starts 10 ms after power on instead of 500 ms. --- radio/src/io/multi_firmware_update.cpp | 23 +++++++++++++++++------ radio/src/io/multi_firmware_update.h | 17 +++++++++++++---- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/radio/src/io/multi_firmware_update.cpp b/radio/src/io/multi_firmware_update.cpp index 2603bf52920..37e4cf32800 100644 --- a/radio/src/io/multi_firmware_update.cpp +++ b/radio/src/io/multi_firmware_update.cpp @@ -76,7 +76,7 @@ class MultiFirmwareUpdateDriver } const char* flashFirmware(FIL* file, const char* label, - ProgressHandler progressHandler); + ProgressHandler progressHandler, bool cc2652); }; static const etx_serial_init serialInitParams = { @@ -305,7 +305,7 @@ void MultiFirmwareUpdateDriver::leaveProgMode() } const char* MultiFirmwareUpdateDriver::flashFirmware( - FIL* file, const char* label, ProgressHandler progressHandler) + FIL* file, const char* label, ProgressHandler progressHandler, bool cc2652) { #if defined(SIMU) for (uint16_t i = 0; i < 100; i++) { @@ -318,9 +318,11 @@ const char* MultiFirmwareUpdateDriver::flashFirmware( const char * result = nullptr; if (!init()) return "Initialisation error"; - /* wait 500ms for power on */ + /* wait 500ms for power on; the MPM v2 boot loader only listens for a + short time after power on (the module must start fast after a radio + reboot): sync at once */ watchdogSuspend(500 /*5s*/); - sleep_ms(500); + sleep_ms(cc2652 ? 10 : 500); result = waitForInitialSync(); if (result) { @@ -344,7 +346,14 @@ const char* MultiFirmwareUpdateDriver::flashFirmware( return STR_DEVICE_FILE_WRONG_SIG; } - if (signature[1] == 0x55 && signature[2] == 0xAA) { + // MPM v2 (CC2652) boot loader: 1E 55 26, the STM32 layout + bool cc2652Device = signature[1] == 0x55 && signature[2] == 0x26; + if (cc2652Device != cc2652) { + leaveProgMode(); + return STR_DEVICE_FILE_WRONG_SIG; + } + + if (signature[1] == 0x55 && (signature[2] == 0xAA || cc2652Device)) { pageSize = 256; writeOffset = 0x1000; // start offset (word address) } @@ -507,6 +516,7 @@ bool MultiDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHan return false; } + bool cc2652 = false; if (type == MULTI_TYPE_MULTIMODULE) { MultiFirmwareInformation firmwareFile; if (firmwareFile.readMultiFirmwareInformation(&file)) { @@ -515,6 +525,7 @@ bool MultiDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHan return false; } f_lseek(&file, 0); + cc2652 = firmwareFile.isMultiCc2652Firmware(); #if defined(HARDWARE_EXTERNAL_MODULE) if (module == EXTERNAL_MODULE) { @@ -555,7 +566,7 @@ bool MultiDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHan sleep_ms(3000); MultiFirmwareUpdateDriver driver(module, type); - const char * result = driver.flashFirmware(&file, getBasename(filename), progressHandler); + const char * result = driver.flashFirmware(&file, getBasename(filename), progressHandler, cc2652); f_close(&file); AUDIO_PLAY(AU_SPECIAL_SOUND_BEEP1); diff --git a/radio/src/io/multi_firmware_update.h b/radio/src/io/multi_firmware_update.h index 29a210818e5..b0440c26e7a 100644 --- a/radio/src/io/multi_firmware_update.h +++ b/radio/src/io/multi_firmware_update.h @@ -25,7 +25,7 @@ /* Signature format is multi-[board type]-[bootloader support][check for bootloader][multi telemetry type][telemetry inversion][debug]-[firmware version] Where: - [board type] is avr, stm, or orx + [board type] is avr, stm, or orx (v2 signature: also 3, CC2652 / MPM v2) [bootloader support] b for optiboot (AVR) / USB (STM) or u (unsupported) [check for bootloader] is c (CHECK_FOR_BOOTLOADER) or u (undefined) [telemetry type] is t (MULTI_TELEMETRY), s (MULTI_STATUS), or u (undefined) for neither @@ -43,6 +43,7 @@ class MultiFirmwareInformation FIRMWARE_MULTI_AVR = 0, FIRMWARE_MULTI_STM, FIRMWARE_MULTI_ORX, + FIRMWARE_MULTI_CC2652, }; enum MultiFirmwareTelemetryType @@ -67,6 +68,12 @@ class MultiFirmwareInformation return boardType == FIRMWARE_MULTI_ORX; } + // MPM v2 (CC2652): its own boot loader, STK500 device signature 1E 55 26 + bool isMultiCc2652Firmware() const + { + return boardType == FIRMWARE_MULTI_CC2652; + } + bool isMultiWithBootloaderFirmware() const { return optibootSupport; @@ -74,14 +81,16 @@ class MultiFirmwareInformation bool isMultiInternalFirmware() const { - return (boardType == FIRMWARE_MULTI_STM && optibootSupport && - bootloaderCheck && + return ((boardType == FIRMWARE_MULTI_STM || + boardType == FIRMWARE_MULTI_CC2652) && + optibootSupport && bootloaderCheck && telemetryType == FIRMWARE_MULTI_TELEM_MULTI_TELEMETRY); } bool isMultiExternalFirmware() const { - return ((telemetryInversion || boardType == FIRMWARE_MULTI_STM) && + return ((telemetryInversion || boardType == FIRMWARE_MULTI_STM || + boardType == FIRMWARE_MULTI_CC2652) && optibootSupport && bootloaderCheck && telemetryType == FIRMWARE_MULTI_TELEM_MULTI_TELEMETRY); } From 7bec5cfddd2853a68c977f0ee1a5f18f44885358 Mon Sep 17 00:00:00 2001 From: raphaelcoeffic <1050031+raphaelcoeffic@users.noreply.github.com> Date: Wed, 4 Nov 2026 08:04:07 +0100 Subject: [PATCH 2/2] refactor(mpm): the firmware's board type to the flash driver, MPM v2 naming The driver gets the file's MultiFirmwareBoardType instead of a bool. FIRMWARE_MULTI_CC2652 is now FIRMWARE_MULTI_MPM_V2: the 1E 55 26 signature names the MPM v2 boot loader, which runs on the CC26xx and the CC27xx alike. --- radio/src/io/multi_firmware_update.cpp | 29 ++++++++++++++++---------- radio/src/io/multi_firmware_update.h | 13 ++++++------ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/radio/src/io/multi_firmware_update.cpp b/radio/src/io/multi_firmware_update.cpp index 37e4cf32800..d988f8a8c24 100644 --- a/radio/src/io/multi_firmware_update.cpp +++ b/radio/src/io/multi_firmware_update.cpp @@ -75,8 +75,9 @@ class MultiFirmwareUpdateDriver { } - const char* flashFirmware(FIL* file, const char* label, - ProgressHandler progressHandler, bool cc2652); + const char* flashFirmware( + FIL* file, const char* label, ProgressHandler progressHandler, + MultiFirmwareInformation::MultiFirmwareBoardType boardType); }; static const etx_serial_init serialInitParams = { @@ -305,7 +306,8 @@ void MultiFirmwareUpdateDriver::leaveProgMode() } const char* MultiFirmwareUpdateDriver::flashFirmware( - FIL* file, const char* label, ProgressHandler progressHandler, bool cc2652) + FIL* file, const char* label, ProgressHandler progressHandler, + MultiFirmwareInformation::MultiFirmwareBoardType boardType) { #if defined(SIMU) for (uint16_t i = 0; i < 100; i++) { @@ -318,11 +320,14 @@ const char* MultiFirmwareUpdateDriver::flashFirmware( const char * result = nullptr; if (!init()) return "Initialisation error"; + bool mpmV2File = + boardType == MultiFirmwareInformation::FIRMWARE_MULTI_MPM_V2; + /* wait 500ms for power on; the MPM v2 boot loader only listens for a short time after power on (the module must start fast after a radio reboot): sync at once */ watchdogSuspend(500 /*5s*/); - sleep_ms(cc2652 ? 10 : 500); + sleep_ms(mpmV2File ? 10 : 500); result = waitForInitialSync(); if (result) { @@ -346,14 +351,14 @@ const char* MultiFirmwareUpdateDriver::flashFirmware( return STR_DEVICE_FILE_WRONG_SIG; } - // MPM v2 (CC2652) boot loader: 1E 55 26, the STM32 layout - bool cc2652Device = signature[1] == 0x55 && signature[2] == 0x26; - if (cc2652Device != cc2652) { + // MPM v2 boot loader (CC26xx, CC27xx): 1E 55 26, the STM32 layout + bool mpmV2Device = signature[1] == 0x55 && signature[2] == 0x26; + if (mpmV2Device != mpmV2File) { leaveProgMode(); return STR_DEVICE_FILE_WRONG_SIG; } - if (signature[1] == 0x55 && (signature[2] == 0xAA || cc2652Device)) { + if (signature[1] == 0x55 && (signature[2] == 0xAA || mpmV2Device)) { pageSize = 256; writeOffset = 0x1000; // start offset (word address) } @@ -516,7 +521,9 @@ bool MultiDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHan return false; } - bool cc2652 = false; + // ELRS files carry no board type: written like STM32 ones, to anything but + // the MPM v2 boot loader + auto boardType = MultiFirmwareInformation::FIRMWARE_MULTI_STM; if (type == MULTI_TYPE_MULTIMODULE) { MultiFirmwareInformation firmwareFile; if (firmwareFile.readMultiFirmwareInformation(&file)) { @@ -525,7 +532,7 @@ bool MultiDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHan return false; } f_lseek(&file, 0); - cc2652 = firmwareFile.isMultiCc2652Firmware(); + boardType = firmwareFile.getBoardType(); #if defined(HARDWARE_EXTERNAL_MODULE) if (module == EXTERNAL_MODULE) { @@ -566,7 +573,7 @@ bool MultiDeviceFirmwareUpdate::flashFirmware(const char * filename, ProgressHan sleep_ms(3000); MultiFirmwareUpdateDriver driver(module, type); - const char * result = driver.flashFirmware(&file, getBasename(filename), progressHandler, cc2652); + const char * result = driver.flashFirmware(&file, getBasename(filename), progressHandler, boardType); f_close(&file); AUDIO_PLAY(AU_SPECIAL_SOUND_BEEP1); diff --git a/radio/src/io/multi_firmware_update.h b/radio/src/io/multi_firmware_update.h index b0440c26e7a..d18abf8aeb2 100644 --- a/radio/src/io/multi_firmware_update.h +++ b/radio/src/io/multi_firmware_update.h @@ -25,7 +25,7 @@ /* Signature format is multi-[board type]-[bootloader support][check for bootloader][multi telemetry type][telemetry inversion][debug]-[firmware version] Where: - [board type] is avr, stm, or orx (v2 signature: also 3, CC2652 / MPM v2) + [board type] is avr, stm, or orx (v2 signature: also 3, MPM v2: TI CC26xx / CC27xx) [bootloader support] b for optiboot (AVR) / USB (STM) or u (unsupported) [check for bootloader] is c (CHECK_FOR_BOOTLOADER) or u (undefined) [telemetry type] is t (MULTI_TELEMETRY), s (MULTI_STATUS), or u (undefined) for neither @@ -43,7 +43,7 @@ class MultiFirmwareInformation FIRMWARE_MULTI_AVR = 0, FIRMWARE_MULTI_STM, FIRMWARE_MULTI_ORX, - FIRMWARE_MULTI_CC2652, + FIRMWARE_MULTI_MPM_V2, // MPM v2: its own boot loader, signature 1E 55 26 }; enum MultiFirmwareTelemetryType @@ -68,10 +68,9 @@ class MultiFirmwareInformation return boardType == FIRMWARE_MULTI_ORX; } - // MPM v2 (CC2652): its own boot loader, STK500 device signature 1E 55 26 - bool isMultiCc2652Firmware() const + MultiFirmwareBoardType getBoardType() const { - return boardType == FIRMWARE_MULTI_CC2652; + return (MultiFirmwareBoardType)boardType; } bool isMultiWithBootloaderFirmware() const @@ -82,7 +81,7 @@ class MultiFirmwareInformation bool isMultiInternalFirmware() const { return ((boardType == FIRMWARE_MULTI_STM || - boardType == FIRMWARE_MULTI_CC2652) && + boardType == FIRMWARE_MULTI_MPM_V2) && optibootSupport && bootloaderCheck && telemetryType == FIRMWARE_MULTI_TELEM_MULTI_TELEMETRY); } @@ -90,7 +89,7 @@ class MultiFirmwareInformation bool isMultiExternalFirmware() const { return ((telemetryInversion || boardType == FIRMWARE_MULTI_STM || - boardType == FIRMWARE_MULTI_CC2652) && + boardType == FIRMWARE_MULTI_MPM_V2) && optibootSupport && bootloaderCheck && telemetryType == FIRMWARE_MULTI_TELEM_MULTI_TELEMETRY); }