From a5bf0bad6eb972e80b00ea9b44ae835be7a35246 Mon Sep 17 00:00:00 2001 From: Takashi Matsuoka Date: Thu, 2 Feb 2023 17:24:35 +0900 Subject: [PATCH 01/84] Support for no button --- src/boards/boards.c | 9 +++++++++ src/boards/boards.h | 10 ++++------ src/main.c | 4 ++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/boards/boards.c b/src/boards/boards.c index e86edd63..8872a93f 100644 --- a/src/boards/boards.c +++ b/src/boards/boards.c @@ -44,6 +44,9 @@ #endif //------------- IMPLEMENTATION -------------// + +#if defined(BUTTON_DFU) || defined(BUTTON_FRESET) + void button_init(uint32_t pin) { if ( BUTTON_PULL == NRF_GPIO_PIN_PULLDOWN ) @@ -62,6 +65,8 @@ bool button_pressed(uint32_t pin) return nrf_gpio_pin_read(pin) == active_state; } +#endif + // This is declared so that a board specific init can be called from here. void __attribute__((weak)) board_init_extra(void) { } void board_init(void) @@ -73,8 +78,12 @@ void board_init(void) NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_RC; NRF_CLOCK->TASKS_LFCLKSTART = 1UL; +#ifdef BUTTON_DFU button_init(BUTTON_DFU); +#endif +#ifdef BUTTON_FRESET button_init(BUTTON_FRESET); +#endif NRFX_DELAY_US(100); // wait for the pin state is stable #if LEDS_NUMBER > 0 diff --git a/src/boards/boards.h b/src/boards/boards.h index 8aa0ece6..e11acf28 100644 --- a/src/boards/boards.h +++ b/src/boards/boards.h @@ -37,11 +37,11 @@ #define UF2_VOLUME_LABEL "NRF52BOOT " #endif -#ifndef BUTTON_DFU +#if !defined(BUTTON_DFU) && defined(BUTTON_1) #define BUTTON_DFU BUTTON_1 #endif -#ifndef BUTTON_FRESET +#if !defined(BUTTON_FRESET) && defined(BUTTON_2) #define BUTTON_FRESET BUTTON_2 #endif @@ -104,13 +104,11 @@ void led_tick(void); //--------------------------------------------------------------------+ // BUTTONS //--------------------------------------------------------------------+ -// Make sure we have at least two buttons (DFU + FRESET since DFU+FRST=OTA) -#if BUTTONS_NUMBER < 2 -#error "At least two buttons required in the BSP (see 'BUTTONS_NUMBER')" -#endif +#if defined(BUTTON_DFU) || defined(BUTTON_FRESET) void button_init(uint32_t pin); bool button_pressed(uint32_t pin); +#endif bool is_ota(void); diff --git a/src/main.c b/src/main.c index 47386ca8..c6f5963e 100644 --- a/src/main.c +++ b/src/main.c @@ -259,10 +259,14 @@ static void check_dfu_mode(void) /*------------- Determine DFU mode (Serial, OTA, FRESET or normal) -------------*/ // DFU button pressed +#if defined(BUTTON_DFU) dfu_start = dfu_start || button_pressed(BUTTON_DFU); +#endif // DFU + FRESET are pressed --> OTA +#if defined(BUTTON_DFU) && defined(BUTTON_FRESET) _ota_dfu = _ota_dfu || ( button_pressed(BUTTON_DFU) && button_pressed(BUTTON_FRESET) ) ; +#endif bool const valid_app = bootloader_app_is_valid(); bool const just_start_app = valid_app && !dfu_start && (*dbl_reset_mem) == DFU_DBL_RESET_APP; From 22224cef7b4fbd6c762821666c625ef790dba236 Mon Sep 17 00:00:00 2001 From: OmarEMbed <62099184+OmarEMbed@users.noreply.github.com> Date: Sun, 16 Feb 2025 11:04:50 +0100 Subject: [PATCH 02/84] Add files via upload --- src/boards/omnimo_nrf52840/board.cmake | 1 + src/boards/omnimo_nrf52840/board.h | 72 ++++++++++++++++++++++++++ src/boards/omnimo_nrf52840/board.mk | 1 + src/boards/omnimo_nrf52840/pinconfig.c | 19 +++++++ 4 files changed, 93 insertions(+) create mode 100644 src/boards/omnimo_nrf52840/board.cmake create mode 100644 src/boards/omnimo_nrf52840/board.h create mode 100644 src/boards/omnimo_nrf52840/board.mk create mode 100644 src/boards/omnimo_nrf52840/pinconfig.c diff --git a/src/boards/omnimo_nrf52840/board.cmake b/src/boards/omnimo_nrf52840/board.cmake new file mode 100644 index 00000000..2715f360 --- /dev/null +++ b/src/boards/omnimo_nrf52840/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/omnimo_nrf52840/board.h b/src/boards/omnimo_nrf52840/board.h new file mode 100644 index 00000000..cf4e3a7d --- /dev/null +++ b/src/boards/omnimo_nrf52840/board.h @@ -0,0 +1,72 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _OMNIMO_NRF52840_H +#define _OMNIMO_NRF52840_H + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN _PINNUM(1, 15) +#define LED_SECONDARY_PIN _PINNUM(1, 10) +#define LED_STATE_ON 1 + +#define LED_NEOPIXEL _PINNUM(0, 16) + +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTONS_NUMBER 2 +#define BUTTON_1 _PINNUM(1, 02) +#define BUTTON_2 _PINNUM(1, 07) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "eAFAQ" +#define BLEDIS_MODEL "OMNIMO nRF52840" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ + +//Shared VID/PID with pca10056 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x00DA +#define USB_DESC_CDC_ONLY_PID 0x00DA + +//------------- UF2 -------------// +#define UF2_PRODUCT_NAME "Omnimo nRF52840" +#define UF2_VOLUME_LABEL "OMNIn52BOOT" +#define UF2_BOARD_ID "nRF52840-Omnimo" +#define UF2_INDEX_URL "https://www.crowdsupply.com/eafaq/omnimo-nrf52840" + +#endif // _OMNIMO_NRF52840_H diff --git a/src/boards/omnimo_nrf52840/board.mk b/src/boards/omnimo_nrf52840/board.mk new file mode 100644 index 00000000..9d29ac69 --- /dev/null +++ b/src/boards/omnimo_nrf52840/board.mk @@ -0,0 +1 @@ +MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/omnimo_nrf52840/pinconfig.c b/src/boards/omnimo_nrf52840/pinconfig.c new file mode 100644 index 00000000..a983e4ab --- /dev/null +++ b/src/boards/omnimo_nrf52840/pinconfig.c @@ -0,0 +1,19 @@ +#include "boards.h" +#include "uf2/configkeys.h" + +__attribute__((used, section(".bootloaderConfig"))) +const uint32_t bootloaderConfig[] = +{ + /* CF2 START */ + CFG_MAGIC0, CFG_MAGIC1, // magic + 5, 100, // used entries, total entries + + 204, 0x100000, // FLASH_BYTES = 0x100000 + 205, 0x40000, // RAM_BYTES = 0x40000 + 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 + 209, 0xada52840, // UF2_FAMILY = 0xada52840 + 210, 0x20, // PINS_PORT_SIZE = PA_32 + + 0, 0, 0, 0, 0, 0, 0, 0 + /* CF2 END */ +}; From 8970125e51544674747fa65b629e3c4ab71e486f Mon Sep 17 00:00:00 2001 From: OmarEMbed <62099184+OmarEMbed@users.noreply.github.com> Date: Sun, 16 Feb 2025 14:00:12 +0100 Subject: [PATCH 03/84] Update board.h --- src/boards/omnimo_nrf52840/board.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/boards/omnimo_nrf52840/board.h b/src/boards/omnimo_nrf52840/board.h index cf4e3a7d..0a3b710f 100644 --- a/src/boards/omnimo_nrf52840/board.h +++ b/src/boards/omnimo_nrf52840/board.h @@ -59,9 +59,9 @@ //--------------------------------------------------------------------+ //Shared VID/PID with pca10056 -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00DA -#define USB_DESC_CDC_ONLY_PID 0x00DA +#define USB_DESC_VID 0x1209 +#define USB_DESC_UF2_PID 0xCECE +#define USB_DESC_CDC_ONLY_PID 0xCECE //------------- UF2 -------------// #define UF2_PRODUCT_NAME "Omnimo nRF52840" From 73020f3567b7ef7ed3bdbb6383d7b1cadeeb72d3 Mon Sep 17 00:00:00 2001 From: Andreas Kurz Date: Fri, 9 May 2025 08:15:14 +0000 Subject: [PATCH 04/84] fix: add size optimization This flag is also present in the Makefile and optimises the resulting binary for the size of the binary. Without that flag the flash consumption would be too high to fit inside the allocated space. Signed-off-by: Andreas Kurz --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 567a815a..d6216474 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,6 +170,7 @@ target_compile_options(bootloader PUBLIC -Wmissing-format-attribute -Wno-endif-labels -Wunreachable-code + -Os # Suppress warning caused by SDK -Wno-unused-parameter -Wno-expansion-to-defined -Wno-array-bounds ) @@ -333,4 +334,4 @@ add_custom_target(flash-mbr add_custom_target(flash-erase COMMAND ${NRFJPROG} -f nrf52 --eraseall - ) \ No newline at end of file + ) From ddcb2dba8c7090ced18a7a8b005ee79f168ae8e3 Mon Sep 17 00:00:00 2001 From: Andreas Kurz Date: Fri, 9 May 2025 10:31:05 +0200 Subject: [PATCH 05/84] chore: add documentation for CMake In this repository CMake and Make co-exists. Unfortunately there was no documentation on how to properly use CMake so this is a starting point. Signed-off-by: Andreas Kurz --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b4e3cdb7..2929ae21 100644 --- a/README.md +++ b/README.md @@ -114,14 +114,20 @@ You must have have a J-Link available to "unbrick" your device. ### Build: -Firstly clone this repo with following commands +Firstly clone this repo including its submodules with following command: +``` +git clone --recurse-submodules -j8 https://github.com/adafruit/Adafruit_nRF52_Bootloader +``` + +For git versions before `2.13.0` you have to do that manually: ``` git clone https://github.com/adafruit/Adafruit_nRF52_Bootloader cd Adafruit_nRF52_Bootloader git submodule update --init ``` +#### Build using `make` Then build it with `make BOARD={board} all`, for example: ``` @@ -137,6 +143,21 @@ Supported boards are: feather_nrf52840_express feather_nrf52840_express pca10056 Makefile:90: *** BOARD not defined. Stop ``` +#### Build using `cmake` + +Firstly initialize your build environment by passing your board to `cmake` via `-DBOARD={board}`: +``` +cmake -DBOARD=feather_nrf52840_express -S . -B _build -GNinja +``` + +And then build it with: +``` +cd _build +ninja +``` + +You can also use the generator of your choice. For example omit the `-GNinja` and then you can invoke `make` from inside `_build` instead of `ninja` + ### Flash To flash the bootloader (without softdevice/mbr) using JLink: From 389e5b004d112df4b406bde5d530b29db2bd9bb9 Mon Sep 17 00:00:00 2001 From: JuergenLeber Date: Sun, 8 Jun 2025 18:53:45 +0200 Subject: [PATCH 06/84] chore: update uf2 submodule to latest --- lib/uf2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/uf2 b/lib/uf2 index adbb8c72..b19ac9ad 160000 --- a/lib/uf2 +++ b/lib/uf2 @@ -1 +1 @@ -Subproject commit adbb8c7260f938e810eb37f2287f8e1a055ff402 +Subproject commit b19ac9ad613e11a062e4d00845e2b80e6b0ad29f From 00b5e7249635795a66d6584ed3013f6a087a25c3 Mon Sep 17 00:00:00 2001 From: JuergenLeber Date: Sun, 8 Jun 2025 19:06:45 +0200 Subject: [PATCH 07/84] fix: fix small typo --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 92ad0208..dd66bd77 100644 --- a/src/main.c +++ b/src/main.c @@ -313,7 +313,7 @@ static void check_dfu_mode(void) { } } -// Initializes the SotdDevice by following SD specs section +// Initializes the SoftDevice by following SD specs section // "Master Boot Record and SoftDevice initializaton procedure" static uint32_t ble_stack_init(void) { // Forward vector table to bootloader address so that we can handle BLE events From b8544f5c8c7c1e5e185232b54fd9c696ceb17fc7 Mon Sep 17 00:00:00 2001 From: JuergenLeber Date: Sun, 8 Jun 2025 21:00:39 +0200 Subject: [PATCH 08/84] feat: add SD S340 support for nRF52840 and nRF52833 --- .github/workflows/githubci.yml | 2 +- .gitignore | 4 +++ CMakeLists.txt | 35 +++++++++++++++------- Makefile | 16 +++++++--- README.md | 19 ++++++++++++ lib/softdevice/s340_nrf52_7.0.1/readme.txt | 1 + src/main.c | 23 ++++++++++++-- 7 files changed, 82 insertions(+), 18 deletions(-) create mode 100644 lib/softdevice/s340_nrf52_7.0.1/readme.txt diff --git a/.github/workflows/githubci.yml b/.github/workflows/githubci.yml index 68b4f7f2..55086fc1 100644 --- a/.github/workflows/githubci.yml +++ b/.github/workflows/githubci.yml @@ -21,7 +21,7 @@ jobs: id: set-matrix working-directory: src/boards run: | - MATRIX_JSON=$(ls -d */ | sed 's/\/$//' | jq -R -s -c 'split("\n")[:-1]') + MATRIX_JSON=$(ls -d */ | sed 's/\/$//' | grep -vE '_s340$' | jq -R -s -c 'split("\n")[:-1]') echo "matrix=$MATRIX_JSON" echo "matrix=$MATRIX_JSON" >> $GITHUB_OUTPUT diff --git a/.gitignore b/.gitignore index 1c244fca..e7b13c9f 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,7 @@ TAGS .DS_Store Makefile.user + +# Exclude all SoftDevice S340 files +lib/softdevice/s340* + diff --git a/CMakeLists.txt b/CMakeLists.txt index 567a815a..6d21fa64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -227,22 +227,37 @@ if (MCU_VARIANT STREQUAL "nrf52") S132 ) elseif (MCU_VARIANT STREQUAL "nrf52833") - set(SD_NAME s140) set(DFU_DEV_REV 52833) set(DFU_APP_DATA_RESERVED 7*4096) - target_compile_definitions(bootloader PUBLIC - NRF52833_XXAA - S140 - ) + if (SD_NAME STREQUAL "s340") + target_compile_definitions(bootloader PUBLIC + NRF52833_XXAA + S340 + ) + else () + set(SD_NAME s140) + target_compile_definitions(bootloader PUBLIC + NRF52833_XXAA + S140 + ) + endif () elseif (MCU_VARIANT STREQUAL "nrf52840") - set(SD_NAME s140) set(DFU_DEV_REV 52840) # App reserved 40KB (8+32) to match circuitpython for 840 set(DFU_APP_DATA_RESERVED 10*4096) - target_compile_definitions(bootloader PUBLIC - NRF52840_XXAA - S140 - ) + if (SD_NAME STREQUAL "s340") + target_compile_definitions(bootloader PUBLIC + NRF52840_XXAA + S340 + ) + else () + set(SD_NAME s140) + target_compile_definitions(bootloader PUBLIC + NRF52840_XXAA + S140 + ) + endif () + else () message(FATAL_ERROR "MCU_VARIANT ${MCU_VARIANT} is unknown") endif () diff --git a/Makefile b/Makefile index f3458267..22ad0072 100644 --- a/Makefile +++ b/Makefile @@ -109,14 +109,22 @@ ifeq ($(MCU_SUB_VARIANT),nrf52) CFLAGS += -DNRF52 -DNRF52832_XXAA -DS132 DFU_APP_DATA_RESERVED=7*4096 else ifeq ($(MCU_SUB_VARIANT),nrf52833) - SD_NAME = s140 + ifeq ($(SD_NAME),s340) + CFLAGS += -DNRF52833_XXAA -DS340 + else + SD_NAME = s140 + CFLAGS += -DNRF52833_XXAA -DS140 + endif DFU_DEV_REV = 52833 - CFLAGS += -DNRF52833_XXAA -DS140 DFU_APP_DATA_RESERVED=7*4096 else ifeq ($(MCU_SUB_VARIANT),nrf52840) - SD_NAME = s140 + ifeq ($(SD_NAME),s340) + CFLAGS += -DNRF52840_XXAA -DS340 + else + SD_NAME = s140 + CFLAGS += -DNRF52840_XXAA -DS140 + endif DFU_DEV_REV = 52840 - CFLAGS += -DNRF52840_XXAA -DS140 # App reserved 40KB (8+32) to match circuitpython for 840 DFU_APP_DATA_RESERVED=10*4096 else diff --git a/README.md b/README.md index b4e3cdb7..ce2ab06d 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,25 @@ Supported boards are: feather_nrf52840_express feather_nrf52840_express pca10056 Makefile:90: *** BOARD not defined. Stop ``` +#### Working with SoftDevice S340: +The SoftDevice S340 is closed-source, not publicly available and is only distributed by Garmin Canada Inc. + +In order to be able to download the required ANT+ capable SoftDevice, you need to register an 'ANT+ Adopter' account at [thisisant.com](https://www.thisisant.com/register/). After around one business day you will receive access to the resources there. Then do the following steps: +- Download the SoftDevice S340 v7.0.1 [(here)](https://www.thisisant.com/developer/components/nrf52832#tab_protocol_stacks_tab) and extract its contents +- Under `lib/softdevice` in this repository there is a folder called `s340_nrf52_7.0.1` + - Copy the API folder `ANT_s340_nrf52_7.0.1.API`, the license agreement `License_Agreement_ANT_Softdevice_rev3_3.pdf` and the hex file `ANT_s340_nrf52_7.0.1.hex` from the extracted contents to it. + - Rename the API folder to `s340_nrf52_7.0.1_API` + - Rename the hex file to `s340_nrf52_7.0.1_softdevice.hex` + - Modify `lib/softdevice/s340_nrf52_7.0.1_API/include/nrf_sdm.h` on line 191 and remove the two slashes at the beginning of `//#define...` to use the *evaluation key* for the ANT SoftDevice. + - **VERY IMPORTANT:** You MUST obtain a valid commercial license key BEFORE releasing a product to market that uses the ANT SoftDevice! + +To add or modify a board with an ANT+ capable SoftDevice S340 the `SD_VERSION` and `SD_NAME` parameters in the corresponding `board.mk` file have to be set: +``` +SD_VERSION = 7.0.1 +SD_NAME = s340 +``` +**Important:** When adding a new board you must add the suffix `_s340` to the folder name to exclude it from automatic builds. + ### Flash To flash the bootloader (without softdevice/mbr) using JLink: diff --git a/lib/softdevice/s340_nrf52_7.0.1/readme.txt b/lib/softdevice/s340_nrf52_7.0.1/readme.txt new file mode 100644 index 00000000..3cde0e65 --- /dev/null +++ b/lib/softdevice/s340_nrf52_7.0.1/readme.txt @@ -0,0 +1 @@ +Place SoftDevice S340 v7.0.1 files here \ No newline at end of file diff --git a/src/main.c b/src/main.c index 92ad0208..b48e1cbd 100644 --- a/src/main.c +++ b/src/main.c @@ -157,6 +157,14 @@ static void mbr_init_sd(void) { sd_mbr_command(&com); } +// Helper function to check if SoftDevice is already enabled. +// Especially SoftDevice S340 locks up when calling sd_softdevice_disable() and it's not enabled. +static bool is_sd_enabled(void) { + uint8_t sd_enabled = 0; + sd_softdevice_is_enabled(&sd_enabled); + return (sd_enabled == 1); +} + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ @@ -206,13 +214,16 @@ int main(void) { if (!_sd_inited) mbr_init_sd(); // Make sure SD is disabled - sd_softdevice_disable(); + if (is_sd_enabled()) { + sd_softdevice_disable(); + } } // clear in case we kept DFU_DBL_RESET_APP there (*dbl_reset_mem) = 0; // start application + PRINTF("Starting app...\r\n"); bootloader_app_start(); } @@ -306,7 +317,9 @@ static void check_dfu_mode(void) { } if (_ota_dfu) { - sd_softdevice_disable(); + if (is_sd_enabled()) { + sd_softdevice_disable(); + } } else { usb_teardown(); } @@ -326,7 +339,11 @@ static uint32_t ble_stack_init(void) { .rc_temp_ctiv = 2, .accuracy = NRF_CLOCK_LF_ACCURACY_250_PPM }; - sd_softdevice_enable(&clock_cfg, app_error_fault_handler); + #ifndef ANT_LICENSE_KEY + sd_softdevice_enable(&clock_cfg, app_error_fault_handler); + #else + sd_softdevice_enable(&clock_cfg, app_error_fault_handler, ANT_LICENSE_KEY); + #endif sd_nvic_EnableIRQ(SD_EVT_IRQn); /*------------- Configure BLE params -------------*/ From de9a9b79f4e60b68bda892e359276ec9be841273 Mon Sep 17 00:00:00 2001 From: JuergenLeber Date: Tue, 10 Jun 2025 18:44:51 +0200 Subject: [PATCH 09/84] refactor: deduplicate code for disabling SD --- src/main.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main.c b/src/main.c index b48e1cbd..2884993b 100644 --- a/src/main.c +++ b/src/main.c @@ -157,12 +157,14 @@ static void mbr_init_sd(void) { sd_mbr_command(&com); } -// Helper function to check if SoftDevice is already enabled. +// Helper function to disable the SoftDevice with upfront check if it is enabled. // Especially SoftDevice S340 locks up when calling sd_softdevice_disable() and it's not enabled. -static bool is_sd_enabled(void) { +static void sd_disable(void) { uint8_t sd_enabled = 0; sd_softdevice_is_enabled(&sd_enabled); - return (sd_enabled == 1); + if (sd_enabled == 1) { + sd_softdevice_disable(); + } } //--------------------------------------------------------------------+ @@ -214,9 +216,7 @@ int main(void) { if (!_sd_inited) mbr_init_sd(); // Make sure SD is disabled - if (is_sd_enabled()) { - sd_softdevice_disable(); - } + sd_disable(); } // clear in case we kept DFU_DBL_RESET_APP there @@ -317,9 +317,7 @@ static void check_dfu_mode(void) { } if (_ota_dfu) { - if (is_sd_enabled()) { - sd_softdevice_disable(); - } + sd_disable(); } else { usb_teardown(); } From 3182f6036d6ecd690dafb655226169031e991251 Mon Sep 17 00:00:00 2001 From: JuergenLeber Date: Sun, 8 Jun 2025 19:04:20 +0200 Subject: [PATCH 10/84] fix: remove -lnosys option for debug linker scripts as it conflicts with RTT printf retarget --- linker/nrf52833_debug.ld | 2 +- linker/nrf52840_debug.ld | 2 +- linker/nrf52_debug.ld | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/linker/nrf52833_debug.ld b/linker/nrf52833_debug.ld index ed8f3586..8898aa2b 100644 --- a/linker/nrf52833_debug.ld +++ b/linker/nrf52833_debug.ld @@ -1,7 +1,7 @@ /* Linker script to configure memory regions. */ SEARCH_DIR(.) -GROUP(-lgcc -lc -lnosys) +GROUP(-lgcc -lc) MEMORY { diff --git a/linker/nrf52840_debug.ld b/linker/nrf52840_debug.ld index 7c68a830..2ca9fd6d 100644 --- a/linker/nrf52840_debug.ld +++ b/linker/nrf52840_debug.ld @@ -1,7 +1,7 @@ /* Linker script to configure memory regions. */ SEARCH_DIR(.) -GROUP(-lgcc -lc -lnosys) +GROUP(-lgcc -lc) MEMORY { diff --git a/linker/nrf52_debug.ld b/linker/nrf52_debug.ld index b04b36a7..2193ace4 100644 --- a/linker/nrf52_debug.ld +++ b/linker/nrf52_debug.ld @@ -1,7 +1,7 @@ /* Linker script to configure memory regions. */ SEARCH_DIR(.) -GROUP(-lgcc -lc -lnosys) +GROUP(-lgcc -lc) MEMORY { From 70da7d88f4cdfd3eab65c4e8b3a6681b41393418 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 30 Jun 2025 10:45:38 +0700 Subject: [PATCH 11/84] set CMAKE_BUILD_TYPE to MinSizeRel if not defined instead of forcing -Os add CONFIG_GPIO_AS_PINRESET --- CMakeLists.txt | 14 ++++++++++++-- README.md | 30 +++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d6216474..36d85f73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,10 @@ set(TINYUSB_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/tinyusb/src) set(UF2CONV_PY ${CMAKE_CURRENT_LIST_DIR}/lib/uf2/utils/uf2conv.py) set(UF2_FAMILY_ID_BOOTLOADER 0xd663823c) +if (NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "") + set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "Build type" FORCE) +endif () + #------------------- # Bootloader #------------------- @@ -131,7 +135,6 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug") target_include_directories(bootloader PUBLIC lib/SEGGER_RTT/RTT ) - target_compile_definitions(bootloader PUBLIC CFG_DEBUG SEGGER_RTT_MODE_DEFAULT=SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL @@ -170,12 +173,12 @@ target_compile_options(bootloader PUBLIC -Wmissing-format-attribute -Wno-endif-labels -Wunreachable-code - -Os # Suppress warning caused by SDK -Wno-unused-parameter -Wno-expansion-to-defined -Wno-array-bounds ) target_compile_definitions(bootloader PUBLIC SOFTDEVICE_PRESENT + CONFIG_GPIO_AS_PINRESET ) if (TRACE_ETM STREQUAL "1") @@ -335,3 +338,10 @@ add_custom_target(flash-mbr add_custom_target(flash-erase COMMAND ${NRFJPROG} -f nrf52 --eraseall ) + +# flash skip crc magic ( app valid = 0x0001, crc = 0x0000 ) +#add_custom_target(flash-skip-crc +# #COMMAND ${NRFJPROG} --memwr $(BOOT_SETTING_ADDR) --val 0x00000001 -f nrf52 +# COMMAND ${NRFJPROG} --memwr 0xFF000 --val 0x00000001 -f nrf52 +# #COMMAND ${NRFJPROG} --memwr 0x7F000 --val 0x00000001 -f nrf52 +# ) diff --git a/README.md b/README.md index 2929ae21..4c7066f3 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ You must have have a J-Link available to "unbrick" your device. Firstly clone this repo including its submodules with following command: ``` -git clone --recurse-submodules -j8 https://github.com/adafruit/Adafruit_nRF52_Bootloader +git clone --recurse-submodules https://github.com/adafruit/Adafruit_nRF52_Bootloader ``` For git versions before `2.13.0` you have to do that manually: @@ -146,17 +146,33 @@ Makefile:90: *** BOARD not defined. Stop #### Build using `cmake` Firstly initialize your build environment by passing your board to `cmake` via `-DBOARD={board}`: -``` -cmake -DBOARD=feather_nrf52840_express -S . -B _build -GNinja + +```bash +mkdir build +cd build +cmake .. -DBOARD=feather_nrf52840_express ``` And then build it with: + +```bash +make ``` -cd _build -ninja -``` -You can also use the generator of your choice. For example omit the `-GNinja` and then you can invoke `make` from inside `_build` instead of `ninja` +You can also use the generator of your choice. For example adding the `-GNinja` and then you can invoke `ninja build` instead of `make`. + +To list all supported targets, run: + +```bash +cmake --build . --target help +``` + +To build individual targets, you can specify it directly with `make` or using `cmake --build`: + +```bash +make bootloader +cmake --build . --target bootloader +``` ### Flash From 31c3081668c0ffe0215c125b88d3cb5c2447050e Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 30 Jun 2025 17:32:49 +0700 Subject: [PATCH 12/84] update makefile/cmake for softdevice macro. Remove the _s340 filter in ci --- .github/workflows/githubci.yml | 2 +- CMakeLists.txt | 34 ++++++----------------- Makefile | 25 ++++++++--------- lib/softdevice/s340_nrf52_7.0.1/readme.md | 3 +- src/main.c | 15 +++++----- 5 files changed, 29 insertions(+), 50 deletions(-) diff --git a/.github/workflows/githubci.yml b/.github/workflows/githubci.yml index 55086fc1..68b4f7f2 100644 --- a/.github/workflows/githubci.yml +++ b/.github/workflows/githubci.yml @@ -21,7 +21,7 @@ jobs: id: set-matrix working-directory: src/boards run: | - MATRIX_JSON=$(ls -d */ | sed 's/\/$//' | grep -vE '_s340$' | jq -R -s -c 'split("\n")[:-1]') + MATRIX_JSON=$(ls -d */ | sed 's/\/$//' | jq -R -s -c 'split("\n")[:-1]') echo "matrix=$MATRIX_JSON" echo "matrix=$MATRIX_JSON" >> $GITHUB_OUTPUT diff --git a/CMakeLists.txt b/CMakeLists.txt index e66a274a..71331356 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -225,47 +225,29 @@ if (MCU_VARIANT STREQUAL "nrf52") set(SD_NAME s132) set(DFU_DEV_REV 0xADAF) set(DFU_APP_DATA_RESERVED 7*4096) - target_compile_definitions(bootloader PUBLIC - NRF52 - NRF52832_XXAA - S132 - ) + target_compile_definitions(bootloader PUBLIC NRF52 NRF52832_XXAA) elseif (MCU_VARIANT STREQUAL "nrf52833") set(DFU_DEV_REV 52833) set(DFU_APP_DATA_RESERVED 7*4096) - if (SD_NAME STREQUAL "s340") - target_compile_definitions(bootloader PUBLIC - NRF52833_XXAA - S340 - ) - else () + target_compile_definitions(bootloader PUBLIC NRF52833_XXAA) + if (NOT DEFINED SD_NAME) set(SD_NAME s140) - target_compile_definitions(bootloader PUBLIC - NRF52833_XXAA - S140 - ) endif () elseif (MCU_VARIANT STREQUAL "nrf52840") set(DFU_DEV_REV 52840) # App reserved 40KB (8+32) to match circuitpython for 840 set(DFU_APP_DATA_RESERVED 10*4096) - if (SD_NAME STREQUAL "s340") - target_compile_definitions(bootloader PUBLIC - NRF52840_XXAA - S340 - ) - else () + target_compile_definitions(bootloader PUBLIC NRF52840_XXAA) + if (NOT DEFINED SD_NAME) set(SD_NAME s140) - target_compile_definitions(bootloader PUBLIC - NRF52840_XXAA - S140 - ) endif () - else () message(FATAL_ERROR "MCU_VARIANT ${MCU_VARIANT} is unknown") endif () +string(TOUPPER ${SD_NAME} SD_NAME_UPPER) +target_compile_definitions(bootloader PUBLIC ${SD_NAME_UPPER}) + set(SD_FILENAME ${SD_NAME}_nrf52_${SD_VERSION}) set(SD_HEX ${SOFTDEVICE_DIR}/${SD_FILENAME}/${SD_FILENAME}_softdevice.hex) diff --git a/Makefile b/Makefile index 22ad0072..bea75cf9 100644 --- a/Makefile +++ b/Makefile @@ -104,33 +104,32 @@ BIN = _bin/$(BOARD) # MCU_SUB_VARIANT can be nrf52 (nrf52832), nrf52833, nrf52840 ifeq ($(MCU_SUB_VARIANT),nrf52) + CFLAGS += -DNRF52 -DNRF52832_XXAA SD_NAME = s132 DFU_DEV_REV = 0xADAF - CFLAGS += -DNRF52 -DNRF52832_XXAA -DS132 DFU_APP_DATA_RESERVED=7*4096 else ifeq ($(MCU_SUB_VARIANT),nrf52833) - ifeq ($(SD_NAME),s340) - CFLAGS += -DNRF52833_XXAA -DS340 - else - SD_NAME = s140 - CFLAGS += -DNRF52833_XXAA -DS140 - endif + CFLAGS += -DNRF52833_XXAA DFU_DEV_REV = 52833 DFU_APP_DATA_RESERVED=7*4096 + ifndef SD_NAME + SD_NAME = s140 + endif else ifeq ($(MCU_SUB_VARIANT),nrf52840) - ifeq ($(SD_NAME),s340) - CFLAGS += -DNRF52840_XXAA -DS340 - else - SD_NAME = s140 - CFLAGS += -DNRF52840_XXAA -DS140 - endif + CFLAGS += -DNRF52840_XXAA DFU_DEV_REV = 52840 # App reserved 40KB (8+32) to match circuitpython for 840 DFU_APP_DATA_RESERVED=10*4096 + ifndef SD_NAME + SD_NAME = s140 + endif else $(error Sub Variant $(MCU_SUB_VARIANT) is unknown) endif +SD_NAME_UPPER = $(subst s,S,${SD_NAME}) +CFLAGS += -D$(SD_NAME_UPPER) + #------------------------------------------------------------------------------ # SOURCE FILES #------------------------------------------------------------------------------ diff --git a/lib/softdevice/s340_nrf52_7.0.1/readme.md b/lib/softdevice/s340_nrf52_7.0.1/readme.md index 55b790be..c408bbda 100644 --- a/lib/softdevice/s340_nrf52_7.0.1/readme.md +++ b/lib/softdevice/s340_nrf52_7.0.1/readme.md @@ -15,5 +15,4 @@ To add or modify a board with an ANT+ capable SoftDevice S340 the `SD_VERSION` a ``` SD_VERSION = 7.0.1 SD_NAME = s340 -``` -**Important:** When adding a new board you must add the suffix `_s340` to the folder name to exclude it from automatic builds. \ No newline at end of file +``` \ No newline at end of file diff --git a/src/main.c b/src/main.c index cd9de877..a4c0a705 100644 --- a/src/main.c +++ b/src/main.c @@ -157,9 +157,8 @@ static void mbr_init_sd(void) { sd_mbr_command(&com); } -// Helper function to disable the SoftDevice with upfront check if it is enabled. -// Especially SoftDevice S340 locks up when calling sd_softdevice_disable() and it's not enabled. -static void sd_disable(void) { +// Disable the SoftDevice if it is enabled. +static void disable_softdevice(void) { uint8_t sd_enabled = 0; sd_softdevice_is_enabled(&sd_enabled); if (sd_enabled == 1) { @@ -216,7 +215,7 @@ int main(void) { if (!_sd_inited) mbr_init_sd(); // Make sure SD is disabled - sd_disable(); + disable_softdevice(); } // clear in case we kept DFU_DBL_RESET_APP there @@ -317,7 +316,7 @@ static void check_dfu_mode(void) { } if (_ota_dfu) { - sd_disable(); + disable_softdevice(); } else { usb_teardown(); } @@ -337,10 +336,10 @@ static uint32_t ble_stack_init(void) { .rc_temp_ctiv = 2, .accuracy = NRF_CLOCK_LF_ACCURACY_250_PPM }; - #ifndef ANT_LICENSE_KEY - sd_softdevice_enable(&clock_cfg, app_error_fault_handler); - #else + #ifdef ANT_LICENSE_KEY sd_softdevice_enable(&clock_cfg, app_error_fault_handler, ANT_LICENSE_KEY); + #else + sd_softdevice_enable(&clock_cfg, app_error_fault_handler); #endif sd_nvic_EnableIRQ(SD_EVT_IRQn); From fa1caccd67536f9c90a1d76683163ebdfa712dd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Leber?= Date: Mon, 27 Oct 2025 20:57:29 +0100 Subject: [PATCH 13/84] feat: add ANT_LICENSE_KEY handling for SoftDevice S340 --- CMakeLists.txt | 7 +++++++ Makefile | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 71331356..a78a2503 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -259,6 +259,13 @@ if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_definitions(bootloader PUBLIC DFU_APP_DATA_RESERVED=${DFU_APP_DATA_RESERVED}) endif () +#---------------------------------- +# ANT_LICENSE_KEY handling +#---------------------------------- +if (DEFINED ANT_LICENSE_KEY) + target_compile_definitions(bootloader PUBLIC ANT_LICENSE_KEY=${ANT_LICENSE_KEY}) +endif + #---------------------------------- # Get UF2 version from git #---------------------------------- diff --git a/Makefile b/Makefile index bea75cf9..326a709e 100644 --- a/Makefile +++ b/Makefile @@ -130,6 +130,13 @@ endif SD_NAME_UPPER = $(subst s,S,${SD_NAME}) CFLAGS += -D$(SD_NAME_UPPER) +#---------------------------------- +# ANT_LICENSE_KEY handling +#---------------------------------- +ifdef ANT_LICENSE_KEY + CFLAGS += -DANT_LICENSE_KEY=\"$(ANT_LICENSE_KEY)\" +endif + #------------------------------------------------------------------------------ # SOURCE FILES #------------------------------------------------------------------------------ From 4ca93c51394e2907333f4a7d475ec6a566ff0af0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Leber?= Date: Mon, 27 Oct 2025 21:05:08 +0100 Subject: [PATCH 14/84] feat: adjust documentation for builds with SoftDevice S340 --- lib/softdevice/s340_nrf52_7.0.1/readme.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/softdevice/s340_nrf52_7.0.1/readme.md b/lib/softdevice/s340_nrf52_7.0.1/readme.md index c408bbda..95a0fee5 100644 --- a/lib/softdevice/s340_nrf52_7.0.1/readme.md +++ b/lib/softdevice/s340_nrf52_7.0.1/readme.md @@ -5,12 +5,17 @@ The SoftDevice S340 is closed-source, not publicly available and is only distrib In order to be able to download the required ANT+ capable SoftDevice and place SoftDevice S340 v7.0.1 files here. You need to register an 'ANT+ Adopter' account at [thisisant.com](https://www.thisisant.com/register/). After around one business day you will receive access to the resources there. Then do the following steps: - Download the SoftDevice S340 v7.0.1 [(here)](https://www.thisisant.com/developer/components/nrf52832#tab_protocol_stacks_tab) and extract its contents - Under `lib/softdevice` in this repository there is a folder called `s340_nrf52_7.0.1` - - Copy the API folder `ANT_s340_nrf52_7.0.1.API`, the license agreement `License_Agreement_ANT_Softdevice_rev3_3.pdf` and the hex file `ANT_s340_nrf52_7.0.1.hex` from the extracted contents to it. + - Copy the API folder `ANT_s340_nrf52_7.0.1.API`, the license agreement `License_Agreement_ANT_Softdevice_rev3_3.pdf` and the hex file `ANT_s340_nrf52_7.0.1.hex` from the extracted contents to it - Rename the API folder to `s340_nrf52_7.0.1_API` - Rename the hex file to `s340_nrf52_7.0.1_softdevice.hex` - - Modify `lib/softdevice/s340_nrf52_7.0.1_API/include/nrf_sdm.h` on line 191 and remove the two slashes at the beginning of `//#define...` to use the *evaluation key* for the ANT SoftDevice. + - Check `lib/softdevice/s340_nrf52_7.0.1_API/include/nrf_sdm.h` on line 191 to get an *evaluation key* for the ANT SoftDevice which can later be used for the `ANT_LICENSE_KEY` parameter - **VERY IMPORTANT:** You MUST obtain a valid commercial license key BEFORE releasing a product to market that uses the ANT SoftDevice! +To use the SoftDevice S340 with an existing board the `SD_NAME`, `SD_VERSION` and `ANT_LICENSE_KEY` parameters can just be passed to the command line of the `make` command, e.g.: +``` +make BOARD=xiao_nrf52840_ble_sense SD_NAME=s340 SD_VERSION=7.0.1 ANT_LICENSE_KEY= all +``` + To add or modify a board with an ANT+ capable SoftDevice S340 the `SD_VERSION` and `SD_NAME` parameters in the corresponding `board.mk` file have to be set: ``` SD_VERSION = 7.0.1 From 6250be4bf1c4efb8e402f157af4663f1cd7c6419 Mon Sep 17 00:00:00 2001 From: Tomasz Duda Date: Tue, 11 Nov 2025 21:50:29 +0100 Subject: [PATCH 15/84] fix boot loop due to wrong value read from REGOUT0 --- src/boards/boards.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/boards/boards.c b/src/boards/boards.c index 9eaf7b07..87842a36 100644 --- a/src/boards/boards.c +++ b/src/boards/boards.c @@ -111,6 +111,12 @@ void board_init(void) { // #define UICR_REGOUT0_VALUE UICR_REGOUT0_VOUT_3V3 // in board.h when using that power configuration. #ifdef UICR_REGOUT0_VALUE + // for some reason bellow condition is true even though debugger show 3.0V + // pyocd> rw 0x10001304 + // 10001304: fffffffc + // it leads to infinite boot loop. Let wait for NVM to be ready first. + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy){} if ((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) == (UICR_REGOUT0_VOUT_DEFAULT << UICR_REGOUT0_VOUT_Pos)){ NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos; while (NRF_NVMC->READY == NVMC_READY_READY_Busy){} @@ -119,8 +125,10 @@ void board_init(void) { NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos; while (NRF_NVMC->READY == NVMC_READY_READY_Busy){} - - NVIC_SystemReset(); + // to avoid infinity boot loop reset only if REGOUT0 was set correctly + if((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) == (UICR_REGOUT0_VALUE << UICR_REGOUT0_VOUT_Pos)){ + NVIC_SystemReset(); + } } #endif From 4493a352cbf377bff913b58993594d6dd45148df Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 19 Dec 2025 16:42:31 +0700 Subject: [PATCH 16/84] add board.cmake --- src/boards/adm_b_nrf52840_1/board.cmake | 1 + src/boards/ae_bl652_bo/board.cmake | 1 + src/boards/aramcon2_badge/board.cmake | 1 + src/boards/aramcon_badge_2019/board.cmake | 1 + src/boards/arcade_feather_nrf52840_express/board.cmake | 1 + src/boards/arduino_nano_33_ble/board.cmake | 1 + src/boards/bast_ble/board.cmake | 1 + src/boards/bluemicro_nrf52833/board.cmake | 1 + src/boards/bluemicro_nrf52840/board.cmake | 1 + src/boards/challenger_840_ble/board.cmake | 1 + src/boards/ebyte_e104_bt5032a/board.cmake | 1 + src/boards/ebyte_e73_tbb/board.cmake | 1 + src/boards/electronut_labs_papyr/board.cmake | 1 + src/boards/feather_nrf52832/board.cmake | 1 + src/boards/feather_nrf52833_express/board.cmake | 1 + src/boards/feather_nrf52840_sense/board.cmake | 1 + src/boards/ikigaisense_vita/board.cmake | 1 + src/boards/ledglasses_nrf52840/board.cmake | 1 + src/boards/m60_keyboard/board.cmake | 1 + src/boards/mbn52832dk/board.cmake | 1 + src/boards/mdk_nrf52840_dongle/board.cmake | 1 + src/boards/metro_nrf52840_express/board.cmake | 1 + src/boards/mikoto/board.cmake | 1 + src/boards/nRF52840_connect_kit/board.cmake | 1 + src/boards/nice_nano/board.cmake | 1 + src/boards/nrf52840_bboard/board.cmake | 1 + src/boards/nrf52840_m2/board.cmake | 1 + src/boards/nrfmicro/board.cmake | 1 + src/boards/ohs2020_badge/board.cmake | 1 + src/boards/particle_argon/board.cmake | 1 + src/boards/particle_boron/board.cmake | 1 + src/boards/particle_xenon/board.cmake | 1 + src/boards/pca10059/board.cmake | 1 + src/boards/pillbug/board.cmake | 1 + src/boards/pitaya_go/board.cmake | 1 + src/boards/raytac_mdbt50q_db_40/board.cmake | 1 + src/boards/raytac_mdbt50q_rx/board.cmake | 1 + src/boards/sparkfun_nrf52840_micromod/board.cmake | 1 + src/boards/t1000_e/board.cmake | 1 + src/boards/waveshare_nrf52840_eval/board.cmake | 1 + src/boards/xiao_nrf52840_ble/board.cmake | 1 + src/boards/xiao_nrf52840_ble_sense/board.cmake | 1 + 42 files changed, 42 insertions(+) create mode 100644 src/boards/adm_b_nrf52840_1/board.cmake create mode 100644 src/boards/ae_bl652_bo/board.cmake create mode 100644 src/boards/aramcon2_badge/board.cmake create mode 100644 src/boards/aramcon_badge_2019/board.cmake create mode 100644 src/boards/arcade_feather_nrf52840_express/board.cmake create mode 100644 src/boards/arduino_nano_33_ble/board.cmake create mode 100644 src/boards/bast_ble/board.cmake create mode 100644 src/boards/bluemicro_nrf52833/board.cmake create mode 100644 src/boards/bluemicro_nrf52840/board.cmake create mode 100644 src/boards/challenger_840_ble/board.cmake create mode 100644 src/boards/ebyte_e104_bt5032a/board.cmake create mode 100644 src/boards/ebyte_e73_tbb/board.cmake create mode 100644 src/boards/electronut_labs_papyr/board.cmake create mode 100644 src/boards/feather_nrf52832/board.cmake create mode 100644 src/boards/feather_nrf52833_express/board.cmake create mode 100644 src/boards/feather_nrf52840_sense/board.cmake create mode 100644 src/boards/ikigaisense_vita/board.cmake create mode 100644 src/boards/ledglasses_nrf52840/board.cmake create mode 100644 src/boards/m60_keyboard/board.cmake create mode 100644 src/boards/mbn52832dk/board.cmake create mode 100644 src/boards/mdk_nrf52840_dongle/board.cmake create mode 100644 src/boards/metro_nrf52840_express/board.cmake create mode 100644 src/boards/mikoto/board.cmake create mode 100644 src/boards/nRF52840_connect_kit/board.cmake create mode 100644 src/boards/nice_nano/board.cmake create mode 100644 src/boards/nrf52840_bboard/board.cmake create mode 100644 src/boards/nrf52840_m2/board.cmake create mode 100644 src/boards/nrfmicro/board.cmake create mode 100644 src/boards/ohs2020_badge/board.cmake create mode 100644 src/boards/particle_argon/board.cmake create mode 100644 src/boards/particle_boron/board.cmake create mode 100644 src/boards/particle_xenon/board.cmake create mode 100644 src/boards/pca10059/board.cmake create mode 100644 src/boards/pillbug/board.cmake create mode 100644 src/boards/pitaya_go/board.cmake create mode 100644 src/boards/raytac_mdbt50q_db_40/board.cmake create mode 100644 src/boards/raytac_mdbt50q_rx/board.cmake create mode 100644 src/boards/sparkfun_nrf52840_micromod/board.cmake create mode 100644 src/boards/t1000_e/board.cmake create mode 100644 src/boards/waveshare_nrf52840_eval/board.cmake create mode 100644 src/boards/xiao_nrf52840_ble/board.cmake create mode 100644 src/boards/xiao_nrf52840_ble_sense/board.cmake diff --git a/src/boards/adm_b_nrf52840_1/board.cmake b/src/boards/adm_b_nrf52840_1/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/adm_b_nrf52840_1/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/ae_bl652_bo/board.cmake b/src/boards/ae_bl652_bo/board.cmake new file mode 100644 index 00000000..e1b9ded7 --- /dev/null +++ b/src/boards/ae_bl652_bo/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52) diff --git a/src/boards/aramcon2_badge/board.cmake b/src/boards/aramcon2_badge/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/aramcon2_badge/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/aramcon_badge_2019/board.cmake b/src/boards/aramcon_badge_2019/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/aramcon_badge_2019/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/arcade_feather_nrf52840_express/board.cmake b/src/boards/arcade_feather_nrf52840_express/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/arcade_feather_nrf52840_express/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/arduino_nano_33_ble/board.cmake b/src/boards/arduino_nano_33_ble/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/arduino_nano_33_ble/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/bast_ble/board.cmake b/src/boards/bast_ble/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/bast_ble/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/bluemicro_nrf52833/board.cmake b/src/boards/bluemicro_nrf52833/board.cmake new file mode 100644 index 00000000..cb4f2f9e --- /dev/null +++ b/src/boards/bluemicro_nrf52833/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52833) diff --git a/src/boards/bluemicro_nrf52840/board.cmake b/src/boards/bluemicro_nrf52840/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/bluemicro_nrf52840/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/challenger_840_ble/board.cmake b/src/boards/challenger_840_ble/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/challenger_840_ble/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/ebyte_e104_bt5032a/board.cmake b/src/boards/ebyte_e104_bt5032a/board.cmake new file mode 100644 index 00000000..e1b9ded7 --- /dev/null +++ b/src/boards/ebyte_e104_bt5032a/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52) diff --git a/src/boards/ebyte_e73_tbb/board.cmake b/src/boards/ebyte_e73_tbb/board.cmake new file mode 100644 index 00000000..e1b9ded7 --- /dev/null +++ b/src/boards/ebyte_e73_tbb/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52) diff --git a/src/boards/electronut_labs_papyr/board.cmake b/src/boards/electronut_labs_papyr/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/electronut_labs_papyr/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/feather_nrf52832/board.cmake b/src/boards/feather_nrf52832/board.cmake new file mode 100644 index 00000000..e1b9ded7 --- /dev/null +++ b/src/boards/feather_nrf52832/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52) diff --git a/src/boards/feather_nrf52833_express/board.cmake b/src/boards/feather_nrf52833_express/board.cmake new file mode 100644 index 00000000..cb4f2f9e --- /dev/null +++ b/src/boards/feather_nrf52833_express/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52833) diff --git a/src/boards/feather_nrf52840_sense/board.cmake b/src/boards/feather_nrf52840_sense/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/feather_nrf52840_sense/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/ikigaisense_vita/board.cmake b/src/boards/ikigaisense_vita/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/ikigaisense_vita/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/ledglasses_nrf52840/board.cmake b/src/boards/ledglasses_nrf52840/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/ledglasses_nrf52840/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/m60_keyboard/board.cmake b/src/boards/m60_keyboard/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/m60_keyboard/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/mbn52832dk/board.cmake b/src/boards/mbn52832dk/board.cmake new file mode 100644 index 00000000..e1b9ded7 --- /dev/null +++ b/src/boards/mbn52832dk/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52) diff --git a/src/boards/mdk_nrf52840_dongle/board.cmake b/src/boards/mdk_nrf52840_dongle/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/mdk_nrf52840_dongle/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/metro_nrf52840_express/board.cmake b/src/boards/metro_nrf52840_express/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/metro_nrf52840_express/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/mikoto/board.cmake b/src/boards/mikoto/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/mikoto/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/nRF52840_connect_kit/board.cmake b/src/boards/nRF52840_connect_kit/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/nRF52840_connect_kit/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/nice_nano/board.cmake b/src/boards/nice_nano/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/nice_nano/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/nrf52840_bboard/board.cmake b/src/boards/nrf52840_bboard/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/nrf52840_bboard/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/nrf52840_m2/board.cmake b/src/boards/nrf52840_m2/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/nrf52840_m2/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/nrfmicro/board.cmake b/src/boards/nrfmicro/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/nrfmicro/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/ohs2020_badge/board.cmake b/src/boards/ohs2020_badge/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/ohs2020_badge/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/particle_argon/board.cmake b/src/boards/particle_argon/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/particle_argon/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/particle_boron/board.cmake b/src/boards/particle_boron/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/particle_boron/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/particle_xenon/board.cmake b/src/boards/particle_xenon/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/particle_xenon/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/pca10059/board.cmake b/src/boards/pca10059/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/pca10059/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/pillbug/board.cmake b/src/boards/pillbug/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/pillbug/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/pitaya_go/board.cmake b/src/boards/pitaya_go/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/pitaya_go/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/raytac_mdbt50q_db_40/board.cmake b/src/boards/raytac_mdbt50q_db_40/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/raytac_mdbt50q_db_40/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/raytac_mdbt50q_rx/board.cmake b/src/boards/raytac_mdbt50q_rx/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/raytac_mdbt50q_rx/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/sparkfun_nrf52840_micromod/board.cmake b/src/boards/sparkfun_nrf52840_micromod/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/sparkfun_nrf52840_micromod/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/t1000_e/board.cmake b/src/boards/t1000_e/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/t1000_e/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/waveshare_nrf52840_eval/board.cmake b/src/boards/waveshare_nrf52840_eval/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/waveshare_nrf52840_eval/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/xiao_nrf52840_ble/board.cmake b/src/boards/xiao_nrf52840_ble/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/xiao_nrf52840_ble/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/xiao_nrf52840_ble_sense/board.cmake b/src/boards/xiao_nrf52840_ble_sense/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/xiao_nrf52840_ble_sense/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) From d7e4a2572a5d07263b44bd4c9bc849220a47028b Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 19 Dec 2025 17:25:24 +0700 Subject: [PATCH 17/84] update cmake build --- CMakeLists.txt | 30 +- cmake/cpu/cortex-m4.cmake | 29 +- cmake/toolchain/arm_gcc.cmake | 22 +- cmake/toolchain/set_flags.cmake | 17 - linker/nrf52.ld | 6 +- linker/nrf52833.ld | 2 +- linker/nrf52833_debug.ld | 2 +- linker/nrf52840.ld | 9 +- linker/nrf52840_debug.ld | 2 +- linker/nrf52_debug.ld | 2 +- segger/Adafruit_nRF52_Bootloader.emProject | 363 - segger/SEGGER_RTT.c | 1329 -- segger/SEGGER_RTT.h | 234 - segger/SEGGER_RTT_Conf.h | 242 - segger/SEGGER_RTT_SES.c | 76 - segger/flash_placement.xml | 43 - segger/nRF52832_xxAA_MemoryMap.xml | 10 - segger/nRF52840_xxAA_MemoryMap.xml | 10 - segger/nRF_Target.js | 19 - segger/nrf52840_Registers.xml | 22310 ------------------- segger/thumb_crt0.s | 420 - 21 files changed, 33 insertions(+), 25144 deletions(-) delete mode 100644 cmake/toolchain/set_flags.cmake delete mode 100644 segger/Adafruit_nRF52_Bootloader.emProject delete mode 100644 segger/SEGGER_RTT.c delete mode 100644 segger/SEGGER_RTT.h delete mode 100644 segger/SEGGER_RTT_Conf.h delete mode 100644 segger/SEGGER_RTT_SES.c delete mode 100644 segger/flash_placement.xml delete mode 100644 segger/nRF52832_xxAA_MemoryMap.xml delete mode 100644 segger/nRF52840_xxAA_MemoryMap.xml delete mode 100644 segger/nRF_Target.js delete mode 100644 segger/nrf52840_Registers.xml delete mode 100644 segger/thumb_crt0.s diff --git a/CMakeLists.txt b/CMakeLists.txt index a78a2503..ae9b9cfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,11 @@ cmake_minimum_required(VERSION 3.17) +include(CMakePrintHelpers) find_package(Python COMPONENTS Interpreter) if (NOT DEFINED BOARD) message(FATAL_ERROR "BOARD is not defined") endif () -if (NOT DEFINED TOOLCHAIN) - set(TOOLCHAIN gcc) -endif () - # include board specific include(${CMAKE_CURRENT_LIST_DIR}/src/boards/${BOARD}/board.cmake) @@ -21,7 +18,8 @@ else () set(JLINK_DEVICE ${MCU_VARIANT}_xxaa) endif () -set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/cmake/toolchain/arm_${TOOLCHAIN}.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/cmake/toolchain/arm_gcc.cmake) + project(Adafruit_nRF52_Bootloader C ASM) @@ -153,6 +151,7 @@ endif () target_link_options(bootloader PUBLIC "LINKER:--script=${LD_FILE}" -L${NRFX_DIR}/mdk + -nostartfiles --specs=nosys.specs --specs=nano.specs ) target_compile_options(bootloader PUBLIC @@ -179,6 +178,8 @@ target_compile_options(bootloader PUBLIC target_compile_definitions(bootloader PUBLIC SOFTDEVICE_PRESENT CONFIG_GPIO_AS_PINRESET + __START=main + __STARTUP_CLEAR_BSS ) if (TRACE_ETM STREQUAL "1") @@ -264,7 +265,7 @@ endif () #---------------------------------- if (DEFINED ANT_LICENSE_KEY) target_compile_definitions(bootloader PUBLIC ANT_LICENSE_KEY=${ANT_LICENSE_KEY}) -endif +endif() #---------------------------------- # Get UF2 version from git @@ -296,15 +297,6 @@ target_compile_definitions(bootloader PUBLIC MK_BOOTLOADER_VERSION=${MK_BOOTLOADER_VERSION} ) -#---------------------------------- -# Post build -#---------------------------------- - -# run size after build -add_custom_command(TARGET bootloader POST_BUILD - COMMAND ${CMAKE_SIZE} $ - ) - # Add bin/hex output add_custom_command(TARGET bootloader POST_BUILD COMMAND ${CMAKE_OBJCOPY} -Obinary $ $/bootloader.bin @@ -316,12 +308,11 @@ add_custom_command(TARGET bootloader POST_BUILD #---------------------------------- # Flashing target #---------------------------------- - if (NOT DEFINED NRFJPROG) set(NRFJPROG nrfjprog) endif() -add_custom_target(flash-jlink +add_custom_target(flash-bootloader DEPENDS bootloader COMMAND ${NRFJPROG} --program $ --verify --sectoranduicrerase -f nrf52 --reset ) @@ -332,6 +323,7 @@ add_custom_target(flash-uf2 ) add_custom_target(flash-sd + DEPENDS flash-mbr COMMAND ${NRFJPROG} --program ${SD_HEX} --verify --sectorerase -f nrf52 --reset ) @@ -343,6 +335,10 @@ add_custom_target(flash-erase COMMAND ${NRFJPROG} -f nrf52 --eraseall ) +add_custom_target(flash-all + DEPENDS flash-erase flash-sd flash-bootloader + ) + # flash skip crc magic ( app valid = 0x0001, crc = 0x0000 ) #add_custom_target(flash-skip-crc # #COMMAND ${NRFJPROG} --memwr $(BOOT_SETTING_ADDR) --val 0x00000001 -f nrf52 diff --git a/cmake/cpu/cortex-m4.cmake b/cmake/cpu/cortex-m4.cmake index 79f97e24..91a615d4 100644 --- a/cmake/cpu/cortex-m4.cmake +++ b/cmake/cpu/cortex-m4.cmake @@ -1,23 +1,6 @@ -if (TOOLCHAIN STREQUAL "gcc") - set(TOOLCHAIN_COMMON_FLAGS - -mthumb - -mcpu=cortex-m4 - -mfloat-abi=hard - -mfpu=fpv4-sp-d16 - ) - - if (NOT DEFINED FREERTOS_PORT) - set(FREERTOS_PORT GCC_ARM_CM4F CACHE INTERNAL "") - endif () - -elseif (TOOLCHAIN STREQUAL "iar") - set(TOOLCHAIN_COMMON_FLAGS - --cpu cortex-m4 - --fpu VFPv4 - ) - - if (NOT DEFINED FREERTOS_PORT) - set(FREERTOS_PORT IAR_ARM_CM4F CACHE INTERNAL "") - endif () - -endif () +set(TOOLCHAIN_COMMON_FLAGS + -mthumb + -mcpu=cortex-m4 + -mfloat-abi=hard + -mfpu=fpv4-sp-d16 +) \ No newline at end of file diff --git a/cmake/toolchain/arm_gcc.cmake b/cmake/toolchain/arm_gcc.cmake index 5203d1e4..d11826c0 100644 --- a/cmake/toolchain/arm_gcc.cmake +++ b/cmake/toolchain/arm_gcc.cmake @@ -3,10 +3,7 @@ set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_C_COMPILER "arm-none-eabi-gcc") set(CMAKE_CXX_COMPILER "arm-none-eabi-g++") set(CMAKE_ASM_COMPILER "arm-none-eabi-gcc") - -set(CMAKE_SIZE "arm-none-eabi-size" CACHE FILEPATH "") -set(CMAKE_OBJCOPY "arm-none-eabi-objcopy" CACHE FILEPATH "") -set(CMAKE_OBJDUMP "arm-none-eabi-objdump" CACHE FILEPATH "") +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE) @@ -16,9 +13,6 @@ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -# pass TOOLCHAIN_CPU to -set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES CMAKE_SYSTEM_PROCESSOR) - include(${CMAKE_CURRENT_LIST_DIR}/../cpu/${CMAKE_SYSTEM_PROCESSOR}.cmake) # enable all possible warnings for building examples @@ -27,6 +21,7 @@ list(APPEND TOOLCHAIN_COMMON_FLAGS -ffunction-sections -fsingle-precision-constant -fno-strict-aliasing + -g ) set(TOOLCHAIN_EXE_LINKER_FLAGS @@ -35,12 +30,9 @@ set(TOOLCHAIN_EXE_LINKER_FLAGS #-Wl,--cref ) -include(${CMAKE_CURRENT_LIST_DIR}/set_flags.cmake) +# join the toolchain flags into a single string +list(JOIN TOOLCHAIN_COMMON_FLAGS " " TOOLCHAIN_COMMON_FLAGS) +list(JOIN TOOLCHAIN_EXE_LINKER_FLAGS " " CMAKE_EXE_LINKER_FLAGS_INIT) -# try_compile is cmake test compiling its own example, -# pass -nostdlib to skip stdlib linking -get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE) -if (IS_IN_TRY_COMPILE) - set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -nostdlib") - set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -nostdlib") -endif () +set(CMAKE_C_FLAGS_INIT "${TOOLCHAIN_COMMON_FLAGS} ${TOOLCHAIN_C_FLAGS}") +set(CMAKE_ASM_FLAGS_INIT "${TOOLCHAIN_COMMON_FLAGS} ${TOOLCHAIN_ASM_FLAGS}") diff --git a/cmake/toolchain/set_flags.cmake b/cmake/toolchain/set_flags.cmake deleted file mode 100644 index bbbd2112..00000000 --- a/cmake/toolchain/set_flags.cmake +++ /dev/null @@ -1,17 +0,0 @@ -include(CMakePrintHelpers) - -# join the toolchain flags into a single string -list(JOIN TOOLCHAIN_COMMON_FLAGS " " TOOLCHAIN_COMMON_FLAGS) - -foreach (LANG IN ITEMS C CXX ASM) - set(CMAKE_${LANG}_FLAGS_INIT ${TOOLCHAIN_COMMON_FLAGS}) - - #cmake_print_variables(CMAKE_${LANG}_FLAGS_INIT) - - # optimization flags for LOG, LOGGER ? - #set(CMAKE_${LANG}_FLAGS_RELEASE_INIT "-Os") - #set(CMAKE_${LANG}_FLAGS_DEBUG_INIT "-O0") -endforeach () - -# Linker -list(JOIN TOOLCHAIN_EXE_LINKER_FLAGS " " CMAKE_EXE_LINKER_FLAGS_INIT) diff --git a/linker/nrf52.ld b/linker/nrf52.ld index 756e4905..f5bd6ce8 100644 --- a/linker/nrf52.ld +++ b/linker/nrf52.ld @@ -1,7 +1,7 @@ /* Linker script to configure memory regions. */ SEARCH_DIR(.) -GROUP(-lgcc -lc -lnosys) +/*GROUP(-lgcc -lc -lnosys)*/ MEMORY { @@ -19,8 +19,6 @@ MEMORY /** Location of bootloader setting in flash. */ BOOTLOADER_SETTINGS (rw) : ORIGIN = 0x7F000, LENGTH = 0x1000 - - /** RAM Region for bootloader. */ RAM (rwx) : ORIGIN = 0x20008000, LENGTH = 0x20010000-0x20008000 @@ -30,8 +28,6 @@ MEMORY /** Location of non initialized RAM. Non initialized RAM is used for exchanging bond information * from application to bootloader when using buttonless DFU OTA. */ NOINIT (rwx) : ORIGIN = 0x20007F80, LENGTH = 0x80 - - /** Location in UICR where bootloader start address is stored. */ UICR_BOOTLOADER (r) : ORIGIN = 0x10001014, LENGTH = 0x04 diff --git a/linker/nrf52833.ld b/linker/nrf52833.ld index 49d8350f..72af5959 100644 --- a/linker/nrf52833.ld +++ b/linker/nrf52833.ld @@ -1,7 +1,7 @@ /* Linker script to configure memory regions. */ SEARCH_DIR(.) -GROUP(-lgcc -lc -lnosys) +/*GROUP(-lgcc -lc -lnosys)*/ MEMORY { diff --git a/linker/nrf52833_debug.ld b/linker/nrf52833_debug.ld index 8898aa2b..f1107389 100644 --- a/linker/nrf52833_debug.ld +++ b/linker/nrf52833_debug.ld @@ -1,7 +1,7 @@ /* Linker script to configure memory regions. */ SEARCH_DIR(.) -GROUP(-lgcc -lc) +/*GROUP(-lgcc -lc)*/ MEMORY { diff --git a/linker/nrf52840.ld b/linker/nrf52840.ld index 5eec85ed..6d426f89 100644 --- a/linker/nrf52840.ld +++ b/linker/nrf52840.ld @@ -1,7 +1,7 @@ /* Linker script to configure memory regions. */ SEARCH_DIR(.) -GROUP(-lgcc -lc -lnosys) +/*GROUP(-lgcc -lc -lnosys)*/ MEMORY { @@ -21,8 +21,6 @@ MEMORY /** Location of bootloader setting in flash. */ BOOTLOADER_SETTINGS (rw) : ORIGIN = 0xFF000, LENGTH = 0x1000 - - /** RAM Region for bootloader. */ /* Avoid conflict with NOINIT for OTA bond sharing */ RAM (rwx) : ORIGIN = 0x20008000, LENGTH = 0x20040000-0x20008000 @@ -33,9 +31,7 @@ MEMORY /** Location of non initialized RAM. Non initialized RAM is used for exchanging bond information * from application to bootloader when using buttonless DFU OTA. */ NOINIT (rwx) : ORIGIN = 0x20007F80, LENGTH = 0x80 - - - + /** Location in UICR where bootloader start address is stored. */ UICR_BOOTLOADER (r) : ORIGIN = 0x10001014, LENGTH = 0x04 @@ -91,7 +87,6 @@ SECTIONS { } > NOINIT - /* other placements follow here... */ } diff --git a/linker/nrf52840_debug.ld b/linker/nrf52840_debug.ld index 2ca9fd6d..4c8e794a 100644 --- a/linker/nrf52840_debug.ld +++ b/linker/nrf52840_debug.ld @@ -1,7 +1,7 @@ /* Linker script to configure memory regions. */ SEARCH_DIR(.) -GROUP(-lgcc -lc) +/*GROUP(-lgcc -lc)*/ MEMORY { diff --git a/linker/nrf52_debug.ld b/linker/nrf52_debug.ld index 2193ace4..27ab4ce7 100644 --- a/linker/nrf52_debug.ld +++ b/linker/nrf52_debug.ld @@ -1,7 +1,7 @@ /* Linker script to configure memory regions. */ SEARCH_DIR(.) -GROUP(-lgcc -lc) +/*GROUP(-lgcc -lc)*/ MEMORY { diff --git a/segger/Adafruit_nRF52_Bootloader.emProject b/segger/Adafruit_nRF52_Bootloader.emProject deleted file mode 100644 index 8084a4d7..00000000 --- a/segger/Adafruit_nRF52_Bootloader.emProject +++ /dev/null @@ -1,363 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/segger/SEGGER_RTT.c b/segger/SEGGER_RTT.c deleted file mode 100644 index aee5bd21..00000000 --- a/segger/SEGGER_RTT.c +++ /dev/null @@ -1,1329 +0,0 @@ -/********************************************************************* -* SEGGER MICROCONTROLLER GmbH & Co. KG * -* Solutions for real time microcontroller applications * -********************************************************************** -* * -* (c) 2014 - 2016 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** -* * -* SEGGER RTT * Real Time Transfer for embedded targets * -* * -********************************************************************** -* * -* All rights reserved. * -* * -* * This software may in its unmodified form be freely redistributed * -* in source form. * -* * The source code may be modified, provided the source code * -* retains the above copyright notice, this list of conditions and * -* the following disclaimer. * -* * Modified versions of this software in source or linkable form * -* may not be distributed without prior consent of SEGGER. * -* * This software may only be used for communication with SEGGER * -* J-Link debug probes. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * -* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * -* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * -* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * -* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * -* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * -* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * -* DAMAGE. * -* * -********************************************************************** -* * -* RTT version: 5.12e * -* * -********************************************************************** ----------------------------END-OF-HEADER------------------------------ -File : SEGGER_RTT.c -Purpose : Implementation of SEGGER real-time transfer (RTT) which - allows real-time communication on targets which support - debugger memory accesses while the CPU is running. - -Additional information: - Type "int" is assumed to be 32-bits in size - H->T Host to target communication - T->H Target to host communication - - RTT channel 0 is always present and reserved for Terminal usage. - Name is fixed to "Terminal" - - Effective buffer size: SizeOfBuffer - 1 - - WrOff == RdOff: Buffer is empty - WrOff == (RdOff - 1): Buffer is full - WrOff > RdOff: Free space includes wrap-around - WrOff < RdOff: Used space includes wrap-around - (WrOff == (SizeOfBuffer - 1)) && (RdOff == 0): - Buffer full and wrap-around after next byte - - ----------------------------------------------------------------------- -*/ - -#include "SEGGER_RTT.h" - -#include // for memcpy - -/********************************************************************* -* -* Configuration, default values -* -********************************************************************** -*/ - -#ifndef BUFFER_SIZE_UP - #define BUFFER_SIZE_UP 1024 // Size of the buffer for terminal output of target, up to host -#endif - -#ifndef BUFFER_SIZE_DOWN - #define BUFFER_SIZE_DOWN 16 // Size of the buffer for terminal input to target from host (Usually keyboard input) -#endif - -#ifndef SEGGER_RTT_MAX_NUM_UP_BUFFERS - #define SEGGER_RTT_MAX_NUM_UP_BUFFERS 2 // Number of up-buffers (T->H) available on this target -#endif - -#ifndef SEGGER_RTT_MAX_NUM_DOWN_BUFFERS - #define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS 2 // Number of down-buffers (H->T) available on this target -#endif - -#ifndef SEGGER_RTT_BUFFER_SECTION - #if defined SEGGER_RTT_SECTION - #define SEGGER_RTT_BUFFER_SECTION SEGGER_RTT_SECTION - #endif -#endif - -#ifndef SEGGER_RTT_MODE_DEFAULT - #define SEGGER_RTT_MODE_DEFAULT SEGGER_RTT_MODE_NO_BLOCK_SKIP -#endif - -#ifndef SEGGER_RTT_LOCK - #define SEGGER_RTT_LOCK() -#endif - -#ifndef SEGGER_RTT_UNLOCK - #define SEGGER_RTT_UNLOCK() -#endif - -#ifndef STRLEN - #define STRLEN(a) strlen((a)) -#endif - -#ifndef MEMCPY - #define MEMCPY(pDest, pSrc, NumBytes) memcpy((pDest), (pSrc), (NumBytes)) -#endif - -#ifndef MIN - #define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -#ifndef MAX - #define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif -// -// For some environments, NULL may not be defined until certain headers are included -// -#ifndef NULL - #define NULL 0 -#endif - -/********************************************************************* -* -* Static const data -* -********************************************************************** -*/ - -static unsigned char _aTerminalId[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - -/********************************************************************* -* -* Static data -* -********************************************************************** -*/ -// -// RTT Control Block and allocate buffers for channel 0 -// -#ifdef SEGGER_RTT_SECTION - #if (defined __GNUC__) - __attribute__ ((section (SEGGER_RTT_SECTION))) SEGGER_RTT_CB _SEGGER_RTT; - #elif (defined __ICCARM__) || (defined __ICCRX__) - #pragma location=SEGGER_RTT_SECTION - SEGGER_RTT_CB _SEGGER_RTT; - #elif (defined __CC_ARM__) - __attribute__ ((section (SEGGER_RTT_SECTION), zero_init)) SEGGER_RTT_CB _SEGGER_RTT; - #else - SEGGER_RTT_CB _SEGGER_RTT; - #endif -#else - SEGGER_RTT_CB _SEGGER_RTT; -#endif - -#ifdef SEGGER_RTT_BUFFER_SECTION - #if (defined __GNUC__) - __attribute__ ((section (SEGGER_RTT_BUFFER_SECTION))) static char _acUpBuffer [BUFFER_SIZE_UP]; - __attribute__ ((section (SEGGER_RTT_BUFFER_SECTION))) static char _acDownBuffer[BUFFER_SIZE_DOWN]; - #elif (defined __ICCARM__) || (defined __ICCRX__) - #pragma location=SEGGER_RTT_BUFFER_SECTION - static char _acUpBuffer [BUFFER_SIZE_UP]; - #pragma location=SEGGER_RTT_BUFFER_SECTION - static char _acDownBuffer[BUFFER_SIZE_DOWN]; - #elif (defined __CC_ARM__) - __attribute__ ((section (SEGGER_RTT_BUFFER_SECTION), zero_init)) static char _acUpBuffer [BUFFER_SIZE_UP]; - __attribute__ ((section (SEGGER_RTT_BUFFER_SECTION), zero_init)) static char _acDownBuffer[BUFFER_SIZE_DOWN]; - #else - static char _acUpBuffer [BUFFER_SIZE_UP]; - static char _acDownBuffer[BUFFER_SIZE_DOWN]; - #endif -#else - static char _acUpBuffer [BUFFER_SIZE_UP]; - static char _acDownBuffer[BUFFER_SIZE_DOWN]; -#endif - -static char _ActiveTerminal; - -/********************************************************************* -* -* Static functions -* -********************************************************************** -*/ - -/********************************************************************* -* -* _DoInit() -* -* Function description -* Initializes the control block an buffers. -* May only be called via INIT() to avoid overriding settings. -* -*/ -#define INIT() do { \ - if (_SEGGER_RTT.acID[0] == '\0') { _DoInit(); } \ - } while (0) -static void _DoInit(void) { - SEGGER_RTT_CB* p; - // - // Initialize control block - // - p = &_SEGGER_RTT; - p->MaxNumUpBuffers = SEGGER_RTT_MAX_NUM_UP_BUFFERS; - p->MaxNumDownBuffers = SEGGER_RTT_MAX_NUM_DOWN_BUFFERS; - // - // Initialize up buffer 0 - // - p->aUp[0].sName = "Terminal"; - p->aUp[0].pBuffer = _acUpBuffer; - p->aUp[0].SizeOfBuffer = sizeof(_acUpBuffer); - p->aUp[0].RdOff = 0u; - p->aUp[0].WrOff = 0u; - p->aUp[0].Flags = SEGGER_RTT_MODE_DEFAULT; - // - // Initialize down buffer 0 - // - p->aDown[0].sName = "Terminal"; - p->aDown[0].pBuffer = _acDownBuffer; - p->aDown[0].SizeOfBuffer = sizeof(_acDownBuffer); - p->aDown[0].RdOff = 0u; - p->aDown[0].WrOff = 0u; - p->aDown[0].Flags = SEGGER_RTT_MODE_DEFAULT; - // - // Finish initialization of the control block. - // Copy Id string in three steps to make sure "SEGGER RTT" is not found - // in initializer memory (usually flash) by J-Link - // - strcpy(&p->acID[7], "RTT"); - strcpy(&p->acID[0], "SEGGER"); - p->acID[6] = ' '; -} - -/********************************************************************* -* -* _WriteBlocking() -* -* Function description -* Stores a specified number of characters in SEGGER RTT ring buffer -* and updates the associated write pointer which is periodically -* read by the host. -* The caller is responsible for managing the write chunk sizes as -* _WriteBlocking() will block until all data has been posted successfully. -* -* Parameters -* pRing Ring buffer to post to. -* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. -* NumBytes Number of bytes to be stored in the SEGGER RTT control block. -* -* Return value -* >= 0 - Number of bytes written into buffer. -*/ -static unsigned _WriteBlocking(SEGGER_RTT_BUFFER_UP* pRing, const char* pBuffer, unsigned NumBytes) { - unsigned NumBytesToWrite; - unsigned NumBytesWritten; - unsigned RdOff; - unsigned WrOff; - // - // Write data to buffer and handle wrap-around if necessary - // - NumBytesWritten = 0u; - WrOff = pRing->WrOff; - do { - RdOff = pRing->RdOff; // May be changed by host (debug probe) in the meantime - if (RdOff > WrOff) { - NumBytesToWrite = RdOff - WrOff - 1u; - } else { - NumBytesToWrite = pRing->SizeOfBuffer - (WrOff - RdOff + 1u); - } - NumBytesToWrite = MIN(NumBytesToWrite, (pRing->SizeOfBuffer - WrOff)); // Number of bytes that can be written until buffer wrap-around - NumBytesToWrite = MIN(NumBytesToWrite, NumBytes); - memcpy(pRing->pBuffer + WrOff, pBuffer, NumBytesToWrite); - NumBytesWritten += NumBytesToWrite; - pBuffer += NumBytesToWrite; - NumBytes -= NumBytesToWrite; - WrOff += NumBytesToWrite; - if (WrOff == pRing->SizeOfBuffer) { - WrOff = 0u; - } - pRing->WrOff = WrOff; - } while (NumBytes); - // - return NumBytesWritten; -} - -/********************************************************************* -* -* _WriteNoCheck() -* -* Function description -* Stores a specified number of characters in SEGGER RTT ring buffer -* and updates the associated write pointer which is periodically -* read by the host. -* It is callers responsibility to make sure data actually fits in buffer. -* -* Parameters -* pRing Ring buffer to post to. -* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. -* NumBytes Number of bytes to be stored in the SEGGER RTT control block. -* -* Notes -* (1) If there might not be enough space in the "Up"-buffer, call _WriteBlocking -*/ -static void _WriteNoCheck(SEGGER_RTT_BUFFER_UP* pRing, const char* pData, unsigned NumBytes) { - unsigned NumBytesAtOnce; - unsigned WrOff; - unsigned Rem; - - WrOff = pRing->WrOff; - Rem = pRing->SizeOfBuffer - WrOff; - if (Rem > NumBytes) { - // - // All data fits before wrap around - // - memcpy(pRing->pBuffer + WrOff, pData, NumBytes); - pRing->WrOff = WrOff + NumBytes; - } else { - // - // We reach the end of the buffer, so need to wrap around - // - NumBytesAtOnce = Rem; - memcpy(pRing->pBuffer + WrOff, pData, NumBytesAtOnce); - NumBytesAtOnce = NumBytes - Rem; - memcpy(pRing->pBuffer, pData + Rem, NumBytesAtOnce); - pRing->WrOff = NumBytesAtOnce; - } -} - -/********************************************************************* -* -* _PostTerminalSwitch() -* -* Function description -* Switch terminal to the given terminal ID. It is the caller's -* responsibility to ensure the terminal ID is correct and there is -* enough space in the buffer for this to complete successfully. -* -* Parameters -* pRing Ring buffer to post to. -* TerminalId Terminal ID to switch to. -*/ -static void _PostTerminalSwitch(SEGGER_RTT_BUFFER_UP* pRing, unsigned char TerminalId) { - char ac[2]; - - ac[0] = 0xFFu; - ac[1] = _aTerminalId[TerminalId]; // Caller made already sure that TerminalId does not exceed our terminal limit - _WriteBlocking(pRing, ac, 2u); -} - -/********************************************************************* -* -* _GetAvailWriteSpace() -* -* Function description -* Returns the number of bytes that can be written to the ring -* buffer without blocking. -* -* Parameters -* pRing Ring buffer to check. -* -* Return value -* Number of bytes that are free in the buffer. -*/ -static unsigned _GetAvailWriteSpace(SEGGER_RTT_BUFFER_UP* pRing) { - unsigned RdOff; - unsigned WrOff; - unsigned r; - // - // Avoid warnings regarding volatile access order. It's not a problem - // in this case, but dampen compiler enthusiasm. - // - RdOff = pRing->RdOff; - WrOff = pRing->WrOff; - if (RdOff <= WrOff) { - r = pRing->SizeOfBuffer - 1u - WrOff + RdOff; - } else { - r = RdOff - WrOff - 1u; - } - return r; -} - -/********************************************************************* -* -* Public code -* -********************************************************************** -*/ -/********************************************************************* -* -* SEGGER_RTT_ReadNoLock() -* -* Function description -* Reads characters from SEGGER real-time-terminal control block -* which have been previously stored by the host. -* Do not lock against interrupts and multiple access. -* -* Parameters -* BufferIndex Index of Down-buffer to be used (e.g. 0 for "Terminal"). -* pBuffer Pointer to buffer provided by target application, to copy characters from RTT-down-buffer to. -* BufferSize Size of the target application buffer. -* -* Return value -* Number of bytes that have been read. -*/ -unsigned SEGGER_RTT_ReadNoLock(unsigned BufferIndex, void* pData, unsigned BufferSize) { - unsigned NumBytesRem; - unsigned NumBytesRead; - unsigned RdOff; - unsigned WrOff; - unsigned char* pBuffer; - SEGGER_RTT_BUFFER_DOWN* pRing; - // - INIT(); - pRing = &_SEGGER_RTT.aDown[BufferIndex]; - pBuffer = (unsigned char*)pData; - RdOff = pRing->RdOff; - WrOff = pRing->WrOff; - NumBytesRead = 0u; - // - // Read from current read position to wrap-around of buffer, first - // - if (RdOff > WrOff) { - NumBytesRem = pRing->SizeOfBuffer - RdOff; - NumBytesRem = MIN(NumBytesRem, BufferSize); - memcpy(pBuffer, pRing->pBuffer + RdOff, NumBytesRem); - NumBytesRead += NumBytesRem; - pBuffer += NumBytesRem; - BufferSize -= NumBytesRem; - RdOff += NumBytesRem; - // - // Handle wrap-around of buffer - // - if (RdOff == pRing->SizeOfBuffer) { - RdOff = 0u; - } - } - // - // Read remaining items of buffer - // - NumBytesRem = WrOff - RdOff; - NumBytesRem = MIN(NumBytesRem, BufferSize); - if (NumBytesRem > 0u) { - memcpy(pBuffer, pRing->pBuffer + RdOff, NumBytesRem); - NumBytesRead += NumBytesRem; - pBuffer += NumBytesRem; - BufferSize -= NumBytesRem; - RdOff += NumBytesRem; - } - if (NumBytesRead) { - pRing->RdOff = RdOff; - } - // - return NumBytesRead; -} - -/********************************************************************* -* -* SEGGER_RTT_Read -* -* Function description -* Reads characters from SEGGER real-time-terminal control block -* which have been previously stored by the host. -* -* Parameters -* BufferIndex Index of Down-buffer to be used (e.g. 0 for "Terminal"). -* pBuffer Pointer to buffer provided by target application, to copy characters from RTT-down-buffer to. -* BufferSize Size of the target application buffer. -* -* Return value -* Number of bytes that have been read. -*/ -unsigned SEGGER_RTT_Read(unsigned BufferIndex, void* pBuffer, unsigned BufferSize) { - unsigned NumBytesRead; - // - SEGGER_RTT_LOCK(); - // - // Call the non-locking read function - // - NumBytesRead = SEGGER_RTT_ReadNoLock(BufferIndex, pBuffer, BufferSize); - // - // Finish up. - // - SEGGER_RTT_UNLOCK(); - // - return NumBytesRead; -} - -/********************************************************************* -* -* SEGGER_RTT_WriteWithOverwriteNoLock -* -* Function description -* Stores a specified number of characters in SEGGER RTT -* control block. -* SEGGER_RTT_WriteWithOverwriteNoLock does not lock the application -* and overwrites data if the data does not fit into the buffer. -* -* Parameters -* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). -* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. -* NumBytes Number of bytes to be stored in the SEGGER RTT control block. -* -* Notes -* (1) If there is not enough space in the "Up"-buffer, data is overwritten. -* (2) For performance reasons this function does not call Init() -* and may only be called after RTT has been initialized. -* Either by calling SEGGER_RTT_Init() or calling another RTT API function first. -* (3) Do not use SEGGER_RTT_WriteWithOverwriteNoLock if a J-Link -* connection reads RTT data. -*/ -void SEGGER_RTT_WriteWithOverwriteNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { - const char* pData; - SEGGER_RTT_BUFFER_UP* pRing; - unsigned Avail; - - pData = (const char *)pBuffer; - // - // Get "to-host" ring buffer and copy some elements into local variables. - // - pRing = &_SEGGER_RTT.aUp[BufferIndex]; - // - // Check if we will overwrite data and need to adjust the RdOff. - // - if (pRing->WrOff == pRing->RdOff) { - Avail = pRing->SizeOfBuffer - 1u; - } else if ( pRing->WrOff < pRing->RdOff) { - Avail = pRing->RdOff - pRing->WrOff - 1u; - } else { - Avail = pRing->RdOff - pRing->WrOff - 1u + pRing->SizeOfBuffer; - } - if (NumBytes > Avail) { - pRing->RdOff += (NumBytes - Avail); - while (pRing->RdOff >= pRing->SizeOfBuffer) { - pRing->RdOff -= pRing->SizeOfBuffer; - } - } - // - // Write all data, no need to check the RdOff, but possibly handle multiple wrap-arounds - // - Avail = pRing->SizeOfBuffer - pRing->WrOff; - do { - if (Avail > NumBytes) { - // - // Last round - // -#if 1 // memcpy() is good for large amounts of data, but the overhead is too big for small amounts. Use a simple byte loop instead. - char* pDst; - pDst = pRing->pBuffer + pRing->WrOff; - pRing->WrOff += NumBytes; - do { - *pDst++ = *pData++; - } while (--NumBytes); -#else - memcpy(pRing->pBuffer + WrOff, pData, NumBytes); - pRing->WrOff += NumBytes; -#endif - break; //Alternatively: NumBytes = 0; - } else { - // - // Wrap-around necessary, write until wrap-around and reset WrOff - // - memcpy(pRing->pBuffer + pRing->WrOff, pData, Avail); - pData += Avail; - pRing->WrOff = 0; - NumBytes -= Avail; - Avail = (pRing->SizeOfBuffer - 1); - } - } while (NumBytes); -} - -/********************************************************************* -* -* SEGGER_RTT_WriteSkipNoLock -* -* Function description -* Stores a specified number of characters in SEGGER RTT -* control block which is then read by the host. -* SEGGER_RTT_WriteSkipNoLock does not lock the application and -* skips all data, if the data does not fit into the buffer. -* -* Parameters -* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). -* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. -* NumBytes Number of bytes to be stored in the SEGGER RTT control block. -* -* Return value -* Number of bytes which have been stored in the "Up"-buffer. -* -* Notes -* (1) If there is not enough space in the "Up"-buffer, all data is dropped. -* (2) For performance reasons this function does not call Init() -* and may only be called after RTT has been initialized. -* Either by calling SEGGER_RTT_Init() or calling another RTT API function first. -*/ -unsigned SEGGER_RTT_WriteSkipNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { - const char* pData; - SEGGER_RTT_BUFFER_UP* pRing; - unsigned Avail; - unsigned RdOff; - unsigned WrOff; - unsigned Rem; - - pData = (const char *)pBuffer; - // - // Get "to-host" ring buffer and copy some elements into local variables. - // - pRing = &_SEGGER_RTT.aUp[BufferIndex]; - RdOff = pRing->RdOff; - WrOff = pRing->WrOff; - // - // Handle the most common cases fastest. - // Which is: - // RdOff <= WrOff -> Space until wrap around is free. - // AND - // WrOff + NumBytes < SizeOfBuffer -> No Wrap around necessary. - // - // OR - // - // RdOff > WrOff -> Space until RdOff - 1 is free. - // AND - // WrOff + NumBytes < RdOff -> Data fits into buffer - // - if (RdOff <= WrOff) { - // - // Get space until WrOff will be at wrap around. - // - Avail = pRing->SizeOfBuffer - 1u - WrOff ; - if (Avail >= NumBytes) { -#if 1 // memcpy() is good for large amounts of data, but the overhead is too big for small amounts. Use a simple byte loop instead. - char* pDst; - pDst = pRing->pBuffer + WrOff; - WrOff += NumBytes; - do { - *pDst++ = *pData++; - } while (--NumBytes); - pRing->WrOff = WrOff + NumBytes; -#else - memcpy(pRing->pBuffer + WrOff, pData, NumBytes); - pRing->WrOff = WrOff + NumBytes; -#endif - return 1; - } - // - // If data did not fit into space until wrap around calculate complete space in buffer. - // - Avail += RdOff; - // - // If there is still no space for the whole of this output, don't bother. - // - if (Avail >= NumBytes) { - // - // OK, we have enough space in buffer. Copy in one or 2 chunks - // - Rem = pRing->SizeOfBuffer - WrOff; // Space until end of buffer - if (Rem > NumBytes) { - memcpy(pRing->pBuffer + WrOff, pData, NumBytes); - pRing->WrOff = WrOff + NumBytes; - } else { - // - // We reach the end of the buffer, so need to wrap around - // - memcpy(pRing->pBuffer + WrOff, pData, Rem); - memcpy(pRing->pBuffer, pData + Rem, NumBytes - Rem); - pRing->WrOff = NumBytes - Rem; - } - return 1; - } - } else { - Avail = RdOff - WrOff - 1u; - if (Avail >= NumBytes) { - memcpy(pRing->pBuffer + WrOff, pData, NumBytes); - pRing->WrOff = WrOff + NumBytes; - return 1; - } - } - // - // If we reach this point no data has been written - // - return 0; -} - -/********************************************************************* -* -* SEGGER_RTT_WriteNoLock -* -* Function description -* Stores a specified number of characters in SEGGER RTT -* control block which is then read by the host. -* SEGGER_RTT_WriteNoLock does not lock the application. -* -* Parameters -* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). -* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. -* NumBytes Number of bytes to be stored in the SEGGER RTT control block. -* -* Return value -* Number of bytes which have been stored in the "Up"-buffer. -* -* Notes -* (1) If there is not enough space in the "Up"-buffer, remaining characters of pBuffer are dropped. -* (2) For performance reasons this function does not call Init() -* and may only be called after RTT has been initialized. -* Either by calling SEGGER_RTT_Init() or calling another RTT API function first. -*/ -unsigned SEGGER_RTT_WriteNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { - unsigned Status; - unsigned Avail; - const char* pData; - SEGGER_RTT_BUFFER_UP* pRing; - - pData = (const char *)pBuffer; - // - // Get "to-host" ring buffer. - // - pRing = &_SEGGER_RTT.aUp[BufferIndex]; - // - // How we output depends upon the mode... - // - switch (pRing->Flags) { - case SEGGER_RTT_MODE_NO_BLOCK_SKIP: - // - // If we are in skip mode and there is no space for the whole - // of this output, don't bother. - // - Avail = _GetAvailWriteSpace(pRing); - if (Avail < NumBytes) { - Status = 0u; - } else { - Status = NumBytes; - _WriteNoCheck(pRing, pData, NumBytes); - } - break; - case SEGGER_RTT_MODE_NO_BLOCK_TRIM: - // - // If we are in trim mode, trim to what we can output without blocking. - // - Avail = _GetAvailWriteSpace(pRing); - Status = Avail < NumBytes ? Avail : NumBytes; - _WriteNoCheck(pRing, pData, Status); - break; - case SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL: - // - // If we are in blocking mode, output everything. - // - Status = _WriteBlocking(pRing, pData, NumBytes); - break; - default: - Status = 0u; - break; - } - // - // Finish up. - // - return Status; -} - -/********************************************************************* -* -* SEGGER_RTT_Write -* -* Function description -* Stores a specified number of characters in SEGGER RTT -* control block which is then read by the host. -* -* Parameters -* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). -* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. -* NumBytes Number of bytes to be stored in the SEGGER RTT control block. -* -* Return value -* Number of bytes which have been stored in the "Up"-buffer. -* -* Notes -* (1) If there is not enough space in the "Up"-buffer, remaining characters of pBuffer are dropped. -*/ -unsigned SEGGER_RTT_Write(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { - unsigned Status; - // - INIT(); - SEGGER_RTT_LOCK(); - // - // Call the non-locking write function - // - Status = SEGGER_RTT_WriteNoLock(BufferIndex, pBuffer, NumBytes); - // - // Finish up. - // - SEGGER_RTT_UNLOCK(); - // - return Status; -} - -/********************************************************************* -* -* SEGGER_RTT_WriteString -* -* Function description -* Stores string in SEGGER RTT control block. -* This data is read by the host. -* -* Parameters -* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). -* s Pointer to string. -* -* Return value -* Number of bytes which have been stored in the "Up"-buffer. -* -* Notes -* (1) If there is not enough space in the "Up"-buffer, depending on configuration, -* remaining characters may be dropped or RTT module waits until there is more space in the buffer. -* (2) String passed to this function has to be \0 terminated -* (3) \0 termination character is *not* stored in RTT buffer -*/ -unsigned SEGGER_RTT_WriteString(unsigned BufferIndex, const char* s) { - unsigned Len; - - Len = STRLEN(s); - return SEGGER_RTT_Write(BufferIndex, s, Len); -} - -/********************************************************************* -* -* SEGGER_RTT_GetKey -* -* Function description -* Reads one character from the SEGGER RTT buffer. -* Host has previously stored data there. -* -* Return value -* < 0 - No character available (buffer empty). -* >= 0 - Character which has been read. (Possible values: 0 - 255) -* -* Notes -* (1) This function is only specified for accesses to RTT buffer 0. -*/ -int SEGGER_RTT_GetKey(void) { - char c; - int r; - - r = (int)SEGGER_RTT_Read(0u, &c, 1u); - if (r == 1) { - r = (int)(unsigned char)c; - } else { - r = -1; - } - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_WaitKey -* -* Function description -* Waits until at least one character is avaible in the SEGGER RTT buffer. -* Once a character is available, it is read and this function returns. -* -* Return value -* >=0 - Character which has been read. -* -* Notes -* (1) This function is only specified for accesses to RTT buffer 0 -* (2) This function is blocking if no character is present in RTT buffer -*/ -int SEGGER_RTT_WaitKey(void) { - int r; - - do { - r = SEGGER_RTT_GetKey(); - } while (r < 0); - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_HasKey -* -* Function description -* Checks if at least one character for reading is available in the SEGGER RTT buffer. -* -* Return value -* == 0 - No characters are available to read. -* == 1 - At least one character is available. -* -* Notes -* (1) This function is only specified for accesses to RTT buffer 0 -*/ -int SEGGER_RTT_HasKey(void) { - unsigned RdOff; - int r; - - INIT(); - RdOff = _SEGGER_RTT.aDown[0].RdOff; - if (RdOff != _SEGGER_RTT.aDown[0].WrOff) { - r = 1; - } else { - r = 0; - } - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_HasData -* -* Function description -* Check if there is data from the host in the given buffer. -* -* Return value: -* ==0: No data -* !=0: Data in buffer -* -*/ -unsigned SEGGER_RTT_HasData(unsigned BufferIndex) { - SEGGER_RTT_BUFFER_DOWN* pRing; - unsigned v; - - pRing = &_SEGGER_RTT.aDown[BufferIndex]; - v = pRing->WrOff; - return v - pRing->RdOff; -} - -/********************************************************************* -* -* SEGGER_RTT_AllocDownBuffer -* -* Function description -* Run-time configuration of the next down-buffer (H->T). -* The next buffer, which is not used yet is configured. -* This includes: Buffer address, size, name, flags, ... -* -* Parameters -* sName Pointer to a constant name string. -* pBuffer Pointer to a buffer to be used. -* BufferSize Size of the buffer. -* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). -* -* Return value -* >= 0 - O.K. Buffer Index -* < 0 - Error -*/ -int SEGGER_RTT_AllocDownBuffer(const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { - int BufferIndex; - - INIT(); - SEGGER_RTT_LOCK(); - BufferIndex = 0; - do { - if (_SEGGER_RTT.aDown[BufferIndex].pBuffer == NULL) { - break; - } - BufferIndex++; - } while (BufferIndex < _SEGGER_RTT.MaxNumDownBuffers); - if (BufferIndex < _SEGGER_RTT.MaxNumDownBuffers) { - _SEGGER_RTT.aDown[BufferIndex].sName = sName; - _SEGGER_RTT.aDown[BufferIndex].pBuffer = pBuffer; - _SEGGER_RTT.aDown[BufferIndex].SizeOfBuffer = BufferSize; - _SEGGER_RTT.aDown[BufferIndex].RdOff = 0u; - _SEGGER_RTT.aDown[BufferIndex].WrOff = 0u; - _SEGGER_RTT.aDown[BufferIndex].Flags = Flags; - } else { - BufferIndex = -1; - } - SEGGER_RTT_UNLOCK(); - return BufferIndex; -} - -/********************************************************************* -* -* SEGGER_RTT_AllocUpBuffer -* -* Function description -* Run-time configuration of the next up-buffer (T->H). -* The next buffer, which is not used yet is configured. -* This includes: Buffer address, size, name, flags, ... -* -* Parameters -* sName Pointer to a constant name string. -* pBuffer Pointer to a buffer to be used. -* BufferSize Size of the buffer. -* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). -* -* Return value -* >= 0 - O.K. Buffer Index -* < 0 - Error -*/ -int SEGGER_RTT_AllocUpBuffer(const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { - int BufferIndex; - - INIT(); - SEGGER_RTT_LOCK(); - BufferIndex = 0; - do { - if (_SEGGER_RTT.aUp[BufferIndex].pBuffer == NULL) { - break; - } - BufferIndex++; - } while (BufferIndex < _SEGGER_RTT.MaxNumUpBuffers); - if (BufferIndex < _SEGGER_RTT.MaxNumUpBuffers) { - _SEGGER_RTT.aUp[BufferIndex].sName = sName; - _SEGGER_RTT.aUp[BufferIndex].pBuffer = pBuffer; - _SEGGER_RTT.aUp[BufferIndex].SizeOfBuffer = BufferSize; - _SEGGER_RTT.aUp[BufferIndex].RdOff = 0u; - _SEGGER_RTT.aUp[BufferIndex].WrOff = 0u; - _SEGGER_RTT.aUp[BufferIndex].Flags = Flags; - } else { - BufferIndex = -1; - } - SEGGER_RTT_UNLOCK(); - return BufferIndex; -} - -/********************************************************************* -* -* SEGGER_RTT_ConfigUpBuffer -* -* Function description -* Run-time configuration of a specific up-buffer (T->H). -* Buffer to be configured is specified by index. -* This includes: Buffer address, size, name, flags, ... -* -* Parameters -* BufferIndex Index of the buffer to configure. -* sName Pointer to a constant name string. -* pBuffer Pointer to a buffer to be used. -* BufferSize Size of the buffer. -* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). -* -* Return value -* >= 0 - O.K. -* < 0 - Error -*/ -int SEGGER_RTT_ConfigUpBuffer(unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { - int r; - - INIT(); - if (BufferIndex < (unsigned)_SEGGER_RTT.MaxNumUpBuffers) { - SEGGER_RTT_LOCK(); - if (BufferIndex > 0u) { - _SEGGER_RTT.aUp[BufferIndex].sName = sName; - _SEGGER_RTT.aUp[BufferIndex].pBuffer = pBuffer; - _SEGGER_RTT.aUp[BufferIndex].SizeOfBuffer = BufferSize; - _SEGGER_RTT.aUp[BufferIndex].RdOff = 0u; - _SEGGER_RTT.aUp[BufferIndex].WrOff = 0u; - } - _SEGGER_RTT.aUp[BufferIndex].Flags = Flags; - SEGGER_RTT_UNLOCK(); - r = 0; - } else { - r = -1; - } - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_ConfigDownBuffer -* -* Function description -* Run-time configuration of a specific down-buffer (H->T). -* Buffer to be configured is specified by index. -* This includes: Buffer address, size, name, flags, ... -* -* Parameters -* BufferIndex Index of the buffer to configure. -* sName Pointer to a constant name string. -* pBuffer Pointer to a buffer to be used. -* BufferSize Size of the buffer. -* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). -* -* Return value -* >= 0 O.K. -* < 0 Error -*/ -int SEGGER_RTT_ConfigDownBuffer(unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { - int r; - - INIT(); - if (BufferIndex < (unsigned)_SEGGER_RTT.MaxNumDownBuffers) { - SEGGER_RTT_LOCK(); - if (BufferIndex > 0u) { - _SEGGER_RTT.aDown[BufferIndex].sName = sName; - _SEGGER_RTT.aDown[BufferIndex].pBuffer = pBuffer; - _SEGGER_RTT.aDown[BufferIndex].SizeOfBuffer = BufferSize; - _SEGGER_RTT.aDown[BufferIndex].RdOff = 0u; - _SEGGER_RTT.aDown[BufferIndex].WrOff = 0u; - } - _SEGGER_RTT.aDown[BufferIndex].Flags = Flags; - SEGGER_RTT_UNLOCK(); - r = 0; - } else { - r = -1; - } - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_SetNameUpBuffer -* -* Function description -* Run-time configuration of a specific up-buffer name (T->H). -* Buffer to be configured is specified by index. -* -* Parameters -* BufferIndex Index of the buffer to renamed. -* sName Pointer to a constant name string. -* -* Return value -* >= 0 O.K. -* < 0 Error -*/ -int SEGGER_RTT_SetNameUpBuffer(unsigned BufferIndex, const char* sName) { - int r; - - INIT(); - if (BufferIndex < (unsigned)_SEGGER_RTT.MaxNumUpBuffers) { - SEGGER_RTT_LOCK(); - _SEGGER_RTT.aUp[BufferIndex].sName = sName; - SEGGER_RTT_UNLOCK(); - r = 0; - } else { - r = -1; - } - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_SetNameDownBuffer -* -* Function description -* Run-time configuration of a specific Down-buffer name (T->H). -* Buffer to be configured is specified by index. -* -* Parameters -* BufferIndex Index of the buffer to renamed. -* sName Pointer to a constant name string. -* -* Return value -* >= 0 O.K. -* < 0 Error -*/ -int SEGGER_RTT_SetNameDownBuffer(unsigned BufferIndex, const char* sName) { - int r; - - INIT(); - if (BufferIndex < (unsigned)_SEGGER_RTT.MaxNumDownBuffers) { - SEGGER_RTT_LOCK(); - _SEGGER_RTT.aDown[BufferIndex].sName = sName; - SEGGER_RTT_UNLOCK(); - r = 0; - } else { - r = -1; - } - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_Init -* -* Function description -* Initializes the RTT Control Block. -* Should be used in RAM targets, at start of the application. -* -*/ -void SEGGER_RTT_Init (void) { - _DoInit(); -} - -/********************************************************************* -* -* SEGGER_RTT_SetTerminal -* -* Function description -* Sets the terminal to be used for output on channel 0. -* -* Parameters -* TerminalId Index of the terminal. -* -* Return value -* >= 0 O.K. -* < 0 Error (e.g. if RTT is configured for non-blocking mode and there was no space in the buffer to set the new terminal Id) -*/ -int SEGGER_RTT_SetTerminal (char TerminalId) { - char ac[2]; - SEGGER_RTT_BUFFER_UP* pRing; - unsigned Avail; - int r; - // - INIT(); - // - r = 0; - ac[0] = 0xFFU; - if ((unsigned char)TerminalId < (unsigned char)sizeof(_aTerminalId)) { // We only support a certain number of channels - ac[1] = _aTerminalId[(unsigned char)TerminalId]; - pRing = &_SEGGER_RTT.aUp[0]; // Buffer 0 is always reserved for terminal I/O, so we can use index 0 here, fixed - SEGGER_RTT_LOCK(); // Lock to make sure that no other task is writing into buffer, while we are and number of free bytes in buffer does not change downwards after checking and before writing - if ((pRing->Flags & SEGGER_RTT_MODE_MASK) == SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL) { - _ActiveTerminal = TerminalId; - _WriteBlocking(pRing, ac, 2u); - } else { // Skipping mode or trim mode? => We cannot trim this command so handling is the same for both modes - Avail = _GetAvailWriteSpace(pRing); - if (Avail >= 2) { - _ActiveTerminal = TerminalId; // Only change active terminal in case of success - _WriteNoCheck(pRing, ac, 2u); - } else { - r = -1; - } - } - SEGGER_RTT_UNLOCK(); - } else { - r = -1; - } - return r; -} - -/********************************************************************* -* -* SEGGER_RTT_TerminalOut -* -* Function description -* Writes a string to the given terminal -* without changing the terminal for channel 0. -* -* Parameters -* TerminalId Index of the terminal. -* s String to be printed on the terminal. -* -* Return value -* >= 0 - Number of bytes written. -* < 0 - Error. -* -*/ -int SEGGER_RTT_TerminalOut (char TerminalId, const char* s) { - int Status; - unsigned FragLen; - unsigned Avail; - SEGGER_RTT_BUFFER_UP* pRing; - // - INIT(); - // - // Validate terminal ID. - // - if (TerminalId < (char)sizeof(_aTerminalId)) { // We only support a certain number of channels - // - // Get "to-host" ring buffer. - // - pRing = &_SEGGER_RTT.aUp[0]; - // - // Need to be able to change terminal, write data, change back. - // Compute the fixed and variable sizes. - // - FragLen = strlen(s); - // - // How we output depends upon the mode... - // - SEGGER_RTT_LOCK(); - Avail = _GetAvailWriteSpace(pRing); - switch (pRing->Flags & SEGGER_RTT_MODE_MASK) { - case SEGGER_RTT_MODE_NO_BLOCK_SKIP: - // - // If we are in skip mode and there is no space for the whole - // of this output, don't bother switching terminals at all. - // - if (Avail < (FragLen + 4u)) { - Status = 0; - } else { - _PostTerminalSwitch(pRing, TerminalId); - Status = (int)_WriteBlocking(pRing, s, FragLen); - _PostTerminalSwitch(pRing, _ActiveTerminal); - } - break; - case SEGGER_RTT_MODE_NO_BLOCK_TRIM: - // - // If we are in trim mode and there is not enough space for everything, - // trim the output but always include the terminal switch. If no room - // for terminal switch, skip that totally. - // - if (Avail < 4u) { - Status = -1; - } else { - _PostTerminalSwitch(pRing, TerminalId); - Status = (int)_WriteBlocking(pRing, s, (FragLen < (Avail - 4u)) ? FragLen : (Avail - 4u)); - _PostTerminalSwitch(pRing, _ActiveTerminal); - } - break; - case SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL: - // - // If we are in blocking mode, output everything. - // - _PostTerminalSwitch(pRing, TerminalId); - Status = (int)_WriteBlocking(pRing, s, FragLen); - _PostTerminalSwitch(pRing, _ActiveTerminal); - break; - default: - Status = -1; - break; - } - // - // Finish up. - // - SEGGER_RTT_UNLOCK(); - } else { - Status = -1; - } - return Status; -} - - -/*************************** End of file ****************************/ diff --git a/segger/SEGGER_RTT.h b/segger/SEGGER_RTT.h deleted file mode 100644 index d3cac44d..00000000 --- a/segger/SEGGER_RTT.h +++ /dev/null @@ -1,234 +0,0 @@ -/********************************************************************* -* SEGGER MICROCONTROLLER GmbH & Co. KG * -* Solutions for real time microcontroller applications * -********************************************************************** -* * -* (c) 2014 - 2016 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** -* * -* SEGGER RTT * Real Time Transfer for embedded targets * -* * -********************************************************************** -* * -* All rights reserved. * -* * -* * This software may in its unmodified form be freely redistributed * -* in source form. * -* * The source code may be modified, provided the source code * -* retains the above copyright notice, this list of conditions and * -* the following disclaimer. * -* * Modified versions of this software in source or linkable form * -* may not be distributed without prior consent of SEGGER. * -* * This software may only be used for communication with SEGGER * -* J-Link debug probes. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * -* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * -* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * -* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * -* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * -* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * -* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * -* DAMAGE. * -* * -********************************************************************** -* * -* RTT version: 5.12e * -* * -********************************************************************** ----------------------------END-OF-HEADER------------------------------ -File : SEGGER_RTT.h -Purpose : Implementation of SEGGER real-time transfer which allows - real-time communication on targets which support debugger - memory accesses while the CPU is running. ----------------------------------------------------------------------- -*/ - -#ifndef SEGGER_RTT_H -#define SEGGER_RTT_H - -#include "SEGGER_RTT_Conf.h" - -/********************************************************************* -* -* Defines, fixed -* -********************************************************************** -*/ - -/********************************************************************* -* -* Types -* -********************************************************************** -*/ - -// -// Description for a circular buffer (also called "ring buffer") -// which is used as up-buffer (T->H) -// -typedef struct { - const char* sName; // Optional name. Standard names so far are: "Terminal", "SysView", "J-Scope_t4i4" - char* pBuffer; // Pointer to start of buffer - unsigned SizeOfBuffer; // Buffer size in bytes. Note that one byte is lost, as this implementation does not fill up the buffer in order to avoid the problem of being unable to distinguish between full and empty. - unsigned WrOff; // Position of next item to be written by either target. - volatile unsigned RdOff; // Position of next item to be read by host. Must be volatile since it may be modified by host. - unsigned Flags; // Contains configuration flags -} SEGGER_RTT_BUFFER_UP; - -// -// Description for a circular buffer (also called "ring buffer") -// which is used as down-buffer (H->T) -// -typedef struct { - const char* sName; // Optional name. Standard names so far are: "Terminal", "SysView", "J-Scope_t4i4" - char* pBuffer; // Pointer to start of buffer - unsigned SizeOfBuffer; // Buffer size in bytes. Note that one byte is lost, as this implementation does not fill up the buffer in order to avoid the problem of being unable to distinguish between full and empty. - volatile unsigned WrOff; // Position of next item to be written by host. Must be volatile since it may be modified by host. - unsigned RdOff; // Position of next item to be read by target (down-buffer). - unsigned Flags; // Contains configuration flags -} SEGGER_RTT_BUFFER_DOWN; - -// -// RTT control block which describes the number of buffers available -// as well as the configuration for each buffer -// -// -typedef struct { - char acID[16]; // Initialized to "SEGGER RTT" - int MaxNumUpBuffers; // Initialized to SEGGER_RTT_MAX_NUM_UP_BUFFERS (type. 2) - int MaxNumDownBuffers; // Initialized to SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (type. 2) - SEGGER_RTT_BUFFER_UP aUp[SEGGER_RTT_MAX_NUM_UP_BUFFERS]; // Up buffers, transferring information up from target via debug probe to host - SEGGER_RTT_BUFFER_DOWN aDown[SEGGER_RTT_MAX_NUM_DOWN_BUFFERS]; // Down buffers, transferring information down from host via debug probe to target -} SEGGER_RTT_CB; - -/********************************************************************* -* -* Global data -* -********************************************************************** -*/ -extern SEGGER_RTT_CB _SEGGER_RTT; - -/********************************************************************* -* -* RTT API functions -* -********************************************************************** -*/ -#ifdef __cplusplus - extern "C" { -#endif -int SEGGER_RTT_AllocDownBuffer (const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); -int SEGGER_RTT_AllocUpBuffer (const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); -int SEGGER_RTT_ConfigUpBuffer (unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); -int SEGGER_RTT_ConfigDownBuffer (unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); -int SEGGER_RTT_GetKey (void); -unsigned SEGGER_RTT_HasData (unsigned BufferIndex); -int SEGGER_RTT_HasKey (void); -void SEGGER_RTT_Init (void); -unsigned SEGGER_RTT_Read (unsigned BufferIndex, void* pBuffer, unsigned BufferSize); -unsigned SEGGER_RTT_ReadNoLock (unsigned BufferIndex, void* pData, unsigned BufferSize); -int SEGGER_RTT_SetNameDownBuffer(unsigned BufferIndex, const char* sName); -int SEGGER_RTT_SetNameUpBuffer (unsigned BufferIndex, const char* sName); -int SEGGER_RTT_WaitKey (void); -unsigned SEGGER_RTT_Write (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); -unsigned SEGGER_RTT_WriteNoLock (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); -unsigned SEGGER_RTT_WriteSkipNoLock (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); -unsigned SEGGER_RTT_WriteString (unsigned BufferIndex, const char* s); -void SEGGER_RTT_WriteWithOverwriteNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); -// -// Function macro for performance optimization -// -#define SEGGER_RTT_HASDATA(n) (_SEGGER_RTT.aDown[n].WrOff - _SEGGER_RTT.aDown[n].RdOff) - -/********************************************************************* -* -* RTT "Terminal" API functions -* -********************************************************************** -*/ -int SEGGER_RTT_SetTerminal (char TerminalId); -int SEGGER_RTT_TerminalOut (char TerminalId, const char* s); - -/********************************************************************* -* -* RTT printf functions (require SEGGER_RTT_printf.c) -* -********************************************************************** -*/ -int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...); -#ifdef __cplusplus - } -#endif - -/********************************************************************* -* -* Defines -* -********************************************************************** -*/ - -// -// Operating modes. Define behavior if buffer is full (not enough space for entire message) -// -#define SEGGER_RTT_MODE_NO_BLOCK_SKIP (0U) // Skip. Do not block, output nothing. (Default) -#define SEGGER_RTT_MODE_NO_BLOCK_TRIM (1U) // Trim: Do not block, output as much as fits. -#define SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL (2U) // Block: Wait until there is space in the buffer. -#define SEGGER_RTT_MODE_MASK (3U) - -// -// Control sequences, based on ANSI. -// Can be used to control color, and clear the screen -// -#define RTT_CTRL_RESET "" // Reset to default colors -#define RTT_CTRL_CLEAR "" // Clear screen, reposition cursor to top left - -#define RTT_CTRL_TEXT_BLACK "" -#define RTT_CTRL_TEXT_RED "" -#define RTT_CTRL_TEXT_GREEN "" -#define RTT_CTRL_TEXT_YELLOW "" -#define RTT_CTRL_TEXT_BLUE "" -#define RTT_CTRL_TEXT_MAGENTA "" -#define RTT_CTRL_TEXT_CYAN "" -#define RTT_CTRL_TEXT_WHITE "" - -#define RTT_CTRL_TEXT_BRIGHT_BLACK "" -#define RTT_CTRL_TEXT_BRIGHT_RED "" -#define RTT_CTRL_TEXT_BRIGHT_GREEN "" -#define RTT_CTRL_TEXT_BRIGHT_YELLOW "" -#define RTT_CTRL_TEXT_BRIGHT_BLUE "" -#define RTT_CTRL_TEXT_BRIGHT_MAGENTA "" -#define RTT_CTRL_TEXT_BRIGHT_CYAN "" -#define RTT_CTRL_TEXT_BRIGHT_WHITE "" - -#define RTT_CTRL_BG_BLACK "" -#define RTT_CTRL_BG_RED "" -#define RTT_CTRL_BG_GREEN "" -#define RTT_CTRL_BG_YELLOW "" -#define RTT_CTRL_BG_BLUE "" -#define RTT_CTRL_BG_MAGENTA "" -#define RTT_CTRL_BG_CYAN "" -#define RTT_CTRL_BG_WHITE "" - -#define RTT_CTRL_BG_BRIGHT_BLACK "" -#define RTT_CTRL_BG_BRIGHT_RED "" -#define RTT_CTRL_BG_BRIGHT_GREEN "" -#define RTT_CTRL_BG_BRIGHT_YELLOW "" -#define RTT_CTRL_BG_BRIGHT_BLUE "" -#define RTT_CTRL_BG_BRIGHT_MAGENTA "" -#define RTT_CTRL_BG_BRIGHT_CYAN "" -#define RTT_CTRL_BG_BRIGHT_WHITE "" - - -#endif - -/*************************** End of file ****************************/ diff --git a/segger/SEGGER_RTT_Conf.h b/segger/SEGGER_RTT_Conf.h deleted file mode 100644 index aef3b405..00000000 --- a/segger/SEGGER_RTT_Conf.h +++ /dev/null @@ -1,242 +0,0 @@ -/********************************************************************* -* SEGGER MICROCONTROLLER GmbH & Co. KG * -* Solutions for real time microcontroller applications * -********************************************************************** -* * -* (c) 2014 - 2016 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** -* * -* SEGGER RTT * Real Time Transfer for embedded targets * -* * -********************************************************************** -* * -* All rights reserved. * -* * -* * This software may in its unmodified form be freely redistributed * -* in source form. * -* * The source code may be modified, provided the source code * -* retains the above copyright notice, this list of conditions and * -* the following disclaimer. * -* * Modified versions of this software in source or linkable form * -* may not be distributed without prior consent of SEGGER. * -* * This software may only be used for communication with SEGGER * -* J-Link debug probes. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * -* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * -* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * -* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * -* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * -* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * -* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * -* DAMAGE. * -* * -********************************************************************** -* * -* RTT version: 5.12e * -* * -********************************************************************** ----------------------------------------------------------------------- -File : SEGGER_RTT_Conf.h -Purpose : Implementation of SEGGER real-time transfer (RTT) which - allows real-time communication on targets which support - debugger memory accesses while the CPU is running. ----------------------------END-OF-HEADER------------------------------ -*/ - -#ifndef SEGGER_RTT_CONF_H -#define SEGGER_RTT_CONF_H - -#ifdef __ICCARM__ - #include -#endif - -/********************************************************************* -* -* Defines, configurable -* -********************************************************************** -*/ - -#define SEGGER_RTT_MAX_NUM_UP_BUFFERS (2) // Max. number of up-buffers (T->H) available on this target (Default: 2) -#define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (2) // Max. number of down-buffers (H->T) available on this target (Default: 2) - -#define BUFFER_SIZE_UP (1024) // Size of the buffer for terminal output of target, up to host (Default: 1k) -#define BUFFER_SIZE_DOWN (16) // Size of the buffer for terminal input to target from host (Usually keyboard input) (Default: 16) - -#define SEGGER_RTT_PRINTF_BUFFER_SIZE (64u) // Size of buffer for RTT printf to bulk-send chars via RTT (Default: 64) - -#define SEGGER_RTT_MODE_DEFAULT SEGGER_RTT_MODE_NO_BLOCK_SKIP // Mode for pre-initialized terminal channel (buffer 0) - -// -// Target is not allowed to perform other RTT operations while string still has not been stored completely. -// Otherwise we would probably end up with a mixed string in the buffer. -// If using RTT from within interrupts, multiple tasks or multi processors, define the SEGGER_RTT_LOCK() and SEGGER_RTT_UNLOCK() function here. -// -// SEGGER_RTT_MAX_INTERRUPT_PRIORITY can be used in the sample lock routines on Cortex-M3/4. -// Make sure to mask all interrupts which can send RTT data, i.e. generate SystemView events, or cause task switches. -// When high-priority interrupts must not be masked while sending RTT data, SEGGER_RTT_MAX_INTERRUPT_PRIORITY needs to be adjusted accordingly. -// (Higher priority = lower priority number) -// Default value for embOS: 128u -// Default configuration in FreeRTOS: configMAX_SYSCALL_INTERRUPT_PRIORITY: ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) -// In case of doubt mask all interrupts: 0u -// - -#define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) // Interrupt priority to lock on SEGGER_RTT_LOCK on Cortex-M3/4 (Default: 0x20) - -/********************************************************************* -* -* RTT lock configuration for SEGGER Embedded Studio, -* Rowley CrossStudio and GCC -*/ -#if (defined __SES_ARM) || (defined __CROSSWORKS_ARM) || (defined __GNUC__) - #ifdef __ARM_ARCH_6M__ - #define SEGGER_RTT_LOCK() { \ - unsigned int LockState; \ - __asm volatile ("mrs %0, primask \n\t" \ - "mov r1, $1 \n\t" \ - "msr primask, r1 \n\t" \ - : "=r" (LockState) \ - : \ - : "r1" \ - ); - - #define SEGGER_RTT_UNLOCK() __asm volatile ("msr primask, %0 \n\t" \ - : \ - : "r" (LockState) \ - : \ - ); \ - } - - #elif (defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)) - #ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY - #define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) - #endif - #define SEGGER_RTT_LOCK() { \ - unsigned int LockState; \ - __asm volatile ("mrs %0, basepri \n\t" \ - "mov r1, %1 \n\t" \ - "msr basepri, r1 \n\t" \ - : "=r" (LockState) \ - : "i"(SEGGER_RTT_MAX_INTERRUPT_PRIORITY) \ - : "r1" \ - ); - - #define SEGGER_RTT_UNLOCK() __asm volatile ("msr basepri, %0 \n\t" \ - : \ - : "r" (LockState) \ - : \ - ); \ - } - - #elif defined(__ARM_ARCH_7A__) - #define SEGGER_RTT_LOCK() { \ - unsigned int LockState; \ - __asm volatile ("mrs r1, CPSR \n\t" \ - "mov %0, r1 \n\t" \ - "orr r1, r1, #0xC0 \n\t" \ - "msr CPSR_c, r1 \n\t" \ - : "=r" (LockState) \ - : \ - : "r1" \ - ); - - #define SEGGER_RTT_UNLOCK() __asm volatile ("mov r0, %0 \n\t" \ - "mrs r1, CPSR \n\t" \ - "bic r1, r1, #0xC0 \n\t" \ - "and r0, r0, #0xC0 \n\t" \ - "orr r1, r1, r0 \n\t" \ - "msr CPSR_c, r1 \n\t" \ - : \ - : "r" (LockState) \ - : "r0", "r1" \ - ); \ - } -#else - #define SEGGER_RTT_LOCK() - #define SEGGER_RTT_UNLOCK() - #endif -#endif - -/********************************************************************* -* -* RTT lock configuration for IAR EWARM -*/ -#ifdef __ICCARM__ - #if (defined (__ARM6M__) && (__CORE__ == __ARM6M__)) - #define SEGGER_RTT_LOCK() { \ - unsigned int LockState; \ - LockState = __get_PRIMASK(); \ - __set_PRIMASK(1); - - #define SEGGER_RTT_UNLOCK() __set_PRIMASK(LockState); \ - } - #elif ((defined (__ARM7EM__) && (__CORE__ == __ARM7EM__)) || (defined (__ARM7M__) && (__CORE__ == __ARM7M__))) - #ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY - #define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) - #endif - #define SEGGER_RTT_LOCK() { \ - unsigned int LockState; \ - LockState = __get_BASEPRI(); \ - __set_BASEPRI(SEGGER_RTT_MAX_INTERRUPT_PRIORITY); - - #define SEGGER_RTT_UNLOCK() __set_BASEPRI(LockState); \ - } - #endif -#endif - -/********************************************************************* -* -* RTT lock configuration for KEIL ARM -*/ -#ifdef __CC_ARM - #if (defined __TARGET_ARCH_6S_M) - #define SEGGER_RTT_LOCK() { \ - unsigned int LockState; \ - register unsigned char PRIMASK __asm( "primask"); \ - LockState = PRIMASK; \ - PRIMASK = 1u; \ - __schedule_barrier(); - - #define SEGGER_RTT_UNLOCK() PRIMASK = LockState; \ - __schedule_barrier(); \ - } - #elif (defined(__TARGET_ARCH_7_M) || defined(__TARGET_ARCH_7E_M)) - #ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY - #define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) - #endif - #define SEGGER_RTT_LOCK() { \ - unsigned int LockState; \ - register unsigned char BASEPRI __asm( "basepri"); \ - LockState = BASEPRI; \ - BASEPRI = SEGGER_RTT_MAX_INTERRUPT_PRIORITY; \ - __schedule_barrier(); - - #define SEGGER_RTT_UNLOCK() BASEPRI = LockState; \ - __schedule_barrier(); \ - } - #endif -#endif - -/********************************************************************* -* -* RTT lock configuration fallback -*/ -#ifndef SEGGER_RTT_LOCK - #define SEGGER_RTT_LOCK() // Lock RTT (nestable) (i.e. disable interrupts) -#endif - -#ifndef SEGGER_RTT_UNLOCK - #define SEGGER_RTT_UNLOCK() // Unlock RTT (nestable) (i.e. enable previous interrupt lock state) -#endif - -#endif -/*************************** End of file ****************************/ diff --git a/segger/SEGGER_RTT_SES.c b/segger/SEGGER_RTT_SES.c deleted file mode 100644 index e6634147..00000000 --- a/segger/SEGGER_RTT_SES.c +++ /dev/null @@ -1,76 +0,0 @@ -/********************************************************************* -* SEGGER MICROCONTROLLER GmbH & Co. KG * -* Solutions for real time microcontroller applications * -********************************************************************** -* * -* (c) 2014 - 2015 SEGGER Microcontroller GmbH & Co. KG * -* * -* www.segger.com Support: support@segger.com * -* * -********************************************************************** -* * -* All rights reserved. * -* * -* * This software may in its unmodified form be freely redistributed * -* in source form. * -* * The source code may be modified, provided the source code * -* retains the above copyright notice, this list of conditions and * -* the following disclaimer. * -* * Modified versions of this software in source or linkable form * -* may not be distributed without prior consent of SEGGER. * -* * This software may only be used for communication with SEGGER * -* J-Link debug probes. * -* * -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * -* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * -* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * -* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * -* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * -* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * -* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * -* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * -* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * -* DAMAGE. * -* * -********************************************************************** --------- END-OF-HEADER --------------------------------------------- -File : SEGGER_RTT_Syscalls_SES.c -Purpose : Reimplementation of printf, puts and - implementation of __putchar and __getchar using RTT in SES. - To use RTT for printf output, include this file in your - application. ----------------------------------------------------------------------- -*/ -#include "SEGGER_RTT.h" -#include "__libc.h" -#include -#include - -int printf(const char *fmt,...) { - char buffer[128]; - va_list args; - va_start (args, fmt); - int n = vsnprintf(buffer, sizeof(buffer), fmt, args); - SEGGER_RTT_Write(0, buffer, n); - va_end(args); - return n; -} - -int puts(const char *s) { - return SEGGER_RTT_WriteString(0, s); -} - -int __putchar(int x, __printf_tag_ptr ctx) { - (void)ctx; - SEGGER_RTT_Write(0, (char *)&x, 1); - return x; -} - -int __getchar() { - return SEGGER_RTT_WaitKey(); -} - -/****** End Of File *************************************************/ diff --git a/segger/flash_placement.xml b/segger/flash_placement.xml deleted file mode 100644 index 70d131ac..00000000 --- a/segger/flash_placement.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/segger/nRF52832_xxAA_MemoryMap.xml b/segger/nRF52832_xxAA_MemoryMap.xml deleted file mode 100644 index 809e2a1e..00000000 --- a/segger/nRF52832_xxAA_MemoryMap.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/segger/nRF52840_xxAA_MemoryMap.xml b/segger/nRF52840_xxAA_MemoryMap.xml deleted file mode 100644 index c37570c5..00000000 --- a/segger/nRF52840_xxAA_MemoryMap.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/segger/nRF_Target.js b/segger/nRF_Target.js deleted file mode 100644 index 20560af4..00000000 --- a/segger/nRF_Target.js +++ /dev/null @@ -1,19 +0,0 @@ -/***************************************************************************** - * SEGGER Microcontroller GmbH & Co. KG * - * Solutions for real time microcontroller applications * - ***************************************************************************** - * * - * (c) 2017 SEGGER Microcontroller GmbH & Co. KG * - * * - * Internet: www.segger.com Support: support@segger.com * - * * - *****************************************************************************/ - -function Reset() { - TargetInterface.resetAndStop(); -} - -function EnableTrace(traceInterfaceType) { - // TODO: Enable trace -} - diff --git a/segger/nrf52840_Registers.xml b/segger/nrf52840_Registers.xml deleted file mode 100644 index 39f861f9..00000000 --- a/segger/nrf52840_Registers.xml +++ /dev/null @@ -1,22310 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/segger/thumb_crt0.s b/segger/thumb_crt0.s deleted file mode 100644 index 71820cb5..00000000 --- a/segger/thumb_crt0.s +++ /dev/null @@ -1,420 +0,0 @@ -// SEGGER Embedded Studio, runtime support. -// -// Copyright (c) 2014-2017 SEGGER Microcontroller GmbH & Co KG -// Copyright (c) 2001-2017 Rowley Associates Limited. -// -// This file may be distributed under the terms of the License Agreement -// provided with this software. -// -// THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING THE -// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -// -// -// Preprocessor Definitions -// ------------------------ -// APP_ENTRY_POINT -// -// Defines the application entry point function, if undefined this setting -// defaults to "main". -// -// INITIALIZE_STACK -// -// If defined, the contents of the stack will be initialized to a the -// value 0xCC. -// -// INITIALIZE_SECONDARY_SECTIONS -// -// If defined, the .data2, .text2, .rodata2 and .bss2 sections will be initialized. -// -// INITIALIZE_TCM_SECTIONS -// -// If defined, the .data_tcm, .text_tcm, .rodata_tcm and .bss_tcm sections -// will be initialized. -// -// INITIALIZE_USER_SECTIONS -// -// If defined, the function InitializeUserMemorySections will be called prior -// to entering main in order to allow the user to initialize any user defined -// memory sections. -// -// FULL_LIBRARY -// -// If defined then -// - argc, argv are setup by the debug_getargs. -// - the exit symbol is defined and executes on return from main. -// - the exit symbol calls destructors, atexit functions and then debug_exit. -// -// If not defined then -// - argc and argv are zero. -// - the exit symbol is defined, executes on return from main and loops -// - -#ifndef APP_ENTRY_POINT -#define APP_ENTRY_POINT main -#endif - -#ifndef ARGSSPACE -#define ARGSSPACE 128 -#endif - .syntax unified - - .global _start - .extern APP_ENTRY_POINT - .global exit - .weak exit - -#ifdef INITIALIZE_USER_SECTIONS - .extern InitializeUserMemorySections -#endif - - .section .init, "ax" - .code 16 - .align 2 - .thumb_func - -_start: - /* Set up main stack if size > 0 */ - ldr r1, =__stack_end__ - ldr r0, =__stack_start__ - subs r2, r1, r0 - beq 1f -#ifdef __ARM_EABI__ - movs r2, #0x7 - bics r1, r2 -#endif - mov sp, r1 -#ifdef INITIALIZE_STACK - movs r2, #0xCC - ldr r0, =__stack_start__ - bl memory_set -#endif -1: - - /* Set up process stack if size > 0 */ - ldr r1, =__stack_process_end__ - ldr r0, =__stack_process_start__ - subs r2, r1, r0 - beq 1f -#ifdef __ARM_EABI__ - movs r2, #0x7 - bics r1, r2 -#endif - msr psp, r1 - movs r2, #2 - msr control, r2 -#ifdef INITIALIZE_STACK - movs r2, #0xCC - bl memory_set -#endif -1: - - /* Copy initialized memory sections into RAM (if necessary). */ - ldr r0, =__data_load_start__ - ldr r1, =__data_start__ - ldr r2, =__data_end__ - bl memory_copy - ldr r0, =__text_load_start__ - ldr r1, =__text_start__ - ldr r2, =__text_end__ - bl memory_copy - ldr r0, =__fast_load_start__ - ldr r1, =__fast_start__ - ldr r2, =__fast_end__ - bl memory_copy - ldr r0, =__ctors_load_start__ - ldr r1, =__ctors_start__ - ldr r2, =__ctors_end__ - bl memory_copy - ldr r0, =__dtors_load_start__ - ldr r1, =__dtors_start__ - ldr r2, =__dtors_end__ - bl memory_copy - ldr r0, =__rodata_load_start__ - ldr r1, =__rodata_start__ - ldr r2, =__rodata_end__ - bl memory_copy - ldr r0, =__tdata_load_start__ - ldr r1, =__tdata_start__ - ldr r2, =__tdata_end__ - bl memory_copy -#ifdef INITIALIZE_SECONDARY_SECTIONS - ldr r0, =__data2_load_start__ - ldr r1, =__data2_start__ - ldr r2, =__data2_end__ - bl memory_copy - ldr r0, =__text2_load_start__ - ldr r1, =__text2_start__ - ldr r2, =__text2_end__ - bl memory_copy - ldr r0, =__rodata2_load_start__ - ldr r1, =__rodata2_start__ - ldr r2, =__rodata2_end__ - bl memory_copy -#endif /* #ifdef INITIALIZE_SECONDARY_SECTIONS */ -#ifdef INITIALIZE_TCM_SECTIONS - ldr r0, =__data_tcm_load_start__ - ldr r1, =__data_tcm_start__ - ldr r2, =__data_tcm_end__ - bl memory_copy - ldr r0, =__text_tcm_load_start__ - ldr r1, =__text_tcm_start__ - ldr r2, =__text_tcm_end__ - bl memory_copy - ldr r0, =__rodata_tcm_load_start__ - ldr r1, =__rodata_tcm_start__ - ldr r2, =__rodata_tcm_end__ - bl memory_copy -#endif /* #ifdef INITIALIZE_TCM_SECTIONS */ - - /* Zero the bss. */ - ldr r0, =__bss_start__ - ldr r1, =__bss_end__ - movs r2, #0 - bl memory_set - ldr r0, =__tbss_start__ - ldr r1, =__tbss_end__ - movs r2, #0 - bl memory_set -#ifdef INITIALIZE_SECONDARY_SECTIONS - ldr r0, =__bss2_start__ - ldr r1, =__bss2_end__ - mov r2, #0 - bl memory_set -#endif /* #ifdef INITIALIZE_SECONDARY_SECTIONS */ -#ifdef INITIALIZE_TCM_SECTIONS - ldr r0, =__bss_tcm_start__ - ldr r1, =__bss_tcm_end__ - mov r2, #0 - bl memory_set -#endif /* #ifdef INITIALIZE_TCM_SECTIONS */ - - /* Initialize the heap */ - ldr r0, = __heap_start__ - ldr r1, = __heap_end__ - subs r1, r1, r0 - cmp r1, #8 - blt 1f - movs r2, #0 - str r2, [r0] - adds r0, r0, #4 - str r1, [r0] -1: - -#ifdef INITIALIZE_USER_SECTIONS - ldr r2, =InitializeUserMemorySections - blx r2 -#endif - - /* Call constructors */ - ldr r0, =__ctors_start__ - ldr r1, =__ctors_end__ -ctor_loop: - cmp r0, r1 - beq ctor_end - ldr r2, [r0] - adds r0, #4 - push {r0-r1} - blx r2 - pop {r0-r1} - b ctor_loop -ctor_end: - - /* Setup initial call frame */ - movs r0, #0 - mov lr, r0 - mov r12, sp - - .type start, function -start: - /* Jump to application entry point */ -#ifdef FULL_LIBRARY - movs r0, #ARGSSPACE - ldr r1, =args - ldr r2, =debug_getargs - blx r2 - ldr r1, =args -#else - movs r0, #0 - movs r1, #0 -#endif - ldr r2, =APP_ENTRY_POINT - blx r2 - - .thumb_func -exit: -#ifdef FULL_LIBRARY - mov r5, r0 // save the exit parameter/return result - - /* Call destructors */ - ldr r0, =__dtors_start__ - ldr r1, =__dtors_end__ -dtor_loop: - cmp r0, r1 - beq dtor_end - ldr r2, [r0] - add r0, #4 - push {r0-r1} - blx r2 - pop {r0-r1} - b dtor_loop -dtor_end: - - /* Call atexit functions */ - ldr r2, =_execute_at_exit_fns - blx r2 - - /* Call debug_exit with return result/exit parameter */ - mov r0, r5 - ldr r2, =debug_exit - blx r2 -#endif - - /* Returned from application entry point, loop forever. */ -exit_loop: - b exit_loop - - .thumb_func -memory_copy: - cmp r0, r1 - beq 2f - subs r2, r2, r1 - beq 2f -1: - ldrb r3, [r0] - adds r0, r0, #1 - strb r3, [r1] - adds r1, r1, #1 - subs r2, r2, #1 - bne 1b -2: - bx lr - - .thumb_func -memory_set: - cmp r0, r1 - beq 1f - strb r2, [r0] - adds r0, r0, #1 - b memory_set -1: - bx lr - - // default C/C++ library helpers - -.macro HELPER helper_name - .section .text.\helper_name, "ax", %progbits - .global \helper_name - .weak \helper_name -\helper_name: - .thumb_func -.endm - -.macro JUMPTO name -#if defined(__thumb__) && !defined(__thumb2__) - mov r12, r0 - ldr r0, =\name - push {r0} - mov r0, r12 - pop {pc} -#else - b \name -#endif -.endm - -HELPER __aeabi_read_tp - ldr r0, =__tbss_start__-8 - bx lr -HELPER __heap_lock - bx lr -HELPER __heap_unlock - bx lr -HELPER __printf_lock - bx lr -HELPER __printf_unlock - bx lr -HELPER __scanf_lock - bx lr -HELPER __scanf_unlock - bx lr -HELPER __debug_io_lock - bx lr -HELPER __debug_io_unlock - bx lr -HELPER abort - b . -HELPER __assert - b . -HELPER __aeabi_assert - b . -HELPER __cxa_pure_virtual - b . -HELPER __cxa_guard_acquire - ldr r3, [r0] -#if defined(__thumb__) && !defined(__thumb2__) - movs r0, #1 - tst r3, r0 -#else - tst r3, #1 -#endif - beq 1f - movs r0, #0 - bx lr -1: - movs r0, #1 - bx lr -HELPER __cxa_guard_release - movs r3, #1 - str r3, [r0] - bx lr -HELPER __cxa_guard_abort - bx lr -HELPER __sync_synchronize - bx lr -HELPER __getchar - JUMPTO debug_getchar -HELPER __putchar - JUMPTO debug_putchar -HELPER __open - JUMPTO debug_fopen -HELPER __close - JUMPTO debug_fclose -HELPER __write - mov r3, r0 - mov r0, r1 - movs r1, #1 - JUMPTO debug_fwrite -HELPER __read - mov r3, r0 - mov r0, r1 - movs r1, #1 - JUMPTO debug_fread -HELPER __seek - push {r4, lr} - mov r4, r0 - bl debug_fseek - cmp r0, #0 - bne 1f - mov r0, r4 - bl debug_ftell - pop {r4, pc} -1: - ldr r0, =-1 - pop {r4, pc} - // char __user_locale_name_buffer[]; - .section .bss.__user_locale_name_buffer, "aw", %nobits - .global __user_locale_name_buffer - .weak __user_locale_name_buffer - __user_locale_name_buffer: - .word 0x0 - -#ifdef FULL_LIBRARY - .bss -args: - .space ARGSSPACE -#endif - - /* Setup attibutes of stack and heap sections so they don't take up room in the elf file */ - .section .stack, "wa", %nobits - .section .stack_process, "wa", %nobits - .section .heap, "wa", %nobits - From 859a721b41654a2212f50d0423162aa344e5ef2f Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 19 Dec 2025 18:11:08 +0700 Subject: [PATCH 18/84] remove git submodules from uf2 version --- CMakeLists.txt | 101 +++++++++++++++++++++---------------------------- Makefile | 4 +- src/screen.c | 6 +-- 3 files changed, 47 insertions(+), 64 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae9b9cfd..401acc25 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,40 +20,26 @@ endif () include(${CMAKE_CURRENT_LIST_DIR}/cmake/toolchain/arm_gcc.cmake) - -project(Adafruit_nRF52_Bootloader C ASM) - set(NRFX_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/nrfx) set(SDK11_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/sdk11/components) set(SDK_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/sdk/components) set(SOFTDEVICE_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/softdevice) +set(MBR_HEX ${SOFTDEVICE_DIR}/mbr/hex/mbr_nrf52_2.4.1_mbr.hex) set(TINYUSB_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/tinyusb/src) set(UF2CONV_PY ${CMAKE_CURRENT_LIST_DIR}/lib/uf2/utils/uf2conv.py) set(UF2_FAMILY_ID_BOOTLOADER 0xd663823c) -if (NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "") - set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "Build type" FORCE) -endif () - #------------------- # Bootloader #------------------- -set(CMAKE_EXECUTABLE_SUFFIX .elf) -add_executable(bootloader) - -# SD_VERSION can be overwritten by board.cmake -if(NOT DEFINED SD_VERSION) - if(MCU_VARIANT STREQUAL "nrf52833") - set(SD_VERSION 7.3.0) - else() - set(SD_VERSION 6.1.1) - endif() +if (NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "") + set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "Build type" FORCE) endif () -set(MBR_HEX ${SOFTDEVICE_DIR}/mbr/hex/mbr_nrf52_2.4.1_mbr.hex) - -target_sources(bootloader PUBLIC +project(Adafruit_nRF52_Bootloader C ASM) +set(CMAKE_EXECUTABLE_SUFFIX .elf) +add_executable(bootloader # src src/dfu_ble_svc.c src/dfu_init.c @@ -86,7 +72,7 @@ target_sources(bootloader PUBLIC ${SDK_DIR}/libraries/hci/hci_slip.c ${SDK_DIR}/libraries/hci/hci_transport.c ${SDK_DIR}/libraries/util/nrf_assert.c - # ASM + # startup ${NRFX_DIR}/mdk/gcc_startup_${MCU_VARIANT}.S ) target_include_directories(bootloader PUBLIC @@ -122,28 +108,9 @@ target_include_directories(bootloader PUBLIC ${SOFTDEVICE_DIR}/mbr/headers ) -# Debug option +# Debug linker file if (CMAKE_BUILD_TYPE STREQUAL "Debug") - # TODO not work yet, also need to add segger rtt, DFU_APP_DATA_RESERVED=0, BOOTLOADER_REGION_START=0xED000 set(LD_FILE ${CMAKE_CURRENT_LIST_DIR}/linker/${MCU_VARIANT}_debug.ld) - - target_sources(bootloader PUBLIC - lib/SEGGER_RTT/RTT/SEGGER_RTT.c - ) - target_include_directories(bootloader PUBLIC - lib/SEGGER_RTT/RTT - ) - target_compile_definitions(bootloader PUBLIC - CFG_DEBUG - SEGGER_RTT_MODE_DEFAULT=SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL - DFU_APP_DATA_RESERVED=0 - ) - - if (MCU_VARIANT STREQUAL "nrf52840") - target_compile_definitions(bootloader PUBLIC BOOTLOADER_REGION_START=0xEA000) - else () - target_compile_definitions(bootloader PUBLIC BOOTLOADER_REGION_START=0x6D000) - endif () else () set(LD_FILE ${CMAKE_CURRENT_LIST_DIR}/linker/${MCU_VARIANT}.ld) endif () @@ -222,6 +189,16 @@ endif () # MCU Variant differences # Supported are: nrf52 (nrf52832), nrf52833, nrf52840 #---------------------------------------------------- + +# SD_VERSION can be overwritten by board.cmake +if(NOT DEFINED SD_VERSION) + if(MCU_VARIANT STREQUAL "nrf52833") + set(SD_VERSION 7.3.0) + else() + set(SD_VERSION 6.1.1) + endif() +endif () + if (MCU_VARIANT STREQUAL "nrf52") set(SD_NAME s132) set(DFU_DEV_REV 0xADAF) @@ -246,17 +223,31 @@ else () message(FATAL_ERROR "MCU_VARIANT ${MCU_VARIANT} is unknown") endif () -string(TOUPPER ${SD_NAME} SD_NAME_UPPER) -target_compile_definitions(bootloader PUBLIC ${SD_NAME_UPPER}) - set(SD_FILENAME ${SD_NAME}_nrf52_${SD_VERSION}) set(SD_HEX ${SOFTDEVICE_DIR}/${SD_FILENAME}/${SD_FILENAME}_softdevice.hex) +string(TOUPPER ${SD_NAME} SD_NAME_UPPER) +target_compile_definitions(bootloader PUBLIC ${SD_NAME_UPPER}) target_include_directories(bootloader PUBLIC ${SOFTDEVICE_DIR}/${SD_FILENAME}/${SD_FILENAME}_API/include ) -if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") +# debug build +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + # TODO not work yet, also need to add segger rtt, DFU_APP_DATA_RESERVED=0, BOOTLOADER_REGION_START=0xED000 + target_sources(bootloader PUBLIC lib/SEGGER_RTT/RTT/SEGGER_RTT.c) + target_include_directories(bootloader PUBLIC lib/SEGGER_RTT/RTT) + target_compile_definitions(bootloader PUBLIC + CFG_DEBUG + SEGGER_RTT_MODE_DEFAULT=SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL + DFU_APP_DATA_RESERVED=0 + ) + if (MCU_VARIANT STREQUAL "nrf52840") + target_compile_definitions(bootloader PUBLIC BOOTLOADER_REGION_START=0xEA000) + else () + target_compile_definitions(bootloader PUBLIC BOOTLOADER_REGION_START=0x6D000) + endif () +else () target_compile_definitions(bootloader PUBLIC DFU_APP_DATA_RESERVED=${DFU_APP_DATA_RESERVED}) endif () @@ -273,13 +264,6 @@ endif() execute_process(COMMAND git describe --dirty --always --tags OUTPUT_VARIABLE GIT_VERSION) string(STRIP ${GIT_VERSION} GIT_VERSION) -execute_process(COMMAND bash "-c" "git submodule status | cut -d\" \" -f3,4 | paste -s -d\" \" -" - OUTPUT_VARIABLE GIT_SUBMODULE_VERSIONS - ) -string(STRIP ${GIT_SUBMODULE_VERSIONS} GIT_SUBMODULE_VERSIONS) -string(REPLACE ../ "" GIT_SUBMODULE_VERSIONS ${GIT_SUBMODULE_VERSIONS}) -string(REPLACE lib/ "" GIT_SUBMODULE_VERSIONS ${GIT_SUBMODULE_VERSIONS}) - string(REPLACE "-" ";" RELEASE_VERSION ${GIT_VERSION}) list(GET RELEASE_VERSION 0 RELEASE_VERSION) string(REPLACE "." ";" RELEASE_VERSION ${RELEASE_VERSION}) @@ -288,11 +272,10 @@ list(GET RELEASE_VERSION 1 RELEASE_VERSION_MINOR) list(GET RELEASE_VERSION 2 RELEASE_VERSION_PATCH) math(EXPR MK_BOOTLOADER_VERSION "(${RELEASE_VERSION_MAJOR} << 16) + (${RELEASE_VERSION_MINOR} << 8) + ${RELEASE_VERSION_PATCH}") -cmake_print_variables(GIT_VERSION GIT_SUBMODULE_VERSIONS MK_BOOTLOADER_VERSION) +cmake_print_variables(GIT_VERSION MK_BOOTLOADER_VERSION) target_compile_definitions(bootloader PUBLIC - UF2_VERSION_BASE="${GIT_VERSION}" - UF2_VERSION="${GIT_VERSION} - ${GIT_SUBMODULE_VERSIONS}" + UF2_VERSION="${GIT_VERSION}" BLEDIS_FW_VERSION="${GIT_VERSION} ${SD_NAME} ${SD_VERSION}" MK_BOOTLOADER_VERSION=${MK_BOOTLOADER_VERSION} ) @@ -324,11 +307,11 @@ add_custom_target(flash-uf2 add_custom_target(flash-sd DEPENDS flash-mbr - COMMAND ${NRFJPROG} --program ${SD_HEX} --verify --sectorerase -f nrf52 --reset + COMMAND ${NRFJPROG} --program ${SD_HEX} --verify --sectorerase -f nrf52 ) add_custom_target(flash-mbr - COMMAND ${NRFJPROG} --program ${MBR_HEX} --verify --sectorerase -f nrf52 --reset + COMMAND ${NRFJPROG} --program ${MBR_HEX} --verify --sectorerase -f nrf52 ) add_custom_target(flash-erase @@ -336,7 +319,9 @@ add_custom_target(flash-erase ) add_custom_target(flash-all - DEPENDS flash-erase flash-sd flash-bootloader + DEPENDS bootloader flash-erase + COMMAND ${NRFJPROG} --program ${SD_HEX} --verify --sectorerase -f nrf52 + COMMAND ${NRFJPROG} --program $ --verify --sectoranduicrerase -f nrf52 --reset ) # flash skip crc magic ( app valid = 0x0001, crc = 0x0000 ) diff --git a/Makefile b/Makefile index 326a709e..dba3c836 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,6 @@ else endif GIT_VERSION := $(shell git describe --dirty --always --tags) -GIT_SUBMODULE_VERSIONS := $(shell git submodule status | cut -d" " -f3,4 | paste -s -d" " -) # compiled file name OUT_NAME = $(BOARD)_bootloader-$(GIT_VERSION) @@ -324,8 +323,7 @@ ifneq ($(USE_NFCT),yes) endif CFLAGS += -DSOFTDEVICE_PRESENT -CFLAGS += -DUF2_VERSION_BASE='"$(GIT_VERSION)"' -CFLAGS += -DUF2_VERSION='"$(GIT_VERSION) $(GIT_SUBMODULE_VERSIONS)"' +CFLAGS += -DUF2_VERSION='"$(GIT_VERSION)"' CFLAGS += -DBLEDIS_FW_VERSION='"$(GIT_VERSION) $(SD_NAME) $(SD_VERSION)"' _VER = $(subst ., ,$(word 1, $(subst -, ,$(GIT_VERSION)))) diff --git a/src/screen.c b/src/screen.c index 8931b550..f51330a3 100644 --- a/src/screen.c +++ b/src/screen.c @@ -242,12 +242,12 @@ void screen_draw_drag(void) { drawBar(52, 55, COLOR_BLUE); drawBar(107, 14, COLOR_ORANGE); - // Center UF2_PRODUCT_NAME and UF2_VERSION_BASE. + // Center UF2_PRODUCT_NAME and UF2_VERSION. int name_x = (DISPLAY_WIDTH - CHAR4_KERNED_WIDTH * (int) strlen(DISPLAY_TITLE)) / 2; print4(name_x >= 0 ? name_x : 0, 5, COLOR_WHITE, DISPLAY_TITLE); - int version_x = (DISPLAY_WIDTH - 6 * (int) strlen(UF2_VERSION_BASE)) / 2; - print(version_x >= 0 ? version_x : 0, 40, COLOR_PURPLE, UF2_VERSION_BASE); + int version_x = (DISPLAY_WIDTH - 6 * (int) strlen(UF2_VERSION)) / 2; + print(version_x >= 0 ? version_x : 0, 40, COLOR_PURPLE, UF2_VERSION); // TODO the reset should be center as well print(23, 110, 1, "circuitpython.org"); From 24768bcd9a97367e349a965fd131de8f002c143a Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 19 Dec 2025 22:50:49 +0700 Subject: [PATCH 19/84] clean up --- .clang-format | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 6 ++-- src/main.c | 67 ++++++++++++++++------------------ 3 files changed, 133 insertions(+), 39 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..2cd0e155 --- /dev/null +++ b/.clang-format @@ -0,0 +1,99 @@ +--- +Language: Cpp +BasedOnStyle: LLVM +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false +AlignConsecutiveBitFields: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false +AlignConsecutiveDeclarations: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: true + AcrossComments: false +AlignConsecutiveShortCaseStatements: + Enabled: true + AcrossEmptyLines: true + AcrossComments: true + AlignCaseColons: false +AlignEscapedNewlines: LeftWithLastLine +AlignOperands: true +AlignTrailingComments: + Kind: Always + OverEmptyLines: 2 +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseExpressionOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: true + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BracedInitializerIndentWidth: 2 +BreakBeforeBinaryOperators: None +BreakConstructorInitializers: AfterColon +BreakConstructorInitializersBeforeComma: false +ContinuationIndentWidth: 2 +ColumnLimit: 120 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +Cpp11BracedListStyle: true +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^<.*' + Priority: 1 + - Regex: '^".*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentPPDirectives: BeforeHash +InsertBraces: true +IndentCaseLabels: true +InsertNewlineAtEOF: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +PenaltyBreakBeforeFirstCallParameter: 1000000 +PenaltyBreakOpenParenthesis: 1000000 +PPIndentWidth: 2 +QualifierAlignment: Custom +QualifierOrder: ['static', 'const', 'volatile', 'restrict', 'type'] +SpaceAfterTemplateKeyword: false +SpaceBeforeRangeBasedForLoopColon: false +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SortIncludes: false +TabWidth: 2 +... diff --git a/README.md b/README.md index 4c7066f3..8fda77a6 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ make BOARD=feather_nrf52840_express all For the list of supported boards, run `make` without `BOARD=` : ``` -$ make +make You must provide a BOARD parameter with 'BOARD=' Supported boards are: feather_nrf52840_express feather_nrf52840_express pca10056 Makefile:90: *** BOARD not defined. Stop @@ -213,7 +213,7 @@ make BOARD=feather_nrf52840_express flash-mbr If you get the following error ... ``` -$ make BOARD=feather_nrf52840_express all +make BOARD=feather_nrf52840_express all Compiling file: main.c /bin/sh: /usr/bin/arm-none-eabi-gcc: No such file or directory make: *** [_build/main.o] Error 127 @@ -222,7 +222,7 @@ make: *** [_build/main.o] Error 127 ... you may need to pass the location of the GCC ARM toolchain binaries to `make` using the variable `CROSS_COMPILE` as below: ``` -$ make CROSS_COMPILE=/opt/gcc-arm-none-eabi-9-2019-q4-major/bin/arm-none-eabi- BOARD=feather_nrf52832 all +make CROSS_COMPILE=/opt/gcc-arm-none-eabi-9-2019-q4-major/bin/arm-none-eabi- BOARD=feather_nrf52832 all ``` For other compile errors, check the gcc version with `arm-none-eabi-gcc --version` to insure it is at least 9.x. diff --git a/src/main.c b/src/main.c index a4c0a705..9ebc9e99 100644 --- a/src/main.c +++ b/src/main.c @@ -39,13 +39,8 @@ #include #include "nrfx.h" -#include "nrf_clock.h" #include "nrfx_power.h" -#include "nrfx_pwm.h" - -#include "nordic_common.h" #include "sdk_common.h" -#include "dfu_transport.h" #include "bootloader.h" #include "bootloader_util.h" @@ -53,10 +48,7 @@ #include "nrf_soc.h" #include "nrf_nvic.h" #include "app_error.h" -#include "nrf_gpio.h" #include "ble.h" -#include "nrf.h" -#include "ble_hci.h" #include "app_scheduler.h" #include "nrf_error.h" @@ -64,13 +56,10 @@ #include "pstorage_platform.h" #include "nrf_mbr.h" -#include "pstorage.h" -#include "nrfx_nvmc.h" #ifdef NRF_USBD #include "uf2/uf2.h" -#include "nrf_usbd.h" #include "tusb.h" void usb_init(bool cdc_only); @@ -250,14 +239,17 @@ static void check_dfu_mode(void) { (((*dbl_reset_mem) == DFU_DBL_RESET_MAGIC) && reason_reset_pin); // Clear GPREGRET if it is our values - if (dfu_start || dfu_skip) NRF_POWER->GPREGRET = 0; + if (dfu_start || dfu_skip) { + NRF_POWER->GPREGRET = 0; + } // skip dfu entirely - if (dfu_skip) return; + if (dfu_skip) { + return; + } /*------------- Determine DFU mode (Serial, OTA, FRESET or normal) -------------*/ - // DFU button pressed - dfu_start = dfu_start || button_pressed(BUTTON_DFU); + dfu_start = dfu_start || button_pressed(BUTTON_DFU); // DFU button pressed // DFU + FRESET are pressed --> OTA _ota_dfu = _ota_dfu || (button_pressed(BUTTON_DFU) && button_pressed(BUTTON_FRESET)); @@ -265,16 +257,16 @@ static void check_dfu_mode(void) { bool const valid_app = bootloader_app_is_valid(); bool const just_start_app = valid_app && !dfu_start && (*dbl_reset_mem) == DFU_DBL_RESET_APP; - if (!just_start_app && APP_ASKS_FOR_SINGLE_TAP_RESET()) dfu_start = 1; + if (!just_start_app && APP_ASKS_FOR_SINGLE_TAP_RESET()) { + dfu_start = 1; + } // App mode: Double Reset detection or DFU startup for nrf52832 if (!(just_start_app || dfu_start || !valid_app)) { #ifdef NRF52832_XXAA /* Even DFU is not active, we still force an 1000 ms dfu serial mode when startup * to support auto programming from Arduino IDE - * - * Note: Double Reset WONT work with nrf52832 since all its SRAM got cleared with GPIO reset. - */ + * Note: Double Reset WONT work with nrf52832 since all its SRAM got cleared with GPIO reset. */ bootloader_dfu_start(false, DFU_SERIAL_STARTUP_INTERVAL, false); #else // Note: RESETREAS is not clear by bootloader, it should be cleared by application upon init() @@ -323,6 +315,10 @@ static void check_dfu_mode(void) { } } +//--------------------------------------------------------------------+ +// BLE +//--------------------------------------------------------------------+ + // Initializes the SoftDevice by following SD specs section // "Master Boot Record and SoftDevice initializaton procedure" static uint32_t ble_stack_init(void) { @@ -389,23 +385,6 @@ static uint32_t ble_stack_init(void) { return NRF_SUCCESS; } -//--------------------------------------------------------------------+ -// Error Handler -//--------------------------------------------------------------------+ -void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info) { - volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ - if ((*ARM_CM_DHCSR) & 1UL) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ - NVIC_SystemReset(); -} - -void assert_nrf_callback(uint16_t line_num, uint8_t const* p_file_name) { - app_error_fault_handler(0xDEADBEEF, 0, 0); -} - -/*------------------------------------------------------------------*/ -/* SoftDevice Event handler - *------------------------------------------------------------------*/ - // Process BLE event from SD uint32_t proc_ble(void) { __ALIGN(4) uint8_t ev_buf[BLE_EVT_LEN_MAX(BLEGATT_ATT_MTU_MAX)]; @@ -479,6 +458,22 @@ void SD_EVT_IRQHandler(void) { app_sched_event_put(NULL, 0, proc_sd_task); } +//--------------------------------------------------------------------+ +// Error Handler +//--------------------------------------------------------------------+ +void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info) { + volatile uint32_t *ARM_CM_DHCSR = ((volatile uint32_t *)0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ + if ((*ARM_CM_DHCSR) & 1UL) { + __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ + } + NVIC_SystemReset(); +} + +void assert_nrf_callback(uint16_t line_num, const uint8_t *p_file_name) { + app_error_fault_handler(0xDEADBEEF, 0, 0); +} + + //--------------------------------------------------------------------+ // RTT printf retarget for Debug //--------------------------------------------------------------------+ From 951a93abb4fd7106fb54dda7d7e4f7b0b845d5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Ma=C5=82ek?= <10601033+piotrmalek@users.noreply.github.com> Date: Wed, 14 Jan 2026 11:32:04 +0100 Subject: [PATCH 20/84] Improve bootloader version parsing from GIT_VERSION --- Makefile | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index dba3c836..5af9ac41 100644 --- a/Makefile +++ b/Makefile @@ -326,8 +326,26 @@ CFLAGS += -DSOFTDEVICE_PRESENT CFLAGS += -DUF2_VERSION='"$(GIT_VERSION)"' CFLAGS += -DBLEDIS_FW_VERSION='"$(GIT_VERSION) $(SD_NAME) $(SD_VERSION)"' -_VER = $(subst ., ,$(word 1, $(subst -, ,$(GIT_VERSION)))) -CFLAGS += -DMK_BOOTLOADER_VERSION='($(word 1,$(_VER)) << 16) + ($(word 2,$(_VER)) << 8) + $(word 3,$(_VER))' +# Extract semantic version numbers (MAJOR.MINOR.PATCH) from GIT_VERSION. +# Supports tags like: +# v1.2.3 +# 1.2.3 +# 1.2.3-147-gd71abcd +# If the version string does not match MAJOR.MINOR.PATCH, defaults to 0.0.0. +_VER3 := $(shell echo "$(GIT_VERSION)" | sed -E 's/^v?([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 \2 \3/; t; s/.*/0 0 0/') + +# Split extracted version into individual numeric components +_VER_MAJ := $(word 1,$(_VER3)) +_VER_MIN := $(word 2,$(_VER3)) +_VER_PAT := $(word 3,$(_VER3)) + +# Pack MAJOR.MINOR.PATCH into a single 32-bit integer: +# [31:16] MAJOR +# [15:8] MINOR +# [7:0] PATCH +# This value is stored in the bootloader version register and must be a valid C integer constant. +CFLAGS += -DMK_BOOTLOADER_VERSION='((($(_VER_MAJ)<<16)|($(_VER_MIN)<<8)|($(_VER_PAT))))' + # Debug option use RTT for printf ifeq ($(DEBUG), 1) From f5ab453f75dbd2b1f6dde58611f5907b40e206b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 00:31:39 -0300 Subject: [PATCH 21/84] Allow BLE characteristics to support both WriteWithoutResponse (for compatibility with Nordic DFU apps) and WriteWithResponse (for improved reliability) --- lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c b/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c index ea716cb0..2a4ede8e 100644 --- a/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c +++ b/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c @@ -17,6 +17,7 @@ #include #include "sdk_common.h" +#define DFU_PKT_ENABLE_WR_WO_RESPONSE_ONLY 0 /**< Enable Write Without Response on Data Endpoint ONLY (otherwise both With and Without response will be supported)*/ #define MAX_DFU_PKT_LEN 20 /**< Maximum length (in bytes) of the DFU Packet characteristic. */ #define PKT_START_DFU_PARAM_LEN 2 /**< Length (in bytes) of the parameters for Packet Start DFU Request. */ #define PKT_INIT_DFU_PARAM_LEN 2 /**< Length (in bytes) of the parameters for Packet Init DFU Request. */ @@ -57,7 +58,12 @@ static uint32_t dfu_pkt_char_add(ble_dfu_t * const p_dfu) memset(&char_md, 0, sizeof(char_md)); +#if DFU_PKT_ENABLE_WR_WO_RESPONSE_ONLY char_md.char_props.write_wo_resp = 1; +#else + char_md.char_props.write = 1; // For those improved apps that can take advantage of ACK + char_md.char_props.write_wo_resp = 1; // Nordic IOS DFU app REQUIRES Write Without Response */ +#endif char_md.p_char_user_desc = NULL; char_md.p_char_pf = NULL; char_md.p_user_desc_md = NULL; From 2910556adc5960618ca8adf5c5b919f6669feb83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 00:36:14 -0300 Subject: [PATCH 22/84] Wait until notification queue has enough room so no notifications of completed operations to the DFU app are lost --- .../ble/ble_services/ble_dfu/ble_dfu.c | 136 ++++++++++-------- 1 file changed, 77 insertions(+), 59 deletions(-) diff --git a/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c b/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c index 2a4ede8e..685fdea1 100644 --- a/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c +++ b/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c @@ -561,29 +561,35 @@ uint32_t ble_dfu_bytes_rcvd_report(ble_dfu_t * p_dfu, uint32_t num_of_firmware_b return NRF_ERROR_INVALID_STATE; } - ble_gatts_hvx_params_t hvx_params; - uint16_t index = 0; - - // Encode the Op Code. - m_notif_buffer[index++] = OP_CODE_RESPONSE; - - // Encode the Reqest Op Code. - m_notif_buffer[index++] = OP_CODE_IMAGE_SIZE_REQ; - - // Encode the Response Value. - m_notif_buffer[index++] = (uint8_t)BLE_DFU_RESP_VAL_SUCCESS; - - index += uint32_encode(num_of_firmware_bytes_rcvd, &m_notif_buffer[index]); - - memset(&hvx_params, 0, sizeof(hvx_params)); - - hvx_params.handle = p_dfu->dfu_ctrl_pt_handles.value_handle; - hvx_params.type = BLE_GATT_HVX_NOTIFICATION; - hvx_params.offset = 0; - hvx_params.p_len = &index; - hvx_params.p_data = m_notif_buffer; - - return sd_ble_gatts_hvx(p_dfu->conn_handle, &hvx_params); + uint32_t err_code; + do { + + ble_gatts_hvx_params_t hvx_params; + uint16_t index = 0; + + // Encode the Op Code. + m_notif_buffer[index++] = OP_CODE_RESPONSE; + + // Encode the Reqest Op Code. + m_notif_buffer[index++] = OP_CODE_IMAGE_SIZE_REQ; + + // Encode the Response Value. + m_notif_buffer[index++] = (uint8_t)BLE_DFU_RESP_VAL_SUCCESS; + + index += uint32_encode(num_of_firmware_bytes_rcvd, &m_notif_buffer[index]); + + memset(&hvx_params, 0, sizeof(hvx_params)); + + hvx_params.handle = p_dfu->dfu_ctrl_pt_handles.value_handle; + hvx_params.type = BLE_GATT_HVX_NOTIFICATION; + hvx_params.offset = 0; + hvx_params.p_len = &index; + hvx_params.p_data = m_notif_buffer; + + err_code = sd_ble_gatts_hvx(p_dfu->conn_handle, &hvx_params); + } while (err_code == NRF_ERROR_RESOURCES); + + return err_code; } @@ -599,22 +605,28 @@ uint32_t ble_dfu_pkts_rcpt_notify(ble_dfu_t * p_dfu, uint32_t num_of_firmware_by return NRF_ERROR_INVALID_STATE; } - ble_gatts_hvx_params_t hvx_params; - uint16_t index = 0; - - m_notif_buffer[index++] = OP_CODE_PKT_RCPT_NOTIF; - - index += uint32_encode(num_of_firmware_bytes_rcvd, &m_notif_buffer[index]); - - memset(&hvx_params, 0, sizeof(hvx_params)); - - hvx_params.handle = p_dfu->dfu_ctrl_pt_handles.value_handle; - hvx_params.type = BLE_GATT_HVX_NOTIFICATION; - hvx_params.offset = 0; - hvx_params.p_len = &index; - hvx_params.p_data = m_notif_buffer; - - return sd_ble_gatts_hvx(p_dfu->conn_handle, &hvx_params); + uint32_t err_code; + do { + + ble_gatts_hvx_params_t hvx_params; + uint16_t index = 0; + + m_notif_buffer[index++] = OP_CODE_PKT_RCPT_NOTIF; + + index += uint32_encode(num_of_firmware_bytes_rcvd, &m_notif_buffer[index]); + + memset(&hvx_params, 0, sizeof(hvx_params)); + + hvx_params.handle = p_dfu->dfu_ctrl_pt_handles.value_handle; + hvx_params.type = BLE_GATT_HVX_NOTIFICATION; + hvx_params.offset = 0; + hvx_params.p_len = &index; + hvx_params.p_data = m_notif_buffer; + + err_code = sd_ble_gatts_hvx(p_dfu->conn_handle, &hvx_params); + } while (err_code == NRF_ERROR_RESOURCES); + + return err_code; } @@ -632,24 +644,30 @@ uint32_t ble_dfu_response_send(ble_dfu_t * p_dfu, return NRF_ERROR_INVALID_STATE; } - ble_gatts_hvx_params_t hvx_params; - uint16_t index = 0; - - m_notif_buffer[index++] = OP_CODE_RESPONSE; - - // Encode the Request Op code - m_notif_buffer[index++] = (uint8_t)dfu_proc; - - // Encode the Response Value. - m_notif_buffer[index++] = (uint8_t)resp_val; - - memset(&hvx_params, 0, sizeof(hvx_params)); - - hvx_params.handle = p_dfu->dfu_ctrl_pt_handles.value_handle; - hvx_params.type = BLE_GATT_HVX_NOTIFICATION; - hvx_params.offset = 0; - hvx_params.p_len = &index; - hvx_params.p_data = m_notif_buffer; - - return sd_ble_gatts_hvx(p_dfu->conn_handle, &hvx_params); + uint32_t err_code; + do { + + ble_gatts_hvx_params_t hvx_params; + uint16_t index = 0; + + m_notif_buffer[index++] = OP_CODE_RESPONSE; + + // Encode the Request Op code + m_notif_buffer[index++] = (uint8_t)dfu_proc; + + // Encode the Response Value. + m_notif_buffer[index++] = (uint8_t)resp_val; + + memset(&hvx_params, 0, sizeof(hvx_params)); + + hvx_params.handle = p_dfu->dfu_ctrl_pt_handles.value_handle; + hvx_params.type = BLE_GATT_HVX_NOTIFICATION; + hvx_params.offset = 0; + hvx_params.p_len = &index; + hvx_params.p_data = m_notif_buffer; + + err_code = sd_ble_gatts_hvx(p_dfu->conn_handle, &hvx_params); + } while (err_code == NRF_ERROR_RESOURCES); + + return err_code; } From b5ddc1879e062621d927c263e13bd593ba51d801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 00:41:16 -0300 Subject: [PATCH 23/84] Fix improper processing of Flash WRITE/ERASE completion notifications that was causing the DFU firmware update to sometimes crash --- .../components/drivers_nrf/pstorage/pstorage_raw.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/sdk11/components/drivers_nrf/pstorage/pstorage_raw.c b/lib/sdk11/components/drivers_nrf/pstorage/pstorage_raw.c index f9404feb..a6ac0676 100644 --- a/lib/sdk11/components/drivers_nrf/pstorage/pstorage_raw.c +++ b/lib/sdk11/components/drivers_nrf/pstorage/pstorage_raw.c @@ -280,6 +280,10 @@ static void app_notify(uint32_t result, cmd_queue_element_t * p_elem) */ void pstorage_sys_event_handler(uint32_t sys_evt) { + // Do not bother processing events we are not interested in + if (sys_evt != NRF_EVT_FLASH_OPERATION_SUCCESS && + sys_evt != NRF_EVT_FLASH_OPERATION_ERROR) return; + uint32_t retval = NRF_SUCCESS; // The event shall only be processed if requested by this module. @@ -339,6 +343,7 @@ void pstorage_sys_event_handler(uint32_t sys_evt) * @brief Function for processing of commands and issuing flash access request to the SoftDevice. * * @return The return value received from SoftDevice. + * @remark This function is only called when m_cmd_queue.flash_access == false */ static uint32_t cmd_process(void) { @@ -352,6 +357,9 @@ static uint32_t cmd_process(void) storage_addr = p_cmd->storage_addr.block_id; + // Let's assume we will do a flash access + m_cmd_queue.flash_access = true; + switch (p_cmd->op_code) { case PSTORAGE_STORE_OP_CODE: @@ -396,9 +404,10 @@ static uint32_t cmd_process(void) break; } - if (retval == NRF_SUCCESS) + // An error means we will not do it + if (retval != NRF_SUCCESS) { - m_cmd_queue.flash_access = true; + m_cmd_queue.flash_access = false; } return retval; From 04d029a5bd9ccc367faee2c8a4d0bac5b886e7ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 00:43:27 -0300 Subject: [PATCH 24/84] Allow overriding device name in DFU mode --- .../components/libraries/bootloader_dfu/dfu_transport_ble.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c index 2715dce7..35d9ba46 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c @@ -47,7 +47,9 @@ enum { BLE_CONN_CFG_HIGH_BANDWIDTH = 1 }; #define BLE_HANDLE_MAX 0xFFFF /**< Max handle value is BLE. */ // limit of 8 chars +#ifndef DEVICE_NAME #define DEVICE_NAME "AdaDFU" /**< Name of device. Will be included in the advertising data. */ +#endif #define MIN_CONN_INTERVAL (uint16_t)(MSEC_TO_UNITS(10, UNIT_1_25_MS)) /**< Minimum acceptable connection interval (11.25 milliseconds). */ #define MAX_CONN_INTERVAL (uint16_t)(MSEC_TO_UNITS(30, UNIT_1_25_MS)) /**< Maximum acceptable connection interval (15 milliseconds). */ From 36422ba612fca418bad9044eab63047d8fe345ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 00:46:27 -0300 Subject: [PATCH 25/84] Speedup FLASH writes by temporarily increasing BLE stack local latency --- .../bootloader_dfu/dfu_transport_ble.c | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c index 35d9ba46..63767e4a 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c @@ -55,6 +55,7 @@ enum { BLE_CONN_CFG_HIGH_BANDWIDTH = 1 }; #define MAX_CONN_INTERVAL (uint16_t)(MSEC_TO_UNITS(30, UNIT_1_25_MS)) /**< Maximum acceptable connection interval (15 milliseconds). */ #define SLAVE_LATENCY 0 /**< Slave latency. */ #define CONN_SUP_TIMEOUT (4 * 100) /**< Connection supervisory timeout (4 seconds). */ +#define SPEEDUP_FLASH_WRITES 1 /**< Speedup FLASH writes by changing Softdevice local latency */ #define APP_ADV_INTERVAL MSEC_TO_UNITS(25, UNIT_0_625_MS) /**< The advertising interval (25 ms.). */ #define APP_ADV_TIMEOUT BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED /**< The advertising timeout in units of seconds. This is set to @ref BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED so that the advertisement is done as long as there there is a call to @ref dfu_transport_close function.*/ @@ -210,6 +211,34 @@ static ble_dfu_resp_val_t nrf_err_code_translate(uint32_t err_c } } +static void prioritize_ble_over_flash_writes(void) +{ +#ifdef SPEEDUP_FLASH_WRITES + // We set the local latency to 0: That will revert latency to the negotiated one with the host. + // That will increase throughput to the maximum possible + ble_opt_t opt; + varclr(&opt); + opt.gap_opt.local_conn_latency.conn_handle = m_conn_handle;/**< Connection Handle */ + opt.gap_opt.local_conn_latency.requested_latency = 0; /**< Requested local connection latency. */ + opt.gap_opt.local_conn_latency.p_actual_latency = NULL; /**< Pointer to storage for the actual local connection latency (can be set to NULL to skip return value). */ + sd_ble_opt_set(BLE_GAP_OPT_LOCAL_CONN_LATENCY, &opt); +#endif +} + +static void prioritize_flash_writes_over_ble(void) +{ +#ifdef SPEEDUP_FLASH_WRITES + // We set the local latency to the maximum possible: That will make FLASH writes faster, as BLE comms will + // be delayed (even losing RXd packets, but this is the same as a poor connection and will be handled by + // the protocol itself) + ble_opt_t opt; + varclr(&opt); + opt.gap_opt.local_conn_latency.conn_handle = m_conn_handle;/**< Connection Handle */ + opt.gap_opt.local_conn_latency.requested_latency = 50; /**< Requested local connection latency. */ + opt.gap_opt.local_conn_latency.p_actual_latency = NULL; /**< Pointer to storage for the actual local connection latency (can be set to NULL to skip return value). */ + sd_ble_opt_set(BLE_GAP_OPT_LOCAL_CONN_LATENCY, &opt); +#endif +} /**@brief Function for handling the callback events from the dfu module. * Callbacks are expected when \ref dfu_data_pkt_handle has been executed. @@ -228,6 +257,9 @@ static void dfu_cb_handler(uint32_t packet, uint32_t result, uint8_t * p_data) case DATA_PACKET: if (result != NRF_SUCCESS) { + // Restore latency to the negotiated one + prioritize_ble_over_flash_writes(); + // Disconnect from peer. if (IS_CONNECTED()) { @@ -244,6 +276,9 @@ static void dfu_cb_handler(uint32_t packet, uint32_t result, uint8_t * p_data) // If the callback matches final data packet received then the peer is notified. if (mp_final_packet == p_data) { + // Restore latency to the negotiated one + prioritize_ble_over_flash_writes(); + // Notify the DFU Controller about the success of the procedure. err_code = ble_dfu_response_send(&m_dfu, BLE_DFU_RECEIVE_APP_PROCEDURE, @@ -254,6 +289,10 @@ static void dfu_cb_handler(uint32_t packet, uint32_t result, uint8_t * p_data) break; case START_PACKET: + + // Restore latency to the negotiated one + prioritize_ble_over_flash_writes(); + // Translate the err_code returned by the above function to DFU Response Value. resp_val = nrf_err_code_translate(result, BLE_DFU_START_PROCEDURE); @@ -326,9 +365,15 @@ static void start_data_process(ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt) start_packet.bl_image_size = uint32_decode(p_length_data + BL_IMAGE_SIZE_OFFSET); start_packet.app_image_size = uint32_decode(p_length_data + APP_IMAGE_SIZE_OFFSET); + // Prioritize FLASH writes over BLE comms + prioritize_flash_writes_over_ble(); + err_code = dfu_start_pkt_handle(&update_packet); if (err_code != NRF_SUCCESS) { + // Prioritize BLE over flash writes + prioritize_ble_over_flash_writes(); + // Translate the err_code returned by the above function to DFU Response Value. ble_dfu_resp_val_t resp_val; From d28c2e08717b4f48423185bc15760d79b4a201ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 01:03:53 -0300 Subject: [PATCH 26/84] Enable negotiation of MTU sizes above 20. This will significatively increase DFU write speed --- .../ble/ble_services/ble_dfu/ble_dfu.c | 16 ++++----- .../bootloader_dfu/dfu_transport_ble.c | 35 +++++++++++++++---- src/main.c | 32 ++++++++++++++--- src/sdk_config.h | 2 +- 4 files changed, 65 insertions(+), 20 deletions(-) diff --git a/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c b/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c index 685fdea1..a6309712 100644 --- a/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c +++ b/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c @@ -17,14 +17,14 @@ #include #include "sdk_common.h" -#define DFU_PKT_ENABLE_WR_WO_RESPONSE_ONLY 0 /**< Enable Write Without Response on Data Endpoint ONLY (otherwise both With and Without response will be supported)*/ -#define MAX_DFU_PKT_LEN 20 /**< Maximum length (in bytes) of the DFU Packet characteristic. */ -#define PKT_START_DFU_PARAM_LEN 2 /**< Length (in bytes) of the parameters for Packet Start DFU Request. */ -#define PKT_INIT_DFU_PARAM_LEN 2 /**< Length (in bytes) of the parameters for Packet Init DFU Request. */ -#define PKT_RCPT_NOTIF_REQ_LEN 3 /**< Length (in bytes) of the Packet Receipt Notification Request. */ -#define MAX_PKTS_RCPT_NOTIF_LEN 6 /**< Maximum length (in bytes) of the Packets Receipt Notification. */ -#define MAX_RESPONSE_LEN 7 /**< Maximum length (in bytes) of the response to a Control Point command. */ -#define MAX_NOTIF_BUFFER_LEN MAX(MAX_PKTS_RCPT_NOTIF_LEN, MAX_RESPONSE_LEN) /**< Maximum length (in bytes) of the buffer needed by DFU Service while sending notifications to peer. */ +#define DFU_PKT_ENABLE_WR_WO_RESPONSE_ONLY 0 /**< Enable Write Without Response on Data Endpoint ONLY (otherwise both With and Without response will be supported)*/ +#define MAX_DFU_PKT_LEN 244 /**< Maximum length (in bytes) of the DFU Packet characteristic. */ +#define PKT_START_DFU_PARAM_LEN 2 /**< Length (in bytes) of the parameters for Packet Start DFU Request. */ +#define PKT_INIT_DFU_PARAM_LEN 2 /**< Length (in bytes) of the parameters for Packet Init DFU Request. */ +#define PKT_RCPT_NOTIF_REQ_LEN 3 /**< Length (in bytes) of the Packet Receipt Notification Request. */ +#define MAX_PKTS_RCPT_NOTIF_LEN 6 /**< Maximum length (in bytes) of the Packets Receipt Notification. */ +#define MAX_RESPONSE_LEN 7 /**< Maximum length (in bytes) of the response to a Control Point command. */ +#define MAX_NOTIF_BUFFER_LEN MAX(MAX_PKTS_RCPT_NOTIF_LEN, MAX_RESPONSE_LEN) /**< Maximum length (in bytes) of the buffer needed by DFU Service while sending notifications to peer. */ enum { diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c index 63767e4a..06017a91 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c @@ -36,8 +36,7 @@ #include "sdk_common.h" -#define BLEGAP_EVENT_LENGTH 6 -#define BLEGATT_ATT_MTU_MAX 23 +#define BLEGATT_ATT_MTU_MAX 247 enum { BLE_CONN_CFG_HIGH_BANDWIDTH = 1 }; #define DFU_REV_MAJOR 0x00 /** DFU Major revision number to be exposed. */ @@ -51,15 +50,15 @@ enum { BLE_CONN_CFG_HIGH_BANDWIDTH = 1 }; #define DEVICE_NAME "AdaDFU" /**< Name of device. Will be included in the advertising data. */ #endif -#define MIN_CONN_INTERVAL (uint16_t)(MSEC_TO_UNITS(10, UNIT_1_25_MS)) /**< Minimum acceptable connection interval (11.25 milliseconds). */ +#define MIN_CONN_INTERVAL (uint16_t)(MSEC_TO_UNITS(15, UNIT_1_25_MS)) /**< Minimum acceptable connection interval (11.25 milliseconds). */ #define MAX_CONN_INTERVAL (uint16_t)(MSEC_TO_UNITS(30, UNIT_1_25_MS)) /**< Maximum acceptable connection interval (15 milliseconds). */ #define SLAVE_LATENCY 0 /**< Slave latency. */ -#define CONN_SUP_TIMEOUT (4 * 100) /**< Connection supervisory timeout (4 seconds). */ +#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(8000, UNIT_10_MS) /**< Connection supervisory timeout (4 seconds). */ #define SPEEDUP_FLASH_WRITES 1 /**< Speedup FLASH writes by changing Softdevice local latency */ #define APP_ADV_INTERVAL MSEC_TO_UNITS(25, UNIT_0_625_MS) /**< The advertising interval (25 ms.). */ #define APP_ADV_TIMEOUT BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED /**< The advertising timeout in units of seconds. This is set to @ref BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED so that the advertisement is done as long as there there is a call to @ref dfu_transport_close function.*/ -#define APP_DIRECTED_ADV_TIMEOUT 50 /**< number of direct advertisement (each lasting 1.28seconds). */ +#define APP_DIRECTED_ADV_TIMEOUT 50 /**< number of direct advertisement (each lasting 1.28seconds). */ #define PEER_ADDRESS_TYPE_INVALID 0xFF /**< Value indicating that no valid peer address exists. This will be the case when a private resolvable address is used in which case there is no address available but instead an IRK is present. */ #define PEER_ADDRESS_TYPE_INVALID 0xFF /**< Value indicating that no valid peer address exists. This will be the case when a private resolvable address is used in which case there is no address available but instead an IRK is present. */ @@ -790,8 +789,22 @@ static void on_ble_evt(ble_evt_t * p_ble_evt) switch (p_ble_evt->header.evt_id) { case BLE_GAP_EVT_CONNECTED: - m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; - m_is_advertising = false; + { + m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; + m_is_advertising = false; + + // Force changing the slave latency to try to improve transfer throughput (bandwidth) + // But Android and IOS will override it with their minimum supported value + ble_gap_conn_params_t p_conn_params; + p_conn_params.min_conn_interval = p_ble_evt->evt.gap_evt.params.connected.conn_params.min_conn_interval; + p_conn_params.max_conn_interval = p_ble_evt->evt.gap_evt.params.connected.conn_params.max_conn_interval; + p_conn_params.slave_latency = SLAVE_LATENCY; + p_conn_params.conn_sup_timeout = p_ble_evt->evt.gap_evt.params.connected.conn_params.conn_sup_timeout; + + err_code = sd_ble_gap_conn_param_update(m_conn_handle, &p_conn_params); + APP_ERROR_CHECK(err_code); + + } break; case BLE_GAP_EVT_DISCONNECTED: @@ -821,6 +834,9 @@ static void on_ble_evt(ble_evt_t * p_ble_evt) break; + case BLE_GAP_EVT_CONN_PARAM_UPDATE: + break; + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: { ble_gap_sec_keyset_t keys; @@ -947,6 +963,11 @@ static void on_ble_evt(ble_evt_t * p_ble_evt) case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: { uint16_t att_mtu = MIN(p_ble_evt->evt.gatts_evt.params.exchange_mtu_request.client_rx_mtu, BLEGATT_ATT_MTU_MAX); + + // Round it to a multiple of 4 plus 3 bytes, as we need data packets to be multiple of 4 bytes + att_mtu &= 0xFFFCU; + att_mtu |= 3; + PRINTF("GAP ATT MTU is changed to %d\r\n", att_mtu); APP_ERROR_CHECK( sd_ble_gatts_exchange_mtu_reply(m_conn_handle, att_mtu) ); } diff --git a/src/main.c b/src/main.c index 9ebc9e99..0011640b 100644 --- a/src/main.c +++ b/src/main.c @@ -115,8 +115,11 @@ extern void tusb_hal_nrf_power_event(uint32_t event); #define APP_ASKS_FOR_SINGLE_TAP_RESET() (*((uint32_t*)(DFU_BANK_0_REGION_START + 0x200)) == 0x87eeb07c) // These value must be the same with one in dfu_transport_ble.c -#define BLEGAP_EVENT_LENGTH 6 -#define BLEGATT_ATT_MTU_MAX 23 +#define BLEGAP_EVENT_LENGTH 12 +#define BLEGATT_ATT_MTU_MAX 247 +#define BLEGATTS_HVN_QSIZE 12 +#define BLEGATTC_WRCMD_QSIZE 2 + enum { BLE_CONN_CFG_HIGH_BANDWIDTH = 1 }; @@ -371,11 +374,23 @@ static uint32_t ble_stack_init(void) { blecfg.conn_cfg.params.gap_conn_cfg.event_length = BLEGAP_EVENT_LENGTH; sd_ble_cfg_set(BLE_CONN_CFG_GAP, &blecfg, ram_start); + // HVN queue size + varclr(&blecfg); + blecfg.conn_cfg.conn_cfg_tag = BLE_CONN_CFG_HIGH_BANDWIDTH; + blecfg.conn_cfg.params.gatts_conn_cfg.hvn_tx_queue_size = BLEGATTS_HVN_QSIZE; + sd_ble_cfg_set(BLE_CONN_CFG_GATTS, &blecfg, ram_start); + + // WRITE COMMAND queue size + varclr(&blecfg); + blecfg.conn_cfg.conn_cfg_tag = BLE_CONN_CFG_HIGH_BANDWIDTH; + blecfg.conn_cfg.params.gattc_conn_cfg.write_cmd_tx_queue_size = BLEGATTC_WRCMD_QSIZE; + sd_ble_cfg_set(BLE_CONN_CFG_GATTC, &blecfg, ram_start); + // Enable BLE stack. // Note: Interrupt state (enabled, forwarding) is not work properly if not enable ble sd_ble_enable(&ram_start); -#if 0 +#if BLEGATT_ATT_MTU_MAX > 23 ble_opt_t opt; varclr(&opt); opt.common_opt.conn_evt_ext.enable = 1; // enable Data Length Extension @@ -401,10 +416,19 @@ uint32_t proc_ble(void) { if (NRF_SUCCESS == err) { switch (evt->header.evt_id) { case BLE_GAP_EVT_CONNECTED: + { + // Try to enable 2M phy,if phone allows it + ble_gap_phys_t const phys = + { + .rx_phys = BLE_GAP_PHY_AUTO, + .tx_phys = BLE_GAP_PHY_AUTO, + }; + sd_ble_gap_phy_update(evt->evt.gap_evt.conn_handle, &phys); + _ota_connected = true; led_state(STATE_BLE_CONNECTED); break; - + } case BLE_GAP_EVT_DISCONNECTED: _ota_connected = false; led_state(STATE_BLE_DISCONNECTED); diff --git a/src/sdk_config.h b/src/sdk_config.h index 50e81969..bef8bc7d 100644 --- a/src/sdk_config.h +++ b/src/sdk_config.h @@ -66,7 +66,7 @@ #define HCI_MEM_POOL_ENABLED 1 #define HCI_TX_BUF_SIZE 600 // not used #define HCI_RX_BUF_SIZE 600 -#define HCI_RX_BUF_QUEUE_SIZE 8 // must be power of 2 +#define HCI_RX_BUF_QUEUE_SIZE 16 // must be power of 2 //========================================================== // UART_ENABLED - nrf_drv_uart - UART/UARTE peripheral driver From ca46deb0a24a1888809e9067d7a684dbb2d13397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 01:14:50 -0300 Subject: [PATCH 27/84] Factor out FLASH erase procedure (non OTA), fix FLASH write procedure (non OTA), use them instead of repeating code --- .../bootloader_dfu/dfu_single_bank.c | 135 +++++++++--------- src/flash_nrf5x.c | 100 +++++++++---- src/flash_nrf5x.h | 3 +- 3 files changed, 136 insertions(+), 102 deletions(-) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c index fa69ec83..ee2d342f 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c @@ -88,31 +88,24 @@ static void pstorage_callback_handler(pstorage_handle_t * p_handle, */ static void dfu_prepare_func_app_erase(uint32_t image_size) { - mp_storage_handle_active = &m_storage_handle_app; - - // Doing a SoftDevice update thus current application must be cleared to ensure enough space - // for new SoftDevice. - m_dfu_state = DFU_STATE_PREPARING; - - if ( is_ota() ) - { - uint32_t err_code = pstorage_clear(&m_storage_handle_app, m_image_size); - APP_ERROR_CHECK(err_code); - } - else - { - uint32_t const page_count = NRFX_CEIL_DIV(m_image_size, CODE_PAGE_SIZE); - - for ( uint32_t i = 0; i < page_count; i++ ) + mp_storage_handle_active = &m_storage_handle_app; + + // Doing a SoftDevice update thus current application must be cleared to ensure enough space + // for new SoftDevice. + m_dfu_state = DFU_STATE_PREPARING; + + if ( is_ota() ) { - uint32_t const addr = DFU_BANK_0_REGION_START + i * CODE_PAGE_SIZE; - PRINTF("Erase 0x%08lX\r\n", addr); - nrfx_nvmc_page_erase(addr); + uint32_t err_code = pstorage_clear(&m_storage_handle_app, m_image_size); + APP_ERROR_CHECK(err_code); } + else + { + flash_nrf5x_erase(DFU_BANK_0_REGION_START, m_image_size); - // invoke complete callback - pstorage_callback_handler(&m_storage_handle_app, PSTORAGE_CLEAR_OP_CODE, NRF_SUCCESS, NULL, 0); - } + // invoke complete callback + pstorage_callback_handler(&m_storage_handle_app, PSTORAGE_CLEAR_OP_CODE, NRF_SUCCESS, NULL, 0); + } } @@ -136,14 +129,14 @@ static void dfu_cleared_func_app(void) uint32_t offset_calculate(uint32_t sd_image_size) { uint32_t offset = 0; - + if (m_start_packet.sd_image_size > DFU_BANK_0_REGION_START) { uint32_t page_mask = (CODE_PAGE_SIZE - 1); uint32_t diff = m_start_packet.sd_image_size - DFU_BANK_0_REGION_START; - + offset = diff & ~page_mask; - + // Align offset to next page if image size is not page sized. if ((diff & page_mask) > 0) { @@ -189,8 +182,7 @@ static uint32_t dfu_activate_sd(void) */ static uint32_t dfu_activate_app(void) { - uint32_t err_code = NRF_SUCCESS; - dfu_update_status_t update_status; + dfu_update_status_t update_status = { 0 }; memset(&update_status, 0, sizeof(dfu_update_status_t )); update_status.status_code = DFU_UPDATE_APP_COMPLETE; @@ -199,7 +191,7 @@ static uint32_t dfu_activate_app(void) bootloader_dfu_update_process(update_status); - return err_code; + return NRF_SUCCESS; } @@ -213,7 +205,7 @@ static uint32_t dfu_activate_app(void) */ static uint32_t dfu_activate_bl(void) { - dfu_update_status_t update_status; + dfu_update_status_t update_status = {0}; update_status.status_code = DFU_UPDATE_BOOT_COMPLETE; update_status.app_crc = m_image_crc; @@ -267,7 +259,7 @@ uint32_t dfu_start_pkt_handle(dfu_update_packet_t * p_packet) // - SoftDevice // - Bootloader // - SoftDevice with Bootloader - if (IS_UPDATING_APP(m_start_packet) && + if (IS_UPDATING_APP(m_start_packet) && (IS_UPDATING_SD(m_start_packet) || IS_UPDATING_BL(m_start_packet))) { // App update is only supported independently. @@ -284,7 +276,7 @@ uint32_t dfu_start_pkt_handle(dfu_update_packet_t * p_packet) m_image_size = m_start_packet.sd_image_size + m_start_packet.bl_image_size + m_start_packet.app_image_size; - + if (m_start_packet.bl_image_size > DFU_BL_IMAGE_MAX_SIZE) { return NRF_ERROR_DATA_SIZE; @@ -296,7 +288,7 @@ uint32_t dfu_start_pkt_handle(dfu_update_packet_t * p_packet) } m_functions.prepare = dfu_prepare_func_app_erase; m_functions.cleared = dfu_cleared_func_app; - + if (IS_UPDATING_SD(m_start_packet)) { m_functions.activate = dfu_activate_sd; @@ -358,13 +350,13 @@ uint32_t dfu_data_pkt_handle(dfu_update_packet_t * p_packet) if ( is_ota() ) { - err_code = pstorage_store(mp_storage_handle_active, (uint8_t *)p_data, data_length, m_data_received); - VERIFY_SUCCESS(err_code); + err_code = pstorage_store(mp_storage_handle_active, (uint8_t *)p_data, data_length, m_data_received); + VERIFY_SUCCESS(err_code); } else { - flash_nrf5x_write(DFU_BANK_0_REGION_START + m_data_received, p_data, data_length, false); - pstorage_callback_handler(mp_storage_handle_active, PSTORAGE_STORE_OP_CODE, NRF_SUCCESS, (uint8_t *) p_data, data_length); + flash_nrf5x_write(DFU_BANK_0_REGION_START + m_data_received, p_data, data_length, false); + pstorage_callback_handler(mp_storage_handle_active, PSTORAGE_STORE_OP_CODE, NRF_SUCCESS, (uint8_t *) p_data, data_length); } m_data_received += data_length; @@ -376,10 +368,10 @@ uint32_t dfu_data_pkt_handle(dfu_update_packet_t * p_packet) } else { - if ( !is_ota() ) flash_nrf5x_flush(false); + if ( !is_ota() ) flash_nrf5x_flush(false); - // The entire image has been received. Return NRF_SUCCESS. - err_code = NRF_SUCCESS; + // The entire image has been received. Return NRF_SUCCESS. + err_code = NRF_SUCCESS; } break; @@ -395,14 +387,14 @@ uint32_t dfu_data_pkt_handle(dfu_update_packet_t * p_packet) uint32_t dfu_init_pkt_complete(void) { uint32_t err_code = NRF_ERROR_INVALID_STATE; - + // DFU initialization has been done and a start packet has been received. if (IMAGE_WRITE_IN_PROGRESS()) { // Image write is already in progress. Cannot handle an init packet now. return NRF_ERROR_INVALID_STATE; } - + if (m_dfu_state == DFU_STATE_RX_INIT_PKT) { err_code = dfu_init_prevalidate(m_init_packet, m_init_packet_length, m_start_packet.dfu_update_mode); @@ -513,7 +505,7 @@ uint32_t dfu_image_activate() void dfu_reset(void) { - dfu_update_status_t update_status; + dfu_update_status_t update_status = { 0 }; update_status.status_code = DFU_RESET; @@ -547,13 +539,13 @@ static uint32_t dfu_copy_sd(uint32_t * src, uint32_t * dst, uint32_t len) } -static uint32_t dfu_sd_img_block_swap(uint32_t * src, - uint32_t * dst, - uint32_t len, +static uint32_t dfu_sd_img_block_swap(uint32_t * src, + uint32_t * dst, + uint32_t len, uint32_t block_size) { // It is neccesarry to swap the new SoftDevice in 3 rounds to ensure correct copy of data - // and verifucation of data in case power reset occurs during write to flash. + // and verification of data in case power reset occurs during write to flash. // To ensure the robustness of swapping the images are compared backwards till start of // image swap. If the back is identical everything is swapped. uint32_t err_code = dfu_compare_block(src, dst, len); @@ -564,9 +556,9 @@ static uint32_t dfu_sd_img_block_swap(uint32_t * src, if ((uint32_t)dst > SOFTDEVICE_REGION_START) { - err_code = dfu_sd_img_block_swap((uint32_t *)((uint32_t)src - block_size), - (uint32_t *)((uint32_t)dst - block_size), - block_size, + err_code = dfu_sd_img_block_swap((uint32_t *)((uint32_t)src - block_size), + (uint32_t *)((uint32_t)dst - block_size), + block_size, block_size); VERIFY_SUCCESS(err_code); } @@ -588,37 +580,37 @@ uint32_t dfu_sd_image_swap(void) { return NRF_SUCCESS; } - + if ((SOFTDEVICE_REGION_START + boot_settings.sd_image_size) > boot_settings.sd_image_start) { uint32_t err_code; uint32_t sd_start = SOFTDEVICE_REGION_START; uint32_t block_size = (boot_settings.sd_image_start - sd_start) / 2; - + /* ##### FIX START ##### */ - block_size &= ~(uint32_t)(CODE_PAGE_SIZE - 1); - /* ##### FIX END ##### */ - + block_size &= ~(uint32_t)(CODE_PAGE_SIZE - 1); + /* ##### FIX END ##### */ + uint32_t image_end = boot_settings.sd_image_start + boot_settings.sd_image_size; uint32_t img_block_start = boot_settings.sd_image_start + 2 * block_size; uint32_t sd_block_start = sd_start + 2 * block_size; - + if (SD_SIZE_GET(MBR_SIZE) < boot_settings.sd_image_size) { // This will clear a page thus ensuring the old image is invalidated before swapping. - err_code = dfu_copy_sd((uint32_t *)(sd_start + block_size), - (uint32_t *)(sd_start + block_size), + err_code = dfu_copy_sd((uint32_t *)(sd_start + block_size), + (uint32_t *)(sd_start + block_size), sizeof(uint32_t)); VERIFY_SUCCESS(err_code); err_code = dfu_copy_sd((uint32_t *)sd_start, (uint32_t *)sd_start, sizeof(uint32_t)); VERIFY_SUCCESS(err_code); } - - return dfu_sd_img_block_swap((uint32_t *)img_block_start, - (uint32_t *)sd_block_start, - image_end - img_block_start, + + return dfu_sd_img_block_swap((uint32_t *)img_block_start, + (uint32_t *)sd_block_start, + image_end - img_block_start, block_size); } else @@ -626,7 +618,7 @@ uint32_t dfu_sd_image_swap(void) if (boot_settings.sd_image_size != 0) { return dfu_copy_sd((uint32_t *)boot_settings.sd_image_start, - (uint32_t *)SOFTDEVICE_REGION_START, + (uint32_t *)SOFTDEVICE_REGION_START, boot_settings.sd_image_size); } } @@ -646,7 +638,7 @@ uint32_t dfu_bl_image_swap(void) { uint32_t bl_image_start = (bootloader_settings.sd_image_size == 0) ? DFU_BANK_0_REGION_START : - bootloader_settings.sd_image_start + + bootloader_settings.sd_image_start + bootloader_settings.sd_image_size; sd_mbr_cmd.command = SD_MBR_COMMAND_COPY_BL; @@ -695,17 +687,18 @@ uint32_t dfu_sd_image_validate(void) { return NRF_SUCCESS; } - + if ((SOFTDEVICE_REGION_START + bootloader_settings.sd_image_size) > bootloader_settings.sd_image_start) { uint32_t sd_start = SOFTDEVICE_REGION_START; uint32_t block_size = (bootloader_settings.sd_image_start - sd_start) / 2; - uint32_t image_end = bootloader_settings.sd_image_start + + uint32_t image_end = bootloader_settings.sd_image_start + bootloader_settings.sd_image_size; /* ##### FIX START ##### */ - block_size &= ~(uint32_t)(CODE_PAGE_SIZE - 1); - /* ##### FIX END ##### */ + block_size &= ~(uint32_t)(CODE_PAGE_SIZE - 1); + /* ##### FIX END ##### */ + uint32_t img_block_start = bootloader_settings.sd_image_start + 2 * block_size; uint32_t sd_block_start = sd_start + 2 * block_size; @@ -714,12 +707,12 @@ uint32_t dfu_sd_image_validate(void) return NRF_ERROR_NULL; } - return dfu_sd_img_block_swap((uint32_t *)img_block_start, - (uint32_t *)sd_block_start, - image_end - img_block_start, + return dfu_sd_img_block_swap((uint32_t *)img_block_start, + (uint32_t *)sd_block_start, + image_end - img_block_start, block_size); } - + sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE; sd_mbr_cmd.params.compare.ptr1 = (uint32_t *)SOFTDEVICE_REGION_START; sd_mbr_cmd.params.compare.ptr2 = (uint32_t *)bootloader_settings.sd_image_start; diff --git a/src/flash_nrf5x.c b/src/flash_nrf5x.c index 12c6797e..ddea500e 100644 --- a/src/flash_nrf5x.c +++ b/src/flash_nrf5x.c @@ -26,48 +26,88 @@ #include "nrf_sdm.h" #include "flash_nrf5x.h" #include "boards.h" +#include "dfu_types.h" -#define FLASH_PAGE_SIZE 4096 #define FLASH_CACHE_INVALID_ADDR 0xffffffff static uint32_t _fl_addr = FLASH_CACHE_INVALID_ADDR; -static uint8_t _fl_buf[FLASH_PAGE_SIZE] __attribute__((aligned(4))); +static uint8_t _fl_buf[CODE_PAGE_SIZE] __attribute__((aligned(4))); -void flash_nrf5x_flush (bool need_erase) +void flash_nrf5x_erase (uint32_t dst, uint32_t len) { - if ( _fl_addr == FLASH_CACHE_INVALID_ADDR ) return; - - // skip the write if contents matches - if ( memcmp(_fl_buf, (void *) _fl_addr, FLASH_PAGE_SIZE) != 0 ) - { - // - nRF52832 dfu via uart can miss incoming byte when erasing because cpu is blocked for > 2ms. - // Since dfu_prepare_func_app_erase() already erase the page for us, we can skip it here. - // - nRF52840 dfu serial/uf2 are USB-based which are DMA and should have no problems. - // - // Note: MSC uf2 does not erase page in advance like dfu serial - if ( need_erase ) + uint32_t page_addr = dst & ~(CODE_PAGE_SIZE - 1); + uint32_t const page_count = NRFX_CEIL_DIV(len, CODE_PAGE_SIZE); + for ( uint32_t i = 0; i < page_count; i++ ) { - PRINTF("Erase and "); - nrfx_nvmc_page_erase(_fl_addr); + uint32_t const addr = page_addr + i * CODE_PAGE_SIZE; + PRINTF("Erase 0x%08lX\r\n", addr); + nrfx_nvmc_page_erase(addr); } +} + - PRINTF("Write 0x%08lX\r\n", _fl_addr); - nrfx_nvmc_words_write(_fl_addr, (uint32_t *) _fl_buf, FLASH_PAGE_SIZE / 4); - } +void flash_nrf5x_flush (bool need_erase) +{ + if ( _fl_addr == FLASH_CACHE_INVALID_ADDR ) + return; + + // skip the write if contents matches + if ( memcmp(_fl_buf, (void *) _fl_addr, CODE_PAGE_SIZE) != 0 ) + { + // - nRF52832 dfu via uart can miss incoming byte when erasing because cpu is blocked for > 2ms. + // Since dfu_prepare_func_app_erase() already erase the page for us, we can skip it here. + // - nRF52840 dfu serial/uf2 are USB-based which are DMA and should have no problems. + // + // Note: MSC uf2 does not erase page in advance like dfu serial + if ( need_erase ) + { + PRINTF("Erase and "); + nrfx_nvmc_page_erase(_fl_addr); + } + + PRINTF("Write 0x%08lX\r\n", _fl_addr); + nrfx_nvmc_words_write(_fl_addr, (uint32_t *) _fl_buf, CODE_PAGE_SIZE / 4); + } - _fl_addr = FLASH_CACHE_INVALID_ADDR; + _fl_addr = FLASH_CACHE_INVALID_ADDR; } -void flash_nrf5x_write (uint32_t dst, void const *src, int len, bool need_erase) +void flash_nrf5x_write (uint32_t dst, void const *src, uint32_t len, bool need_erase) { - uint32_t newAddr = dst & ~(FLASH_PAGE_SIZE - 1); - - if ( newAddr != _fl_addr ) - { - flash_nrf5x_flush(need_erase); - _fl_addr = newAddr; - memcpy(_fl_buf, (void *) newAddr, FLASH_PAGE_SIZE); - } - memcpy(_fl_buf + (dst & (FLASH_PAGE_SIZE - 1)), src, len); + // While something to write... + while (len > 0) { + + // Align to start of page + uint32_t page_addr = dst & ~(CODE_PAGE_SIZE - 1); + + // If page changed, write modified contents + if ( page_addr != _fl_addr ) + { + flash_nrf5x_flush(need_erase); + + // Remember new page + _fl_addr = page_addr; + + // And read its contents + memcpy(_fl_buf, (void *) page_addr, CODE_PAGE_SIZE); + } + + // Compute the write offset into the current page + uint32_t offset_in_page = dst & (CODE_PAGE_SIZE - 1); + + // Compute how many bytes remaining to complete the page + uint32_t bytes_to_end = CODE_PAGE_SIZE - offset_in_page; + + // Limit the count to the available bytes in the current page + uint32_t max_bytes = bytes_to_end < len ? bytes_to_end : len; + + // Copy contents to our buffer + memcpy(_fl_buf + offset_in_page, src, max_bytes); + + // Update variables + len -= max_bytes; + dst += max_bytes; + src = (void const *) ((uint8_t*)src + max_bytes); + }; } diff --git a/src/flash_nrf5x.h b/src/flash_nrf5x.h index 9ab4619e..d3fbb490 100644 --- a/src/flash_nrf5x.h +++ b/src/flash_nrf5x.h @@ -34,7 +34,8 @@ extern "C" { #endif -void flash_nrf5x_write (uint32_t dst, void const *src, int len, bool need_erase); +void flash_nrf5x_erase (uint32_t dst, uint32_t len); +void flash_nrf5x_write (uint32_t dst, void const *src, uint32_t len, bool need_erase); void flash_nrf5x_flush (bool need_erase); #ifdef __cplusplus From efe383d0905dafe9119d5834f31092759cb06f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 01:19:15 -0300 Subject: [PATCH 28/84] Add DFU mode exit timeout of 3 minutes. If the user enters DFU mode BUT does nothing, in about 3 minutes, the bootloader will exit and start executing the user application --- .../bootloader_dfu/dfu_bank_internal.h | 2 + .../bootloader_dfu/dfu_single_bank.c | 87 ++++++++++++++++++- 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_bank_internal.h b/lib/sdk11/components/libraries/bootloader_dfu/dfu_bank_internal.h index 03d20f61..f84d1115 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_bank_internal.h +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_bank_internal.h @@ -40,6 +40,8 @@ typedef enum DFU_STATE_WAIT_4_ACTIVATE /**< State for: waiting for dfu_image_activate(). */ } dfu_state_t; +#define DFU_TIMEOUT_INTERVAL APP_TIMER_TICKS(360000) /**< DFU timeout interval in milliseconds. */ + #define IS_UPDATING_SD(START_PKT) ((START_PKT).dfu_update_mode & DFU_UPDATE_SD) /**< Macro for determining if a SoftDevice update is ongoing. */ #define IS_UPDATING_BL(START_PKT) ((START_PKT).dfu_update_mode & DFU_UPDATE_BL) /**< Macro for determining if a Bootloader update is ongoing. */ #define IS_UPDATING_APP(START_PKT) ((START_PKT).dfu_update_mode & DFU_UPDATE_APP) /**< Macro for determining if a Application update is ongoing. */ diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c index ee2d342f..a01e0ba9 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c @@ -35,6 +35,9 @@ static uint8_t m_init_packet[64]; /**< Init packet static uint8_t m_init_packet_length; /**< Length of init packet received. */ static uint16_t m_image_crc; /**< Calculated CRC of the image received. */ +APP_TIMER_DEF(m_dfu_timer_id); /**< Application timer id. */ +static bool m_dfu_timed_out = false; /**< Boolean flag value for tracking DFU timer timeout state. */ + static pstorage_handle_t m_storage_handle_app; /**< Pstorage handle for the application area (bank 0). Bank used when updating a SoftDevice w/wo bootloader. Handle also used when swapping received application from bank 1 to bank 0. */ static pstorage_handle_t * mp_storage_handle_active; /**< Pointer to the pstorage handle for the active bank for receiving of data packets. */ @@ -80,6 +83,47 @@ static void pstorage_callback_handler(pstorage_handle_t * p_handle, APP_ERROR_CHECK(result); } + +/**@brief Function for handling the DFU timeout. + * + * @param[in] p_context The timeout context. + */ +static void dfu_timeout_handler(void * p_context) +{ + UNUSED_PARAMETER(p_context); + dfu_update_status_t update_status; + + m_dfu_timed_out = true; + update_status.status_code = DFU_TIMEOUT; + + bootloader_dfu_update_process(update_status); +} + + +/**@brief Function for restarting the DFU Timer. + * + * @details This function will stop and restart the DFU timer. This function will be called by the + * functions handling any DFU packet received from the peer that is transferring a firmware + * image. + */ +static uint32_t dfu_timer_restart(void) +{ + if (m_dfu_timed_out) + { + // The DFU timer had already timed out. + return NRF_ERROR_INVALID_STATE; + } + + uint32_t err_code = app_timer_stop(m_dfu_timer_id); + APP_ERROR_CHECK(err_code); + + err_code = app_timer_start(m_dfu_timer_id, DFU_TIMEOUT_INTERVAL, NULL); + APP_ERROR_CHECK(err_code); + + return err_code; +} + + /**@brief Function for preparing of flash before receiving SoftDevice image. * * @details This function will erase current application area to ensure sufficient amount of @@ -236,6 +280,16 @@ uint32_t dfu_init(void) m_storage_handle_app.block_id = DFU_BANK_0_REGION_START; + // Create the timer to monitor the activity by the peer doing the firmware update. + err_code = app_timer_create(&m_dfu_timer_id, + APP_TIMER_MODE_SINGLE_SHOT, + dfu_timeout_handler); + APP_ERROR_CHECK(err_code); + + // Start the DFU timer. + err_code = app_timer_start(m_dfu_timer_id, DFU_TIMEOUT_INTERVAL, NULL); + APP_ERROR_CHECK(err_code); + m_data_received = 0; m_dfu_state = DFU_STATE_IDLE; @@ -251,6 +305,8 @@ void dfu_register_callback(dfu_callback_t callback_handler) uint32_t dfu_start_pkt_handle(dfu_update_packet_t * p_packet) { + uint32_t err_code; + m_start_packet = *(p_packet->params.start_packet); // Check that the requested update procedure is supported. @@ -304,6 +360,10 @@ uint32_t dfu_start_pkt_handle(dfu_update_packet_t * p_packet) if ( DFU_STATE_IDLE != m_dfu_state ) return NRF_ERROR_INVALID_STATE; + // Valid peer activity detected. Hence restart the DFU timer. + err_code = dfu_timer_restart(); + VERIFY_SUCCESS(err_code); + m_functions.prepare(m_image_size); return NRF_SUCCESS; @@ -346,6 +406,10 @@ uint32_t dfu_data_pkt_handle(dfu_update_packet_t * p_packet) return NRF_ERROR_DATA_SIZE; } + // Valid peer activity detected. Hence restart the DFU timer. + err_code = dfu_timer_restart(); + VERIFY_SUCCESS(err_code); + p_data = (uint32_t *)p_packet->params.data_packet.p_data_packet; if ( is_ota() ) @@ -413,6 +477,7 @@ uint32_t dfu_init_pkt_complete(void) uint32_t dfu_init_pkt_handle(dfu_update_packet_t * p_packet) { + uint32_t err_code = NRF_SUCCESS; uint32_t length; switch (m_dfu_state) @@ -430,6 +495,10 @@ uint32_t dfu_init_pkt_handle(dfu_update_packet_t * p_packet) return NRF_ERROR_INVALID_STATE; } + // Valid peer activity detected. Hence restart the DFU timer. + err_code = dfu_timer_restart(); + VERIFY_SUCCESS(err_code); + length = p_packet->params.data_packet.packet_length * sizeof(uint32_t); if ((m_init_packet_length + length) > sizeof(m_init_packet)) { @@ -469,9 +538,16 @@ uint32_t dfu_image_validate() { m_dfu_state = DFU_STATE_VALIDATE; - err_code = dfu_init_postvalidate((uint8_t *)mp_storage_handle_active->block_id, m_image_size); - VERIFY_SUCCESS(err_code); - m_dfu_state = DFU_STATE_WAIT_4_ACTIVATE; + // Valid peer activity detected. Hence restart the DFU timer. + err_code = dfu_timer_restart(); + if (err_code == NRF_SUCCESS) + { + err_code = dfu_init_postvalidate((uint8_t *)mp_storage_handle_active->block_id, + m_image_size); + VERIFY_SUCCESS(err_code); + + m_dfu_state = DFU_STATE_WAIT_4_ACTIVATE; + } } break; @@ -491,6 +567,11 @@ uint32_t dfu_image_activate() switch (m_dfu_state) { case DFU_STATE_WAIT_4_ACTIVATE: + + // Stop the DFU Timer because the peer activity need not be monitored any longer. + err_code = app_timer_stop(m_dfu_timer_id); + APP_ERROR_CHECK(err_code); + err_code = m_functions.activate(); break; From 450ce3b3c6b0c480c34f60b62b994496626bba59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 01:22:12 -0300 Subject: [PATCH 29/84] Definitions for DUAL BANKED firmware updates (storing 2 copies of the firmware, so if a firmware update fails, the bootloader can revert to the previously working firmware) --- lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h b/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h index 4148e1c9..f86a684c 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h @@ -74,8 +74,12 @@ static inline bool is_sd_existed(void) #endif #define DFU_IMAGE_MAX_SIZE_FULL (DFU_REGION_TOTAL_SIZE - DFU_APP_DATA_RESERVED) /**< Maximum size of an application, excluding save data from the application. */ +#define DFU_IMAGE_MAX_SIZE_BANKED (((DFU_IMAGE_MAX_SIZE_FULL) - \ + (DFU_IMAGE_MAX_SIZE_FULL % (2 * CODE_PAGE_SIZE)))/2) /**< Maximum size of an application, excluding save data from the application. */ + #define DFU_BL_IMAGE_MAX_SIZE (BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS - BOOTLOADER_REGION_START) /**< Maximum size of a bootloader, excluding save data from the current bootloader. */ #define DFU_BANK_0_REGION_START CODE_REGION_1_START /**< Bank 0 region start. */ +#define DFU_BANK_1_REGION_START (DFU_BANK_0_REGION_START + DFU_IMAGE_MAX_SIZE_BANKED) /**< Bank 1 region start. */ #define EMPTY_FLASH_MASK 0xFFFFFFFF /**< Bit mask that defines an empty address in flash. */ From 88eab38a6c8692c3a4902639148013373de1e303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 01:26:03 -0300 Subject: [PATCH 30/84] Allow flashing of a SINGLE BANK bootloader after a DUAL BANKED bootloader was installed. Without this patch, the SINGLE BANK bootloader will end in a continuous loop and no recovery without a JTAG programmer is possible --- .../bootloader_dfu/dfu_single_bank.c | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c index a01e0ba9..6017e1f2 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c @@ -731,27 +731,38 @@ uint32_t dfu_bl_image_swap(void) return NRF_SUCCESS; } - uint32_t dfu_bl_image_validate(void) { bootloader_settings_t bootloader_settings; - sd_mbr_command_t sd_mbr_cmd; + + sd_mbr_command_t sd_mbr_cmd_1; + sd_mbr_command_t sd_mbr_cmd_2; bootloader_settings_get(&bootloader_settings); if (bootloader_settings.bl_image_size != 0) { - uint32_t bl_image_start = (bootloader_settings.sd_image_size == 0) ? + uint32_t bl_image_start_1 = (bootloader_settings.sd_image_size == 0) ? DFU_BANK_0_REGION_START : bootloader_settings.sd_image_start + bootloader_settings.sd_image_size; - sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE; - sd_mbr_cmd.params.compare.ptr1 = (uint32_t *)BOOTLOADER_REGION_START; - sd_mbr_cmd.params.compare.ptr2 = (uint32_t *)(bl_image_start); - sd_mbr_cmd.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t); + sd_mbr_cmd_1.command = SD_MBR_COMMAND_COMPARE; + sd_mbr_cmd_1.params.compare.ptr1 = (uint32_t *)BOOTLOADER_REGION_START; + sd_mbr_cmd_1.params.compare.ptr2 = (uint32_t *)(bl_image_start_1); + sd_mbr_cmd_1.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t); - return sd_mbr_command(&sd_mbr_cmd); + uint32_t bl_image_start_2 = (bootloader_settings.sd_image_size == 0) ? + DFU_BANK_1_REGION_START : + bootloader_settings.sd_image_start + + bootloader_settings.sd_image_size; + + sd_mbr_cmd_2.command = SD_MBR_COMMAND_COMPARE; + sd_mbr_cmd_2.params.compare.ptr1 = (uint32_t *)BOOTLOADER_REGION_START; + sd_mbr_cmd_2.params.compare.ptr2 = (uint32_t *)(bl_image_start_2); + sd_mbr_cmd_2.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t); + + return sd_mbr_command(&sd_mbr_cmd_1) && sd_mbr_command(&sd_mbr_cmd_2); } return NRF_SUCCESS; } From d8c27bb7fd9dd0a167af2437431964bcfbb97568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 01:31:36 -0300 Subject: [PATCH 31/84] If there is no application in FLASH, or flash was corrupted, make sure to restart in DFU mode --- src/main.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/main.c b/src/main.c index 0011640b..11112206 100644 --- a/src/main.c +++ b/src/main.c @@ -48,7 +48,10 @@ #include "nrf_soc.h" #include "nrf_nvic.h" #include "app_error.h" +#include "nrf_gpio.h" #include "ble.h" +#include "nrf.h" +#include "ble_hci.h" #include "app_scheduler.h" #include "nrf_error.h" @@ -56,10 +59,13 @@ #include "pstorage_platform.h" #include "nrf_mbr.h" +#include "pstorage.h" +#include "nrfx_nvmc.h" #ifdef NRF_USBD #include "uf2/uf2.h" +#include "nrf_usbd.h" #include "tusb.h" void usb_init(bool cdc_only); @@ -218,6 +224,9 @@ int main(void) { bootloader_app_start(); } + // No application was loaded, reset the system with the OTA DFU update + NRF_POWER->GPREGRET = 0xA8; + NVIC_SystemReset(); } @@ -242,17 +251,14 @@ static void check_dfu_mode(void) { (((*dbl_reset_mem) == DFU_DBL_RESET_MAGIC) && reason_reset_pin); // Clear GPREGRET if it is our values - if (dfu_start || dfu_skip) { - NRF_POWER->GPREGRET = 0; - } + if (dfu_start || dfu_skip) NRF_POWER->GPREGRET = 0; // skip dfu entirely - if (dfu_skip) { - return; - } + if (dfu_skip) return; /*------------- Determine DFU mode (Serial, OTA, FRESET or normal) -------------*/ - dfu_start = dfu_start || button_pressed(BUTTON_DFU); // DFU button pressed + // DFU button pressed + dfu_start = dfu_start || button_pressed(BUTTON_DFU); // DFU + FRESET are pressed --> OTA _ota_dfu = _ota_dfu || (button_pressed(BUTTON_DFU) && button_pressed(BUTTON_FRESET)); @@ -260,16 +266,16 @@ static void check_dfu_mode(void) { bool const valid_app = bootloader_app_is_valid(); bool const just_start_app = valid_app && !dfu_start && (*dbl_reset_mem) == DFU_DBL_RESET_APP; - if (!just_start_app && APP_ASKS_FOR_SINGLE_TAP_RESET()) { - dfu_start = 1; - } + if (!just_start_app && APP_ASKS_FOR_SINGLE_TAP_RESET()) dfu_start = 1; // App mode: Double Reset detection or DFU startup for nrf52832 if (!(just_start_app || dfu_start || !valid_app)) { #ifdef NRF52832_XXAA /* Even DFU is not active, we still force an 1000 ms dfu serial mode when startup * to support auto programming from Arduino IDE - * Note: Double Reset WONT work with nrf52832 since all its SRAM got cleared with GPIO reset. */ + * + * Note: Double Reset WONT work with nrf52832 since all its SRAM got cleared with GPIO reset. + */ bootloader_dfu_start(false, DFU_SERIAL_STARTUP_INTERVAL, false); #else // Note: RESETREAS is not clear by bootloader, it should be cleared by application upon init() From f20bfe9f633bd7aa209a2a84c36d9866f3a144f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 01:40:51 -0300 Subject: [PATCH 32/84] Add DUAL_BANK functionality (disabled by default, can be enabled passing DUAL_BANK=1 to the Makefile in the make command line --- Makefile | 13 +- .../libraries/bootloader_dfu/dfu_dual_bank.c | 891 ++++++++++++++++++ src/main.c | 5 + 3 files changed, 905 insertions(+), 4 deletions(-) create mode 100644 lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c diff --git a/Makefile b/Makefile index dba3c836..48a4d4dd 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,11 @@ #------------------------------------------------------------------------------ # CONFIGURE -# - SDK_PATH : path to SDK directory +# - SDK_PATH : path to SDK directory # -# - SD_NAME : e.g s132, s140 -# - SD_VERSION : SoftDevice version e.g 6.0.0 -# - SD_HEX : to bootloader hex binary +# - SD_NAME : e.g s132, s140 +# - SD_VERSION : SoftDevice version e.g 6.0.0 +# - SD_HEX : to bootloader hex binary +# - DUALBANK_FW : If bootloader will implement a dual bank feature to allow autorecover from failed #------------------------------------------------------------------------------ # local customization @@ -163,7 +164,11 @@ C_SRC += $(SDK11_PATH)/libraries/bootloader_dfu/bootloader_settings.c C_SRC += $(SDK11_PATH)/libraries/bootloader_dfu/bootloader_util.c C_SRC += $(SDK11_PATH)/libraries/bootloader_dfu/dfu_transport_serial.c C_SRC += $(SDK11_PATH)/libraries/bootloader_dfu/dfu_transport_ble.c +ifeq ($(DUALBANK_FW), 1) +C_SRC += $(SDK11_PATH)/libraries/bootloader_dfu/dfu_dual_bank.c +else C_SRC += $(SDK11_PATH)/libraries/bootloader_dfu/dfu_single_bank.c +endif C_SRC += $(SDK11_PATH)/ble/ble_services/ble_dfu/ble_dfu.c C_SRC += $(SDK11_PATH)/ble/ble_services/ble_dis/ble_dis.c C_SRC += $(SDK11_PATH)/drivers_nrf/pstorage/pstorage_raw.c diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c new file mode 100644 index 00000000..4516e4f0 --- /dev/null +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c @@ -0,0 +1,891 @@ +/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +#include +#include "dfu.h" +#include +#include "dfu_bank_internal.h" +#include "nrf.h" +#include "nrf_sdm.h" +#include "app_error.h" +#include "app_timer.h" +#include "bootloader.h" +#include "bootloader_types.h" +#include "pstorage.h" +#include "nrf_mbr.h" +#include "dfu_init.h" +#include "sdk_common.h" + +static dfu_state_t m_dfu_state; /**< Current DFU state. */ +static uint32_t m_image_size; /**< Size of the image that will be transmitted. */ + +static dfu_start_packet_t m_start_packet; /**< Start packet received for this update procedure. Contains update mode and image sizes information to be used for image transfer. */ +static uint8_t m_init_packet[128]; /**< Init packet, can hold CRC, Hash, Signed Hash and similar, for image validation, integrety check and authorization checking. */ +static uint8_t m_init_packet_length; /**< Length of init packet received. */ +static uint16_t m_image_crc; /**< Calculated CRC of the image received. */ + +APP_TIMER_DEF(m_dfu_timer_id); /**< Application timer id. */ +static bool m_dfu_timed_out = false; /**< Boolean flag value for tracking DFU timer timeout state. */ + +static pstorage_handle_t m_storage_handle_swap; /**< Pstorage handle for the swap area (bank 1). Bank used when updating an application or bootloader without SoftDevice. */ +static pstorage_handle_t m_storage_handle_app; /**< Pstorage handle for the application area (bank 0). Bank used when updating a SoftDevice w/wo bootloader. Handle also used when swapping received application from bank 1 to bank 0. */ +static pstorage_handle_t * mp_storage_handle_active; /**< Pointer to the pstorage handle for the active bank for receiving of data packets. */ + +static dfu_callback_t m_data_pkt_cb; /**< Callback from DFU Bank module for notification of asynchronous operation such as flash prepare. */ +static dfu_bank_func_t m_functions; /**< Structure holding operations for the selected update process. */ + + +/**@brief Function for handling callbacks from pstorage module. + * + * @details Handles pstorage results for clear and storage operation. For detailed description of + * the parameters provided with the callback, please refer to \ref pstorage_ntf_cb_t. + */ +static void pstorage_callback_handler(pstorage_handle_t * p_handle, + uint8_t op_code, + uint32_t result, + uint8_t * p_data, + uint32_t data_len) +{ + switch (op_code) + { + case PSTORAGE_STORE_OP_CODE: + if ((m_dfu_state == DFU_STATE_RX_DATA_PKT) && (m_data_pkt_cb != NULL)) + { + m_data_pkt_cb(DATA_PACKET, result, p_data); + } + break; + + case PSTORAGE_CLEAR_OP_CODE: + if (m_dfu_state == DFU_STATE_PREPARING) + { + m_functions.cleared(); + m_dfu_state = DFU_STATE_RDY; + if (m_data_pkt_cb != NULL) + { + m_data_pkt_cb(START_PACKET, result, p_data); + } + } + break; + + default: + break; + } + APP_ERROR_CHECK(result); +} + + +/**@brief Function for handling the DFU timeout. + * + * @param[in] p_context The timeout context. + */ +static void dfu_timeout_handler(void * p_context) +{ + UNUSED_PARAMETER(p_context); + dfu_update_status_t update_status = {0}; + + m_dfu_timed_out = true; + update_status.status_code = DFU_TIMEOUT; + + bootloader_dfu_update_process(update_status); +} + + +/**@brief Function for restarting the DFU Timer. + * + * @details This function will stop and restart the DFU timer. This function will be called by the + * functions handling any DFU packet received from the peer that is transferring a firmware + * image. + */ +static uint32_t dfu_timer_restart(void) +{ + if (m_dfu_timed_out) + { + // The DFU timer had already timed out. + return NRF_ERROR_INVALID_STATE; + } + + uint32_t err_code = app_timer_stop(m_dfu_timer_id); + APP_ERROR_CHECK(err_code); + + err_code = app_timer_start(m_dfu_timer_id, DFU_TIMEOUT_INTERVAL, NULL); + APP_ERROR_CHECK(err_code); + + return err_code; +} + + +/**@brief Function for preparing of flash before receiving SoftDevice image. + * + * @details This function will erase current application area to ensure sufficient amount of + * storage for the SoftDevice image. Upon erase complete a callback will be done. + * See \ref dfu_bank_prepare_t for further details. + */ +static void dfu_prepare_func_app_erase(uint32_t image_size) +{ + mp_storage_handle_active = &m_storage_handle_app; + + // Doing a SoftDevice update thus current application must be cleared to ensure enough space + // for new SoftDevice. + m_dfu_state = DFU_STATE_PREPARING; + + if ( is_ota() ) + { + uint32_t err_code = pstorage_clear(&m_storage_handle_app, m_image_size); + APP_ERROR_CHECK(err_code); + } + else + { + flash_nrf5x_erase(DFU_BANK_0_REGION_START, m_image_size); + + // invoke complete callback + pstorage_callback_handler(&m_storage_handle_app, PSTORAGE_CLEAR_OP_CODE, NRF_SUCCESS, NULL, 0); + } +} + + +/**@brief Function for preparing swap before receiving application or bootloader image. + * + * @details This function will erase current swap area to ensure flash is ready for storage of the + * Application or Bootloader image. Upon erase complete a callback will be done. + * See \ref dfu_bank_prepare_t for further details. + */ +static void dfu_prepare_func_swap_erase(uint32_t image_size) +{ + mp_storage_handle_active = &m_storage_handle_swap; + + m_dfu_state = DFU_STATE_PREPARING; + + if ( is_ota() ) + { + uint32_t err_code = pstorage_clear(&m_storage_handle_swap, image_size); + APP_ERROR_CHECK(err_code); + } + else + { + flash_nrf5x_erase(DFU_BANK_1_REGION_START, image_size); + + // invoke complete callback + pstorage_callback_handler(&m_storage_handle_swap, PSTORAGE_CLEAR_OP_CODE, NRF_SUCCESS, NULL, 0); + } +} + + +/**@brief Function for handling behaviour when clear operation has completed. + */ +static void dfu_cleared_func_swap(void) +{ + // Do nothing. +} + + +/**@brief Function for handling behaviour when clear operation has completed. + */ +static void dfu_cleared_func_app(void) +{ + dfu_update_status_t update_status = { 0 }; + update_status.status_code = DFU_BANK_0_ERASED; + bootloader_dfu_update_process(update_status); +} + + +/**@brief Function for calculating storage offset for receiving SoftDevice image. + * + * @details When a new SoftDevice is received it will be temporary stored in flash before moved to + * address 0x0. In order to succesfully validate transfer and relocation it is important + * that temporary image and final installed image does not ovwerlap hence an offset must + * be calculated in case new image is larger than currently installed SoftDevice. + */ +uint32_t offset_calculate(uint32_t sd_image_size) +{ + uint32_t offset = 0; + + if (m_start_packet.sd_image_size > DFU_BANK_0_REGION_START) + { + uint32_t page_mask = (CODE_PAGE_SIZE - 1); + uint32_t diff = m_start_packet.sd_image_size - DFU_BANK_0_REGION_START; + + offset = diff & ~page_mask; + + // Align offset to next page if image size is not page sized. + if ((diff & page_mask) > 0) + { + offset += CODE_PAGE_SIZE; + } + } + + return offset; +} + + +/**@brief Function for activating received SoftDevice image. + * + * @note This function will not move the SoftDevice image. + * The bootloader settings will be marked as SoftDevice update complete and the swapping of + * current SoftDevice will occur after system reset. + * + * @return NRF_SUCCESS on success. + */ +static uint32_t dfu_activate_sd(void) +{ + dfu_update_status_t update_status = {0}; + + update_status.status_code = DFU_UPDATE_SD_COMPLETE; + update_status.app_crc = m_image_crc; + update_status.sd_image_start = DFU_BANK_0_REGION_START; + update_status.sd_size = m_start_packet.sd_image_size; + update_status.bl_size = m_start_packet.bl_image_size; + update_status.app_size = m_start_packet.app_image_size; + + bootloader_dfu_update_process(update_status); + + return NRF_SUCCESS; +} + + +/**@brief Function for activating received Application image. + * + * @details This function will move the received application image fram swap (bank 1) to + * application area (bank 0). + * + * @return NRF_SUCCESS on success. Error code otherwise. + */ +static uint32_t dfu_activate_app(void) +{ + uint32_t err_code = NRF_SUCCESS; + + // Erase BANK 0. + if ( is_ota() ) + { + err_code = pstorage_clear(&m_storage_handle_app, m_start_packet.app_image_size); + APP_ERROR_CHECK(err_code); + + err_code = pstorage_store(&m_storage_handle_app, + (uint8_t *)m_storage_handle_swap.block_id, + m_start_packet.app_image_size, + 0); + } + else + { + flash_nrf5x_erase(DFU_BANK_0_REGION_START, m_start_packet.app_image_size); + pstorage_callback_handler(&m_storage_handle_app, PSTORAGE_CLEAR_OP_CODE, NRF_SUCCESS, NULL, 0); + + flash_nrf5x_write(DFU_BANK_0_REGION_START, (uint8_t *)DFU_BANK_1_REGION_START, m_start_packet.app_image_size, false); + flash_nrf5x_flush(false); + pstorage_callback_handler(&m_storage_handle_app, PSTORAGE_STORE_OP_CODE, NRF_SUCCESS, (uint8_t *) DFU_BANK_1_REGION_START, m_start_packet.app_image_size); + } + + if (err_code == NRF_SUCCESS) + { + dfu_update_status_t update_status = { 0 }; + + memset(&update_status, 0, sizeof(dfu_update_status_t )); + update_status.status_code = DFU_UPDATE_APP_COMPLETE; + update_status.app_crc = m_image_crc; + update_status.app_size = m_start_packet.app_image_size; + + bootloader_dfu_update_process(update_status); + } + + return err_code; +} + + +/**@brief Function for activating received Bootloader image. + * + * @note This function will not move the bootloader image. + * The bootloader settings will be marked as Bootloader update complete and the swapping of + * current bootloader will occur after system reset. + * + * @return NRF_SUCCESS on success. + */ +static uint32_t dfu_activate_bl(void) +{ + dfu_update_status_t update_status = {0}; + + update_status.status_code = DFU_UPDATE_BOOT_COMPLETE; + update_status.app_crc = m_image_crc; + update_status.sd_size = m_start_packet.sd_image_size; + update_status.bl_size = m_start_packet.bl_image_size; + update_status.app_size = m_start_packet.app_image_size; + + bootloader_dfu_update_process(update_status); + + return NRF_SUCCESS; +} + + +uint32_t dfu_init(void) +{ + uint32_t err_code; + pstorage_module_param_t storage_module_param = {.cb = pstorage_callback_handler}; + + m_init_packet_length = 0; + m_image_crc = 0; + + err_code = pstorage_register(&storage_module_param, &m_storage_handle_app); + if (err_code != NRF_SUCCESS) + { + m_dfu_state = DFU_STATE_INIT_ERROR; + return err_code; + } + + m_storage_handle_app.block_id = DFU_BANK_0_REGION_START; + m_storage_handle_swap = m_storage_handle_app; + m_storage_handle_swap.block_id = DFU_BANK_1_REGION_START; + + // Create the timer to monitor the activity by the peer doing the firmware update. + err_code = app_timer_create(&m_dfu_timer_id, + APP_TIMER_MODE_SINGLE_SHOT, + dfu_timeout_handler); + APP_ERROR_CHECK(err_code); + + // Start the DFU timer. + err_code = app_timer_start(m_dfu_timer_id, DFU_TIMEOUT_INTERVAL, NULL); + APP_ERROR_CHECK(err_code); + + m_data_received = 0; + m_dfu_state = DFU_STATE_IDLE; + + return NRF_SUCCESS; +} + + +void dfu_register_callback(dfu_callback_t callback_handler) +{ + m_data_pkt_cb = callback_handler; +} + + +uint32_t dfu_start_pkt_handle(dfu_update_packet_t * p_packet) +{ + uint32_t err_code; + + m_start_packet = *(p_packet->params.start_packet); + + // Check that the requested update procedure is supported. + // Currently the following combinations are allowed: + // - Application + // - SoftDevice + // - Bootloader + // - SoftDevice with Bootloader + if (IS_UPDATING_APP(m_start_packet) && + (IS_UPDATING_SD(m_start_packet) || IS_UPDATING_BL(m_start_packet))) + { + // App update is only supported independently. + return NRF_ERROR_NOT_SUPPORTED; + } + + if (!(IS_WORD_SIZED(m_start_packet.sd_image_size) && + IS_WORD_SIZED(m_start_packet.bl_image_size) && + IS_WORD_SIZED(m_start_packet.app_image_size))) + { + // Image_sizes are not a multiple of 4 (word size). + return NRF_ERROR_NOT_SUPPORTED; + } + + m_image_size = m_start_packet.sd_image_size + m_start_packet.bl_image_size + + m_start_packet.app_image_size; + + if (m_start_packet.bl_image_size > DFU_BL_IMAGE_MAX_SIZE) + { + return NRF_ERROR_DATA_SIZE; + } + + if (IS_UPDATING_SD(m_start_packet)) + { + if (m_image_size > (DFU_IMAGE_MAX_SIZE_FULL)) + { + return NRF_ERROR_DATA_SIZE; + } + m_functions.prepare = dfu_prepare_func_app_erase; + m_functions.cleared = dfu_cleared_func_app; + m_functions.activate = dfu_activate_sd; + } + else + { + if (m_image_size > DFU_IMAGE_MAX_SIZE_BANKED) + { + return NRF_ERROR_DATA_SIZE; + } + + m_functions.prepare = dfu_prepare_func_swap_erase; + m_functions.cleared = dfu_cleared_func_swap; + if (IS_UPDATING_BL(m_start_packet)) + { + m_functions.activate = dfu_activate_bl; + } + else + { + m_functions.activate = dfu_activate_app; + } + } + + if ( DFU_STATE_IDLE != m_dfu_state ) + return NRF_ERROR_INVALID_STATE; + + // Valid peer activity detected. Hence restart the DFU timer. + err_code = dfu_timer_restart(); + VERIFY_SUCCESS(err_code); + + m_functions.prepare(m_image_size); + + return NRF_SUCCESS; +} + + +uint32_t dfu_data_pkt_handle(dfu_update_packet_t * p_packet) +{ + uint32_t data_length; + uint32_t err_code; + uint32_t * p_data; + + VERIFY_PARAM_NOT_NULL(p_packet); + + // Check pointer alignment. + if (!is_word_aligned(p_packet->params.data_packet.p_data_packet)) + { + // The p_data_packet is not word aligned address. + return NRF_ERROR_INVALID_ADDR; + } + + switch (m_dfu_state) + { + case DFU_STATE_RDY: + case DFU_STATE_RX_INIT_PKT: + return NRF_ERROR_INVALID_STATE; + + case DFU_STATE_RX_DATA_PKT: + data_length = p_packet->params.data_packet.packet_length * sizeof(uint32_t); + + if ((m_data_received + data_length) > m_image_size) + { + // The caller is trying to write more bytes into the flash than the size provided to + // the dfu_image_size_set function. This is treated as a serious error condition and + // an unrecoverable one. Hence point the variable mp_app_write_address to the top of + // the flash area. This will ensure that all future application data packet writes + // will be blocked because of the above check. + m_data_received = 0xFFFFFFFF; + + return NRF_ERROR_DATA_SIZE; + } + + // Valid peer activity detected. Hence restart the DFU timer. + err_code = dfu_timer_restart(); + VERIFY_SUCCESS(err_code); + + p_data = (uint32_t *)p_packet->params.data_packet.p_data_packet; + + if ( is_ota() ) + { + err_code = pstorage_store(mp_storage_handle_active, (uint8_t *)p_data, data_length, m_data_received); + VERIFY_SUCCESS(err_code); + } + else + { + flash_nrf5x_write( + (mp_storage_handle_active == &m_storage_handle_app + ? DFU_BANK_0_REGION_START + : DFU_BANK_1_REGION_START) + + m_data_received, p_data, data_length, false); + pstorage_callback_handler(mp_storage_handle_active, PSTORAGE_STORE_OP_CODE, NRF_SUCCESS, (uint8_t *) p_data, data_length); + } + + m_data_received += data_length; + + if (m_data_received != m_image_size) + { + // The entire image is not received yet. More data is expected. + err_code = NRF_ERROR_INVALID_LENGTH; + } + else + { + if ( !is_ota() ) flash_nrf5x_flush(false); + + // The entire image has been received. Return NRF_SUCCESS. + err_code = NRF_SUCCESS; + } + break; + + default: + err_code = NRF_ERROR_INVALID_STATE; + break; + } + + return err_code; +} + + +uint32_t dfu_init_pkt_complete(void) +{ + uint32_t err_code = NRF_ERROR_INVALID_STATE; + + // DFU initialization has been done and a start packet has been received. + if (IMAGE_WRITE_IN_PROGRESS()) + { + // Image write is already in progress. Cannot handle an init packet now. + return NRF_ERROR_INVALID_STATE; + } + + if (m_dfu_state == DFU_STATE_RX_INIT_PKT) + { + err_code = dfu_init_prevalidate(m_init_packet, m_init_packet_length, m_start_packet.dfu_update_mode); + if (err_code == NRF_SUCCESS) + { + m_dfu_state = DFU_STATE_RX_DATA_PKT; + } + else + { + m_init_packet_length = 0; + } + } + return err_code; +} + + +uint32_t dfu_init_pkt_handle(dfu_update_packet_t * p_packet) +{ + uint32_t err_code = NRF_SUCCESS; + uint32_t length; + + switch (m_dfu_state) + { + case DFU_STATE_RDY: + m_dfu_state = DFU_STATE_RX_INIT_PKT; + // When receiving init packet in state ready just update and fall through this case. + /* FALLTHRU */ + + case DFU_STATE_RX_INIT_PKT: + // DFU initialization has been done and a start packet has been received. + if (IMAGE_WRITE_IN_PROGRESS()) + { + // Image write is already in progress. Cannot handle an init packet now. + return NRF_ERROR_INVALID_STATE; + } + + // Valid peer activity detected. Hence restart the DFU timer. + err_code = dfu_timer_restart(); + VERIFY_SUCCESS(err_code); + + length = p_packet->params.data_packet.packet_length * sizeof(uint32_t); + if ((m_init_packet_length + length) > sizeof(m_init_packet)) + { + return NRF_ERROR_INVALID_LENGTH; + } + + memcpy(&m_init_packet[m_init_packet_length], + &p_packet->params.data_packet.p_data_packet[0], + length); + m_init_packet_length += length; + break; + + default: + // Either the start packet was not received or dfu_init function was not called before. + return NRF_ERROR_INVALID_STATE; + } + + return NRF_SUCCESS; +} + + +uint32_t dfu_image_validate() +{ + uint32_t err_code; + + switch (m_dfu_state) + { + case DFU_STATE_RX_DATA_PKT: + // Check if the application image write has finished. + if (m_data_received != m_image_size) + { + // Image not yet fully transfered by the peer or the peer has attempted to write + // too much data. Hence the validation should fail. + err_code = NRF_ERROR_INVALID_STATE; + } + else + { + m_dfu_state = DFU_STATE_VALIDATE; + + // Valid peer activity detected. Hence restart the DFU timer. + err_code = dfu_timer_restart(); + if (err_code == NRF_SUCCESS) + { + err_code = dfu_init_postvalidate((uint8_t *)mp_storage_handle_active->block_id, + m_image_size); + VERIFY_SUCCESS(err_code); + + m_dfu_state = DFU_STATE_WAIT_4_ACTIVATE; + } + } + break; + + default: + err_code = NRF_ERROR_INVALID_STATE; + break; + } + + return err_code; +} + + +uint32_t dfu_image_activate() +{ + uint32_t err_code; + + switch (m_dfu_state) + { + case DFU_STATE_WAIT_4_ACTIVATE: + + // Stop the DFU Timer because the peer activity need not be monitored any longer. + err_code = app_timer_stop(m_dfu_timer_id); + APP_ERROR_CHECK(err_code); + + err_code = m_functions.activate(); + break; + + default: + err_code = NRF_ERROR_INVALID_STATE; + break; + } + + return err_code; +} + + +void dfu_reset(void) +{ + dfu_update_status_t update_status = { 0 }; + + update_status.status_code = DFU_RESET; + + bootloader_dfu_update_process(update_status); +} + + +static uint32_t dfu_compare_block(uint32_t * ptr1, uint32_t * ptr2, uint32_t len) +{ + sd_mbr_command_t sd_mbr_cmd; + + sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE; + sd_mbr_cmd.params.compare.ptr1 = ptr1; + sd_mbr_cmd.params.compare.ptr2 = ptr2; + sd_mbr_cmd.params.compare.len = len / sizeof(uint32_t); + + return sd_mbr_command(&sd_mbr_cmd); +} + + +static uint32_t dfu_copy_sd(uint32_t * src, uint32_t * dst, uint32_t len) +{ + sd_mbr_command_t sd_mbr_cmd; + + sd_mbr_cmd.command = SD_MBR_COMMAND_COPY_SD; + sd_mbr_cmd.params.copy_sd.src = src; + sd_mbr_cmd.params.copy_sd.dst = dst; + sd_mbr_cmd.params.copy_sd.len = len / sizeof(uint32_t); + + return sd_mbr_command(&sd_mbr_cmd); +} + + +static uint32_t dfu_sd_img_block_swap(uint32_t * src, + uint32_t * dst, + uint32_t len, + uint32_t block_size) +{ + // It is neccesarry to swap the new SoftDevice in 3 rounds to ensure correct copy of data + // and verifucation of data in case power reset occurs during write to flash. + // To ensure the robustness of swapping the images are compared backwards till start of + // image swap. If the back is identical everything is swapped. + uint32_t err_code = dfu_compare_block(src, dst, len); + if (err_code == NRF_SUCCESS) + { + return err_code; + } + + if ((uint32_t)dst > SOFTDEVICE_REGION_START) + { + err_code = dfu_sd_img_block_swap((uint32_t *)((uint32_t)src - block_size), + (uint32_t *)((uint32_t)dst - block_size), + block_size, + block_size); + VERIFY_SUCCESS(err_code); + } + + err_code = dfu_copy_sd(src, dst, len); + VERIFY_SUCCESS(err_code); + + return dfu_compare_block(src, dst, len); +} + + +uint32_t dfu_sd_image_swap(void) +{ + bootloader_settings_t boot_settings; + + bootloader_settings_get(&boot_settings); + + if (boot_settings.sd_image_size == 0) + { + return NRF_SUCCESS; + } + + if ((SOFTDEVICE_REGION_START + boot_settings.sd_image_size) > boot_settings.sd_image_start) + { + uint32_t err_code; + uint32_t sd_start = SOFTDEVICE_REGION_START; + uint32_t block_size = (boot_settings.sd_image_start - sd_start) / 2; + + /* ##### FIX START ##### */ + block_size &= ~(uint32_t)(CODE_PAGE_SIZE - 1); + /* ##### FIX END ##### */ + + uint32_t image_end = boot_settings.sd_image_start + boot_settings.sd_image_size; + + uint32_t img_block_start = boot_settings.sd_image_start + 2 * block_size; + uint32_t sd_block_start = sd_start + 2 * block_size; + + if (SD_SIZE_GET(MBR_SIZE) < boot_settings.sd_image_size) + { + // This will clear a page thus ensuring the old image is invalidated before swapping. + err_code = dfu_copy_sd((uint32_t *)(sd_start + block_size), + (uint32_t *)(sd_start + block_size), + sizeof(uint32_t)); + VERIFY_SUCCESS(err_code); + + err_code = dfu_copy_sd((uint32_t *)sd_start, (uint32_t *)sd_start, sizeof(uint32_t)); + VERIFY_SUCCESS(err_code); + } + + return dfu_sd_img_block_swap((uint32_t *)img_block_start, + (uint32_t *)sd_block_start, + image_end - img_block_start, + block_size); + } + else + { + if (boot_settings.sd_image_size != 0) + { + return dfu_copy_sd((uint32_t *)boot_settings.sd_image_start, + (uint32_t *)SOFTDEVICE_REGION_START, + boot_settings.sd_image_size); + } + } + + return NRF_SUCCESS; +} + + +uint32_t dfu_bl_image_swap(void) +{ + bootloader_settings_t bootloader_settings; + sd_mbr_command_t sd_mbr_cmd; + + bootloader_settings_get(&bootloader_settings); + + if (bootloader_settings.bl_image_size != 0) + { + uint32_t bl_image_start = (bootloader_settings.sd_image_size == 0) ? + DFU_BANK_1_REGION_START : + bootloader_settings.sd_image_start + + bootloader_settings.sd_image_size; + + sd_mbr_cmd.command = SD_MBR_COMMAND_COPY_BL; + sd_mbr_cmd.params.copy_bl.bl_src = (uint32_t *)(bl_image_start); + sd_mbr_cmd.params.copy_bl.bl_len = bootloader_settings.bl_image_size / sizeof(uint32_t); + + return sd_mbr_command(&sd_mbr_cmd); + } + return NRF_SUCCESS; +} + + +uint32_t dfu_bl_image_validate(void) +{ + bootloader_settings_t bootloader_settings;\ + + sd_mbr_command_t sd_mbr_cmd_1; + sd_mbr_command_t sd_mbr_cmd_2; + + bootloader_settings_get(&bootloader_settings); + + if (bootloader_settings.bl_image_size != 0) + { + uint32_t bl_image_start_1 = (bootloader_settings.sd_image_size == 0) ? + DFU_BANK_0_REGION_START : + bootloader_settings.sd_image_start + + bootloader_settings.sd_image_size; + + sd_mbr_cmd_1.command = SD_MBR_COMMAND_COMPARE; + sd_mbr_cmd_1.params.compare.ptr1 = (uint32_t *)BOOTLOADER_REGION_START; + sd_mbr_cmd_1.params.compare.ptr2 = (uint32_t *)(bl_image_start_1); + sd_mbr_cmd_1.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t); + + uint32_t bl_image_start_2 = (bootloader_settings.sd_image_size == 0) ? + DFU_BANK_1_REGION_START : + bootloader_settings.sd_image_start + + bootloader_settings.sd_image_size; + + sd_mbr_cmd_2.command = SD_MBR_COMMAND_COMPARE; + sd_mbr_cmd_2.params.compare.ptr1 = (uint32_t *)BOOTLOADER_REGION_START; + sd_mbr_cmd_2.params.compare.ptr2 = (uint32_t *)(bl_image_start_2); + sd_mbr_cmd_2.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t); + + return sd_mbr_command(&sd_mbr_cmd_1) && sd_mbr_command(&sd_mbr_cmd_2); + } + return NRF_SUCCESS; +} + + +uint32_t dfu_sd_image_validate(void) +{ + bootloader_settings_t bootloader_settings; + sd_mbr_command_t sd_mbr_cmd; + + bootloader_settings_get(&bootloader_settings); + + if (bootloader_settings.sd_image_size == 0) + { + return NRF_SUCCESS; + } + + if ((SOFTDEVICE_REGION_START + bootloader_settings.sd_image_size) > bootloader_settings.sd_image_start) + { + uint32_t sd_start = SOFTDEVICE_REGION_START; + uint32_t block_size = (bootloader_settings.sd_image_start - sd_start) / 2; + uint32_t image_end = bootloader_settings.sd_image_start + + bootloader_settings.sd_image_size; + + /* ##### FIX START ##### */ + block_size &= ~(uint32_t)(CODE_PAGE_SIZE - 1); + /* ##### FIX END ##### */ + + uint32_t img_block_start = bootloader_settings.sd_image_start + 2 * block_size; + uint32_t sd_block_start = sd_start + 2 * block_size; + + if (SD_SIZE_GET(MBR_SIZE) < bootloader_settings.sd_image_size) + { + return NRF_ERROR_NULL; + } + + return dfu_sd_img_block_swap((uint32_t *)img_block_start, + (uint32_t *)sd_block_start, + image_end - img_block_start, + block_size); + } + + sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE; + sd_mbr_cmd.params.compare.ptr1 = (uint32_t *)SOFTDEVICE_REGION_START; + sd_mbr_cmd.params.compare.ptr2 = (uint32_t *)bootloader_settings.sd_image_start; + sd_mbr_cmd.params.compare.len = bootloader_settings.sd_image_size / sizeof(uint32_t); + + return sd_mbr_command(&sd_mbr_cmd); +} diff --git a/src/main.c b/src/main.c index 11112206..39096515 100644 --- a/src/main.c +++ b/src/main.c @@ -39,8 +39,13 @@ #include #include "nrfx.h" +#include "nrf_clock.h" #include "nrfx_power.h" +#include "nrfx_pwm.h" + +#include "nordic_common.h" #include "sdk_common.h" +#include "dfu_transport.h" #include "bootloader.h" #include "bootloader_util.h" From c8af92c4d8af6ea6836025b36e0552078d399b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 01:53:57 -0300 Subject: [PATCH 33/84] Implement signed firmware verification: If enabled (Disabled by default), the bootloader will reject and not flash any firmware that is not digitally signed with the proper key --- .gitmodules | 3 + Makefile | 49 ++- .../bootloader_dfu/dfu_single_bank.c | 2 +- src/dfu_init.c | 313 ++++++++++++------ src/main.c | 4 + src/usb/tusb_config.h | 2 + src/usb/usb.c | 2 + src/usb/usb_desc.c | 16 + 8 files changed, 282 insertions(+), 109 deletions(-) diff --git a/.gitmodules b/.gitmodules index bd80adc1..f8d40238 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,3 +8,6 @@ path = lib/uf2 url = https://github.com/microsoft/uf2.git +[submodule "lib/tinycrypt"] + path = lib/tinycrypt + url = https://github.com/intel/tinycrypt diff --git a/Makefile b/Makefile index 48a4d4dd..45dc1df4 100644 --- a/Makefile +++ b/Makefile @@ -5,9 +5,15 @@ # - SD_NAME : e.g s132, s140 # - SD_VERSION : SoftDevice version e.g 6.0.0 # - SD_HEX : to bootloader hex binary +# - SIGNED_FW : if bootloader will ONLY accept signed firmware +# - SIGNED_FW_QX : Qx for signed firmware verification +# - SIGNED_FW_QY : Qy for signed firmware verification # - DUALBANK_FW : If bootloader will implement a dual bank feature to allow autorecover from failed +# - FORCE_UF2 : if SIGNED_FW is 1, will force to include UF2 support (UNSECURE, UF2 does NOT validate signature!) #------------------------------------------------------------------------------ +PYTHON = python + # local customization -include Makefile.user @@ -17,6 +23,7 @@ SDK_PATH = lib/sdk/components SDK11_PATH = lib/sdk11/components TUSB_PATH = lib/tinyusb/src +TCRYPT_PATH = lib/tinycrypt/lib NRFX_PATH = lib/nrfx SD_PATH = lib/softdevice/$(SD_FILENAME) @@ -150,6 +157,15 @@ C_SRC += \ src/screen.c \ src/images.c \ +# if using a signed firmware +ifeq ($(SIGNED_FW), 1) +C_SRC += \ + $(TCRYPT_PATH)/source/sha256.c \ + $(TCRYPT_PATH)/source/ecc.c \ + $(TCRYPT_PATH)/source/ecc_dsa.c \ + $(TCRYPT_PATH)/source/utils.c +endif + # all files in boards C_SRC += src/boards/boards.c @@ -202,10 +218,10 @@ C_SRC += src/boards/$(BOARD)/pinconfig.c # USB Application ( MSC + UF2 ) C_SRC += \ - src/usb/msc_uf2.c \ src/usb/usb_desc.c \ - src/usb/usb.c \ - src/usb/uf2/ghostfat.c + src/usb/msc_uf2.c \ + src/usb/uf2/ghostfat.c \ + src/usb/usb.c # TinyUSB stack C_SRC += \ @@ -216,9 +232,9 @@ C_SRC += \ $(TUSB_PATH)/class/cdc/cdc_device.c \ $(TUSB_PATH)/class/msc/msc_device.c \ $(TUSB_PATH)/tusb.c - endif + #------------------------------------------------------------------------------ # Assembly Files #------------------------------------------------------------------------------ @@ -236,6 +252,11 @@ IPATH += \ src/cmsis/include \ src/usb \ $(TUSB_PATH) + +ifeq ($(SIGNED_FW), 1) +IPATH += \ + $(TCRYPT_PATH)/include +endif # nrfx IPATH += \ @@ -331,6 +352,16 @@ CFLAGS += -DSOFTDEVICE_PRESENT CFLAGS += -DUF2_VERSION='"$(GIT_VERSION)"' CFLAGS += -DBLEDIS_FW_VERSION='"$(GIT_VERSION) $(SD_NAME) $(SD_VERSION)"' +ifeq ($(SIGNED_FW), 1) +CFLAGS += -DSIGNED_FW +CFLAGS += -DSIGNED_FW_QX='$(SIGNED_FW_QX)' +CFLAGS += -DSIGNED_FW_QY='$(SIGNED_FW_QY)' +endif + +ifeq ($(FORCE_UF2), 1) +CFLAGS += -DFORCE_UF2 +endif + _VER = $(subst ., ,$(word 1, $(subst -, ,$(GIT_VERSION)))) CFLAGS += -DMK_BOOTLOADER_VERSION='($(word 1,$(_VER)) << 16) + ($(word 2,$(_VER)) << 8) + $(word 3,$(_VER))' @@ -354,9 +385,9 @@ CFLAGS += -DDFU_APP_DATA_RESERVED=$(DFU_APP_DATA_RESERVED) # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105523 # Fixes for gcc version 12, 13 and 14. -ifneq (,$(filter 12.% 13.% 14.%,$(shell $(CC) -dumpversion 2>/dev/null))) +#ifneq (,$(filter 12.% 13.% 14.%,$(shell $(CC) -dumpversion 2>/dev/null))) CFLAGS += --param=min-pagesize=0 -endif +#endif #------------------------------------------------------------------------------ # Linker Flags @@ -449,17 +480,17 @@ $(BUILD)/$(OUT_NAME).hex: $(BUILD)/$(OUT_NAME).out # Hex file with mbr (still no SD) $(BUILD)/$(OUT_NAME)_nosd.hex: $(BUILD)/$(OUT_NAME).hex @echo Create $(notdir $@) - @python3 tools/hexmerge.py --overlap=replace -o $@ $< $(MBR_HEX) + @$(PYTHON) tools/hexmerge.py --overlap=replace -o $@ $< $(MBR_HEX) # Bootolader self-update uf2 $(BUILD)/update-$(OUT_NAME)_nosd.uf2: $(BUILD)/$(OUT_NAME)_nosd.hex @echo Create $(notdir $@) - @python3 lib/uf2/utils/uf2conv.py -f $(UF2_FAMILY_ID_BOOTLOADER) -c -o $@ $^ + $(PYTHON) lib/uf2/utils/uf2conv.py -f $(UF2_FAMILY_ID_BOOTLOADER) -c -o $@ $^ # merge bootloader and sd hex together $(BUILD)/$(MERGED_FILE).hex: $(BUILD)/$(OUT_NAME).hex @echo Create $(notdir $@) - @python3 tools/hexmerge.py -o $@ $< $(SD_HEX) + @$(PYTHON) tools/hexmerge.py -o $@ $< $(SD_HEX) # Create pkg zip file for bootloader+SD combo to use with DFU CDC $(BUILD)/$(MERGED_FILE).zip: $(BUILD)/$(OUT_NAME).hex diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c index 6017e1f2..bf003c63 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c @@ -31,7 +31,7 @@ static dfu_state_t m_dfu_state; /**< Current DFU static uint32_t m_image_size; /**< Size of the image that will be transmitted. */ static dfu_start_packet_t m_start_packet; /**< Start packet received for this update procedure. Contains update mode and image sizes information to be used for image transfer. */ -static uint8_t m_init_packet[64]; /**< Init packet, can hold CRC, Hash, Signed Hash and similar, for image validation, integrety check and authorization checking. */ +static uint8_t m_init_packet[128]; /**< Init packet, can hold CRC, Hash, Signed Hash and similar, for image validation, integrety check and authorization checking. */ static uint8_t m_init_packet_length; /**< Length of init packet received. */ static uint16_t m_image_crc; /**< Calculated CRC of the image received. */ diff --git a/src/dfu_init.c b/src/dfu_init.c index e19cffa3..c848d265 100644 --- a/src/dfu_init.c +++ b/src/dfu_init.c @@ -66,16 +66,24 @@ * If the init packet is signed by a trusted source, it must be decrypted before it can be * processed. */ - + #include "dfu_init.h" #include #include #include #include "nrf_error.h" + +#ifndef SIGNED_FW #include "crc16.h" +#else +#include +#include +#include +#include +#endif /* ADAFRUIT - * - All firmware init data must has Device Type ADAFRUIT_DEVICE_TYPE (nrf52832 and nrf52840) + * - All firmware init data must have Device Type ADAFRUIT_DEVICE_TYPE (nrf52832 and nrf52840) * - SD + Bootloader upgrade must have correct Device Revision to make sure bootloader is not flashed * on the wrong device (e.g flah nRF52832's bootloader on nRF52840 board and vice versa) * - nrf52832 dev-rev is 0xADAF @@ -93,7 +101,52 @@ #error Unknown MCU #endif +#ifdef SIGNED_FW + +// The following is the layout of the extended init packet if using image length and sha256 to validate image +// and NIST P-256 + SHA256 to sign the init_package including the extended part +#define DFU_INIT_PACKET_POS_EXT_IDENTIFIER 0 //< Position of the identifier for the ext package +#define DFU_INIT_PACKET_POS_EXT_IMAGE_LENGTH 4 //< Position of the image length +#define DFU_INIT_PACKET_POS_EXT_IMAGE_HASH256 8 //< Position of the 256 +#define DFU_INIT_PACKET_POS_EXT_INIT_SIGNATURE_R 40 //< Position of the Signature R +#define DFU_INIT_PACKET_POS_EXT_INIT_SIGNATURE_S 72 //< Position of the Signature S +#define DFU_INIT_PACKET_EXT_LENGTH_SIGNED 40 //< Length of the extended init packet that is part of the signed data +#define DFU_INIT_PACKET_EXT_BEGIN + +#define DFU_SHA256_DIGEST_LENGTH 32 //< Length of SHA-256 digest +#define DFU_SIGNATURE_R_LENGTH 32 //< Length of the signature part r +#define DFU_SIGNATURE_S_LENGTH 32 //< Length of the signature part s + +#define DFU_INIT_PACKET_USES_CRC16 (0) +#define DFU_INIT_PACKET_USES_HASH (1) +#define DFU_INIT_PACKET_USES_ECDS (2) + +#if !defined(SIGNED_FW_QX) || !defined(SIGNED_FW_QY) +# error Missing definitions for SIGNED_FW_QX and/or SIGNED_FW_QY required for signed firmware verification +#endif +/** @snippet [DFU BLE Signing public key curve points] */ +static const uint8_t pk[] = { + /* x-coord : static uint8_t Qx[] = {*/ + SIGNED_FW_QX + /* 0x39, 0xb0, 0x58, 0x3d, 0x27, 0x07, 0x91, 0x38, + 0x6a, 0xa3, 0x36, 0x0f, 0xa2, 0xb5, 0x86, 0x7e, + 0xae, 0xba, 0xf7, 0xa3, 0xf4, 0x81, 0x5f, 0x78, + 0x02, 0xf2, 0xa1, 0x21, 0xd5, 0x21, 0x84, 0x12 */ + /* };*/ + , + /* y-coord : static uint8_t Qy[] = {*/ + SIGNED_FW_QY + /* 0x4a, 0x0d, 0xfe, 0xa4, 0x77, 0x50, 0xb1, 0xb5, 0x26, 0xc0, 0x9d, 0xdd, 0xf0, 0x24, 0x90, 0x57, 0x6c, 0x64, 0x3b, 0xd3, 0xdf, 0x92, 0x3b, 0xb3, 0x47, 0x97, 0x83, 0xd4, 0xfc, 0x76, 0xf5, 0x9d */ + /* };*/ +}; +/** @snippet [DFU BLE Signing public key curve points] */ + +// Helper macros for stringification +//#define VSTRINGIFY(...) #__VA_ARGS__ +//#define TO_STRING(...) VSTRINGIFY(__VA_ARGS__) +//#pragma message ("Qx: " TO_STRING(SIGNED_FW_QX)) +//#pragma message ("Qy: " TO_STRING(SIGNED_FW_QY)) #define DFU_INIT_PACKET_EXT_LENGTH_MIN 2 //< Minimum length of the extended init packet. The extended init packet may contain a CRC, a HASH, or other data. This value must be changed according to the requirements of the system. The template uses a minimum value of two in order to hold a CRC. */ #define DFU_INIT_PACKET_EXT_LENGTH_MAX 10 //< Maximum length of the extended init packet. The extended init packet may contain a CRC, a HASH, or other data. This value must be changed according to the requirements of the system. The template uses a maximum value of 10 in order to hold a CRC and any padded data on transport layer without overflow. */ @@ -101,114 +154,176 @@ static uint8_t m_extended_packet[DFU_INIT_PACKET_EXT_LENGTH_MAX]; //< Data array for storage of the extended data received. The extended data follows the normal init data of type \ref dfu_init_packet_t. Extended data can be used for a CRC, hash, signature, or other data. */ static uint8_t m_extended_packet_length; //< Length of the extended data received with init packet. */ - uint32_t dfu_init_prevalidate(uint8_t * p_init_data, uint32_t init_data_len, uint8_t image_type) { - uint32_t i = 0; - - // In order to support signing or encryption then any init packet decryption function / library - // should be called from here or implemented at this location. - - // Length check to ensure valid data are parsed. - if (init_data_len < sizeof(dfu_init_packet_t)) - { - return NRF_ERROR_INVALID_LENGTH; - } - - // Current template uses clear text data so they can be casted for pre-check. - dfu_init_packet_t * p_init_packet = (dfu_init_packet_t *)p_init_data; - - m_extended_packet_length = ((uint32_t)p_init_data + init_data_len) - - (uint32_t)&p_init_packet->softdevice[p_init_packet->softdevice_len]; - if (m_extended_packet_length < DFU_INIT_PACKET_EXT_LENGTH_MIN) - { - return NRF_ERROR_INVALID_LENGTH; - } - - if (((uint32_t)p_init_data + init_data_len) < - (uint32_t)&p_init_packet->softdevice[p_init_packet->softdevice_len]) - { - return NRF_ERROR_INVALID_LENGTH; - } - - memcpy(m_extended_packet, - &p_init_packet->softdevice[p_init_packet->softdevice_len], - m_extended_packet_length); - - /** [DFU init application version] */ - // To support application versioning, this check should be updated. - // This template allows for any application to be installed. However, - // customers can place a revision number at the bottom of the application - // to be verified by the bootloader. This can be done at a location - // relative to the application, for example the application start - // address + 0x0100. - /** [DFU init application version] */ - - // First check to verify the image to be transfered matches the device type. - // If no Device type is present in DFU_DEVICE_INFO then any image will be accepted. + uint32_t i = 0; + + // In order to support signing or encryption then any init packet decryption function / library + // should be called from here or implemented at this location. + + // Length check to ensure valid data are parsed. + if (init_data_len < sizeof(dfu_init_packet_t)) + { + return NRF_ERROR_INVALID_LENGTH; + } + + // Current template uses clear text data so they can be casted for pre-check. + dfu_init_packet_t * p_init_packet = (dfu_init_packet_t *)p_init_data; + + m_extended_packet_length = ((uint32_t)p_init_data + init_data_len) - + (uint32_t)&p_init_packet->softdevice[p_init_packet->softdevice_len]; + if (m_extended_packet_length < DFU_INIT_PACKET_EXT_LENGTH_MIN) + { + return NRF_ERROR_INVALID_LENGTH; + } + + if (((uint32_t)p_init_data + init_data_len) < + (uint32_t)&p_init_packet->softdevice[p_init_packet->softdevice_len]) + { + return NRF_ERROR_INVALID_LENGTH; + } + + memcpy(m_extended_packet, + &p_init_packet->softdevice[p_init_packet->softdevice_len], + m_extended_packet_length); + + /** [DFU init application version] */ + // To support application versioning, this check should be updated. + // This template allows for any application to be installed. However, + // customers can place a revision number at the bottom of the application + // to be verified by the bootloader. This can be done at a location + // relative to the application, for example the application start + // address + 0x0100. + /** [DFU init application version] */ + + // First check to verify the image to be transfered matches the device type. + // If no Device type is present in DFU_DEVICE_INFO then any image will be accepted. // if ((DFU_DEVICE_INFO->device_type != DFU_DEVICE_TYPE_EMPTY) && // (p_init_packet->device_type != DFU_DEVICE_INFO->device_type)) // { // return NRF_ERROR_INVALID_DATA; // } - - // Second check to verify the image to be transfered matches the device revision. - // If no Device revision is present in DFU_DEVICE_INFO then any image will be accepted. - // if ((DFU_DEVICE_INFO->device_rev != DFU_DEVICE_REVISION_EMPTY) && - // (p_init_packet->device_rev != DFU_DEVICE_INFO->device_rev)) - - if ( p_init_packet->device_type != ADAFRUIT_DEVICE_TYPE ) - { - return NRF_ERROR_FORBIDDEN; - } - - // Adafruit unlock code must match to upgrade SoftDevice and/or Bootloader - if ( image_type & (DFU_UPDATE_SD | DFU_UPDATE_BL) ) - { - if (p_init_packet->device_rev != ADAFRUIT_DEV_REV) - { - return NRF_ERROR_FORBIDDEN; - } - } - - // Third check: Check the array of supported SoftDevices by this application. - // If the installed SoftDevice does not match any SoftDevice in the list then an - // error is returned. - while (i < p_init_packet->softdevice_len) - { - if (p_init_packet->softdevice[i] == DFU_SOFTDEVICE_ANY || - p_init_packet->softdevice[i++] == SD_FWID_GET(MBR_SIZE)) - { - return NRF_SUCCESS; - } - } - - // No matching SoftDevice found - Return NRF_ERROR_INVALID_DATA. - return NRF_ERROR_INVALID_DATA; + + // Second check to verify the image to be transfered matches the device revision. + // If no Device revision is present in DFU_DEVICE_INFO then any image will be accepted. + // if ((DFU_DEVICE_INFO->device_rev != DFU_DEVICE_REVISION_EMPTY) && + // (p_init_packet->device_rev != DFU_DEVICE_INFO->device_rev)) + + if ( p_init_packet->device_type != ADAFRUIT_DEVICE_TYPE ) + { + return NRF_ERROR_FORBIDDEN; + } + + // Adafruit unlock code must match to upgrade SoftDevice and/or Bootloader + if ( image_type & (DFU_UPDATE_SD | DFU_UPDATE_BL) ) + { + if (p_init_packet->device_rev != ADAFRUIT_DEV_REV) + { + return NRF_ERROR_FORBIDDEN; + } + } + + // Third check: Check the array of supported SoftDevices by this application. + // If the installed SoftDevice does not match any SoftDevice in the list then an + // error is returned. + while (i < p_init_packet->softdevice_len) + { + if (p_init_packet->softdevice[i] == DFU_SOFTDEVICE_ANY || + p_init_packet->softdevice[i] == SD_FWID_GET(MBR_SIZE)) + { + // Found a match. Break the loop. + break; + } + ++i; + } + + // No matching SoftDevice found - Return NRF_ERROR_INVALID_DATA. + if (i >= p_init_packet->softdevice_len) + return NRF_ERROR_INVALID_DATA; + +#ifndef SIGNED_FW + return NRF_SUCCESS; +#else + // Check that we have the correct identifier indicating that ECDS is used + if(*(uint32_t*)&m_extended_packet[DFU_INIT_PACKET_POS_EXT_IDENTIFIER] != DFU_INIT_PACKET_USES_ECDS) + { + return NRF_ERROR_INVALID_DATA; + } + + uint8_t* msg = p_init_data; + size_t msg_len = (init_data_len - m_extended_packet_length) + DFU_INIT_PACKET_EXT_LENGTH_SIGNED; + + // Create a sha256 digest + struct tc_sha256_state_struct state = {0}; + uint8_t digest[TC_SHA256_DIGEST_SIZE]; + if (tc_sha256_init(&state) != TC_CRYPTO_SUCCESS) return NRF_ERROR_INVALID_DATA; + if (tc_sha256_update(&state,msg,msg_len) != TC_CRYPTO_SUCCESS) return NRF_ERROR_INVALID_DATA; + if (tc_sha256_final(digest,&state) != TC_CRYPTO_SUCCESS) return NRF_ERROR_INVALID_DATA; + + // And verify signature + const struct uECC_Curve_t * curve = uECC_secp256r1(); + if (uECC_valid_public_key(pk, curve) != 0) + return NRF_ERROR_INVALID_DATA; + + if (uECC_valid_public_key(pk, curve) != 0) + return NRF_ERROR_INVALID_DATA; + + unsigned char* sig = &m_extended_packet[DFU_INIT_PACKET_POS_EXT_INIT_SIGNATURE_R]; +# if (DFU_INIT_PACKET_POS_EXT_INIT_SIGNATURE_S - DFU_INIT_PACKET_POS_EXT_INIT_SIGNATURE_R) != DFU_SIGNATURE_R_LENGTH +# error Incorrect layout: r and s must be contiguous! +# endif + + return uECC_verify(pk, digest, sizeof(digest), sig, curve) == 1 ? NRF_SUCCESS : NRF_ERROR_INVALID_DATA; +#endif } uint32_t dfu_init_postvalidate(uint8_t * p_image, uint32_t image_len) { - uint16_t image_crc; - uint16_t received_crc; - - // In order to support hashing (and signing) then the (decrypted) hash should be fetched and - // the corresponding hash should be calculated over the image at this location. - // If hashing (or signing) is added to the system then the CRC validation should be removed. - - // calculate CRC from active block. - image_crc = crc16_compute(p_image, image_len, NULL); - - // Decode the received CRC from extended data. - received_crc = uint16_decode((uint8_t *)&m_extended_packet[0]); - - // Compare the received and calculated CRC. - if (image_crc != received_crc) - { - return NRF_ERROR_INVALID_DATA; - } - - return NRF_SUCCESS; +#ifndef SIGNED_FW + uint16_t image_crc; + uint16_t received_crc; + + // calculate CRC from active block. + image_crc = crc16_compute(p_image, image_len, NULL); + + // Decode the received CRC from extended data. + received_crc = uint16_decode((uint8_t *)&m_extended_packet[0]); + + // Compare the received and calculated CRC. + if (image_crc != received_crc) + { + return NRF_ERROR_INVALID_DATA; + } + + return NRF_SUCCESS; + +#else + + // Compare image size received with signed init_packet data + if(image_len != *(uint32_t*)&m_extended_packet[DFU_INIT_PACKET_POS_EXT_IMAGE_LENGTH]) + { + return NRF_ERROR_INVALID_DATA; + } + + uint8_t image_digest[DFU_SHA256_DIGEST_LENGTH]; + uint8_t * received_digest; + + // Create a sha256 digest + struct tc_sha256_state_struct state = {0}; + if (tc_sha256_init(&state) != TC_CRYPTO_SUCCESS) return NRF_ERROR_INVALID_DATA; + if (tc_sha256_update(&state, p_image, image_len) != TC_CRYPTO_SUCCESS) return NRF_ERROR_INVALID_DATA; + if (tc_sha256_final(image_digest, &state) != TC_CRYPTO_SUCCESS) return NRF_ERROR_INVALID_DATA; + + received_digest = &m_extended_packet[DFU_INIT_PACKET_POS_EXT_IMAGE_HASH256]; + + // Compare the received and calculated digests. + if (memcmp(&image_digest[0], received_digest, DFU_SHA256_DIGEST_LENGTH) != 0) + { + return NRF_ERROR_INVALID_DATA; + } + + return NRF_SUCCESS; +#endif } diff --git a/src/main.c b/src/main.c index 39096515..a5110bac 100644 --- a/src/main.c +++ b/src/main.c @@ -411,6 +411,10 @@ static uint32_t ble_stack_init(void) { return NRF_SUCCESS; } +/*------------------------------------------------------------------*/ +/* SoftDevice Event handler + *------------------------------------------------------------------*/ + // Process BLE event from SD uint32_t proc_ble(void) { __ALIGN(4) uint8_t ev_buf[BLE_EVT_LEN_MAX(BLEGATT_ATT_MTU_MAX)]; diff --git a/src/usb/tusb_config.h b/src/usb/tusb_config.h index b76c87b6..e75a63da 100644 --- a/src/usb/tusb_config.h +++ b/src/usb/tusb_config.h @@ -53,7 +53,9 @@ //------------- Class enabled -------------// #define CFG_TUD_CDC 1 +#if !defined(SIGNED_FW) || defined(FORCE_UF2) #define CFG_TUD_MSC 1 +#endif /*------------------------------------------------------------------*/ /* CLASS DRIVER diff --git a/src/usb/usb.c b/src/usb/usb.c index 30d91c0d..331a9f96 100644 --- a/src/usb/usb.c +++ b/src/usb/usb.c @@ -94,7 +94,9 @@ void usb_init(bool cdc_only) { } usb_desc_init(cdc_only); +#if !defined(SIGNED_FW) || defined(FORCE_UF2) uf2_init(); +#endif tusb_init(); #ifdef DISPLAY_PIN_SCK diff --git a/src/usb/usb_desc.c b/src/usb/usb_desc.c index ac1a8acd..25c9b1d3 100644 --- a/src/usb/usb_desc.c +++ b/src/usb/usb_desc.c @@ -30,11 +30,15 @@ enum { STRID_PRODUCT , STRID_SERIAL , STRID_CDC , +#if !defined(SIGNED_FW) || defined(FORCE_UF2) STRID_MSC +#endif }; +#if !defined(SIGNED_FW) || defined(FORCE_UF2) // CDC + MSC or CDC only mode static bool _cdc_only = false; +#endif // Serial is 64-bit DeviceID -> 16 chars len static char desc_str_serial[1+16]; @@ -84,6 +88,7 @@ enum { ITF_NUM_TOTAL }; +#if !defined(SIGNED_FW) || defined(FORCE_UF2) uint8_t desc_configuration_cdc_msc[] = { // Interface count, string index, total length, attribute, power in mA @@ -95,6 +100,7 @@ uint8_t desc_configuration_cdc_msc[] = // Interface number, string index, EP Out & EP In address, EP size TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, STRID_MSC, 0x03, 0x83, 64), }; +#endif uint8_t desc_configuration_cdc_only[] = { @@ -112,19 +118,27 @@ uint8_t desc_configuration_cdc_only[] = uint8_t const * tud_descriptor_configuration_cb(uint8_t index) { (void) index; // for multiple configurations +#if !defined(SIGNED_FW) || defined(FORCE_UF2) return _cdc_only ? desc_configuration_cdc_only : desc_configuration_cdc_msc; +#else + return desc_configuration_cdc_only; +#endif } // Enumerate as CDC + MSC or CDC only void usb_desc_init(bool cdc_only) { +#if !defined(SIGNED_FW) || defined(FORCE_UF2) _cdc_only = cdc_only; if ( cdc_only ) { +#endif // Change PID to CDC only desc_device.idProduct = USB_DESC_CDC_ONLY_PID; +#if !defined(SIGNED_FW) || defined(FORCE_UF2) } +#endif // Create Serial string descriptor uint8_t const* device_id = (uint8_t const*) &NRF_FICR->DEVICEID; @@ -154,7 +168,9 @@ char const* string_desc_arr [] = BLEDIS_MODEL, // 2: Product desc_str_serial, // 3: Serials, should use chip ID "nRF Serial", // 4: CDC Interface +#if !defined(SIGNED_FW) || defined(FORCE_UF2) "nRF UF2", // 5: MSC Interface +#endif }; // up to 64 unicode characters From 01290c728bfca9ba48779844cf37117c5f9add2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 02:35:05 -0300 Subject: [PATCH 34/84] Improved documentation on dual bank and signed bootloader --- README.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8fda77a6..611c63d0 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,8 @@ In addition, there is also lots of other 3rd-party boards which are added by oth - Self-upgradable via Serial and OTA - DFU using UF2 (https://github.com/Microsoft/uf2) (application only) - Auto-enter DFU briefly on startup for DTR auto-reset trick (832 only) +- Supports dual bank firmware updates (disabled by default) +- Supports signed firmware updates (disabled by default) ## How to use @@ -101,7 +103,7 @@ Pre-builtin binaries are available on GitHub [releases](https://github.com/adafr Note: The bootloader can be downgraded. Since the binary release is a merged version of both bootloader and the Nordic SoftDevice, you can freely upgrade/downgrade to any version you like. -## How to compile and build +## How to compile and build You should only continue if you are looking to develop bootloader for your own. You must have have a J-Link available to "unbrick" your device. @@ -206,6 +208,57 @@ To flash MBR only make BOARD=feather_nrf52840_express flash-mbr ``` +### Dual bank firmware support: + +This bootloader can split the FLASH memory into 2 partitions, one for the last successfully uploaded firmware, and the other for the new firmware that is being uploaded. In case the firmware upload fails, the bootloader will revert to the last working firmware version. The only drawback is that the maximum firmware size is half of the device FLASH size: That is why it is disabled by default. +You can enable this feature by passing DUALBANK_FW=1 to the make process while compiling the bootloader + +### Signed firmware support: +This bootloader can validate that the uploaded firmware is digitally signed, and refuse to install unsigned or signed with the improper key firmware. Because this will make Arduino uploads stop working (because they are not digitally signed), this feature is disabled by default. +But if you want to ensure noone else is allowed to upload firmware to your device, this option is probably what you want to enable. + +To create a signing key, you will need the adafruit-nrfutil utility. First, generate a signing key and store at given path by using + +``` +adafruit-nrfutil keys --gen-key stored_key.pem +``` + +This command will create a signing key and store it in the file stored_key.pem. STORE THIS FILE IN A SAFE PLACE, as without it, you won't be able to sign your firmware packages, and the bootloader will refuse to flash them!! + +Now, you must show the verification key (that is calculated from the signing key): + +``` +adafruit-nrfutil keys --show-vk code stored_key.pem +``` + +This command will read your signing key (from the file stored_key.pem) and display C source code with the Qx and Qy arrays: Something along the lines + +``` +static uint8_t Qx[] = { ... }; +static uint8_t Qy[] = { ... }; +``` + +You must save thw Qx and Qy values, remove all spaces, an pass them to the make command line +``` +make BOARD=feather_nrf52840_express SIGNED_FW=1 SIGNED_FW_QX='Qx values' SIGNED_FW_QY='Qy values' +``` + +And replace Qx values with the Qx array values, and the Qy values with the Qy array values. That will create a bootloader that ONLY accepts firmware signed with the stored_key.pem signing key. Take into account that the option SIGNED_FW=1 is what enables to build a bootloader that enforces signing, and will ALSO result in disabling UF2 support, as UF2 format does NOT support signing keys. There is an option (only for testing!) called FORCE_UF2 that will FORCE UF2 support on bootloaders that enforce firmware security. It is there to bypass security so you can test a secure bootloader and have a way to recover it something goes wrong, but should NEVER be used if you want to enforce security + +Finally, to create a signed firmware application/bootloader update package, you can use, for example + +``` +adafruit-nrfutil dfu genpkg --dev-type 0x0052 --sd-req 0x0123 --application "application.hex" --key-file "stored_key.pem" "application_package.zip" +``` + +Please, use full paths for all files. The examples omit them for clarity! + +And, to upload it to your device, + +``` +adafruit-nrfutil.exe --verbose dfu serial -pkg "application_package.zip" -p /dev/tty0 -b 115200 +``` + ### Common makefile problems #### `arm-none-eabi-gcc`: No such file or directory From 33070836f9a98311f02ddf522f0dbe88a150ee83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 02:38:13 -0300 Subject: [PATCH 35/84] Add missing tinycrypt dependency --- lib/tinycrypt | 1 + 1 file changed, 1 insertion(+) create mode 160000 lib/tinycrypt diff --git a/lib/tinycrypt b/lib/tinycrypt new file mode 160000 index 00000000..cf24c907 --- /dev/null +++ b/lib/tinycrypt @@ -0,0 +1 @@ +Subproject commit cf24c907a61c01bc2c4e1ee0ae24457c27a840a2 From 0623a20120da328edec15600d0f4947d3339a7da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 02:51:01 -0300 Subject: [PATCH 36/84] Fixed compilation --- src/dfu_init.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/dfu_init.c b/src/dfu_init.c index c848d265..ae6244fc 100644 --- a/src/dfu_init.c +++ b/src/dfu_init.c @@ -148,8 +148,12 @@ static const uint8_t pk[] = { //#pragma message ("Qx: " TO_STRING(SIGNED_FW_QX)) //#pragma message ("Qy: " TO_STRING(SIGNED_FW_QY)) -#define DFU_INIT_PACKET_EXT_LENGTH_MIN 2 //< Minimum length of the extended init packet. The extended init packet may contain a CRC, a HASH, or other data. This value must be changed according to the requirements of the system. The template uses a minimum value of two in order to hold a CRC. */ -#define DFU_INIT_PACKET_EXT_LENGTH_MAX 10 //< Maximum length of the extended init packet. The extended init packet may contain a CRC, a HASH, or other data. This value must be changed according to the requirements of the system. The template uses a maximum value of 10 in order to hold a CRC and any padded data on transport layer without overflow. */ +_Static_assert(sizeof(pk) == 64, "Public key must be 64 bytes in size"); + +#endif + +#define DFU_INIT_PACKET_EXT_LENGTH_MIN 2 //< Minimum length of the extended init packet. Init packet and CRC16 +#define DFU_INIT_PACKET_EXT_LENGTH_MAX 104 //< Identifier (4 bytes) + Image length (4 bytes) + SHA-256 digest (32 bytes) + NIST P-256 using SHA-256 (64 bytes) static uint8_t m_extended_packet[DFU_INIT_PACKET_EXT_LENGTH_MAX]; //< Data array for storage of the extended data received. The extended data follows the normal init data of type \ref dfu_init_packet_t. Extended data can be used for a CRC, hash, signature, or other data. */ static uint8_t m_extended_packet_length; //< Length of the extended data received with init packet. */ From 6b5158a6827b22d49cbf4ed684a13be0e01d6ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 15:31:16 -0300 Subject: [PATCH 37/84] Removed redundant call to verify if the supplied public key is valid --- src/dfu_init.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/dfu_init.c b/src/dfu_init.c index ae6244fc..612fed9c 100644 --- a/src/dfu_init.c +++ b/src/dfu_init.c @@ -269,9 +269,6 @@ uint32_t dfu_init_prevalidate(uint8_t * p_init_data, uint32_t init_data_len, uin if (uECC_valid_public_key(pk, curve) != 0) return NRF_ERROR_INVALID_DATA; - if (uECC_valid_public_key(pk, curve) != 0) - return NRF_ERROR_INVALID_DATA; - unsigned char* sig = &m_extended_packet[DFU_INIT_PACKET_POS_EXT_INIT_SIGNATURE_R]; # if (DFU_INIT_PACKET_POS_EXT_INIT_SIGNATURE_S - DFU_INIT_PACKET_POS_EXT_INIT_SIGNATURE_R) != DFU_SIGNATURE_R_LENGTH # error Incorrect layout: r and s must be contiguous! From 16477539d9ec7d40e57eaa8d571239bf9b77b8d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 15:43:59 -0300 Subject: [PATCH 38/84] Fix autodetection of workarounds for GCC 12.x 13.x and 14.x under Windows --- Makefile | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 45dc1df4..0c9ccd6c 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,15 @@ SD_HEX = $(SD_PATH)/$(SD_FILENAME)_softdevice.hex MBR_HEX = lib/softdevice/mbr/hex/mbr_nrf52_2.4.1_mbr.hex +# Detect the operating system +# The "OS" environment variable on Windows is always "Windows_NT" +# when running under a modern shell like cmd.exe or powershell +ifeq ($(OS),Windows_NT) + NULL_DEVICE = NUL +else + NULL_DEVICE = /dev/null +endif + # linker by MCU eg. nrf52840.ld ifeq ($(DEBUG), 1) LD_FILE = linker/$(MCU_SUB_VARIANT)_debug.ld @@ -385,9 +394,9 @@ CFLAGS += -DDFU_APP_DATA_RESERVED=$(DFU_APP_DATA_RESERVED) # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105523 # Fixes for gcc version 12, 13 and 14. -#ifneq (,$(filter 12.% 13.% 14.%,$(shell $(CC) -dumpversion 2>/dev/null))) +ifneq (,$(filter 12.% 13.% 14.%,$(shell $(CC) -dumpversion 2>$(NULL_DEVICE)))) CFLAGS += --param=min-pagesize=0 -#endif +endif #------------------------------------------------------------------------------ # Linker Flags From 827edd212b9560cd8994576848b76f85da3aad1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Thu, 15 Jan 2026 19:14:41 -0300 Subject: [PATCH 39/84] Bumped SLAVE_LATENCY to 4. Does not make any difference in our tests, but https://stackoverflow.com/questions/32588325/ios-bluetooth-le-code-6-the-connection-has-timed-out-unexpectedly suggests this is the best to solve the ConnectionTimedOutUnexpectedly on IOS --- .../components/libraries/bootloader_dfu/dfu_transport_ble.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c index 06017a91..08893d05 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c @@ -52,7 +52,7 @@ enum { BLE_CONN_CFG_HIGH_BANDWIDTH = 1 }; #define MIN_CONN_INTERVAL (uint16_t)(MSEC_TO_UNITS(15, UNIT_1_25_MS)) /**< Minimum acceptable connection interval (11.25 milliseconds). */ #define MAX_CONN_INTERVAL (uint16_t)(MSEC_TO_UNITS(30, UNIT_1_25_MS)) /**< Maximum acceptable connection interval (15 milliseconds). */ -#define SLAVE_LATENCY 0 /**< Slave latency. */ +#define SLAVE_LATENCY 4 /**< Slave latency. */ #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(8000, UNIT_10_MS) /**< Connection supervisory timeout (4 seconds). */ #define SPEEDUP_FLASH_WRITES 1 /**< Speedup FLASH writes by changing Softdevice local latency */ From 3e87e079213cc09a37df5059690bf61856c16a71 Mon Sep 17 00:00:00 2001 From: Ha Thach Date: Wed, 21 Jan 2026 09:06:12 +0700 Subject: [PATCH 40/84] fix typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- .../libraries/bootloader_dfu/dfu_dual_bank.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 611c63d0..583f6d32 100644 --- a/README.md +++ b/README.md @@ -238,7 +238,7 @@ static uint8_t Qx[] = { ... }; static uint8_t Qy[] = { ... }; ``` -You must save thw Qx and Qy values, remove all spaces, an pass them to the make command line +You must save the Qx and Qy values, remove all spaces, and pass them to the make command line ``` make BOARD=feather_nrf52840_express SIGNED_FW=1 SIGNED_FW_QX='Qx values' SIGNED_FW_QY='Qy values' ``` diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c index 4516e4f0..1bbeffae 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c @@ -29,7 +29,7 @@ static dfu_state_t m_dfu_state; /**< Current DFU static uint32_t m_image_size; /**< Size of the image that will be transmitted. */ static dfu_start_packet_t m_start_packet; /**< Start packet received for this update procedure. Contains update mode and image sizes information to be used for image transfer. */ -static uint8_t m_init_packet[128]; /**< Init packet, can hold CRC, Hash, Signed Hash and similar, for image validation, integrety check and authorization checking. */ +static uint8_t m_init_packet[128]; /**< Init packet, can hold CRC, Hash, Signed Hash and similar, for image validation, integrity check and authorization checking. */ static uint8_t m_init_packet_length; /**< Length of init packet received. */ static uint16_t m_image_crc; /**< Calculated CRC of the image received. */ @@ -200,8 +200,8 @@ static void dfu_cleared_func_app(void) /**@brief Function for calculating storage offset for receiving SoftDevice image. * * @details When a new SoftDevice is received it will be temporary stored in flash before moved to - * address 0x0. In order to succesfully validate transfer and relocation it is important - * that temporary image and final installed image does not ovwerlap hence an offset must + * address 0x0. In order to successfully validate transfer and relocation it is important + * that temporary image and final installed image does not overlap hence an offset must * be calculated in case new image is larger than currently installed SoftDevice. */ uint32_t offset_calculate(uint32_t sd_image_size) @@ -253,7 +253,7 @@ static uint32_t dfu_activate_sd(void) /**@brief Function for activating received Application image. * - * @details This function will move the received application image fram swap (bank 1) to + * @details This function will move the received application image from swap (bank 1) to * application area (bank 0). * * @return NRF_SUCCESS on success. Error code otherwise. @@ -606,7 +606,7 @@ uint32_t dfu_image_validate() // Check if the application image write has finished. if (m_data_received != m_image_size) { - // Image not yet fully transfered by the peer or the peer has attempted to write + // Image not yet fully transferred by the peer or the peer has attempted to write // too much data. Hence the validation should fail. err_code = NRF_ERROR_INVALID_STATE; } @@ -701,8 +701,8 @@ static uint32_t dfu_sd_img_block_swap(uint32_t * src, uint32_t len, uint32_t block_size) { - // It is neccesarry to swap the new SoftDevice in 3 rounds to ensure correct copy of data - // and verifucation of data in case power reset occurs during write to flash. + // It is necessary to swap the new SoftDevice in 3 rounds to ensure correct copy of data + // and verification of data in case power reset occurs during write to flash. // To ensure the robustness of swapping the images are compared backwards till start of // image swap. If the back is identical everything is swapped. uint32_t err_code = dfu_compare_block(src, dst, len); @@ -810,7 +810,7 @@ uint32_t dfu_bl_image_swap(void) uint32_t dfu_bl_image_validate(void) { - bootloader_settings_t bootloader_settings;\ + bootloader_settings_t bootloader_settings; sd_mbr_command_t sd_mbr_cmd_1; sd_mbr_command_t sd_mbr_cmd_2; From 3c4470889b52052e9b420aa536adc5f43f412898 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 23 Jan 2026 14:30:58 +0700 Subject: [PATCH 41/84] minor clean up and fix dfu_bl_image_validate() return nrf error code --- CMakeLists.txt | 49 ++++++++++++++- README.md | 6 +- .../ble/ble_services/ble_dfu/ble_dfu.c | 11 +--- .../libraries/bootloader_dfu/dfu_dual_bank.c | 7 ++- .../bootloader_dfu/dfu_single_bank.c | 7 ++- .../bootloader_dfu/dfu_transport_ble.c | 61 +++++++++---------- src/main.c | 46 +++++++------- src/sdk_config.h | 8 ++- src/usb/usb.c | 2 +- src/usb/usb_desc.c | 17 +++--- 10 files changed, 132 insertions(+), 82 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 401acc25..5b50cfd6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,13 @@ set(SDK_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/sdk/components) set(SOFTDEVICE_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/softdevice) set(MBR_HEX ${SOFTDEVICE_DIR}/mbr/hex/mbr_nrf52_2.4.1_mbr.hex) set(TINYUSB_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/tinyusb/src) +set(TCRYPT_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/tinycrypt/lib) + +option(SIGNED_FW "Enable signed firmware verification" OFF) +option(DUALBANK_FW "Enable dual bank DFU support" OFF) +option(FORCE_UF2 "Force UF2 support even with SIGNED_FW" OFF) +set(SIGNED_FW_QX "" CACHE STRING "Public key Qx (comma-separated bytes)") +set(SIGNED_FW_QY "" CACHE STRING "Public key Qy (comma-separated bytes)") set(UF2CONV_PY ${CMAKE_CURRENT_LIST_DIR}/lib/uf2/utils/uf2conv.py) set(UF2_FAMILY_ID_BOOTLOADER 0xd663823c) @@ -58,7 +65,6 @@ add_executable(bootloader ${SDK11_DIR}/libraries/bootloader_dfu/bootloader_util.c ${SDK11_DIR}/libraries/bootloader_dfu/dfu_transport_serial.c ${SDK11_DIR}/libraries/bootloader_dfu/dfu_transport_ble.c - ${SDK11_DIR}/libraries/bootloader_dfu/dfu_single_bank.c ${SDK11_DIR}/ble/ble_services/ble_dfu/ble_dfu.c ${SDK11_DIR}/ble/ble_services/ble_dis/ble_dis.c ${SDK11_DIR}/drivers_nrf/pstorage/pstorage_raw.c @@ -75,6 +81,30 @@ add_executable(bootloader # startup ${NRFX_DIR}/mdk/gcc_startup_${MCU_VARIANT}.S ) + +if (DUALBANK_FW) + target_sources(bootloader PUBLIC + ${SDK11_DIR}/libraries/bootloader_dfu/dfu_dual_bank.c + ) +else () + target_sources(bootloader PUBLIC + ${SDK11_DIR}/libraries/bootloader_dfu/dfu_single_bank.c + ) +endif () + +if (SIGNED_FW) + target_sources(bootloader PUBLIC + ${TCRYPT_DIR}/source/sha256.c + ${TCRYPT_DIR}/source/ecc.c + ${TCRYPT_DIR}/source/ecc_dsa.c + ${TCRYPT_DIR}/source/utils.c + ) + + target_include_directories(bootloader PUBLIC + ${TCRYPT_DIR}/include + ) +endif () + target_include_directories(bootloader PUBLIC src src/boards @@ -149,6 +179,23 @@ target_compile_definitions(bootloader PUBLIC __STARTUP_CLEAR_BSS ) +if (SIGNED_FW) + if (SIGNED_FW_QX STREQUAL "" OR SIGNED_FW_QY STREQUAL "") + message(FATAL_ERROR "SIGNED_FW requires SIGNED_FW_QX and SIGNED_FW_QY to be set") + endif () + string(REPLACE " " "" SIGNED_FW_QX_STR "${SIGNED_FW_QX}") + string(REPLACE " " "" SIGNED_FW_QY_STR "${SIGNED_FW_QY}") + target_compile_definitions(bootloader PUBLIC + SIGNED_FW + "SIGNED_FW_QX=${SIGNED_FW_QX_STR}" + "SIGNED_FW_QY=${SIGNED_FW_QY_STR}" + ) +endif () + +if (FORCE_UF2) + target_compile_definitions(bootloader PUBLIC FORCE_UF2) +endif () + if (TRACE_ETM STREQUAL "1") # ENABLE_TRACE will cause system_nrf5x.c to set up ETM trace target_compile_definitions(bootloader PUBLIC ENABLE_TRACE) diff --git a/README.md b/README.md index 583f6d32..8d16b37b 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,17 @@ In addition, there is also lots of other 3rd-party boards which are added by oth ## Features -- DFU over Serial and OTA ( application, Bootloader+SD ) +- DFU over Serial and OTA (application, Bootloader+SD) - Self-upgradable via Serial and OTA - DFU using UF2 (https://github.com/Microsoft/uf2) (application only) - Auto-enter DFU briefly on startup for DTR auto-reset trick (832 only) - Supports dual bank firmware updates (disabled by default) - Supports signed firmware updates (disabled by default) +Note: For OTA, `Packet Receipt Notification` (PRN) must be 8 or less, which can be configured in nRF DFU. Otherwise, the +bootloader will run out of +memory. + ## How to use There are two pins, `DFU` and `FRST` that bootloader will check upon reset/power: diff --git a/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c b/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c index a6309712..a79e4cb0 100644 --- a/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c +++ b/lib/sdk11/components/ble/ble_services/ble_dfu/ble_dfu.c @@ -17,8 +17,7 @@ #include #include "sdk_common.h" -#define DFU_PKT_ENABLE_WR_WO_RESPONSE_ONLY 0 /**< Enable Write Without Response on Data Endpoint ONLY (otherwise both With and Without response will be supported)*/ -#define MAX_DFU_PKT_LEN 244 /**< Maximum length (in bytes) of the DFU Packet characteristic. */ +#define MAX_DFU_PKT_LEN (BLEGATT_ATT_MTU_MAX - 3) /**< Maximum length (in bytes) of the DFU Packet characteristic. */ #define PKT_START_DFU_PARAM_LEN 2 /**< Length (in bytes) of the parameters for Packet Start DFU Request. */ #define PKT_INIT_DFU_PARAM_LEN 2 /**< Length (in bytes) of the parameters for Packet Init DFU Request. */ #define PKT_RCPT_NOTIF_REQ_LEN 3 /**< Length (in bytes) of the Packet Receipt Notification Request. */ @@ -58,12 +57,8 @@ static uint32_t dfu_pkt_char_add(ble_dfu_t * const p_dfu) memset(&char_md, 0, sizeof(char_md)); -#if DFU_PKT_ENABLE_WR_WO_RESPONSE_ONLY - char_md.char_props.write_wo_resp = 1; -#else - char_md.char_props.write = 1; // For those improved apps that can take advantage of ACK - char_md.char_props.write_wo_resp = 1; // Nordic IOS DFU app REQUIRES Write Without Response */ -#endif + char_md.char_props.write = 1; // For those improved apps that can take advantage of ACK + char_md.char_props.write_wo_resp = 1; // Nordic IOS DFU app REQUIRES Write Without Response */ char_md.p_char_user_desc = NULL; char_md.p_char_pf = NULL; char_md.p_user_desc_md = NULL; diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c index 1bbeffae..94feef10 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c @@ -829,6 +829,11 @@ uint32_t dfu_bl_image_validate(void) sd_mbr_cmd_1.params.compare.ptr2 = (uint32_t *)(bl_image_start_1); sd_mbr_cmd_1.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t); + uint32_t err_code = sd_mbr_command(&sd_mbr_cmd_1); + if (err_code != NRF_SUCCESS) { + return err_code; + } + uint32_t bl_image_start_2 = (bootloader_settings.sd_image_size == 0) ? DFU_BANK_1_REGION_START : bootloader_settings.sd_image_start + @@ -839,7 +844,7 @@ uint32_t dfu_bl_image_validate(void) sd_mbr_cmd_2.params.compare.ptr2 = (uint32_t *)(bl_image_start_2); sd_mbr_cmd_2.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t); - return sd_mbr_command(&sd_mbr_cmd_1) && sd_mbr_command(&sd_mbr_cmd_2); + return sd_mbr_command(&sd_mbr_cmd_2); } return NRF_SUCCESS; } diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c index bf003c63..5a4677f0 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c @@ -752,6 +752,11 @@ uint32_t dfu_bl_image_validate(void) sd_mbr_cmd_1.params.compare.ptr2 = (uint32_t *)(bl_image_start_1); sd_mbr_cmd_1.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t); + uint32_t err_code = sd_mbr_command(&sd_mbr_cmd_1); + if (err_code != NRF_SUCCESS) { + return err_code; + } + uint32_t bl_image_start_2 = (bootloader_settings.sd_image_size == 0) ? DFU_BANK_1_REGION_START : bootloader_settings.sd_image_start + @@ -762,7 +767,7 @@ uint32_t dfu_bl_image_validate(void) sd_mbr_cmd_2.params.compare.ptr2 = (uint32_t *)(bl_image_start_2); sd_mbr_cmd_2.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t); - return sd_mbr_command(&sd_mbr_cmd_1) && sd_mbr_command(&sd_mbr_cmd_2); + return sd_mbr_command(&sd_mbr_cmd_2); } return NRF_SUCCESS; } diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c index 08893d05..be6b6726 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c @@ -35,10 +35,6 @@ #include "nrf_delay.h" #include "sdk_common.h" - -#define BLEGATT_ATT_MTU_MAX 247 -enum { BLE_CONN_CFG_HIGH_BANDWIDTH = 1 }; - #define DFU_REV_MAJOR 0x00 /** DFU Major revision number to be exposed. */ #define DFU_REV_MINOR 0x08 /** DFU Minor revision number to be exposed. */ #define DFU_REVISION ((DFU_REV_MAJOR << 8) | DFU_REV_MINOR) /** DFU Revision number to be exposed. Combined of major and minor versions. */ @@ -54,7 +50,7 @@ enum { BLE_CONN_CFG_HIGH_BANDWIDTH = 1 }; #define MAX_CONN_INTERVAL (uint16_t)(MSEC_TO_UNITS(30, UNIT_1_25_MS)) /**< Maximum acceptable connection interval (15 milliseconds). */ #define SLAVE_LATENCY 4 /**< Slave latency. */ #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(8000, UNIT_10_MS) /**< Connection supervisory timeout (4 seconds). */ -#define SPEEDUP_FLASH_WRITES 1 /**< Speedup FLASH writes by changing Softdevice local latency */ +#define SPEEDUP_FLASH_WRITES 1 /**< Speedup FLASH writes by changing Softdevice local latency */ #define APP_ADV_INTERVAL MSEC_TO_UNITS(25, UNIT_0_625_MS) /**< The advertising interval (25 ms.). */ #define APP_ADV_TIMEOUT BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED /**< The advertising timeout in units of seconds. This is set to @ref BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED so that the advertisement is done as long as there there is a call to @ref dfu_transport_close function.*/ @@ -77,8 +73,9 @@ enum { BLE_CONN_CFG_HIGH_BANDWIDTH = 1 }; #define IS_CONNECTED() (m_conn_handle != BLE_CONN_HANDLE_INVALID) /**< Macro to determine if the device is in connected state. */ -#define APP_FEATURE_NOT_SUPPORTED BLE_GATT_STATUS_ATTERR_APP_BEGIN + 2 /**< Reply when unsupported features are requested. */ -#define SD_IMAGE_SIZE_OFFSET 0 /**< Offset in start packet for the size information for SoftDevice. */ +#define APP_FEATURE_NOT_SUPPORTED \ + (BLE_GATT_STATUS_ATTERR_APP_BEGIN + 2) /**< Reply when unsupported features are requested. */ +#define SD_IMAGE_SIZE_OFFSET 0 /**< Offset in start packet for the size information for SoftDevice. */ #define BL_IMAGE_SIZE_OFFSET 4 /**< Offset in start packet for the size information for bootloader. */ #define APP_IMAGE_SIZE_OFFSET 8 /**< Offset in start packet for the size information for application. */ @@ -210,35 +207,35 @@ static ble_dfu_resp_val_t nrf_err_code_translate(uint32_t err_c } } -static void prioritize_ble_over_flash_writes(void) -{ #ifdef SPEEDUP_FLASH_WRITES - // We set the local latency to 0: That will revert latency to the negotiated one with the host. - // That will increase throughput to the maximum possible - ble_opt_t opt; - varclr(&opt); - opt.gap_opt.local_conn_latency.conn_handle = m_conn_handle;/**< Connection Handle */ - opt.gap_opt.local_conn_latency.requested_latency = 0; /**< Requested local connection latency. */ - opt.gap_opt.local_conn_latency.p_actual_latency = NULL; /**< Pointer to storage for the actual local connection latency (can be set to NULL to skip return value). */ - sd_ble_opt_set(BLE_GAP_OPT_LOCAL_CONN_LATENCY, &opt); -#endif +static void prioritize_ble_over_flash_writes(void) { + // We set the local latency to 0: That will revert latency to the negotiated one with the host. + // That will increase throughput to the maximum possible + ble_opt_t opt; + varclr(&opt); + opt.gap_opt.local_conn_latency.conn_handle = m_conn_handle; /**< Connection Handle */ + opt.gap_opt.local_conn_latency.requested_latency = 0; /**< Requested local connection latency. */ + opt.gap_opt.local_conn_latency.p_actual_latency = + NULL; /**< Pointer to storage for the actual local connection latency (can be set to NULL to skip return value). */ + sd_ble_opt_set(BLE_GAP_OPT_LOCAL_CONN_LATENCY, &opt); } -static void prioritize_flash_writes_over_ble(void) -{ -#ifdef SPEEDUP_FLASH_WRITES - // We set the local latency to the maximum possible: That will make FLASH writes faster, as BLE comms will - // be delayed (even losing RXd packets, but this is the same as a poor connection and will be handled by - // the protocol itself) - ble_opt_t opt; - varclr(&opt); - opt.gap_opt.local_conn_latency.conn_handle = m_conn_handle;/**< Connection Handle */ - opt.gap_opt.local_conn_latency.requested_latency = 50; /**< Requested local connection latency. */ - opt.gap_opt.local_conn_latency.p_actual_latency = NULL; /**< Pointer to storage for the actual local connection latency (can be set to NULL to skip return value). */ - sd_ble_opt_set(BLE_GAP_OPT_LOCAL_CONN_LATENCY, &opt); -#endif +static void prioritize_flash_writes_over_ble(void) { + // We set the local latency to the maximum possible: That will make FLASH writes faster, as BLE comms will + // be delayed (even losing RXd packets, but this is the same as a poor connection and will be handled by + // the protocol itself) + ble_opt_t opt; + varclr(&opt); + opt.gap_opt.local_conn_latency.conn_handle = m_conn_handle; /**< Connection Handle */ + opt.gap_opt.local_conn_latency.requested_latency = 50; /**< Requested local connection latency. */ + opt.gap_opt.local_conn_latency.p_actual_latency = + NULL; /**< Pointer to storage for the actual local connection latency (can be set to NULL to skip return value). */ + sd_ble_opt_set(BLE_GAP_OPT_LOCAL_CONN_LATENCY, &opt); } - +#else + #define prioritize_ble_over_flash_writes() + #define prioritize_flash_writes_over_ble() +#endif /**@brief Function for handling the callback events from the dfu module. * Callbacks are expected when \ref dfu_data_pkt_handle has been executed. * diff --git a/src/main.c b/src/main.c index a5110bac..d428fd71 100644 --- a/src/main.c +++ b/src/main.c @@ -43,9 +43,7 @@ #include "nrfx_power.h" #include "nrfx_pwm.h" -#include "nordic_common.h" #include "sdk_common.h" -#include "dfu_transport.h" #include "bootloader.h" #include "bootloader_util.h" @@ -65,7 +63,6 @@ #include "pstorage_platform.h" #include "nrf_mbr.h" #include "pstorage.h" -#include "nrfx_nvmc.h" #ifdef NRF_USBD @@ -125,15 +122,9 @@ extern void tusb_hal_nrf_power_event(uint32_t event); // DFU_DBL_RESET magic is used to determined which mode is entered #define APP_ASKS_FOR_SINGLE_TAP_RESET() (*((uint32_t*)(DFU_BANK_0_REGION_START + 0x200)) == 0x87eeb07c) -// These value must be the same with one in dfu_transport_ble.c #define BLEGAP_EVENT_LENGTH 12 -#define BLEGATT_ATT_MTU_MAX 247 #define BLEGATTS_HVN_QSIZE 12 -#define BLEGATTC_WRCMD_QSIZE 2 - -enum { - BLE_CONN_CFG_HIGH_BANDWIDTH = 1 -}; +#define BLEGATTC_WRCMD_QSIZE 2 //--------------------------------------------------------------------+ // @@ -230,8 +221,8 @@ int main(void) { } // No application was loaded, reset the system with the OTA DFU update - NRF_POWER->GPREGRET = 0xA8; - + NRF_POWER->GPREGRET = DFU_MAGIC_OTA_RESET; + NVIC_SystemReset(); } @@ -256,14 +247,17 @@ static void check_dfu_mode(void) { (((*dbl_reset_mem) == DFU_DBL_RESET_MAGIC) && reason_reset_pin); // Clear GPREGRET if it is our values - if (dfu_start || dfu_skip) NRF_POWER->GPREGRET = 0; + if (dfu_start || dfu_skip) { + NRF_POWER->GPREGRET = 0; + } // skip dfu entirely - if (dfu_skip) return; + if (dfu_skip) { + return; + } /*------------- Determine DFU mode (Serial, OTA, FRESET or normal) -------------*/ - // DFU button pressed - dfu_start = dfu_start || button_pressed(BUTTON_DFU); + dfu_start = dfu_start || button_pressed(BUTTON_DFU); // DFU button pressed // DFU + FRESET are pressed --> OTA _ota_dfu = _ota_dfu || (button_pressed(BUTTON_DFU) && button_pressed(BUTTON_FRESET)); @@ -271,16 +265,16 @@ static void check_dfu_mode(void) { bool const valid_app = bootloader_app_is_valid(); bool const just_start_app = valid_app && !dfu_start && (*dbl_reset_mem) == DFU_DBL_RESET_APP; - if (!just_start_app && APP_ASKS_FOR_SINGLE_TAP_RESET()) dfu_start = 1; + if (!just_start_app && APP_ASKS_FOR_SINGLE_TAP_RESET()) { + dfu_start = 1; + } // App mode: Double Reset detection or DFU startup for nrf52832 if (!(just_start_app || dfu_start || !valid_app)) { #ifdef NRF52832_XXAA /* Even DFU is not active, we still force an 1000 ms dfu serial mode when startup * to support auto programming from Arduino IDE - * - * Note: Double Reset WONT work with nrf52832 since all its SRAM got cleared with GPIO reset. - */ + * Note: Double Reset WONT work with nrf52832 since all its SRAM got cleared with GPIO reset. */ bootloader_dfu_start(false, DFU_SERIAL_STARTUP_INTERVAL, false); #else // Note: RESETREAS is not clear by bootloader, it should be cleared by application upon init() @@ -414,7 +408,8 @@ static uint32_t ble_stack_init(void) { /*------------------------------------------------------------------*/ /* SoftDevice Event handler *------------------------------------------------------------------*/ - +extern void ble_evt_dispatch(ble_evt_t *p_ble_evt); + // Process BLE event from SD uint32_t proc_ble(void) { __ALIGN(4) uint8_t ev_buf[BLE_EVT_LEN_MAX(BLEGATT_ATT_MTU_MAX)]; @@ -430,8 +425,7 @@ uint32_t proc_ble(void) { // Handle valid event, ignore error if (NRF_SUCCESS == err) { switch (evt->header.evt_id) { - case BLE_GAP_EVT_CONNECTED: - { + case BLE_GAP_EVT_CONNECTED: { // Try to enable 2M phy,if phone allows it ble_gap_phys_t const phys = { @@ -444,6 +438,7 @@ uint32_t proc_ble(void) { led_state(STATE_BLE_CONNECTED); break; } + case BLE_GAP_EVT_DISCONNECTED: _ota_connected = false; led_state(STATE_BLE_DISCONNECTED); @@ -454,7 +449,6 @@ uint32_t proc_ble(void) { } // from dfu_transport_ble - extern void ble_evt_dispatch(ble_evt_t* p_ble_evt); ble_evt_dispatch(evt); } @@ -475,7 +469,9 @@ uint32_t proc_soc(void) { (soc_evt == NRF_EVT_POWER_USB_POWER_READY) ? NRFX_POWER_USB_EVT_READY : (soc_evt == NRF_EVT_POWER_USB_REMOVED) ? NRFX_POWER_USB_EVT_REMOVED : -1; - if (usbevt >= 0) tusb_hal_nrf_power_event((uint32_t) usbevt); + if (usbevt >= 0) { + tusb_hal_nrf_power_event((uint32_t)usbevt); + } #endif } diff --git a/src/sdk_config.h b/src/sdk_config.h index bef8bc7d..b61b1f0c 100644 --- a/src/sdk_config.h +++ b/src/sdk_config.h @@ -38,8 +38,6 @@ * */ - - #ifndef SDK_CONFIG_H #define SDK_CONFIG_H @@ -119,6 +117,12 @@ #define CRC16_ENABLED 1 #define NRF_STRERROR_ENABLED 1 +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ +#define BLEGATT_ATT_MTU_MAX 247 +#define BLE_CONN_CFG_HIGH_BANDWIDTH 1 + #endif //SDK_CONFIG_H diff --git a/src/usb/usb.c b/src/usb/usb.c index 331a9f96..ecc881b7 100644 --- a/src/usb/usb.c +++ b/src/usb/usb.c @@ -94,7 +94,7 @@ void usb_init(bool cdc_only) { } usb_desc_init(cdc_only); -#if !defined(SIGNED_FW) || defined(FORCE_UF2) +#if CFG_TUD_MSC uf2_init(); #endif tusb_init(); diff --git a/src/usb/usb_desc.c b/src/usb/usb_desc.c index 25c9b1d3..4db68ec7 100644 --- a/src/usb/usb_desc.c +++ b/src/usb/usb_desc.c @@ -30,9 +30,7 @@ enum { STRID_PRODUCT , STRID_SERIAL , STRID_CDC , -#if !defined(SIGNED_FW) || defined(FORCE_UF2) STRID_MSC -#endif }; #if !defined(SIGNED_FW) || defined(FORCE_UF2) @@ -88,7 +86,7 @@ enum { ITF_NUM_TOTAL }; -#if !defined(SIGNED_FW) || defined(FORCE_UF2) +#if CFG_TUD_MSC uint8_t desc_configuration_cdc_msc[] = { // Interface count, string index, total length, attribute, power in mA @@ -118,7 +116,7 @@ uint8_t desc_configuration_cdc_only[] = uint8_t const * tud_descriptor_configuration_cb(uint8_t index) { (void) index; // for multiple configurations -#if !defined(SIGNED_FW) || defined(FORCE_UF2) +#if CFG_TUD_MSC return _cdc_only ? desc_configuration_cdc_only : desc_configuration_cdc_msc; #else return desc_configuration_cdc_only; @@ -128,17 +126,16 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index) // Enumerate as CDC + MSC or CDC only void usb_desc_init(bool cdc_only) { -#if !defined(SIGNED_FW) || defined(FORCE_UF2) - _cdc_only = cdc_only; + (void)cdc_only; // for non MSC build +#if CFG_TUD_MSC + _cdc_only = cdc_only; if ( cdc_only ) - { #endif + { // Change PID to CDC only desc_device.idProduct = USB_DESC_CDC_ONLY_PID; -#if !defined(SIGNED_FW) || defined(FORCE_UF2) } -#endif // Create Serial string descriptor uint8_t const* device_id = (uint8_t const*) &NRF_FICR->DEVICEID; @@ -168,7 +165,7 @@ char const* string_desc_arr [] = BLEDIS_MODEL, // 2: Product desc_str_serial, // 3: Serials, should use chip ID "nRF Serial", // 4: CDC Interface -#if !defined(SIGNED_FW) || defined(FORCE_UF2) +#if CFG_TUD_MSC "nRF UF2", // 5: MSC Interface #endif }; From e1ea1c6dd135a45fdd920a908fbf32ee738ca9e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Mon, 26 Jan 2026 00:44:54 -0300 Subject: [PATCH 42/84] Wait until FLASH operation queue has enough available room, instead of silently dropping operations --- .../libraries/bootloader_dfu/bootloader.c | 28 ++- .../libraries/bootloader_dfu/bootloader.h | 2 + .../libraries/bootloader_dfu/dfu_dual_bank.c | 176 +++++++++++------- .../bootloader_dfu/dfu_single_bank.c | 38 +++- src/pstorage_platform.h | 2 +- 5 files changed, 176 insertions(+), 70 deletions(-) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c index 875ab6c4..79557904 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c @@ -186,10 +186,34 @@ static void bootloader_settings_save(bootloader_settings_t * p_settings) { if ( is_ota() ) { - uint32_t err_code = pstorage_clear(&m_bootsettings_handle, sizeof(bootloader_settings_t)); + uint32_t err_code; + while(1) { + err_code = pstorage_clear(&m_bootsettings_handle, sizeof(bootloader_settings_t)); + if (err_code != NRF_ERROR_NO_MEM) + break; + // This means the write/erase queue of commands was completely full - Should not + // happen, but better safe than sorry, wait until space becomes available - + // the pstorage event handler is only run from the main loop, and we are also + // running from a BLE event on the main loop: This means if we wait here, the FLASH + // completion events will never be handled (will be queued by app_scheduler, but + // not handled, and that means this wait will never end... So, process SOC events + // from the SD (but NOT BLE events!) - This will free entries on the pstorage queue + // as soon as operations are complete, allowing this op to be queued + while (NRF_ERROR_NOT_FOUND != proc_soc()) { + // nothing + } + } APP_ERROR_CHECK(err_code); - err_code = pstorage_store(&m_bootsettings_handle, (uint8_t *) p_settings, sizeof(bootloader_settings_t), 0); + while(1) { + err_code = pstorage_store(&m_bootsettings_handle, (uint8_t *) p_settings, sizeof(bootloader_settings_t), 0); + if (err_code != NRF_ERROR_NO_MEM) + break; + // No space, wait until an entry in the queue is freed + while (NRF_ERROR_NOT_FOUND != proc_soc()) { + // nothing + } + } APP_ERROR_CHECK(err_code); } else diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h index efd72e75..5f94e7b6 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h @@ -92,6 +92,8 @@ uint32_t bootloader_dfu_sd_update_finalize(void); void bootloader_mbr_addrs_populate(void); +extern uint32_t proc_soc(void); + #endif // BOOTLOADER_H__ /**@} */ diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c index 4516e4f0..00956e79 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c @@ -82,7 +82,6 @@ static void pstorage_callback_handler(pstorage_handle_t * p_handle, APP_ERROR_CHECK(result); } - /**@brief Function for handling the DFU timeout. * * @param[in] p_context The timeout context. @@ -136,19 +135,28 @@ static void dfu_prepare_func_app_erase(uint32_t image_size) // Doing a SoftDevice update thus current application must be cleared to ensure enough space // for new SoftDevice. m_dfu_state = DFU_STATE_PREPARING; - - if ( is_ota() ) - { - uint32_t err_code = pstorage_clear(&m_storage_handle_app, m_image_size); - APP_ERROR_CHECK(err_code); - } - else - { - flash_nrf5x_erase(DFU_BANK_0_REGION_START, m_image_size); - - // invoke complete callback - pstorage_callback_handler(&m_storage_handle_app, PSTORAGE_CLEAR_OP_CODE, NRF_SUCCESS, NULL, 0); - } + + if ( is_ota() ) + { + uint32_t err_code; + while(1) { + err_code = pstorage_clear(&m_storage_handle_app, m_image_size); + if (err_code != NRF_ERROR_NO_MEM) + break; + // No space, wait until an entry in the queue is freed + while (NRF_ERROR_NOT_FOUND != proc_soc()) { + // nothing + } + } + APP_ERROR_CHECK(err_code); + } + else + { + flash_nrf5x_erase(DFU_BANK_0_REGION_START, m_image_size); + + // invoke complete callback + pstorage_callback_handler(&m_storage_handle_app, PSTORAGE_CLEAR_OP_CODE, NRF_SUCCESS, NULL, 0); + } } @@ -163,19 +171,28 @@ static void dfu_prepare_func_swap_erase(uint32_t image_size) mp_storage_handle_active = &m_storage_handle_swap; m_dfu_state = DFU_STATE_PREPARING; - - if ( is_ota() ) - { - uint32_t err_code = pstorage_clear(&m_storage_handle_swap, image_size); - APP_ERROR_CHECK(err_code); - } - else - { - flash_nrf5x_erase(DFU_BANK_1_REGION_START, image_size); - - // invoke complete callback - pstorage_callback_handler(&m_storage_handle_swap, PSTORAGE_CLEAR_OP_CODE, NRF_SUCCESS, NULL, 0); - } + + if ( is_ota() ) + { + uint32_t err_code; + while(1) { + err_code = pstorage_clear(&m_storage_handle_swap, image_size); + if (err_code != NRF_ERROR_NO_MEM) + break; + // No space, wait until an entry in the queue is freed + while (NRF_ERROR_NOT_FOUND != proc_soc()) { + // nothing + } + } + APP_ERROR_CHECK(err_code); + } + else + { + flash_nrf5x_erase(DFU_BANK_1_REGION_START, image_size); + + // invoke complete callback + pstorage_callback_handler(&m_storage_handle_swap, PSTORAGE_CLEAR_OP_CODE, NRF_SUCCESS, NULL, 0); + } } @@ -263,26 +280,45 @@ static uint32_t dfu_activate_app(void) uint32_t err_code = NRF_SUCCESS; // Erase BANK 0. - if ( is_ota() ) - { - err_code = pstorage_clear(&m_storage_handle_app, m_start_packet.app_image_size); - APP_ERROR_CHECK(err_code); - - err_code = pstorage_store(&m_storage_handle_app, - (uint8_t *)m_storage_handle_swap.block_id, - m_start_packet.app_image_size, - 0); - } - else - { + if ( is_ota() ) + { + while(1) { + err_code = pstorage_clear(&m_storage_handle_app, m_start_packet.app_image_size); + if (err_code != NRF_ERROR_NO_MEM) + break; + // No space, wait until an entry in the queue is freed + while (NRF_ERROR_NOT_FOUND != proc_soc()) { + // nothing + } + } + APP_ERROR_CHECK(err_code); + + while(1) { + err_code = pstorage_store(&m_storage_handle_app, + (uint8_t *)m_storage_handle_swap.block_id, + m_start_packet.app_image_size, + 0); + + if (err_code != NRF_ERROR_NO_MEM) + break; + // No space, wait until an entry in the queue is freed + while (NRF_ERROR_NOT_FOUND != proc_soc()) { + // nothing + } + } + VERIFY_SUCCESS(err_code); + + } + else + { flash_nrf5x_erase(DFU_BANK_0_REGION_START, m_start_packet.app_image_size); pstorage_callback_handler(&m_storage_handle_app, PSTORAGE_CLEAR_OP_CODE, NRF_SUCCESS, NULL, 0); - + flash_nrf5x_write(DFU_BANK_0_REGION_START, (uint8_t *)DFU_BANK_1_REGION_START, m_start_packet.app_image_size, false); - flash_nrf5x_flush(false); + flash_nrf5x_flush(false); pstorage_callback_handler(&m_storage_handle_app, PSTORAGE_STORE_OP_CODE, NRF_SUCCESS, (uint8_t *) DFU_BANK_1_REGION_START, m_start_packet.app_image_size); - } - + } + if (err_code == NRF_SUCCESS) { dfu_update_status_t update_status = { 0 }; @@ -430,13 +466,13 @@ uint32_t dfu_start_pkt_handle(dfu_update_packet_t * p_packet) } if ( DFU_STATE_IDLE != m_dfu_state ) - return NRF_ERROR_INVALID_STATE; - - // Valid peer activity detected. Hence restart the DFU timer. - err_code = dfu_timer_restart(); - VERIFY_SUCCESS(err_code); - - m_functions.prepare(m_image_size); + return NRF_ERROR_INVALID_STATE; + + // Valid peer activity detected. Hence restart the DFU timer. + err_code = dfu_timer_restart(); + VERIFY_SUCCESS(err_code); + + m_functions.prepare(m_image_size); return NRF_SUCCESS; } @@ -486,17 +522,25 @@ uint32_t dfu_data_pkt_handle(dfu_update_packet_t * p_packet) if ( is_ota() ) { - err_code = pstorage_store(mp_storage_handle_active, (uint8_t *)p_data, data_length, m_data_received); - VERIFY_SUCCESS(err_code); + while(1) { + err_code = pstorage_store(mp_storage_handle_active, (uint8_t *)p_data, data_length, m_data_received); + if (err_code != NRF_ERROR_NO_MEM) + break; + // No space, wait until an entry in the queue is freed + while (NRF_ERROR_NOT_FOUND != proc_soc()) { + // nothing + } + } + VERIFY_SUCCESS(err_code); } else { - flash_nrf5x_write( - (mp_storage_handle_active == &m_storage_handle_app - ? DFU_BANK_0_REGION_START - : DFU_BANK_1_REGION_START) - + m_data_received, p_data, data_length, false); - pstorage_callback_handler(mp_storage_handle_active, PSTORAGE_STORE_OP_CODE, NRF_SUCCESS, (uint8_t *) p_data, data_length); + flash_nrf5x_write( + (mp_storage_handle_active == &m_storage_handle_app + ? DFU_BANK_0_REGION_START + : DFU_BANK_1_REGION_START) + + m_data_received, p_data, data_length, false); + pstorage_callback_handler(mp_storage_handle_active, PSTORAGE_STORE_OP_CODE, NRF_SUCCESS, (uint8_t *) p_data, data_length); } m_data_received += data_length; @@ -508,8 +552,8 @@ uint32_t dfu_data_pkt_handle(dfu_update_packet_t * p_packet) } else { - if ( !is_ota() ) flash_nrf5x_flush(false); - + if ( !is_ota() ) flash_nrf5x_flush(false); + // The entire image has been received. Return NRF_SUCCESS. err_code = NRF_SUCCESS; } @@ -561,7 +605,7 @@ uint32_t dfu_init_pkt_handle(dfu_update_packet_t * p_packet) case DFU_STATE_RDY: m_dfu_state = DFU_STATE_RX_INIT_PKT; // When receiving init packet in state ready just update and fall through this case. - /* FALLTHRU */ + /* FALLTHRU */ case DFU_STATE_RX_INIT_PKT: // DFU initialization has been done and a start packet has been received. @@ -811,7 +855,7 @@ uint32_t dfu_bl_image_swap(void) uint32_t dfu_bl_image_validate(void) { bootloader_settings_t bootloader_settings;\ - + sd_mbr_command_t sd_mbr_cmd_1; sd_mbr_command_t sd_mbr_cmd_2; @@ -829,6 +873,10 @@ uint32_t dfu_bl_image_validate(void) sd_mbr_cmd_1.params.compare.ptr2 = (uint32_t *)(bl_image_start_1); sd_mbr_cmd_1.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t); + uint32_t err_code = sd_mbr_command(&sd_mbr_cmd_1); + if (err_code == NRF_SUCCESS) + return NRF_SUCCESS; + uint32_t bl_image_start_2 = (bootloader_settings.sd_image_size == 0) ? DFU_BANK_1_REGION_START : bootloader_settings.sd_image_start + @@ -839,7 +887,7 @@ uint32_t dfu_bl_image_validate(void) sd_mbr_cmd_2.params.compare.ptr2 = (uint32_t *)(bl_image_start_2); sd_mbr_cmd_2.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t); - return sd_mbr_command(&sd_mbr_cmd_1) && sd_mbr_command(&sd_mbr_cmd_2); + return sd_mbr_command(&sd_mbr_cmd_2); } return NRF_SUCCESS; } @@ -863,7 +911,7 @@ uint32_t dfu_sd_image_validate(void) uint32_t block_size = (bootloader_settings.sd_image_start - sd_start) / 2; uint32_t image_end = bootloader_settings.sd_image_start + bootloader_settings.sd_image_size; - + /* ##### FIX START ##### */ block_size &= ~(uint32_t)(CODE_PAGE_SIZE - 1); /* ##### FIX END ##### */ diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c index bf003c63..af25970f 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c @@ -140,7 +140,16 @@ static void dfu_prepare_func_app_erase(uint32_t image_size) if ( is_ota() ) { - uint32_t err_code = pstorage_clear(&m_storage_handle_app, m_image_size); + uint32_t err_code; + while(1) { + err_code = pstorage_clear(&m_storage_handle_app, m_image_size); + if (err_code != NRF_ERROR_NO_MEM) + break; + // No space, wait until an entry in the queue is freed + while (NRF_ERROR_NOT_FOUND != proc_soc()) { + // nothing + } + } APP_ERROR_CHECK(err_code); } else @@ -414,7 +423,15 @@ uint32_t dfu_data_pkt_handle(dfu_update_packet_t * p_packet) if ( is_ota() ) { - err_code = pstorage_store(mp_storage_handle_active, (uint8_t *)p_data, data_length, m_data_received); + while(1) { + err_code = pstorage_store(mp_storage_handle_active, (uint8_t *)p_data, data_length, m_data_received); + if (err_code != NRF_ERROR_NO_MEM) + break; + // No space, wait until an entry in the queue is freed + while (NRF_ERROR_NOT_FOUND != proc_soc()) { + // nothing + } + } VERIFY_SUCCESS(err_code); } else @@ -742,6 +759,17 @@ uint32_t dfu_bl_image_validate(void) if (bootloader_settings.bl_image_size != 0) { + /* + * The problem with updating the bootloader from a dual to single bank bootloader is that the new + * bootloader expects the firmware image to be at the start of BANK 0, while it's actually loaded + * to BANK1 by the old bootloader. After the update the new single bank bootloader verifies that + * the image was correctly written to flash by comparing itself with the firmware image that it + * expects to be located in BANK0. Since the actual firmware image is in BANK 1 this check will + * fail and the new bootloader updates itself with whatever data located in BANK0 using the MBR. + + * The fix is to modify the dfu_bl_image_validate() function in dfu_single_bank.c run verification + * on both addresses (BANK0 and BANK1) + */ uint32_t bl_image_start_1 = (bootloader_settings.sd_image_size == 0) ? DFU_BANK_0_REGION_START : bootloader_settings.sd_image_start + @@ -752,6 +780,10 @@ uint32_t dfu_bl_image_validate(void) sd_mbr_cmd_1.params.compare.ptr2 = (uint32_t *)(bl_image_start_1); sd_mbr_cmd_1.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t); + uint32_t err_code = sd_mbr_command(&sd_mbr_cmd_1); + if (err_code == NRF_SUCCESS) + return NRF_SUCCESS; + uint32_t bl_image_start_2 = (bootloader_settings.sd_image_size == 0) ? DFU_BANK_1_REGION_START : bootloader_settings.sd_image_start + @@ -762,7 +794,7 @@ uint32_t dfu_bl_image_validate(void) sd_mbr_cmd_2.params.compare.ptr2 = (uint32_t *)(bl_image_start_2); sd_mbr_cmd_2.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t); - return sd_mbr_command(&sd_mbr_cmd_1) && sd_mbr_command(&sd_mbr_cmd_2); + return sd_mbr_command(&sd_mbr_cmd_2); } return NRF_SUCCESS; } diff --git a/src/pstorage_platform.h b/src/pstorage_platform.h index bbc573cc..959005c5 100644 --- a/src/pstorage_platform.h +++ b/src/pstorage_platform.h @@ -78,7 +78,7 @@ static __INLINE uint32_t pstorage_flash_page_end() #define PSTORAGE_SWAP_ADDR PSTORAGE_DATA_END_ADDR #define PSTORAGE_MAX_BLOCK_SIZE PSTORAGE_FLASH_PAGE_SIZE /**< Maximum size of block that can be registered with the module. Should be configured based on system requirements. And should be greater than or equal to the minimum size. */ -#define PSTORAGE_CMD_QUEUE_SIZE 10 /**< Maximum number of flash access commands that can be maintained by the module for all applications. Configurable. */ +#define PSTORAGE_CMD_QUEUE_SIZE 18 /**< Maximum number of flash access commands that can be maintained by the module for all applications. Configurable. */ /**@breif Define this flag in case Raw access to persistent memory is to be enabled. Raw mode * unlike the data mode is for uses other than storing data from various mode. This mode is From 794aef2053e05497e0957696a9abcfe996abb5c4 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 26 Jan 2026 17:27:57 +0700 Subject: [PATCH 43/84] clean up --- src/boards/boards.c | 1 - src/boards/boards.h | 1 - src/main.c | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/boards/boards.c b/src/boards/boards.c index 5bf70dc1..9f986324 100644 --- a/src/boards/boards.c +++ b/src/boards/boards.c @@ -63,7 +63,6 @@ bool button_pressed(uint32_t pin) { uint32_t const active_state = (BUTTON_PULL == NRF_GPIO_PIN_PULLDOWN ? 1 : 0); return nrf_gpio_pin_read(pin) == active_state; } - #endif // This is declared so that a board specific init can be called from here. diff --git a/src/boards/boards.h b/src/boards/boards.h index 2841b70d..583e044f 100644 --- a/src/boards/boards.h +++ b/src/boards/boards.h @@ -104,7 +104,6 @@ void led_tick(void); //--------------------------------------------------------------------+ // BUTTONS //--------------------------------------------------------------------+ - #if defined(BUTTON_DFU) || defined(BUTTON_FRESET) void button_init(uint32_t pin); bool button_pressed(uint32_t pin); diff --git a/src/main.c b/src/main.c index 938f2209..2e2fc0c2 100644 --- a/src/main.c +++ b/src/main.c @@ -264,7 +264,7 @@ static void check_dfu_mode(void) { // DFU + FRESET are pressed --> OTA #if defined(BUTTON_DFU) && defined(BUTTON_FRESET) - _ota_dfu = _ota_dfu || ( button_pressed(BUTTON_DFU) && button_pressed(BUTTON_FRESET) ) ; + _ota_dfu = _ota_dfu || (button_pressed(BUTTON_DFU) && button_pressed(BUTTON_FRESET)); #endif bool const valid_app = bootloader_app_is_valid(); From 210c6f4a40f66b437af0f4f6e827ebabdef7e1df Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 26 Jan 2026 18:17:14 +0700 Subject: [PATCH 44/84] add AGENTs.md, remove unused BUTTONS_NUMBER --- AGENTS.md | 25 +++ src/boards/adm_b_nrf52840_1/board.h | 1 - src/boards/ae_bl652_bo/board.h | 1 - src/boards/aramcon2_badge/board.h | 1 - src/boards/aramcon_badge_2019/board.h | 1 - .../arcade_feather_nrf52840_express/board.h | 139 +++++++++-------- src/boards/arduino_nano_33_ble/board.h | 1 - src/boards/bast_ble/board.h | 1 - src/boards/bluemicro_nrf52833/board.h | 1 - src/boards/bluemicro_nrf52840/board.h | 1 - src/boards/challenger_840_ble/board.h | 145 +++++++++--------- src/boards/circuitplayground_nrf52840/board.h | 1 - src/boards/clue_nrf52840/board.h | 1 - src/boards/ebyte_e104_bt5032a/board.h | 131 ++++++++-------- src/boards/ebyte_e73_tbb/board.h | 133 ++++++++-------- src/boards/electronut_labs_papyr/board.h | 1 - src/boards/feather_nrf52832/board.h | 123 ++++++++------- src/boards/feather_nrf52833_express/board.h | 137 ++++++++--------- src/boards/feather_nrf52840_express/board.h | 139 +++++++++-------- src/boards/feather_nrf52840_sense/board.h | 1 - src/boards/feather_nrf52840_sense_tft/board.h | 1 - src/boards/ikigaisense_vita/board.h | 137 ++++++++--------- src/boards/itsybitsy_nrf52840_express/board.h | 1 - src/boards/ledglasses_nrf52840/board.h | 1 - src/boards/m60_keyboard/board.h | 1 - src/boards/mbn52832dk/board.h | 125 ++++++++------- src/boards/mdk_nrf52840_dongle/board.h | 1 - src/boards/metro_nrf52840_express/board.h | 1 - src/boards/mikoto/board.h | 1 - src/boards/nRF52840_connect_kit/board.h | 1 - src/boards/nice_nano/board.h | 1 - src/boards/nrf52840_bboard/board.h | 1 - src/boards/nrf52840_m2/board.h | 1 - src/boards/nrfmicro/board.h | 131 ++++++++-------- src/boards/ohs2020_badge/board.h | 1 - src/boards/omnimo_nrf52840/board.h | 143 +++++++++-------- src/boards/particle_argon/board.h | 139 +++++++++-------- src/boards/particle_boron/board.h | 139 +++++++++-------- src/boards/particle_xenon/board.h | 139 +++++++++-------- src/boards/pca10056/board.h | 1 - src/boards/pca10059/board.h | 1 - src/boards/pca10100/board.h | 1 - src/boards/pillbug/board.h | 1 - src/boards/pitaya_go/board.h | 1 - src/boards/raytac_mdbt50q_db_40/board.h | 1 - src/boards/raytac_mdbt50q_rx/board.h | 1 - src/boards/sparkfun_nrf52840_micromod/board.h | 1 - src/boards/t1000_e/board.h | 1 - src/boards/waveshare_nrf52840_eval/board.h | 1 - src/boards/xiao_nrf52840_ble/board.h | 135 ++++++++-------- src/boards/xiao_nrf52840_ble_sense/board.h | 135 ++++++++-------- 51 files changed, 1102 insertions(+), 1127 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..4df910ee --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,25 @@ +# Agent Instructions + +## Style +- Follow the repo `.clang-format` when making changes. + +## Build / Verify +- Prefer CMake for testing/verification. +- Always build both boards during verification: + - `feather_nrf52832` + - `feather_nrf52840_express` + +### CMake (preferred) +```bash +cmake -S . -B cmake-build-feather_nrf52832 -DBOARD=feather_nrf52832 +cmake --build cmake-build-feather_nrf52832 + +cmake -S . -B cmake-build-feather_nrf52840_express -DBOARD=feather_nrf52840_express +cmake --build cmake-build-feather_nrf52840_express +``` + +### Make (alternate) +```bash +make BOARD=feather_nrf52832 all +make BOARD=feather_nrf52840_express all +``` diff --git a/src/boards/adm_b_nrf52840_1/board.h b/src/boards/adm_b_nrf52840_1/board.h index 49ea04bf..11c041a4 100644 --- a/src/boards/adm_b_nrf52840_1/board.h +++ b/src/boards/adm_b_nrf52840_1/board.h @@ -38,7 +38,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(0, 11) #define BUTTON_2 _PINNUM(0, 12) diff --git a/src/boards/ae_bl652_bo/board.h b/src/boards/ae_bl652_bo/board.h index c4b78dbd..1636ccfa 100644 --- a/src/boards/ae_bl652_bo/board.h +++ b/src/boards/ae_bl652_bo/board.h @@ -36,7 +36,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 11 // BTN1 #define BUTTON_2 15 // BTN2 #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/aramcon2_badge/board.h b/src/boards/aramcon2_badge/board.h index f651915f..788fa3bd 100644 --- a/src/boards/aramcon2_badge/board.h +++ b/src/boards/aramcon2_badge/board.h @@ -40,7 +40,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(0, 2) // left Button #define BUTTON_2 _PINNUM(0, 29) // middle button #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/aramcon_badge_2019/board.h b/src/boards/aramcon_badge_2019/board.h index 3dc666d0..c5458802 100644 --- a/src/boards/aramcon_badge_2019/board.h +++ b/src/boards/aramcon_badge_2019/board.h @@ -41,7 +41,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(0, 2) // left Button #define BUTTON_2 _PINNUM(0, 29) // middle button #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/arcade_feather_nrf52840_express/board.h b/src/boards/arcade_feather_nrf52840_express/board.h index aed04edb..b8b91f53 100644 --- a/src/boards/arcade_feather_nrf52840_express/board.h +++ b/src/boards/arcade_feather_nrf52840_express/board.h @@ -1,70 +1,69 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _ARCARDE_FEATHER_NRF52840_H -#define _ARCARDE_FEATHER_NRF52840_H - -#define _PINNUM(port, pin) ((port)*32 + (pin)) - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN _PINNUM(1, 15) -#define LED_SECONDARY_PIN _PINNUM(1, 10) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL _PINNUM(0, 16) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_1 _PINNUM(1, 02) -#define BUTTON_2 _PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Feather nRF52840 Express" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0029 -#define USB_DESC_CDC_ONLY_PID 0x002A - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit Feather nRF52840 Express" -#define UF2_VOLUME_LABEL "ARCADE-N4" -#define UF2_BOARD_ID "nRF52840-Feather-revD" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4062" - -#endif // _ARCARDE_FEATHER_NRF52840_H - +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _ARCARDE_FEATHER_NRF52840_H +#define _ARCARDE_FEATHER_NRF52840_H + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN _PINNUM(1, 15) +#define LED_SECONDARY_PIN _PINNUM(1, 10) +#define LED_STATE_ON 1 + +#define LED_NEOPIXEL _PINNUM(0, 16) +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_1 _PINNUM(1, 02) +#define BUTTON_2 _PINNUM(0, 10) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "Adafruit Industries" +#define BLEDIS_MODEL "Feather nRF52840 Express" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0029 +#define USB_DESC_CDC_ONLY_PID 0x002A + +//------------- UF2 -------------// +#define UF2_PRODUCT_NAME "Adafruit Feather nRF52840 Express" +#define UF2_VOLUME_LABEL "ARCADE-N4" +#define UF2_BOARD_ID "nRF52840-Feather-revD" +#define UF2_INDEX_URL "https://www.adafruit.com/product/4062" + +#endif // _ARCARDE_FEATHER_NRF52840_H + diff --git a/src/boards/arduino_nano_33_ble/board.h b/src/boards/arduino_nano_33_ble/board.h index 00fb2ef3..d29fc368 100644 --- a/src/boards/arduino_nano_33_ble/board.h +++ b/src/boards/arduino_nano_33_ble/board.h @@ -37,7 +37,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(1, 11) // D2 switch #define BUTTON_2 _PINNUM(1, 12) // D3 switch #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/bast_ble/board.h b/src/boards/bast_ble/board.h index fb6e8cb5..93a0eb68 100644 --- a/src/boards/bast_ble/board.h +++ b/src/boards/bast_ble/board.h @@ -36,7 +36,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(0, 05) #define BUTTON_2 _PINNUM(0, 10) #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/bluemicro_nrf52833/board.h b/src/boards/bluemicro_nrf52833/board.h index 9b647c38..763a0f4f 100644 --- a/src/boards/bluemicro_nrf52833/board.h +++ b/src/boards/bluemicro_nrf52833/board.h @@ -42,7 +42,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 // none connected at all #define BUTTON_1 _PINNUM(0, 18) // unusable: RESET #define BUTTON_2 _PINNUM(1, 1) // no connection on E73-2G4M08S1E Module #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/bluemicro_nrf52840/board.h b/src/boards/bluemicro_nrf52840/board.h index 393e538c..f590dfe0 100644 --- a/src/boards/bluemicro_nrf52840/board.h +++ b/src/boards/bluemicro_nrf52840/board.h @@ -38,7 +38,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 // none connected at all #define BUTTON_1 _PINNUM(0, 18) // unusable: RESET #define BUTTON_2 _PINNUM(1, 1) // no connection on E73-2G4M08S1C Module #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/challenger_840_ble/board.h b/src/boards/challenger_840_ble/board.h index 371be084..25d4c6e9 100644 --- a/src/boards/challenger_840_ble/board.h +++ b/src/boards/challenger_840_ble/board.h @@ -1,73 +1,72 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries, 2022 Invector Labs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _CHALLENGER_840_BLE_H -#define _CHALLENGER_840_BLE_H - -#define _PINNUM(port, pin) ((port)*32 + (pin)) - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 12) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL _PINNUM(1, 8) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_1 _PINNUM(0, 19) -#define BUTTON_2 _PINNUM(0, 8) // Pulls flash cs high -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -/*------------------------------------------------------------------*/ -/* On board LDO control - *------------------------------------------------------------------*/ -#define LDO_CONTROL_PIN _PINNUM(1, 9) // Enables external pwr - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Invector Labs" -#define BLEDIS_MODEL "Challenger 840 BLE" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1209 -#define USB_DESC_UF2_PID 0x7380 -#define USB_DESC_CDC_ONLY_PID 0x7381 - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "ILabs Challenger 840" -#define UF2_VOLUME_LABEL "CH840BOOT" -#define UF2_BOARD_ID "nRF52840-Challenger-840" -#define UF2_INDEX_URL "https://www.ilabs.se" - -#endif // _CHALLENGER_840_BLE_H +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries, 2022 Invector Labs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _CHALLENGER_840_BLE_H +#define _CHALLENGER_840_BLE_H + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN _PINNUM(0, 12) +#define LED_STATE_ON 1 + +#define LED_NEOPIXEL _PINNUM(1, 8) +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_1 _PINNUM(0, 19) +#define BUTTON_2 _PINNUM(0, 8) // Pulls flash cs high +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +/*------------------------------------------------------------------*/ +/* On board LDO control + *------------------------------------------------------------------*/ +#define LDO_CONTROL_PIN _PINNUM(1, 9) // Enables external pwr + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "Invector Labs" +#define BLEDIS_MODEL "Challenger 840 BLE" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ +#define USB_DESC_VID 0x1209 +#define USB_DESC_UF2_PID 0x7380 +#define USB_DESC_CDC_ONLY_PID 0x7381 + +//------------- UF2 -------------// +#define UF2_PRODUCT_NAME "ILabs Challenger 840" +#define UF2_VOLUME_LABEL "CH840BOOT" +#define UF2_BOARD_ID "nRF52840-Challenger-840" +#define UF2_INDEX_URL "https://www.ilabs.se" + +#endif // _CHALLENGER_840_BLE_H diff --git a/src/boards/circuitplayground_nrf52840/board.h b/src/boards/circuitplayground_nrf52840/board.h index 5544502c..62a537e8 100644 --- a/src/boards/circuitplayground_nrf52840/board.h +++ b/src/boards/circuitplayground_nrf52840/board.h @@ -41,7 +41,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(1, 02) // left button #define BUTTON_2 _PINNUM(1, 15) // right button #define BUTTON_PULL NRF_GPIO_PIN_PULLDOWN diff --git a/src/boards/clue_nrf52840/board.h b/src/boards/clue_nrf52840/board.h index 818f2f4d..61466e37 100644 --- a/src/boards/clue_nrf52840/board.h +++ b/src/boards/clue_nrf52840/board.h @@ -41,7 +41,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(1, 02) // left button #define BUTTON_2 _PINNUM(1, 10) // right button #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/ebyte_e104_bt5032a/board.h b/src/boards/ebyte_e104_bt5032a/board.h index da20812f..3cc675d7 100644 --- a/src/boards/ebyte_e104_bt5032a/board.h +++ b/src/boards/ebyte_e104_bt5032a/board.h @@ -1,66 +1,65 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 Pierre Constantineau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _EBYTE_E104_BT5032A_H -#define _EBYTE_E104_BT5032A_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 31 // Red - named "Data" -#define LED_SECONDARY_PIN 30 // Blue - named "Link" (also a red one on the Test Board) -#define LED_STATE_ON 0 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_1 29 // DISC Button on E104-BT5032A-TB Test Board - // BUTTON_1 is DFU. The module does not have reset circuitry. - // The Test Board from Ebyte (E104-BT5032A-TB) also does not have reset circuitry - // To be able to upload using the Arduino IDE, (To enter DFU mode) - // Press "DISC", press and release "RST", then release "DISC" -#define BUTTON_2 28 // No button - WKP pin -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -/*------------------------------------------------------------------*/ -/* UART (only used by nRF52832) - *------------------------------------------------------------------*/ -#define RX_PIN_NUMBER 14 -#define TX_PIN_NUMBER 18 -#define CTS_PIN_NUMBER 20 -#define RTS_PIN_NUMBER 19 -#define HWFC false // leaving it false to make GPIO available - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "CDEBYTE" -#define BLEDIS_MODEL "E104-BT5032A" - -#define UF2_PRODUCT_NAME "Ebyte E104-BT5032A nRF52832" -#define UF2_INDEX_URL "https://www.ebyte.com/en/product-view-news.html?id=756" - -#endif // _EBYTE_E104_BT5032A_H +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Pierre Constantineau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _EBYTE_E104_BT5032A_H +#define _EBYTE_E104_BT5032A_H + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN 31 // Red - named "Data" +#define LED_SECONDARY_PIN 30 // Blue - named "Link" (also a red one on the Test Board) +#define LED_STATE_ON 0 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_1 29 // DISC Button on E104-BT5032A-TB Test Board + // BUTTON_1 is DFU. The module does not have reset circuitry. + // The Test Board from Ebyte (E104-BT5032A-TB) also does not have reset circuitry + // To be able to upload using the Arduino IDE, (To enter DFU mode) + // Press "DISC", press and release "RST", then release "DISC" +#define BUTTON_2 28 // No button - WKP pin +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +/*------------------------------------------------------------------*/ +/* UART (only used by nRF52832) + *------------------------------------------------------------------*/ +#define RX_PIN_NUMBER 14 +#define TX_PIN_NUMBER 18 +#define CTS_PIN_NUMBER 20 +#define RTS_PIN_NUMBER 19 +#define HWFC false // leaving it false to make GPIO available + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "CDEBYTE" +#define BLEDIS_MODEL "E104-BT5032A" + +#define UF2_PRODUCT_NAME "Ebyte E104-BT5032A nRF52832" +#define UF2_INDEX_URL "https://www.ebyte.com/en/product-view-news.html?id=756" + +#endif // _EBYTE_E104_BT5032A_H diff --git a/src/boards/ebyte_e73_tbb/board.h b/src/boards/ebyte_e73_tbb/board.h index 30976e60..f6f4c90b 100644 --- a/src/boards/ebyte_e73_tbb/board.h +++ b/src/boards/ebyte_e73_tbb/board.h @@ -1,67 +1,66 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 Pierre Constantineau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _EBYTE_E73_TBB_H -#define _EBYTE_E73_TBB_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 17 // Red - named "LED1" -#define LED_SECONDARY_PIN 18 // Blue - named "LED2" (also a red one on the Test Board) -#define LED_STATE_ON 0 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_1 14 // BUTTON_1 is DFU. SW1 on E73-TBB Test Board. - // This Test board uses the E73-2G4M04S1B module which contains a nrf52832. - // The module does not have reset circuitry. - // The Test Board from Ebyte (E73-TBB) also does not have reset circuitry - // To be able to upload using the Arduino IDE, (To enter DFU mode) - // Press "SW1", momentarily ground "RST/P0.21", then release "SW1" -#define BUTTON_2 13 // SW2 on board -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -/*------------------------------------------------------------------*/ -/* UART (only used by nRF52832) - *------------------------------------------------------------------*/ -#define RX_PIN_NUMBER 8 -#define TX_PIN_NUMBER 6 -#define CTS_PIN_NUMBER 5 -#define RTS_PIN_NUMBER 7 -#define HWFC false // leaving it false to make GPIO available - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "CDEBYTE" -#define BLEDIS_MODEL "E73-TBB" - -#define UF2_PRODUCT_NAME "Ebyte E73-TBB nRF52832" -#define UF2_INDEX_URL "https://www.ebyte.com/en/product-view-news.aspx?id=889" - -#endif // _EBYTE_E73_TBB_H +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 Pierre Constantineau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _EBYTE_E73_TBB_H +#define _EBYTE_E73_TBB_H + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN 17 // Red - named "LED1" +#define LED_SECONDARY_PIN 18 // Blue - named "LED2" (also a red one on the Test Board) +#define LED_STATE_ON 0 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_1 14 // BUTTON_1 is DFU. SW1 on E73-TBB Test Board. + // This Test board uses the E73-2G4M04S1B module which contains a nrf52832. + // The module does not have reset circuitry. + // The Test Board from Ebyte (E73-TBB) also does not have reset circuitry + // To be able to upload using the Arduino IDE, (To enter DFU mode) + // Press "SW1", momentarily ground "RST/P0.21", then release "SW1" +#define BUTTON_2 13 // SW2 on board +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +/*------------------------------------------------------------------*/ +/* UART (only used by nRF52832) + *------------------------------------------------------------------*/ +#define RX_PIN_NUMBER 8 +#define TX_PIN_NUMBER 6 +#define CTS_PIN_NUMBER 5 +#define RTS_PIN_NUMBER 7 +#define HWFC false // leaving it false to make GPIO available + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "CDEBYTE" +#define BLEDIS_MODEL "E73-TBB" + +#define UF2_PRODUCT_NAME "Ebyte E73-TBB nRF52832" +#define UF2_INDEX_URL "https://www.ebyte.com/en/product-view-news.aspx?id=889" + +#endif // _EBYTE_E73_TBB_H diff --git a/src/boards/electronut_labs_papyr/board.h b/src/boards/electronut_labs_papyr/board.h index 2dd931c8..70fa231d 100644 --- a/src/boards/electronut_labs_papyr/board.h +++ b/src/boards/electronut_labs_papyr/board.h @@ -42,7 +42,6 @@ // App can trigger OTA bootloader by writing DFU_MAGIC_OTA_APPJUM to // GPREGRET register if softdevice is not initialized; or by // writing DFU_MAGIC_OTA_RESET in case softdevice is initialized. -#define BUTTONS_NUMBER 2 #define BUTTON_1 18 // RESET also by default #define BUTTON_2 1 // P0.1 not exposed anywhere, FRST n/a #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/feather_nrf52832/board.h b/src/boards/feather_nrf52832/board.h index a31bafa8..dfb6175a 100644 --- a/src/boards/feather_nrf52832/board.h +++ b/src/boards/feather_nrf52832/board.h @@ -1,62 +1,61 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _FEATHER_NRF52832_H -#define _FEATHER_NRF52832_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 17 // Red -#define LED_SECONDARY_PIN 19 // Blue -#define LED_STATE_ON 1 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_1 20 -#define BUTTON_2 22 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -/*------------------------------------------------------------------*/ -/* UART (only used by nRF52832) - *------------------------------------------------------------------*/ -#define RX_PIN_NUMBER 8 -#define TX_PIN_NUMBER 6 -#define CTS_PIN_NUMBER 0 -#define RTS_PIN_NUMBER 0 -#define HWFC false - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Feather nRF52832" - -#define UF2_PRODUCT_NAME "Adafruit Feather nRF52832" -#define UF2_INDEX_URL "https://www.adafruit.com/product/3406" - -#endif // _FEATHER_NRF52832_H +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _FEATHER_NRF52832_H +#define _FEATHER_NRF52832_H + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN 17 // Red +#define LED_SECONDARY_PIN 19 // Blue +#define LED_STATE_ON 1 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_1 20 +#define BUTTON_2 22 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +/*------------------------------------------------------------------*/ +/* UART (only used by nRF52832) + *------------------------------------------------------------------*/ +#define RX_PIN_NUMBER 8 +#define TX_PIN_NUMBER 6 +#define CTS_PIN_NUMBER 0 +#define RTS_PIN_NUMBER 0 +#define HWFC false + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "Adafruit Industries" +#define BLEDIS_MODEL "Feather nRF52832" + +#define UF2_PRODUCT_NAME "Adafruit Feather nRF52832" +#define UF2_INDEX_URL "https://www.adafruit.com/product/3406" + +#endif // _FEATHER_NRF52832_H diff --git a/src/boards/feather_nrf52833_express/board.h b/src/boards/feather_nrf52833_express/board.h index af0e1ec4..e52dc86e 100644 --- a/src/boards/feather_nrf52833_express/board.h +++ b/src/boards/feather_nrf52833_express/board.h @@ -1,69 +1,68 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _FEATHER_NRF52833_H -#define _FEATHER_NRF52833_H - -#define _PINNUM(port, pin) ((port)*32 + (pin)) - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN _PINNUM(1, 15) -#define LED_SECONDARY_PIN _PINNUM(1, 10) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL _PINNUM(0, 16) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_1 _PINNUM(1, 02) -#define BUTTON_2 _PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Feather nRF52833 Express" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0029 // TODO change later -#define USB_DESC_CDC_ONLY_PID 0x002A // TODO change later - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit Feather nRF52833 Express" -#define UF2_VOLUME_LABEL "FTHR833BOOT" -#define UF2_BOARD_ID "nRF52833-Feather-revD" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4062" // TODO change later - -#endif +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _FEATHER_NRF52833_H +#define _FEATHER_NRF52833_H + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN _PINNUM(1, 15) +#define LED_SECONDARY_PIN _PINNUM(1, 10) +#define LED_STATE_ON 1 + +#define LED_NEOPIXEL _PINNUM(0, 16) +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_1 _PINNUM(1, 02) +#define BUTTON_2 _PINNUM(0, 10) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "Adafruit Industries" +#define BLEDIS_MODEL "Feather nRF52833 Express" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0029 // TODO change later +#define USB_DESC_CDC_ONLY_PID 0x002A // TODO change later + +//------------- UF2 -------------// +#define UF2_PRODUCT_NAME "Adafruit Feather nRF52833 Express" +#define UF2_VOLUME_LABEL "FTHR833BOOT" +#define UF2_BOARD_ID "nRF52833-Feather-revD" +#define UF2_INDEX_URL "https://www.adafruit.com/product/4062" // TODO change later + +#endif diff --git a/src/boards/feather_nrf52840_express/board.h b/src/boards/feather_nrf52840_express/board.h index 49994f0c..cec7e68d 100644 --- a/src/boards/feather_nrf52840_express/board.h +++ b/src/boards/feather_nrf52840_express/board.h @@ -1,70 +1,69 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _FEATHER_NRF52840_H -#define _FEATHER_NRF52840_H - -#define _PINNUM(port, pin) ((port)*32 + (pin)) - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN _PINNUM(1, 15) -#define LED_SECONDARY_PIN _PINNUM(1, 10) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL _PINNUM(0, 16) -#define NEOPIXEL_POWER_PIN _PINNUM(1, 14) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_1 _PINNUM(1, 02) -#define BUTTON_2 _PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Feather nRF52840 Express" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0029 -#define USB_DESC_CDC_ONLY_PID 0x002A - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit Feather nRF52840 Express" -#define UF2_VOLUME_LABEL "FTHR840BOOT" -#define UF2_BOARD_ID "nRF52840-Feather-revE" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4062" - -#endif // _FEATHER_NRF52840_H +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _FEATHER_NRF52840_H +#define _FEATHER_NRF52840_H + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN _PINNUM(1, 15) +#define LED_SECONDARY_PIN _PINNUM(1, 10) +#define LED_STATE_ON 1 + +#define LED_NEOPIXEL _PINNUM(0, 16) +#define NEOPIXEL_POWER_PIN _PINNUM(1, 14) +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_1 _PINNUM(1, 02) +#define BUTTON_2 _PINNUM(0, 10) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "Adafruit Industries" +#define BLEDIS_MODEL "Feather nRF52840 Express" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0029 +#define USB_DESC_CDC_ONLY_PID 0x002A + +//------------- UF2 -------------// +#define UF2_PRODUCT_NAME "Adafruit Feather nRF52840 Express" +#define UF2_VOLUME_LABEL "FTHR840BOOT" +#define UF2_BOARD_ID "nRF52840-Feather-revE" +#define UF2_INDEX_URL "https://www.adafruit.com/product/4062" + +#endif // _FEATHER_NRF52840_H diff --git a/src/boards/feather_nrf52840_sense/board.h b/src/boards/feather_nrf52840_sense/board.h index fbaf9bc8..4c9b2505 100644 --- a/src/boards/feather_nrf52840_sense/board.h +++ b/src/boards/feather_nrf52840_sense/board.h @@ -42,7 +42,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(1, 02) #define BUTTON_2 _PINNUM(0, 10) #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/feather_nrf52840_sense_tft/board.h b/src/boards/feather_nrf52840_sense_tft/board.h index 1fa03bd3..f46c14be 100644 --- a/src/boards/feather_nrf52840_sense_tft/board.h +++ b/src/boards/feather_nrf52840_sense_tft/board.h @@ -42,7 +42,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(1, 6) #define BUTTON_2 _PINNUM(1, 10) // DFU pin #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/ikigaisense_vita/board.h b/src/boards/ikigaisense_vita/board.h index dab28e38..afb1958c 100644 --- a/src/boards/ikigaisense_vita/board.h +++ b/src/boards/ikigaisense_vita/board.h @@ -1,69 +1,68 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _IKIGAISENSE_VITA_NRF52840_H -#define _IKIGAISENSE_VITA_NRF52840_H - -#define _PINNUM(port, pin) ((port)*32 + (pin)) - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN _PINNUM(1, 15) -#define LED_SECONDARY_PIN _PINNUM(1, 10) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL _PINNUM(0, 16) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_1 _PINNUM(1, 02) -#define BUTTON_2 _PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "IkigaiSense Technologies" -#define BLEDIS_MODEL "Vita nRF52840" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0093 -#define USB_DESC_CDC_ONLY_PID 0x0093 - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "IkigaiSense Vita nRF52840" -#define UF2_VOLUME_LABEL "ISVITABoot" -#define UF2_BOARD_ID "nRF52840-isVITA-revA" -#define UF2_INDEX_URL "https://github.com/ikigaisense/vita" - -#endif // _IKIGAISENSE_VITA_NRF52840_H +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _IKIGAISENSE_VITA_NRF52840_H +#define _IKIGAISENSE_VITA_NRF52840_H + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN _PINNUM(1, 15) +#define LED_SECONDARY_PIN _PINNUM(1, 10) +#define LED_STATE_ON 1 + +#define LED_NEOPIXEL _PINNUM(0, 16) +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_1 _PINNUM(1, 02) +#define BUTTON_2 _PINNUM(0, 10) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "IkigaiSense Technologies" +#define BLEDIS_MODEL "Vita nRF52840" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0093 +#define USB_DESC_CDC_ONLY_PID 0x0093 + +//------------- UF2 -------------// +#define UF2_PRODUCT_NAME "IkigaiSense Vita nRF52840" +#define UF2_VOLUME_LABEL "ISVITABoot" +#define UF2_BOARD_ID "nRF52840-isVITA-revA" +#define UF2_INDEX_URL "https://github.com/ikigaisense/vita" + +#endif // _IKIGAISENSE_VITA_NRF52840_H diff --git a/src/boards/itsybitsy_nrf52840_express/board.h b/src/boards/itsybitsy_nrf52840_express/board.h index a3b6c08b..a9372d15 100644 --- a/src/boards/itsybitsy_nrf52840_express/board.h +++ b/src/boards/itsybitsy_nrf52840_express/board.h @@ -46,7 +46,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(0, 29) // user switch #define BUTTON_2 _PINNUM(1, 02) // D2 breakout #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/ledglasses_nrf52840/board.h b/src/boards/ledglasses_nrf52840/board.h index dd676d71..e2ea2f01 100644 --- a/src/boards/ledglasses_nrf52840/board.h +++ b/src/boards/ledglasses_nrf52840/board.h @@ -41,7 +41,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(0, 30) // user switch #define BUTTON_2 _PINNUM(0, 28) // NC #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/m60_keyboard/board.h b/src/boards/m60_keyboard/board.h index 77371b14..20822b5d 100644 --- a/src/boards/m60_keyboard/board.h +++ b/src/boards/m60_keyboard/board.h @@ -41,7 +41,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(0, 27) #define BUTTON_2 _PINNUM(0, 19) // ESC #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/mbn52832dk/board.h b/src/boards/mbn52832dk/board.h index c9fda306..b731d681 100644 --- a/src/boards/mbn52832dk/board.h +++ b/src/boards/mbn52832dk/board.h @@ -1,63 +1,62 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _MBN52832DK_H -#define _MBN52832DK_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 17 // LED1 -#define LED_SECONDARY_PIN 18 // LED2 -#define LED_STATE_ON 1 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_1 13 -#define BUTTON_2 14 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -/*------------------------------------------------------------------*/ -/* UART (only used by nRF52832) - *------------------------------------------------------------------*/ -#define RX_PIN_NUMBER 8 -#define TX_PIN_NUMBER 6 -#define CTS_PIN_NUMBER 0 -#define RTS_PIN_NUMBER 0 -#define HWFC false - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Murata" -#define BLEDIS_MODEL "MBN52832DK" - -#define UF2_BOARD_ID "nRF52832-MBN52832DK-v1" -#define UF2_PRODUCT_NAME "Murata MBN52832DK" -#define UF2_INDEX_URL "https://www.murata.com/ja-jp/products/connectivitymodule/bluetooth/overview/lineup/mbn52832" - -#endif // _MBN52832DK_H +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _MBN52832DK_H +#define _MBN52832DK_H + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN 17 // LED1 +#define LED_SECONDARY_PIN 18 // LED2 +#define LED_STATE_ON 1 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_1 13 +#define BUTTON_2 14 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +/*------------------------------------------------------------------*/ +/* UART (only used by nRF52832) + *------------------------------------------------------------------*/ +#define RX_PIN_NUMBER 8 +#define TX_PIN_NUMBER 6 +#define CTS_PIN_NUMBER 0 +#define RTS_PIN_NUMBER 0 +#define HWFC false + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "Murata" +#define BLEDIS_MODEL "MBN52832DK" + +#define UF2_BOARD_ID "nRF52832-MBN52832DK-v1" +#define UF2_PRODUCT_NAME "Murata MBN52832DK" +#define UF2_INDEX_URL "https://www.murata.com/ja-jp/products/connectivitymodule/bluetooth/overview/lineup/mbn52832" + +#endif // _MBN52832DK_H diff --git a/src/boards/mdk_nrf52840_dongle/board.h b/src/boards/mdk_nrf52840_dongle/board.h index 65284516..066c66b2 100644 --- a/src/boards/mdk_nrf52840_dongle/board.h +++ b/src/boards/mdk_nrf52840_dongle/board.h @@ -44,7 +44,6 @@ /* BUTTON *------------------------------------------------------------------*/ // TODO(gpshead): simplify, have code support 0. double reset only. -#define BUTTONS_NUMBER 2 // none connected at all #define BUTTON_1 _PINNUM(0, 18) // unusable: RESET #define BUTTON_2 _PINNUM(0, 19) // no connection #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/metro_nrf52840_express/board.h b/src/boards/metro_nrf52840_express/board.h index 0b47a1c6..53519ed8 100644 --- a/src/boards/metro_nrf52840_express/board.h +++ b/src/boards/metro_nrf52840_express/board.h @@ -42,7 +42,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(1, 0) #define BUTTON_2 _PINNUM(2, 2) #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/mikoto/board.h b/src/boards/mikoto/board.h index 6431f67c..f46ef525 100644 --- a/src/boards/mikoto/board.h +++ b/src/boards/mikoto/board.h @@ -39,7 +39,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 // none connected at all #define BUTTON_1 _PINNUM(0, 18) // unusable: RESET #define BUTTON_2 _PINNUM(0, 19) // no connection #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/nRF52840_connect_kit/board.h b/src/boards/nRF52840_connect_kit/board.h index 37409284..2cdaa240 100644 --- a/src/boards/nRF52840_connect_kit/board.h +++ b/src/boards/nRF52840_connect_kit/board.h @@ -41,7 +41,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(1, 0) // P1.00: User Button #define BUTTON_2 _PINNUM(1, 7) // P1.07: NC #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/nice_nano/board.h b/src/boards/nice_nano/board.h index 0ec4cde5..c0825182 100644 --- a/src/boards/nice_nano/board.h +++ b/src/boards/nice_nano/board.h @@ -39,7 +39,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 // none connected at all #define BUTTON_1 _PINNUM(0, 18) // unusable: RESET #define BUTTON_2 _PINNUM(0, 19) // no connection #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/nrf52840_bboard/board.h b/src/boards/nrf52840_bboard/board.h index 515201d0..43844751 100644 --- a/src/boards/nrf52840_bboard/board.h +++ b/src/boards/nrf52840_bboard/board.h @@ -41,7 +41,6 @@ // App can trigger OTA bootloader by writing DFU_MAGIC_OTA_APPJUM to // GPREGRET register if softdevice is not initialized; or by // writing DFU_MAGIC_OTA_RESET in case softdevice is initialized. -#define BUTTONS_NUMBER 2 #define BUTTON_1 18 // RESET also by default #define BUTTON_2 1 // P0.1 not exposed anywhere, FRST n/a #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/nrf52840_m2/board.h b/src/boards/nrf52840_m2/board.h index 57265abe..27fc7605 100644 --- a/src/boards/nrf52840_m2/board.h +++ b/src/boards/nrf52840_m2/board.h @@ -42,7 +42,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(0, 19) #define BUTTON_2 _PINNUM(1, 7) #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/nrfmicro/board.h b/src/boards/nrfmicro/board.h index c6b25288..f3bdb4e9 100644 --- a/src/boards/nrfmicro/board.h +++ b/src/boards/nrfmicro/board.h @@ -1,66 +1,65 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _NRFMICRO_NRF52840 -#define _NRFMICRO_NRF52840 - -#define _PINNUM(port, pin) ((port)*32 + (pin)) - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(1, 10) -#define LED_STATE_ON 1 // State when LED is lit (active HIGH) - -#define NEOPIXELS_NUMBER 0 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_1 _PINNUM(1, 2) // DFU -#define BUTTON_2 _PINNUM(0, 12) // FRST -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "nRFMicro" -#define BLEDIS_MODEL "nRFMicro nRF52840" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1209 -#define USB_DESC_UF2_PID 0x5284 -#define USB_DESC_CDC_ONLY_PID 0x5284 - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "nRFMicro" -#define UF2_VOLUME_LABEL "NRFMICRO" -#define UF2_BOARD_ID "nRF52840-nRFMicro-v0" -#define UF2_INDEX_URL "https://github.com/joric/nrfmicro/wiki" - -#endif // _NRFMICRO_NRF52840 +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _NRFMICRO_NRF52840 +#define _NRFMICRO_NRF52840 + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN _PINNUM(1, 10) +#define LED_STATE_ON 1 // State when LED is lit (active HIGH) + +#define NEOPIXELS_NUMBER 0 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_1 _PINNUM(1, 2) // DFU +#define BUTTON_2 _PINNUM(0, 12) // FRST +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "nRFMicro" +#define BLEDIS_MODEL "nRFMicro nRF52840" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ +#define USB_DESC_VID 0x1209 +#define USB_DESC_UF2_PID 0x5284 +#define USB_DESC_CDC_ONLY_PID 0x5284 + +//------------- UF2 -------------// +#define UF2_PRODUCT_NAME "nRFMicro" +#define UF2_VOLUME_LABEL "NRFMICRO" +#define UF2_BOARD_ID "nRF52840-nRFMicro-v0" +#define UF2_INDEX_URL "https://github.com/joric/nrfmicro/wiki" + +#endif // _NRFMICRO_NRF52840 diff --git a/src/boards/ohs2020_badge/board.h b/src/boards/ohs2020_badge/board.h index 1a628111..0172e6db 100644 --- a/src/boards/ohs2020_badge/board.h +++ b/src/boards/ohs2020_badge/board.h @@ -38,7 +38,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(0, 29) // SW2 #define BUTTON_2 _PINNUM(0, 03) // SW1 #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/omnimo_nrf52840/board.h b/src/boards/omnimo_nrf52840/board.h index 0a3b710f..f591a557 100644 --- a/src/boards/omnimo_nrf52840/board.h +++ b/src/boards/omnimo_nrf52840/board.h @@ -1,72 +1,71 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _OMNIMO_NRF52840_H -#define _OMNIMO_NRF52840_H - -#define _PINNUM(port, pin) ((port)*32 + (pin)) - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN _PINNUM(1, 15) -#define LED_SECONDARY_PIN _PINNUM(1, 10) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL _PINNUM(0, 16) - -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_1 _PINNUM(1, 02) -#define BUTTON_2 _PINNUM(1, 07) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "eAFAQ" -#define BLEDIS_MODEL "OMNIMO nRF52840" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ - -//Shared VID/PID with pca10056 -#define USB_DESC_VID 0x1209 -#define USB_DESC_UF2_PID 0xCECE -#define USB_DESC_CDC_ONLY_PID 0xCECE - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Omnimo nRF52840" -#define UF2_VOLUME_LABEL "OMNIn52BOOT" -#define UF2_BOARD_ID "nRF52840-Omnimo" -#define UF2_INDEX_URL "https://www.crowdsupply.com/eafaq/omnimo-nrf52840" - -#endif // _OMNIMO_NRF52840_H +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _OMNIMO_NRF52840_H +#define _OMNIMO_NRF52840_H + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN _PINNUM(1, 15) +#define LED_SECONDARY_PIN _PINNUM(1, 10) +#define LED_STATE_ON 1 + +#define LED_NEOPIXEL _PINNUM(0, 16) + +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_1 _PINNUM(1, 02) +#define BUTTON_2 _PINNUM(1, 07) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "eAFAQ" +#define BLEDIS_MODEL "OMNIMO nRF52840" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ + +//Shared VID/PID with pca10056 +#define USB_DESC_VID 0x1209 +#define USB_DESC_UF2_PID 0xCECE +#define USB_DESC_CDC_ONLY_PID 0xCECE + +//------------- UF2 -------------// +#define UF2_PRODUCT_NAME "Omnimo nRF52840" +#define UF2_VOLUME_LABEL "OMNIn52BOOT" +#define UF2_BOARD_ID "nRF52840-Omnimo" +#define UF2_INDEX_URL "https://www.crowdsupply.com/eafaq/omnimo-nrf52840" + +#endif // _OMNIMO_NRF52840_H diff --git a/src/boards/particle_argon/board.h b/src/boards/particle_argon/board.h index 1f31d931..c8ce5d6f 100644 --- a/src/boards/particle_argon/board.h +++ b/src/boards/particle_argon/board.h @@ -1,70 +1,69 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _PARTICLE_ARGON_H -#define _PARTICLE_ARGON_H - -#define _PINNUM(port, pin) ((port)*32 + (pin)) - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(1, 12) -#define LED_STATE_ON 1 - -#define LED_RGB_RED_PIN _PINNUM(0, 13) -#define LED_RGB_GREEN_PIN _PINNUM(0, 14) -#define LED_RGB_BLUE_PIN _PINNUM(0, 15) -#define BOARD_RGB_BRIGHTNESS 0x202020 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_DFU _PINNUM(0, 11) -#define BUTTON_FRESET _PINNUM(0, 03) // A0 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Particle Industries" -#define BLEDIS_MODEL "Argon" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ - -// Shared VID/PID with pca10056 -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00DA -#define USB_DESC_CDC_ONLY_PID 0x00DA - -#define UF2_PRODUCT_NAME "Particle Argon" -#define UF2_VOLUME_LABEL "ARGONBOOT " -#define UF2_BOARD_ID "nRF52840-Argon-v1" -#define UF2_INDEX_URL "https://www.particle.io/mesh/" - -#endif // _PARTICLE_ARGON_H +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _PARTICLE_ARGON_H +#define _PARTICLE_ARGON_H + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN _PINNUM(1, 12) +#define LED_STATE_ON 1 + +#define LED_RGB_RED_PIN _PINNUM(0, 13) +#define LED_RGB_GREEN_PIN _PINNUM(0, 14) +#define LED_RGB_BLUE_PIN _PINNUM(0, 15) +#define BOARD_RGB_BRIGHTNESS 0x202020 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_DFU _PINNUM(0, 11) +#define BUTTON_FRESET _PINNUM(0, 03) // A0 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "Particle Industries" +#define BLEDIS_MODEL "Argon" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ + +// Shared VID/PID with pca10056 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x00DA +#define USB_DESC_CDC_ONLY_PID 0x00DA + +#define UF2_PRODUCT_NAME "Particle Argon" +#define UF2_VOLUME_LABEL "ARGONBOOT " +#define UF2_BOARD_ID "nRF52840-Argon-v1" +#define UF2_INDEX_URL "https://www.particle.io/mesh/" + +#endif // _PARTICLE_ARGON_H diff --git a/src/boards/particle_boron/board.h b/src/boards/particle_boron/board.h index ee5236af..a4fa749b 100644 --- a/src/boards/particle_boron/board.h +++ b/src/boards/particle_boron/board.h @@ -1,70 +1,69 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _PARTICLE_BORON_H -#define _PARTICLE_BORON_H - -#define _PINNUM(port, pin) ((port)*32 + (pin)) - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(1, 12) -#define LED_STATE_ON 1 - -#define LED_RGB_RED_PIN _PINNUM(0, 13) -#define LED_RGB_GREEN_PIN _PINNUM(0, 14) -#define LED_RGB_BLUE_PIN _PINNUM(0, 15) -#define BOARD_RGB_BRIGHTNESS 0x202020 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_DFU _PINNUM(0, 11) -#define BUTTON_FRESET _PINNUM(0, 03) // A0 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Particle Industries" -#define BLEDIS_MODEL "Boron" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ - -// Shared VID/PID with pca10056 -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00DA -#define USB_DESC_CDC_ONLY_PID 0x00DA - -#define UF2_PRODUCT_NAME "Particle Boron" -#define UF2_VOLUME_LABEL "BORONBOOT " -#define UF2_BOARD_ID "nRF52840-Boron-v1" -#define UF2_INDEX_URL "https://www.particle.io/mesh/" - -#endif // _PARTICLE_BORON_H +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _PARTICLE_BORON_H +#define _PARTICLE_BORON_H + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN _PINNUM(1, 12) +#define LED_STATE_ON 1 + +#define LED_RGB_RED_PIN _PINNUM(0, 13) +#define LED_RGB_GREEN_PIN _PINNUM(0, 14) +#define LED_RGB_BLUE_PIN _PINNUM(0, 15) +#define BOARD_RGB_BRIGHTNESS 0x202020 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_DFU _PINNUM(0, 11) +#define BUTTON_FRESET _PINNUM(0, 03) // A0 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "Particle Industries" +#define BLEDIS_MODEL "Boron" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ + +// Shared VID/PID with pca10056 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x00DA +#define USB_DESC_CDC_ONLY_PID 0x00DA + +#define UF2_PRODUCT_NAME "Particle Boron" +#define UF2_VOLUME_LABEL "BORONBOOT " +#define UF2_BOARD_ID "nRF52840-Boron-v1" +#define UF2_INDEX_URL "https://www.particle.io/mesh/" + +#endif // _PARTICLE_BORON_H diff --git a/src/boards/particle_xenon/board.h b/src/boards/particle_xenon/board.h index 3c778281..01da00ae 100644 --- a/src/boards/particle_xenon/board.h +++ b/src/boards/particle_xenon/board.h @@ -1,70 +1,69 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _PARTICLE_XENON_H -#define _PARTICLE_XENON_H - -#define _PINNUM(port, pin) ((port)*32 + (pin)) - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(1, 12) -#define LED_STATE_ON 1 - -#define LED_RGB_RED_PIN _PINNUM(0, 13) -#define LED_RGB_GREEN_PIN _PINNUM(0, 14) -#define LED_RGB_BLUE_PIN _PINNUM(0, 15) -#define BOARD_RGB_BRIGHTNESS 0x202020 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_DFU _PINNUM(0, 11) -#define BUTTON_FRESET _PINNUM(0, 03) // A0 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Particle Industries" -#define BLEDIS_MODEL "Xenon" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ - -// Shared VID/PID with pca10056 -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00DA -#define USB_DESC_CDC_ONLY_PID 0x00DA - -#define UF2_PRODUCT_NAME "Particle Xenon" -#define UF2_VOLUME_LABEL "XENONBOOT " -#define UF2_BOARD_ID "nRF52840-Xenon-v1" -#define UF2_INDEX_URL "https://www.particle.io/mesh/" - -#endif // _PARTICLE_XENON_H +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _PARTICLE_XENON_H +#define _PARTICLE_XENON_H + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN _PINNUM(1, 12) +#define LED_STATE_ON 1 + +#define LED_RGB_RED_PIN _PINNUM(0, 13) +#define LED_RGB_GREEN_PIN _PINNUM(0, 14) +#define LED_RGB_BLUE_PIN _PINNUM(0, 15) +#define BOARD_RGB_BRIGHTNESS 0x202020 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_DFU _PINNUM(0, 11) +#define BUTTON_FRESET _PINNUM(0, 03) // A0 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "Particle Industries" +#define BLEDIS_MODEL "Xenon" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ + +// Shared VID/PID with pca10056 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x00DA +#define USB_DESC_CDC_ONLY_PID 0x00DA + +#define UF2_PRODUCT_NAME "Particle Xenon" +#define UF2_VOLUME_LABEL "XENONBOOT " +#define UF2_BOARD_ID "nRF52840-Xenon-v1" +#define UF2_INDEX_URL "https://www.particle.io/mesh/" + +#endif // _PARTICLE_XENON_H diff --git a/src/boards/pca10056/board.h b/src/boards/pca10056/board.h index 51dc2fb2..0fea16d1 100644 --- a/src/boards/pca10056/board.h +++ b/src/boards/pca10056/board.h @@ -36,7 +36,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 11 #define BUTTON_2 12 #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/pca10059/board.h b/src/boards/pca10059/board.h index 8b2d89d9..47e6bc2c 100644 --- a/src/boards/pca10059/board.h +++ b/src/boards/pca10059/board.h @@ -38,7 +38,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(1, 6) #define BUTTON_2 _PINNUM(1, 10) diff --git a/src/boards/pca10100/board.h b/src/boards/pca10100/board.h index e96b11c8..1df02003 100644 --- a/src/boards/pca10100/board.h +++ b/src/boards/pca10100/board.h @@ -36,7 +36,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 11 #define BUTTON_2 12 #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/pillbug/board.h b/src/boards/pillbug/board.h index 7acb62cb..8c50985c 100644 --- a/src/boards/pillbug/board.h +++ b/src/boards/pillbug/board.h @@ -39,7 +39,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 // none connected at all #define BUTTON_1 _PINNUM(0, 18) // unusable: RESET #define BUTTON_2 _PINNUM(0, 21) // no connection #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/pitaya_go/board.h b/src/boards/pitaya_go/board.h index 9480a933..8daf1f1d 100644 --- a/src/boards/pitaya_go/board.h +++ b/src/boards/pitaya_go/board.h @@ -41,7 +41,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(1, 0) // P1.00: User Button #define BUTTON_2 _PINNUM(1, 7) // P1.07: NC #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/raytac_mdbt50q_db_40/board.h b/src/boards/raytac_mdbt50q_db_40/board.h index 81e07dba..86763e38 100644 --- a/src/boards/raytac_mdbt50q_db_40/board.h +++ b/src/boards/raytac_mdbt50q_db_40/board.h @@ -44,7 +44,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 4 #define BUTTON_1 _PINNUM(0, 11) #define BUTTON_2 _PINNUM(0, 12) diff --git a/src/boards/raytac_mdbt50q_rx/board.h b/src/boards/raytac_mdbt50q_rx/board.h index 1436d8b3..ac3f771e 100644 --- a/src/boards/raytac_mdbt50q_rx/board.h +++ b/src/boards/raytac_mdbt50q_rx/board.h @@ -37,7 +37,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(0, 15) #define BUTTON_2 _PINNUM(0, 10) diff --git a/src/boards/sparkfun_nrf52840_micromod/board.h b/src/boards/sparkfun_nrf52840_micromod/board.h index bf43912b..c889354a 100644 --- a/src/boards/sparkfun_nrf52840_micromod/board.h +++ b/src/boards/sparkfun_nrf52840_micromod/board.h @@ -38,7 +38,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(0, 7) #define BUTTON_2 _PINNUM(0, 10) #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/t1000_e/board.h b/src/boards/t1000_e/board.h index 1a087416..a8eae88e 100644 --- a/src/boards/t1000_e/board.h +++ b/src/boards/t1000_e/board.h @@ -37,7 +37,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 #define BUTTON_1 _PINNUM(0, 6) // Primary Button #define BUTTON_2 _PINNUM(0, 18) // unusable: RESET #define BUTTON_PULL NRF_GPIO_PIN_PULLUP diff --git a/src/boards/waveshare_nrf52840_eval/board.h b/src/boards/waveshare_nrf52840_eval/board.h index 27991df4..81a5fc17 100644 --- a/src/boards/waveshare_nrf52840_eval/board.h +++ b/src/boards/waveshare_nrf52840_eval/board.h @@ -40,7 +40,6 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 /* This is the 'KEY1' in top left of the board under RESET key. * It is used to remain in bootloader mode after reset. */ #define BUTTON_1 _PINNUM(0, 11) diff --git a/src/boards/xiao_nrf52840_ble/board.h b/src/boards/xiao_nrf52840_ble/board.h index 9f4c45e6..1543189b 100644 --- a/src/boards/xiao_nrf52840_ble/board.h +++ b/src/boards/xiao_nrf52840_ble/board.h @@ -1,68 +1,67 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _XIAO_BLE -#define _XIAO_BLE - -#define _PINNUM(port, pin) ((port)*32 + (pin)) - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -// The board has 3 leds, but changing the number here causes OTA issues. -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 26) -#define LED_STATE_ON 0 - -#define NEOPIXELS_NUMBER 0 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_1 _PINNUM(0, 18) -// Button 2 is defined as D1 from expansion board. -#define BUTTON_2 _PINNUM(0, 3) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Seeed" -#define BLEDIS_MODEL "XIAO nRF52840" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x2886 -#define USB_DESC_UF2_PID 0x0044 -#define USB_DESC_CDC_ONLY_PID 0x0044 - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Seeed XIAO nRF52840" -#define UF2_VOLUME_LABEL "XIAO-BOOT" -#define UF2_BOARD_ID "nRF52840-SeeedXiao-v1" -#define UF2_INDEX_URL "https://www.seeedstudio.com/" - -#endif // _XIAO_BLE +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _XIAO_BLE +#define _XIAO_BLE + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +// The board has 3 leds, but changing the number here causes OTA issues. +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN _PINNUM(0, 26) +#define LED_STATE_ON 0 + +#define NEOPIXELS_NUMBER 0 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_1 _PINNUM(0, 18) +// Button 2 is defined as D1 from expansion board. +#define BUTTON_2 _PINNUM(0, 3) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "Seeed" +#define BLEDIS_MODEL "XIAO nRF52840" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ +#define USB_DESC_VID 0x2886 +#define USB_DESC_UF2_PID 0x0044 +#define USB_DESC_CDC_ONLY_PID 0x0044 + +//------------- UF2 -------------// +#define UF2_PRODUCT_NAME "Seeed XIAO nRF52840" +#define UF2_VOLUME_LABEL "XIAO-BOOT" +#define UF2_BOARD_ID "nRF52840-SeeedXiao-v1" +#define UF2_INDEX_URL "https://www.seeedstudio.com/" + +#endif // _XIAO_BLE diff --git a/src/boards/xiao_nrf52840_ble_sense/board.h b/src/boards/xiao_nrf52840_ble_sense/board.h index e512c1b7..fd30e148 100644 --- a/src/boards/xiao_nrf52840_ble_sense/board.h +++ b/src/boards/xiao_nrf52840_ble_sense/board.h @@ -1,68 +1,67 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _XIAO_BLE_SENSE -#define _XIAO_BLE_SENSE - -#define _PINNUM(port, pin) ((port)*32 + (pin)) - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -// The board has 3 leds, but changing the number here causes OTA issues. -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 26) -#define LED_STATE_ON 0 - -#define NEOPIXELS_NUMBER 0 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 -#define BUTTON_1 _PINNUM(0, 18) -// Button 2 is defined as D1 from expansion board. -#define BUTTON_2 _PINNUM(0, 3) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Seeed" -#define BLEDIS_MODEL "XIAO nRF52840 Sense" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x2886 -#define USB_DESC_UF2_PID 0x0045 -#define USB_DESC_CDC_ONLY_PID 0x0045 - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Seeed XIAO nRF52840" -#define UF2_VOLUME_LABEL "XIAO-SENSE" -#define UF2_BOARD_ID "nRF52840-SeeedXiaoSense-v1" -#define UF2_INDEX_URL "https://www.seeedstudio.com/" - -#endif // _XIAO_BLE_SENSE +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _XIAO_BLE_SENSE +#define _XIAO_BLE_SENSE + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +// The board has 3 leds, but changing the number here causes OTA issues. +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN _PINNUM(0, 26) +#define LED_STATE_ON 0 + +#define NEOPIXELS_NUMBER 0 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_1 _PINNUM(0, 18) +// Button 2 is defined as D1 from expansion board. +#define BUTTON_2 _PINNUM(0, 3) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "Seeed" +#define BLEDIS_MODEL "XIAO nRF52840 Sense" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ +#define USB_DESC_VID 0x2886 +#define USB_DESC_UF2_PID 0x0045 +#define USB_DESC_CDC_ONLY_PID 0x0045 + +//------------- UF2 -------------// +#define UF2_PRODUCT_NAME "Seeed XIAO nRF52840" +#define UF2_VOLUME_LABEL "XIAO-SENSE" +#define UF2_BOARD_ID "nRF52840-SeeedXiaoSense-v1" +#define UF2_INDEX_URL "https://www.seeedstudio.com/" + +#endif // _XIAO_BLE_SENSE From da75a5443f545e57bb1a5b9d00c1125d131ef88d Mon Sep 17 00:00:00 2001 From: ambraglow Date: Tue, 27 Jan 2026 17:08:45 +0100 Subject: [PATCH 45/84] Add support for Syldra development board --- src/boards/syldra/board.cmake | 1 + src/boards/syldra/board.h | 66 +++++++++++++++++++++++++++++++++++ src/boards/syldra/board.mk | 2 ++ src/boards/syldra/pinconfig.c | 19 ++++++++++ 4 files changed, 88 insertions(+) create mode 100644 src/boards/syldra/board.cmake create mode 100644 src/boards/syldra/board.h create mode 100644 src/boards/syldra/board.mk create mode 100644 src/boards/syldra/pinconfig.c diff --git a/src/boards/syldra/board.cmake b/src/boards/syldra/board.cmake new file mode 100644 index 00000000..25daf71d --- /dev/null +++ b/src/boards/syldra/board.cmake @@ -0,0 +1 @@ +set(MCU_VARIANT nrf52840) diff --git a/src/boards/syldra/board.h b/src/boards/syldra/board.h new file mode 100644 index 00000000..4afa78d8 --- /dev/null +++ b/src/boards/syldra/board.h @@ -0,0 +1,66 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Ambraglow + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _SYLDRA_H +#define _SYLDRA_H + +#define _PINNUM(port, pin) ((port)*32 + (pin)) + +//------------------------------------------------------------------// +// LED +//------------------------------------------------------------------// +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN _PINNUM(0, 17) // AD10 LED +#define LED_STATE_ON 1 + +//------------------------------------------------------------------// +// BUTTON +//------------------------------------------------------------------// +#define BUTTONS_NUMBER 2 +#define BUTTON_1 _PINNUM(0, 24) // AD20 -- DFU +#define BUTTON_2 _PINNUM(1, 00) // AD22 -- FRST + +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + + +//-------------------------------------------------------------------// +// BLE OTA +//-------------------------------------------------------------------// +#define BLEDIS_MANUFACTURER "Ambraglow-&-localcc" +#define BLEDIS_MODEL "Syldra_dev_r1" + + +//--------------------------------------------------------------------+ +// USB - using test PIDs +//--------------------------------------------------------------------+ +#define USB_DESC_VID 0x1209 +#define USB_DESC_UF2_PID 0x0001 +#define USB_DESC_CDC_ONLY_PID 0x7A01 + +#define UF2_PRODUCT_NAME "Syldra nrf52840 Development Board" +#define UF2_VOLUME_LABEL "Syldra-boot" +#define UF2_BOARD_ID "Syldra-dev-v1" +#define UF2_INDEX_URL "https://github.com/ambraglow/nonorf" + +#endif \ No newline at end of file diff --git a/src/boards/syldra/board.mk b/src/boards/syldra/board.mk new file mode 100644 index 00000000..befd7cba --- /dev/null +++ b/src/boards/syldra/board.mk @@ -0,0 +1,2 @@ +MCU_SUB_VARIANT = nrf52840 +SD_NAME = s140 \ No newline at end of file diff --git a/src/boards/syldra/pinconfig.c b/src/boards/syldra/pinconfig.c new file mode 100644 index 00000000..2a592df9 --- /dev/null +++ b/src/boards/syldra/pinconfig.c @@ -0,0 +1,19 @@ +#include "boards.h" +#include "uf2/configkeys.h" + +__attribute__((used, section(".bootloaderConfig"))) +const uint32_t bootloaderConfig[] = +{ + /* CF2 START */ + CFG_MAGIC0, CFG_MAGIC1, // magic + 5, 100, // used entries, total entries + + 204, 0x100000, // FLASH_BYTES = 0x100000 + 205, 0x40000, // RAM_BYTES = 0x40000 + 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 + 209, 0xada52840, // UF2_FAMILY = 0xada52840 + 210, 0x20, // PINS_PORT_SIZE = PA_32 + + 0, 0, 0, 0, 0, 0, 0, 0 + /* CF2 END */ +}; \ No newline at end of file From 3249f41dd965d366a71b769ab42aa8ee76ebd9c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Tue, 27 Jan 2026 22:41:20 -0300 Subject: [PATCH 46/84] In case a RESET command is received in the middle of a OTA DFU, reboot to bootloader to allow the DFU app to recover previously interrupted transfers, instead of reverting to the last working firmware and launching it --- .../libraries/bootloader_dfu/bootloader.c | 14 ++++++++--- .../libraries/bootloader_dfu/bootloader.h | 7 ++++++ .../libraries/bootloader_dfu/dfu_dual_bank.c | 9 ++++++++ .../bootloader_dfu/dfu_single_bank.c | 8 +++++++ .../libraries/bootloader_dfu/dfu_types.h | 2 ++ src/main.c | 23 +++++++++++++++---- src/usb/msc_uf2.c | 4 +++- src/usb/uf2/uf2cfg.h | 2 +- 8 files changed, 60 insertions(+), 9 deletions(-) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c index 79557904..9a8e55e7 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c @@ -44,7 +44,8 @@ typedef enum BOOTLOADER_SETTINGS_SAVING, /**< Bootloader status for indicating that saving of bootloader settings is in progress. */ BOOTLOADER_COMPLETE, /**< Bootloader status for indicating that all operations for the update procedure has completed and it is safe to reset the system. */ BOOTLOADER_TIMEOUT, /**< Bootloader status field for indicating that a timeout has occured and current update process should be aborted. */ - BOOTLOADER_RESET, /**< Bootloader status field for indicating that a reset has been requested and current update process should be aborted. */ + BOOTLOADER_SYS_RESET, /**< Bootloader status field for indicating that a reset has been requested and current update process should be aborted. */ + BOOTLOADER_RESET_TO_SELF, /**< Bootloader status field for indicating that a reset has been requested and current update process should be aborted and the bootloader must be reentered. */ } bootloader_status_t; static pstorage_handle_t m_bootsettings_handle; /**< Pstorage handle to use for registration and identifying the bootloader module on subsequent calls to the pstorage module for load and store of bootloader setting in flash. */ @@ -136,7 +137,8 @@ static void wait_for_events(void) if ((m_update_status == BOOTLOADER_COMPLETE) || (m_update_status == BOOTLOADER_TIMEOUT) || - (m_update_status == BOOTLOADER_RESET) ) + (m_update_status == BOOTLOADER_SYS_RESET) || + (m_update_status == BOOTLOADER_RESET_TO_SELF)) { // When update has completed or a timeout/reset occured we will return. return; @@ -320,7 +322,9 @@ void bootloader_dfu_update_process(dfu_update_status_t update_status) } else if (update_status.status_code == DFU_RESET) { - m_update_status = BOOTLOADER_RESET; + m_update_status = update_status.restart_into_bootloader == false + ? BOOTLOADER_SYS_RESET + : BOOTLOADER_RESET_TO_SELF; } else { @@ -328,6 +332,10 @@ void bootloader_dfu_update_process(dfu_update_status_t update_status) } } +bool bootloader_must_reset_to_self(void) +{ + return m_update_status == BOOTLOADER_RESET_TO_SELF; +} uint32_t bootloader_init(void) { diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h index 5f94e7b6..9b6f7540 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h @@ -32,6 +32,13 @@ */ uint32_t bootloader_init(void); +/**@brief Function to check if bootloader must be reentered after a reset + * @details: This function can only be called after bootloader_dfu_start returns + * @retval true If device must reboot to the bootloader + * @retval false If device must reboot to the application, if possible + */ +bool bootloader_must_reset_to_self(void); + /**@brief Function for validating application region in flash. * @retval true If Application region is valid. * @retval false If Application region is not valid. diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c index cba833df..32cc71a5 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c @@ -82,6 +82,7 @@ static void pstorage_callback_handler(pstorage_handle_t * p_handle, APP_ERROR_CHECK(result); } + /**@brief Function for handling the DFU timeout. * * @param[in] p_context The timeout context. @@ -710,6 +711,14 @@ void dfu_reset(void) update_status.status_code = DFU_RESET; + // If we are still receiving packets, and we are expecting more of them, + // and we receive the RESET command, this means the DFU app is trying + // to interrupt and recover a failed previous attempt of FLASHing a new + // application. Make sure to reboot into DFU mode instead of starting + // a previously stored application (specially valid for double bank + // bootloaders + update_status.restart_into_bootloader = (m_dfu_state == DFU_STATE_RX_DATA_PKT && m_data_received != m_image_size); + bootloader_dfu_update_process(update_status); } diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c index af25970f..b53bf957 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c @@ -607,6 +607,14 @@ void dfu_reset(void) update_status.status_code = DFU_RESET; + // If we are still receiving packets, and we are expecting more of them, + // and we receive the RESET command, this means the DFU app is trying + // to interrupt and recover a failed previous attempt of FLASHing a new + // application. Make sure to reboot into DFU mode instead of starting + // a previously stored application (specially valid for double bank + // bootloaders + update_status.restart_into_bootloader = (m_dfu_state == DFU_STATE_RX_DATA_PKT && m_data_received != m_image_size); + bootloader_dfu_update_process(update_status); } diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h b/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h index f86a684c..e186e055 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h @@ -24,6 +24,7 @@ #define DFU_TYPES_H__ #include +#include #include "nrf_sdm.h" #include "nrf_mbr.h" #include "nrf.h" @@ -154,6 +155,7 @@ typedef struct uint32_t bl_size; /**< Size of the recieved BootLoader. */ uint32_t app_size; /**< Size of the recieved Application. */ uint32_t sd_image_start; /**< Location in flash where the received SoftDevice image is stored. */ + bool restart_into_bootloader; /**< If the chip must be reset and must reenter bootloader mode. */ } dfu_update_status_t; /**@brief Update complete handler type. */ diff --git a/src/main.c b/src/main.c index 2e2fc0c2..819b9e29 100644 --- a/src/main.c +++ b/src/main.c @@ -134,6 +134,7 @@ uint32_t* dbl_reset_mem = ((uint32_t*) DFU_DBL_RESET_MEM); // true if ble, false if serial bool _ota_dfu = false; bool _ota_connected = false; +bool _ota_was_active = false; bool _sd_inited = false; bool is_ota(void) { @@ -192,6 +193,10 @@ int main(void) { // Return when DFU process is complete (or not entered at all) check_dfu_mode(); + // Check if we must reenter the bootloader after reset, instead of + // launching the user application + bool bootloader_must_be_reentered = bootloader_must_reset_to_self(); + // Reset peripherals board_teardown(); @@ -202,7 +207,9 @@ int main(void) { * - sd_softdevice_vector_table_base_set(APP_ADDR) * - jump to App reset */ - if (bootloader_app_is_valid() && !bootloader_dfu_sd_in_progress()) { + if (!bootloader_must_be_reentered && + bootloader_app_is_valid() && + !bootloader_dfu_sd_in_progress()) { PRINTF("App is valid\r\n"); if (is_sd_existed()) { // MBR forward IRQ to SD (if not already) @@ -220,9 +227,14 @@ int main(void) { bootloader_app_start(); } - // No application was loaded, reset the system with the OTA DFU update - NRF_POWER->GPREGRET = DFU_MAGIC_OTA_RESET; - + // No application was loaded or we need to reenter the bootloader + + // Reset the system with the OTA DFU update in case we were in it, + // to allow completion of FLASHING, otherwise, default to normal reset + if (_ota_was_active) { + NRF_POWER->GPREGRET = DFU_MAGIC_OTA_RESET; + } + NVIC_SystemReset(); } @@ -440,6 +452,9 @@ uint32_t proc_ble(void) { sd_ble_gap_phy_update(evt->evt.gap_evt.conn_handle, &phys); _ota_connected = true; + + // Remember someone connected to BLE + _ota_was_active = true; led_state(STATE_BLE_CONNECTED); break; } diff --git a/src/usb/msc_uf2.c b/src/usb/msc_uf2.c index 4b6817fd..96d705bc 100644 --- a/src/usb/msc_uf2.c +++ b/src/usb/msc_uf2.c @@ -171,6 +171,7 @@ void tud_msc_write10_complete_cb(uint8_t lun) dfu_update_status_t update_status; memset(&update_status, 0, sizeof(dfu_update_status_t )); update_status.status_code = DFU_RESET; + update_status.restart_into_bootloader = false; bootloader_dfu_update_process(update_status); @@ -195,9 +196,10 @@ void tud_msc_write10_complete_cb(uint8_t lun) { // update bootloader always end with reset update_status.status_code = DFU_RESET; + update_status.restart_into_bootloader = false; // Location of current stored new bootloader - uint32_t * new_bootloader = (uint32_t *) BOOTLOADER_ADDR_NEW_RECIEVED; + uint32_t * new_bootloader = (uint32_t *) BOOTLOADER_ADDR_NEW_RECEIVED; PRINT_HEX(new_bootloader); diff --git a/src/usb/uf2/uf2cfg.h b/src/usb/uf2/uf2cfg.h index e5a63988..c23a12ef 100644 --- a/src/usb/uf2/uf2cfg.h +++ b/src/usb/uf2/uf2cfg.h @@ -32,4 +32,4 @@ #define BOOTLOADER_ADDR_END BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS // Address where new bootloader is written before activation (skip application data) -#define BOOTLOADER_ADDR_NEW_RECIEVED (USER_FLASH_END-DFU_BL_IMAGE_MAX_SIZE) +#define BOOTLOADER_ADDR_NEW_RECEIVED (USER_FLASH_END-DFU_BL_IMAGE_MAX_SIZE) From d435d66921203b2194da372f339bc468938db574 Mon Sep 17 00:00:00 2001 From: Willow Herring <31960031+ReFil@users.noreply.github.com> Date: Wed, 28 Jan 2026 18:55:22 +0000 Subject: [PATCH 47/84] fix: Fix build errors on GCC15 --- Makefile | 4 ++-- src/usb/uf2/ghostfat.c | 15 +++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 0c9ccd6c..9d2d4df3 100644 --- a/Makefile +++ b/Makefile @@ -393,8 +393,8 @@ endif CFLAGS += -DDFU_APP_DATA_RESERVED=$(DFU_APP_DATA_RESERVED) # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105523 -# Fixes for gcc version 12, 13 and 14. -ifneq (,$(filter 12.% 13.% 14.%,$(shell $(CC) -dumpversion 2>$(NULL_DEVICE)))) +# Fixes for gcc version 12, 13, 14 and 15. +ifneq (,$(filter 12.% 13.% 14.% 15.%,$(shell $(CC) -dumpversion 2>$(NULL_DEVICE)))) CFLAGS += --param=min-pagesize=0 endif diff --git a/src/usb/uf2/ghostfat.c b/src/usb/uf2/ghostfat.c index d7489045..0564182a 100644 --- a/src/usb/uf2/ghostfat.c +++ b/src/usb/uf2/ghostfat.c @@ -40,9 +40,16 @@ // //--------------------------------------------------------------------+ +// Add nonstring attribute if supported to avoid errors in GCC 15 +#ifdef __has_attribute + #define __NONSTRING__ __attribute__((nonstring)) +#else + #define __NONSTRING__ +#endif + typedef struct { uint8_t JumpInstruction[3]; - uint8_t OEMInfo[8]; + uint8_t OEMInfo[8] __NONSTRING__; uint16_t SectorSize; uint8_t SectorsPerCluster; uint16_t ReservedSectors; @@ -59,8 +66,8 @@ typedef struct { uint8_t Reserved; uint8_t ExtendedBootSig; uint32_t VolumeSerialNumber; - uint8_t VolumeLabel[11]; - uint8_t FilesystemIdentifier[8]; + uint8_t VolumeLabel[11] __NONSTRING__; + uint8_t FilesystemIdentifier[8] __NONSTRING__; } __attribute__((packed)) FAT_BootBlock; typedef struct { @@ -81,7 +88,7 @@ typedef struct { STATIC_ASSERT(sizeof(DirEntry) == 32); struct TextFile { - char const name[11]; + char const name[11] __NONSTRING__; char const *content; }; From 87b8354d1fbc19e5a91b63cb62645ab459a00b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Fri, 30 Jan 2026 13:09:24 -0300 Subject: [PATCH 48/84] Allow defaulting to OTA DFU instead of Serial/UF2 DFU (turned off by default) --- CMakeLists.txt | 5 +++++ Makefile | 21 +++++++++++++-------- src/main.c | 10 ++++++++++ 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b50cfd6..1e17d10e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ set(MBR_HEX ${SOFTDEVICE_DIR}/mbr/hex/mbr_nrf52_2.4.1_mbr.hex) set(TINYUSB_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/tinyusb/src) set(TCRYPT_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/tinycrypt/lib) +option(DEFAULT_TO_OTA_DFU "Default to OTA DFU instead of Serial DFU" OFF) option(SIGNED_FW "Enable signed firmware verification" OFF) option(DUALBANK_FW "Enable dual bank DFU support" OFF) option(FORCE_UF2 "Force UF2 support even with SIGNED_FW" OFF) @@ -196,6 +197,10 @@ if (FORCE_UF2) target_compile_definitions(bootloader PUBLIC FORCE_UF2) endif () +if (DEFAULT_TO_OTA_DFU) + target_compile_definitions(bootloader PUBLIC DEFAULT_TO_OTA_DFU) +endif () + if (TRACE_ETM STREQUAL "1") # ENABLE_TRACE will cause system_nrf5x.c to set up ETM trace target_compile_definitions(bootloader PUBLIC ENABLE_TRACE) diff --git a/Makefile b/Makefile index 0c9ccd6c..5b9b2e51 100644 --- a/Makefile +++ b/Makefile @@ -2,14 +2,15 @@ # CONFIGURE # - SDK_PATH : path to SDK directory # -# - SD_NAME : e.g s132, s140 -# - SD_VERSION : SoftDevice version e.g 6.0.0 -# - SD_HEX : to bootloader hex binary -# - SIGNED_FW : if bootloader will ONLY accept signed firmware -# - SIGNED_FW_QX : Qx for signed firmware verification -# - SIGNED_FW_QY : Qy for signed firmware verification -# - DUALBANK_FW : If bootloader will implement a dual bank feature to allow autorecover from failed -# - FORCE_UF2 : if SIGNED_FW is 1, will force to include UF2 support (UNSECURE, UF2 does NOT validate signature!) +# - SD_NAME : e.g s132, s140 +# - SD_VERSION : SoftDevice version e.g 6.0.0 +# - SD_HEX : to bootloader hex binary +# - SIGNED_FW : if bootloader will ONLY accept signed firmware +# - SIGNED_FW_QX : Qx for signed firmware verification +# - SIGNED_FW_QY : Qy for signed firmware verification +# - DUALBANK_FW : If bootloader will implement a dual bank feature to allow autorecover from failed +# - FORCE_UF2 : if SIGNED_FW is 1, will force to include UF2 support (UNSECURE, UF2 does NOT validate signature!) +# - DEFAULT_TO_OTA_DFU : if entering DFU, by default enter OTA DFU instead of Serial DFU #------------------------------------------------------------------------------ PYTHON = python @@ -371,6 +372,10 @@ ifeq ($(FORCE_UF2), 1) CFLAGS += -DFORCE_UF2 endif +ifeq ($(DEFAULT_TO_OTA_DFU), 1) +CFLAGS += -DEFAULT_TO_OTA_DFU +endif + _VER = $(subst ., ,$(word 1, $(subst -, ,$(GIT_VERSION)))) CFLAGS += -DMK_BOOTLOADER_VERSION='($(word 1,$(_VER)) << 16) + ($(word 2,$(_VER)) << 8) + $(word 3,$(_VER))' diff --git a/src/main.c b/src/main.c index 819b9e29..5beedec8 100644 --- a/src/main.c +++ b/src/main.c @@ -286,6 +286,16 @@ static void check_dfu_mode(void) { dfu_start = 1; } +#ifdef DEFAULT_TO_OTA_DFU + // Default to OTA DFU mode, instead of Serial DFU mode, if there is no app present, + // because otherwise, if there is no application, it will restart in Serial DFU mode, + // making it IMPOSSIBLE to recover devices in the field if there are no user + // accessible USB ports + if (!valid_app || dfu_start) { + _ota_dfu = 1; + } +#endif + // App mode: Double Reset detection or DFU startup for nrf52832 if (!(just_start_app || dfu_start || !valid_app)) { #ifdef NRF52832_XXAA From cb30e6f172aaf22566346f6275aaee7eb99a471f Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 31 Jan 2026 11:52:15 +0700 Subject: [PATCH 49/84] update board and supported_boards.md --- README.md | 3 +- .../feather_nrf52833_express/board.cmake | 1 - src/boards/feather_nrf52833_express/board.h | 68 ---------- src/boards/feather_nrf52833_express/board.mk | 1 - .../feather_nrf52833_express/pinconfig.c | 19 --- src/boards/itsybitsy_nrf52840_express/board.h | 5 +- supported_boards.md | 60 +++++++++ tools/gen_boards.py | 117 ++++++++++++++++++ 8 files changed, 181 insertions(+), 93 deletions(-) delete mode 100644 src/boards/feather_nrf52833_express/board.cmake delete mode 100644 src/boards/feather_nrf52833_express/board.h delete mode 100644 src/boards/feather_nrf52833_express/board.mk delete mode 100644 src/boards/feather_nrf52833_express/pinconfig.c create mode 100644 supported_boards.md create mode 100644 tools/gen_boards.py diff --git a/README.md b/README.md index 8d16b37b..e66eb64b 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,8 @@ Officially supported boards are: - Adafruit Metro nRF52840 Express - [Raytac MDBT50Q-RX Dongle](https://www.adafruit.com/product/5199) -In addition, there is also lots of other 3rd-party boards which are added by other makers, users and community. Check out the [complete list of all boards here](/src/boards). +In addition, there is also lots of other 3rd-party boards which are added by other makers, users and community. Check +out the [complete list of all boards here](/supported_boards.md). ## Features diff --git a/src/boards/feather_nrf52833_express/board.cmake b/src/boards/feather_nrf52833_express/board.cmake deleted file mode 100644 index cb4f2f9e..00000000 --- a/src/boards/feather_nrf52833_express/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52833) diff --git a/src/boards/feather_nrf52833_express/board.h b/src/boards/feather_nrf52833_express/board.h deleted file mode 100644 index e52dc86e..00000000 --- a/src/boards/feather_nrf52833_express/board.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _FEATHER_NRF52833_H -#define _FEATHER_NRF52833_H - -#define _PINNUM(port, pin) ((port)*32 + (pin)) - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN _PINNUM(1, 15) -#define LED_SECONDARY_PIN _PINNUM(1, 10) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL _PINNUM(0, 16) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(1, 02) -#define BUTTON_2 _PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Feather nRF52833 Express" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0029 // TODO change later -#define USB_DESC_CDC_ONLY_PID 0x002A // TODO change later - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit Feather nRF52833 Express" -#define UF2_VOLUME_LABEL "FTHR833BOOT" -#define UF2_BOARD_ID "nRF52833-Feather-revD" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4062" // TODO change later - -#endif diff --git a/src/boards/feather_nrf52833_express/board.mk b/src/boards/feather_nrf52833_express/board.mk deleted file mode 100644 index d60ac481..00000000 --- a/src/boards/feather_nrf52833_express/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52833 diff --git a/src/boards/feather_nrf52833_express/pinconfig.c b/src/boards/feather_nrf52833_express/pinconfig.c deleted file mode 100644 index 72308c4a..00000000 --- a/src/boards/feather_nrf52833_express/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x80000, // FLASH_BYTES - 205, 0x20000, // RAM_BYTES - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0x621e937a, // UF2_FAMILY - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/itsybitsy_nrf52840_express/board.h b/src/boards/itsybitsy_nrf52840_express/board.h index a9372d15..efa92ac8 100644 --- a/src/boards/itsybitsy_nrf52840_express/board.h +++ b/src/boards/itsybitsy_nrf52840_express/board.h @@ -42,7 +42,6 @@ // For dotstart set to max for colour information is not masked off #define BOARD_RGB_BRIGHTNESS 0xffffffff - /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ @@ -67,6 +66,6 @@ #define UF2_PRODUCT_NAME "Adafruit ItsyBitsy nRF52840 Express" #define UF2_VOLUME_LABEL "ITSY840BOOT" #define UF2_BOARD_ID "nRF52840-ItsyBitsy-revA" -#define UF2_INDEX_URL "https://www.adafruit.com/" +#define UF2_INDEX_URL "https://www.adafruit.com/product/4481" -#endif // _ITSY_NRF52840_H +#endif diff --git a/supported_boards.md b/supported_boards.md new file mode 100644 index 00000000..f733ac3d --- /dev/null +++ b/supported_boards.md @@ -0,0 +1,60 @@ +# Supported Boards + +## Adafruit Boards + +| Board | Name | VID PID | URL | +| --- | --- | --- | --- | +| arcade_feather_nrf52840_express | Adafruit Feather nRF52840 Express | 0x239A:0x0029 | https://www.adafruit.com/product/4062 | +| circuitplayground_nrf52840 | Adafruit Circuit Playground nRF52840 | 0x239A:0x0045 | https://www.adafruit.com/product/4333 | +| clue_nrf52840 | Adafruit CLUE nRF52840 | 0x239A:0x0071 | https://www.adafruit.com/product/4500 | +| feather_nrf52832 | Adafruit Feather nRF52832 | N/A | https://www.adafruit.com/product/3406 | +| feather_nrf52840_express | Adafruit Feather nRF52840 Express | 0x239A:0x0029 | https://www.adafruit.com/product/4062 | +| feather_nrf52840_sense | Adafruit Feather nRF52840 Sense | 0x239A:0x0087 | https://www.adafruit.com/product/4516 | +| feather_nrf52840_sense_tft | Adafruit Feather nRF52840 Sense TFT | 0x239A:0x0087 | https://www.adafruit.com/product/ | +| itsybitsy_nrf52840_express | Adafruit ItsyBitsy nRF52840 Express | 0x239A:0x0051 | https://www.adafruit.com/product/4481 | +| ledglasses_nrf52840 | Adafruit LED Glasses Driver nRF52840 | 0x239A:0x010D | https://www.adafruit.com/product/5217 | +| metro_nrf52840_express | Adafruit Metro nRF52840 Express | 0x239A:0x003F | https://www.adafruit.com/ | + +## 3rd Party Boards + +| Board | Name | VID PID | URL | +| --- | --- | --- | --- | +| adm_b_nrf52840_1 | AtelierDuMaker NRF52840 Breakout | 0x239A:0x009F | https://github.com/Atelier-Du-Maker/HolyIoT_NRF52840 | +| ae_bl652_bo | N/A | N/A | N/A | +| aramcon2_badge | ARAMCON2 Badge | 0x239A:0x007B | https://github.com/aramcon-badge | +| aramcon_badge_2019 | ARAMCON Badge 2019 | 0x239A:0x0079 | https://github.com/aramcon-badge | +| arduino_nano_33_ble | Arduino Nano 33 BLE | 0x239A:0x0063 | https://www.arduino.cc/ | +| bast_ble | Bast BLE | 0x1209:0x805A | https://www.electroniccats.com | +| bluemicro_nrf52833 | BlueMicro | 0x1D50:0x616F | http://bluemicro.jpconstantineau.com/ | +| bluemicro_nrf52840 | BlueMicro | 0x1D50:0x6160 | http://bluemicro.jpconstantineau.com/ | +| challenger_840_ble | ILabs Challenger 840 | 0x1209:0x7380 | https://www.ilabs.se | +| ebyte_e104_bt5032a | Ebyte E104-BT5032A nRF52832 | N/A | https://www.ebyte.com/en/product-view-news.html?id=756 | +| ebyte_e73_tbb | Ebyte E73-TBB nRF52832 | N/A | https://www.ebyte.com/en/product-view-news.aspx?id=889 | +| electronut_labs_papyr | Electronut Labs Papyr | 0x239A:0x003B | https://docs.electronut.in/papyr | +| ikigaisense_vita | IkigaiSense Vita nRF52840 | 0x239A:0x0093 | https://github.com/ikigaisense/vita | +| m60_keyboard | MakerDiary M60 Mechanical Keyboard | 0x2886:0xF00F | https://wiki.makerdiary.com/m60/ | +| mbn52832dk | Murata MBN52832DK | N/A | https://www.murata.com/ja-jp/products/connectivitymodule/bluetooth/overview/lineup/mbn52832 | +| mdk_nrf52840_dongle | MDK nRF52840 USB Dongle | 0x239A:0x0029 | https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/ | +| mikoto | Mikoto nRF52840 | 0x1209:0x7A01 | https://github.com/zhiayang/mikoto | +| nice_nano | nice!nano | 0x239A:0x00B3 | https://nicekeyboards.com/docs/nice-nano | +| nrf52840_bboard | nRF52840 BBoard | 0x1D50:0x6157 | https://github.com/ddB0515/nRF52840-BBoard | +| nRF52840_connect_kit | nRF52840 Connect Kit | 0x2886:0xF00F | https://wiki.makerdiary.com/nrf52840-connectkit/ | +| nrf52840_m2 | MakerDiary nRF52840 M.2 Module | 0x2886:0xF00F | https://wiki.makerdiary.com/nrf52840-m2 | +| nrfmicro | nRFMicro | 0x1209:0x5284 | https://github.com/joric/nrfmicro/wiki | +| ohs2020_badge | OHS2020 Badge | 0x239A:0x007F | https://2020.oshwa.org/ | +| omnimo_nrf52840 | Omnimo nRF52840 | 0x1209:0xCECE | https://www.crowdsupply.com/eafaq/omnimo-nrf52840 | +| particle_argon | Particle Argon | 0x239A:0x00DA | https://www.particle.io/mesh/ | +| particle_boron | Particle Boron | 0x239A:0x00DA | https://www.particle.io/mesh/ | +| particle_xenon | Particle Xenon | 0x239A:0x00DA | https://www.particle.io/mesh/ | +| pca10056 | Nordic nRF52840 DK | 0x239A:0x00DA | https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK | +| pca10059 | Nordic nRF52840 Dongle | 0x239A:0x0029 | https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-Dongle | +| pca10100 | Nordic nRF52833 DK | 0x239A:0x00D8 | https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52833-DK | +| pillbug | PillBug | 0x16D0:0x10ED | https://mechwild.com | +| pitaya_go | Pitaya Go | 0x2886:0xF00E | https://wiki.makerdiary.com/pitaya-go | +| raytac_mdbt50q_db_40 | Raytac MDBT50Q Demo Board 40 | 0x239A:0x00BB | https://www.raytac.com/product/ins.php?index_id=81 | +| raytac_mdbt50q_rx | Raytac MDBT50Q-RX | 0x239A:0x010B | https://www.adafruit.com/product/5199 | +| sparkfun_nrf52840_micromod | SparkFun MicroMod nRF52840 | 0x1B4F:0x0022 | https://www.sparkfun.com/products/16984 | +| t1000_e | Seeed T1000-E for Meshtastic | 0x2886:0x0057 | https://www.seeedstudio.com/SenseCAP-Card-Tracker-T1000-E-for-Meshtastic-p-5913.html | +| waveshare_nrf52840_eval | Waveshare nRF52840 Eval | 0x239A:0x0029 | https://www.waveshare.com/wiki/NRF52840_Eval_Kit | +| xiao_nrf52840_ble | Seeed XIAO nRF52840 | 0x2886:0x0044 | https://www.seeedstudio.com/ | +| xiao_nrf52840_ble_sense | Seeed XIAO nRF52840 | 0x2886:0x0045 | https://www.seeedstudio.com/ | diff --git a/tools/gen_boards.py b/tools/gen_boards.py new file mode 100644 index 00000000..140a0dd4 --- /dev/null +++ b/tools/gen_boards.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import re +from pathlib import Path + + +BOARD_H_GLOB = "src/boards/*/board.h" + + +def _first_match(pattern: str, text: str) -> str | None: + match = re.search(pattern, text, flags=re.MULTILINE) + return match.group(1).strip() if match else None + + +def _parse_int(value: str | None) -> int | None: + if value is None: + return None + value = value.strip() + if value.lower().startswith("0x"): + try: + return int(value, 16) + except ValueError: + return None + try: + return int(value, 10) + except ValueError: + return None + + +def _fmt_vid_pid(vid: int | None, pid: int | None) -> str: + if vid is None or pid is None: + return "N/A" + return f"0x{vid:04X}:0x{pid:04X}" + + +def _escape_md(value: str) -> str: + return value.replace("|", "\\|") + + +def _load_board(board_h: Path, repo_root: Path) -> dict[str, str]: + text = board_h.read_text(errors="ignore") + board = board_h.parent.name + name = _first_match(r'^\s*#define\s+UF2_PRODUCT_NAME\s+"([^"]+)"', text) + url = _first_match(r'^\s*#define\s+UF2_INDEX_URL\s+"([^"]+)"', text) + vid = _parse_int(_first_match(r"^\s*#define\s+USB_DESC_VID\s+([0-9A-Fa-fxX]+)", text)) + pid = _parse_int( + _first_match(r"^\s*#define\s+USB_DESC_UF2_PID\s+([0-9A-Fa-fxX]+)", text) + ) + + return { + "board": board, + "name": name or "N/A", + "vid_pid": _fmt_vid_pid(vid, pid), + "url": url or "N/A", + } + + +def generate(repo_root: Path, output: Path) -> None: + boards: list[dict[str, str]] = [] + for board_h in sorted(repo_root.glob(BOARD_H_GLOB)): + boards.append(_load_board(board_h, repo_root)) + + lines: list[str] = [] + lines.append("# Supported Boards") + lines.append("") + def _is_adafruit(entry: dict[str, str]) -> bool: + name = entry["name"].lower() + board = entry["board"].lower() + return name.startswith("adafruit") or board.startswith("adafruit") + + def _render_table(entries: list[dict[str, str]]) -> None: + lines.append("| Board | Name | VID PID | URL |") + lines.append("| --- | --- | --- | --- |") + for entry in sorted(entries, key=lambda e: e["board"].lower()): + lines.append( + f"| {_escape_md(entry['board'])} | {_escape_md(entry['name'])} | " + f"{_escape_md(entry['vid_pid'])} | {_escape_md(entry['url'])} |" + ) + lines.append("") + + adafruit = [entry for entry in boards if _is_adafruit(entry)] + third_party = [entry for entry in boards if not _is_adafruit(entry)] + + lines.append("## Adafruit Boards") + lines.append("") + _render_table(adafruit) + + lines.append("## 3rd Party Boards") + lines.append("") + _render_table(third_party) + + output.write_text("\n".join(lines)) + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Generate supported_boards.md from src/boards/*/board.h" + ) + parser.add_argument( + "--output", + default="supported_boards.md", + help="Output markdown file (default: supported_boards.md)", + ) + args = parser.parse_args() + + repo_root = Path(__file__).resolve().parents[1] + output = Path(args.output) + if not output.is_absolute(): + output = repo_root / output + + generate(repo_root, output) + + +if __name__ == "__main__": + main() From d0f13ea1fed2a78c3272209a0bdf10a2a836f269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Sat, 31 Jan 2026 16:17:22 -0300 Subject: [PATCH 50/84] Made sure to clear pending interrupts and exceptions before jumping to the user application. Was rarely causing locks --- .../bootloader_dfu/bootloader_util.c | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c b/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c index f6c2bcc9..62cb2856 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c @@ -32,6 +32,12 @@ #if defined ( __CC_ARM ) __asm static void bootloader_util_reset(uint32_t start_addr) { + MOVS R5, #0x00 ; R5 = 0 + MSR CONTROL, R5 ; Clear CONTROL + MSR PRIMASK, R5 ; Clear PRIMASK + MSR BASEPRI, R5 ; Clear BASEPRI + MSR FAULTMASK, R5 ; Clear FAULTMASK + LDR R5, [R0] ; Get App initial MSP for bootloader. MSR MSP, R5 ; Set the main stack pointer to the applications MSP. LDR R0, [R0, #0x04] ; Load Reset handler into R0. This will be first argument to branch instruction (BX). @@ -69,6 +75,12 @@ static inline void bootloader_util_reset (uint32_t start_addr) __attribute__ ((o static inline void bootloader_util_reset(uint32_t start_addr) { __asm volatile( + "movs r5, #0x00\t\n" // R5 = 0 + "msr control, r5\t\n" // Clear CONTROL + "msr primask, r5\t\n" // Clear PRIMASK + "msr basepri, r5\t\n" // Clear BASEPRI + "msr faultmask, r5\t\n" // Clear FAULTMASK + "ldr r0, [%0]\t\n" // Get App initial MSP for bootloader. "msr msp, r0\t\n" // Set the main stack pointer to the applications MSP. "ldr r0, [%0, #0x04]\t\n" // Load Reset handler into R0. @@ -108,7 +120,13 @@ static inline void bootloader_util_reset(uint32_t start_addr) #elif defined ( __ICCARM__ ) static inline void bootloader_util_reset(uint32_t start_addr) { - asm("ldr r5, [%0]\n" // Get App initial MSP for bootloader. + asm("movs r5, #0x00\n" // R5 = 0 + "msr control, r5\n" // Clear CONTROL + "msr primask, r5\n" // Clear PRIMASK + "msr basepri, r5\n" // Clear BASEPRI + "msr faultmask, r5\n" // Clear FAULTMASK + + "ldr r5, [%0]\n" // Get App initial MSP for bootloader. "msr msp, r5\n" // Set the main stack pointer to the applications MSP. "ldr r0, [%0, #0x04]\n" // Load Reset handler into R0. From e8ffbd019a963815e1ca5edb067b327868aac4d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Sat, 31 Jan 2026 16:19:57 -0300 Subject: [PATCH 51/84] Allow using an external LNA/PA to increase BLE range --- src/main.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 5beedec8..cccb097a 100644 --- a/src/main.c +++ b/src/main.c @@ -422,13 +422,76 @@ static uint32_t ble_stack_init(void) { // Note: Interrupt state (enabled, forwarding) is not work properly if not enable ble sd_ble_enable(&ram_start); -#if BLEGATT_ATT_MTU_MAX > 23 +#if BLEGATT_ATT_MTU_MAX > 23 || defined(GPIO_PA_PIN) || defined(GPIO_LNA_PIN) ble_opt_t opt; +#endif + +#if BLEGATT_ATT_MTU_MAX > 23 varclr(&opt); opt.common_opt.conn_evt_ext.enable = 1; // enable Data Length Extension sd_ble_opt_set(BLE_COMMON_OPT_CONN_EVT_EXT, &opt); #endif +#if defined(GPIO_PA_LNA_MODE_PIN) + // Set PA / LNA Mode Pin: Low for Normal operation + nrf_gpio_cfg_output(GPIO_PA_LNA_MODE_PIN); + nrf_gpio_pin_clear(GPIO_PA_LNA_MODE_PIN); +#endif + +#if defined(GPIO_PA_LNA_SELECT_PIN) + // Set PA / LNA Select Pin: low for u.FL + nrf_gpio_cfg_output(GPIO_PA_LNA_SELECT_PIN); + nrf_gpio_pin_clear(GPIO_PA_LNA_SELECT_PIN); +#endif + + // Configure SoftDevice PA / LNA assist if required +#if defined(GPIO_PA_PIN) || defined(GPIO_LNA_PIN) + + static const uint32_t gpio_toggle_ch = 0; + static const uint32_t ppi_set_ch = 0; + static const uint32_t ppi_clr_ch = 1; + + varclr(&opt); + + // Common PA / LNA config + // GPIOTE channel + opt.common_opt.pa_lna.gpiote_ch_id = gpio_toggle_ch; + // PPI channel for pin learing + opt.common_opt.pa_lna.ppi_ch_id_clr = ppi_clr_ch; + // PPI channel for pin setting + opt.common_opt.pa_lna.ppi_ch_id_set = ppi_set_ch; + +# if defined(GPIO_PA_PIN) + // -- PA config -- + // Set the pin to be active high + opt.common_opt.pa_lna.pa_cfg.active_high = GPIO_PA_PIN_ACTIVE_STATE; + // Enable toggling + opt.common_opt.pa_lna.pa_cfg.enable = 1; + // The GPIO pin to toggle + opt.common_opt.pa_lna.pa_cfg.gpio_pin = GPIO_PA_PIN; +# endif + +# if defined(GPIO_LNA_PIN) + // -- LNA config -- + // Set the pin to be active high + opt.common_opt.pa_lna.lna_cfg.active_high = GPIO_LNA_PIN_ACTIVE_STATE; + // Enable toggling + opt.common_opt.pa_lna.lna_cfg.enable = 1; + + // The GPIO pin to toggle + opt.common_opt.pa_lna.lna_cfg.gpio_pin = GPIO_LNA_PIN; + sd_ble_opt_set(BLE_COMMON_OPT_PA_LNA, &opt); +# endif + + // Set TX power for scan responses + sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_SCAN_INIT, 0, RADIO_TXPOWER_TXPOWER_Neg8dBm); + + // Set TX power for advertisements + sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, 0, RADIO_TXPOWER_TXPOWER_Neg8dBm); + // (Tx power setting for connections inherit the scan or advertising power setting) + +#endif + return NRF_SUCCESS; } From 4d1ca56d0a3b054edb7cdc37031363857e22b717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Sat, 31 Jan 2026 16:20:36 -0300 Subject: [PATCH 52/84] nice!nano board has no buttons --- src/boards/nice_nano/board.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/boards/nice_nano/board.h b/src/boards/nice_nano/board.h index 0ec4cde5..d70dc535 100644 --- a/src/boards/nice_nano/board.h +++ b/src/boards/nice_nano/board.h @@ -39,9 +39,7 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTONS_NUMBER 2 // none connected at all -#define BUTTON_1 _PINNUM(0, 18) // unusable: RESET -#define BUTTON_2 _PINNUM(0, 19) // no connection +#define BUTTONS_NUMBER 0 // none connected at all #define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ From e6da12ab45c1707c54e6e169c85fc31afe6919b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Sat, 31 Jan 2026 16:23:36 -0300 Subject: [PATCH 53/84] Update Makefile Yes, the '-D' was missing Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5b9b2e51..108ae15f 100644 --- a/Makefile +++ b/Makefile @@ -373,7 +373,7 @@ CFLAGS += -DFORCE_UF2 endif ifeq ($(DEFAULT_TO_OTA_DFU), 1) -CFLAGS += -DEFAULT_TO_OTA_DFU +CFLAGS += -DDEFAULT_TO_OTA_DFU endif _VER = $(subst ., ,$(word 1, $(subst -, ,$(GIT_VERSION)))) From a76d419c34fb9b7478af58623f2b3f590148988b Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 2 Feb 2026 14:30:32 +0700 Subject: [PATCH 54/84] minor clean up --- .../libraries/bootloader_dfu/bootloader.c | 13 +++++++------ .../libraries/bootloader_dfu/dfu_single_bank.c | 5 +++-- src/main.c | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c index 9a8e55e7..bd9b8cce 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c @@ -140,7 +140,7 @@ static void wait_for_events(void) (m_update_status == BOOTLOADER_SYS_RESET) || (m_update_status == BOOTLOADER_RESET_TO_SELF)) { - // When update has completed or a timeout/reset occured we will return. + // When update has completed or a timeout/reset occurred we will return. return; } } @@ -191,8 +191,9 @@ static void bootloader_settings_save(bootloader_settings_t * p_settings) uint32_t err_code; while(1) { err_code = pstorage_clear(&m_bootsettings_handle, sizeof(bootloader_settings_t)); - if (err_code != NRF_ERROR_NO_MEM) + if (err_code != NRF_ERROR_NO_MEM) { break; + } // This means the write/erase queue of commands was completely full - Should not // happen, but better safe than sorry, wait until space becomes available - // the pstorage event handler is only run from the main loop, and we are also @@ -209,8 +210,9 @@ static void bootloader_settings_save(bootloader_settings_t * p_settings) while(1) { err_code = pstorage_store(&m_bootsettings_handle, (uint8_t *) p_settings, sizeof(bootloader_settings_t), 0); - if (err_code != NRF_ERROR_NO_MEM) + if (err_code != NRF_ERROR_NO_MEM) { break; + } // No space, wait until an entry in the queue is freed while (NRF_ERROR_NOT_FOUND != proc_soc()) { // nothing @@ -322,9 +324,8 @@ void bootloader_dfu_update_process(dfu_update_status_t update_status) } else if (update_status.status_code == DFU_RESET) { - m_update_status = update_status.restart_into_bootloader == false - ? BOOTLOADER_SYS_RESET - : BOOTLOADER_RESET_TO_SELF; + m_update_status = + (update_status.restart_into_bootloader == false ? BOOTLOADER_SYS_RESET : BOOTLOADER_RESET_TO_SELF); } else { diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c index b53bf957..114a57c9 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c @@ -789,8 +789,9 @@ uint32_t dfu_bl_image_validate(void) sd_mbr_cmd_1.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t); uint32_t err_code = sd_mbr_command(&sd_mbr_cmd_1); - if (err_code == NRF_SUCCESS) - return NRF_SUCCESS; + if (err_code == NRF_SUCCESS) { + return NRF_SUCCESS; + } uint32_t bl_image_start_2 = (bootloader_settings.sd_image_size == 0) ? DFU_BANK_1_REGION_START : diff --git a/src/main.c b/src/main.c index cccb097a..f9a95d8c 100644 --- a/src/main.c +++ b/src/main.c @@ -134,7 +134,7 @@ uint32_t* dbl_reset_mem = ((uint32_t*) DFU_DBL_RESET_MEM); // true if ble, false if serial bool _ota_dfu = false; bool _ota_connected = false; -bool _ota_was_active = false; +bool _ota_was_connected = false; bool _sd_inited = false; bool is_ota(void) { @@ -231,7 +231,7 @@ int main(void) { // Reset the system with the OTA DFU update in case we were in it, // to allow completion of FLASHING, otherwise, default to normal reset - if (_ota_was_active) { + if (_ota_was_connected) { NRF_POWER->GPREGRET = DFU_MAGIC_OTA_RESET; } @@ -526,8 +526,8 @@ uint32_t proc_ble(void) { _ota_connected = true; - // Remember someone connected to BLE - _ota_was_active = true; + // Remember someone connected to BLE + _ota_was_connected = true; led_state(STATE_BLE_CONNECTED); break; } From 29173a4de126f68aea29ca44047faf82a3c70398 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 2 Feb 2026 16:49:26 +0700 Subject: [PATCH 55/84] rename BUTTON_FRESET to BUTTON_DFU_OTA change board.d BUTTON_1/2 directly to BUTTON_DFU and BUTTON_DFU_OTA --- src/boards/adm_b_nrf52840_1/board.h | 32 ++++----- src/boards/ae_bl652_bo/board.h | 24 +++---- src/boards/aramcon2_badge/board.h | 36 +++++----- src/boards/aramcon_badge_2019/board.h | 36 +++++----- .../arcade_feather_nrf52840_express/board.h | 41 +++++------ src/boards/arduino_nano_33_ble/board.h | 32 ++++----- src/boards/bast_ble/board.h | 34 ++++----- src/boards/bluemicro_nrf52833/board.h | 40 +++++------ src/boards/bluemicro_nrf52840/board.h | 36 +++++----- src/boards/boards.c | 6 +- src/boards/boards.h | 11 ++- src/boards/challenger_840_ble/board.h | 40 +++++------ src/boards/circuitplayground_nrf52840/board.h | 38 +++++----- src/boards/clue_nrf52840/board.h | 66 +++++++++-------- src/boards/ebyte_e104_bt5032a/board.h | 33 ++++----- src/boards/ebyte_e73_tbb/board.h | 35 ++++----- src/boards/electronut_labs_papyr/board.h | 30 ++++---- src/boards/feather_nrf52832/board.h | 24 +++---- src/boards/feather_nrf52840_express/board.h | 13 ++-- src/boards/feather_nrf52840_sense/board.h | 40 +++++------ src/boards/feather_nrf52840_sense_tft/board.h | 72 +++++++++---------- src/boards/ikigaisense_vita/board.h | 40 +++++------ src/boards/itsybitsy_nrf52840_express/board.h | 12 ++-- src/boards/ledglasses_nrf52840/board.h | 38 +++++----- src/boards/m60_keyboard/board.h | 42 ++++++----- src/boards/m60_keyboard/pinconfig.c | 66 +++++++++-------- src/boards/mbn52832dk/board.h | 24 +++---- src/boards/mdk_nrf52840_dongle/board.h | 42 ++++++----- src/boards/metro_nrf52840_express/board.h | 40 +++++------ src/boards/mikoto/board.h | 34 +++++---- src/boards/nRF52840_connect_kit/board.h | 40 +++++------ src/boards/nice_nano/board.h | 34 +++++---- src/boards/nrf52840_bboard/board.h | 32 ++++----- src/boards/nrf52840_m2/board.h | 42 ++++++----- src/boards/nrfmicro/board.h | 34 +++++---- src/boards/ohs2020_badge/board.h | 32 ++++----- src/boards/omnimo_nrf52840/board.h | 42 ++++++----- src/boards/particle_argon/board.h | 14 ++-- src/boards/particle_boron/board.h | 36 +++++----- src/boards/particle_xenon/board.h | 36 +++++----- src/boards/pca10056/board.h | 30 ++++---- src/boards/pca10059/board.h | 24 +++---- src/boards/pca10100/board.h | 32 ++++----- src/boards/pillbug/board.h | 34 +++++---- src/boards/pitaya_go/board.h | 40 +++++------ src/boards/raytac_mdbt50q_db_40/board.h | 16 ++--- src/boards/raytac_mdbt50q_rx/board.h | 32 ++++----- src/boards/sparkfun_nrf52840_micromod/board.h | 32 ++++----- src/boards/t1000_e/board.h | 32 ++++----- src/boards/waveshare_nrf52840_eval/board.h | 34 +++++---- src/boards/xiao_nrf52840_ble/board.h | 34 +++++---- src/boards/xiao_nrf52840_ble_sense/board.h | 34 +++++---- src/main.c | 4 +- 53 files changed, 851 insertions(+), 926 deletions(-) diff --git a/src/boards/adm_b_nrf52840_1/board.h b/src/boards/adm_b_nrf52840_1/board.h index 11c041a4..34dfa54f 100644 --- a/src/boards/adm_b_nrf52840_1/board.h +++ b/src/boards/adm_b_nrf52840_1/board.h @@ -25,42 +25,40 @@ #ifndef ADM_B_NRF52840_1_H #define ADM_B_NRF52840_1_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 19) // Red -//#define LED_SECONDARY_PIN 12 // Blue -#define LED_STATE_ON 0 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 19) // Red +// #define LED_SECONDARY_PIN 12 // Blue +#define LED_STATE_ON 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 11) -#define BUTTON_2 _PINNUM(0, 12) +#define BUTTON_DFU PINNUM(0, 11) +#define BUTTON_DFU_OTA PINNUM(0, 12) #define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Atelier-Du-Maker" -#define BLEDIS_MODEL "ADM_B_NRF52840_1" +#define BLEDIS_MANUFACTURER "Atelier-Du-Maker" +#define BLEDIS_MODEL "ADM_B_NRF52840_1" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x009F -#define USB_DESC_CDC_ONLY_PID 0x009F +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x009F +#define USB_DESC_CDC_ONLY_PID 0x009F //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "AtelierDuMaker NRF52840 Breakout" -#define UF2_VOLUME_LABEL "ADM840BOOT" -#define UF2_BOARD_ID "ADM_B_NRF52840_1" -#define UF2_INDEX_URL "https://github.com/Atelier-Du-Maker/HolyIoT_NRF52840" +#define UF2_PRODUCT_NAME "AtelierDuMaker NRF52840 Breakout" +#define UF2_VOLUME_LABEL "ADM840BOOT" +#define UF2_BOARD_ID "ADM_B_NRF52840_1" +#define UF2_INDEX_URL "https://github.com/Atelier-Du-Maker/HolyIoT_NRF52840" #endif // ADM_B_NRF52840_1_H diff --git a/src/boards/ae_bl652_bo/board.h b/src/boards/ae_bl652_bo/board.h index 1636ccfa..77232d89 100644 --- a/src/boards/ae_bl652_bo/board.h +++ b/src/boards/ae_bl652_bo/board.h @@ -28,26 +28,26 @@ /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 17 // LED1 -#define LED_SECONDARY_PIN 19 // LED2 -#define LED_STATE_ON 1 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN 17 // LED1 +#define LED_SECONDARY_PIN 19 // LED2 +#define LED_STATE_ON 1 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 11 // BTN1 -#define BUTTON_2 15 // BTN2 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU 11 // BTN1 +#define BUTTON_DFU_OTA 15 // BTN2 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP /*------------------------------------------------------------------*/ /* UART (only used by nRF52832) *------------------------------------------------------------------*/ -#define RX_PIN_NUMBER 8 -#define TX_PIN_NUMBER 6 -#define CTS_PIN_NUMBER 0 -#define RTS_PIN_NUMBER 0 -#define HWFC false +#define RX_PIN_NUMBER 8 +#define TX_PIN_NUMBER 6 +#define CTS_PIN_NUMBER 0 +#define RTS_PIN_NUMBER 0 +#define HWFC false //--------------------------------------------------------------------+ // BLE OTA diff --git a/src/boards/aramcon2_badge/board.h b/src/boards/aramcon2_badge/board.h index 788fa3bd..cf61be98 100644 --- a/src/boards/aramcon2_badge/board.h +++ b/src/boards/aramcon2_badge/board.h @@ -25,40 +25,38 @@ #ifndef _ARAMCON2_BADGE_H #define _ARAMCON2_BADGE_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(1, 11) // Red +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(1, 11) // Red -#define LED_NEOPIXEL _PINNUM(0, 8) -#define NEOPIXELS_NUMBER 2 -#define BOARD_RGB_BRIGHTNESS 0x040404 +#define LED_NEOPIXEL PINNUM(0, 8) +#define NEOPIXELS_NUMBER 2 +#define BOARD_RGB_BRIGHTNESS 0x040404 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 2) // left Button -#define BUTTON_2 _PINNUM(0, 29) // middle button -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 2) // left Button +#define BUTTON_DFU_OTA PINNUM(0, 29) // middle button +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP // Used as model string in OTA mode -#define BLEDIS_MANUFACTURER "ARAMCON Badge Team" -#define BLEDIS_MODEL "ARAMCON2 Badge" +#define BLEDIS_MANUFACTURER "ARAMCON Badge Team" +#define BLEDIS_MODEL "ARAMCON2 Badge" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x007B -#define USB_DESC_CDC_ONLY_PID 0x007B +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x007B +#define USB_DESC_CDC_ONLY_PID 0x007B //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "ARAMCON2 Badge" -#define UF2_VOLUME_LABEL "ARAMBOOT" -#define UF2_BOARD_ID "nrf52840-ARAMCON2-Badge" -#define UF2_INDEX_URL "https://github.com/aramcon-badge" +#define UF2_PRODUCT_NAME "ARAMCON2 Badge" +#define UF2_VOLUME_LABEL "ARAMBOOT" +#define UF2_BOARD_ID "nrf52840-ARAMCON2-Badge" +#define UF2_INDEX_URL "https://github.com/aramcon-badge" #endif // _ARAMCON2_BADGE_H diff --git a/src/boards/aramcon_badge_2019/board.h b/src/boards/aramcon_badge_2019/board.h index c5458802..b81fdbd9 100644 --- a/src/boards/aramcon_badge_2019/board.h +++ b/src/boards/aramcon_badge_2019/board.h @@ -26,42 +26,40 @@ #ifndef _ARAMCON_BADGE_2019_H #define _ARAMCON_BADGE_2019_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(1, 11) // Red +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(1, 11) // Red -#define LED_NEOPIXEL _PINNUM(0, 8) -#define NEOPIXELS_NUMBER 4 -#define BOARD_RGB_BRIGHTNESS 0x040404 +#define LED_NEOPIXEL PINNUM(0, 8) +#define NEOPIXELS_NUMBER 4 +#define BOARD_RGB_BRIGHTNESS 0x040404 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 2) // left Button -#define BUTTON_2 _PINNUM(0, 29) // middle button -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 2) // left Button +#define BUTTON_DFU_OTA PINNUM(0, 29) // middle button +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "ARAMCON Badge Team" -#define BLEDIS_MODEL "ARAMCON Badge 2019" +#define BLEDIS_MANUFACTURER "ARAMCON Badge Team" +#define BLEDIS_MODEL "ARAMCON Badge 2019" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0079 -#define USB_DESC_CDC_ONLY_PID 0x0079 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0079 +#define USB_DESC_CDC_ONLY_PID 0x0079 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "ARAMCON Badge 2019" -#define UF2_VOLUME_LABEL "ARAMBOOT" -#define UF2_BOARD_ID "nrf52840-ARAMCON-Badge-2019" -#define UF2_INDEX_URL "https://github.com/aramcon-badge" +#define UF2_PRODUCT_NAME "ARAMCON Badge 2019" +#define UF2_VOLUME_LABEL "ARAMBOOT" +#define UF2_BOARD_ID "nrf52840-ARAMCON-Badge-2019" +#define UF2_INDEX_URL "https://github.com/aramcon-badge" #endif // _ARAMCON_BADGE_2019_H diff --git a/src/boards/arcade_feather_nrf52840_express/board.h b/src/boards/arcade_feather_nrf52840_express/board.h index b8b91f53..c33b644f 100644 --- a/src/boards/arcade_feather_nrf52840_express/board.h +++ b/src/boards/arcade_feather_nrf52840_express/board.h @@ -25,45 +25,42 @@ #ifndef _ARCARDE_FEATHER_NRF52840_H #define _ARCARDE_FEATHER_NRF52840_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN _PINNUM(1, 15) -#define LED_SECONDARY_PIN _PINNUM(1, 10) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN PINNUM(1, 15) +#define LED_SECONDARY_PIN PINNUM(1, 10) +#define LED_STATE_ON 1 -#define LED_NEOPIXEL _PINNUM(0, 16) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 +#define LED_NEOPIXEL PINNUM(0, 16) +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(1, 02) -#define BUTTON_2 _PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(1, 02) +#define BUTTON_DFU_OTA PINNUM(0, 10) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Feather nRF52840 Express" +#define BLEDIS_MANUFACTURER "Adafruit Industries" +#define BLEDIS_MODEL "Feather nRF52840 Express" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0029 -#define USB_DESC_CDC_ONLY_PID 0x002A +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0029 +#define USB_DESC_CDC_ONLY_PID 0x002A //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit Feather nRF52840 Express" -#define UF2_VOLUME_LABEL "ARCADE-N4" -#define UF2_BOARD_ID "nRF52840-Feather-revD" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4062" +#define UF2_PRODUCT_NAME "Adafruit Feather nRF52840 Express" +#define UF2_VOLUME_LABEL "ARCADE-N4" +#define UF2_BOARD_ID "nRF52840-Feather-revD" +#define UF2_INDEX_URL "https://www.adafruit.com/product/4062" #endif // _ARCARDE_FEATHER_NRF52840_H - diff --git a/src/boards/arduino_nano_33_ble/board.h b/src/boards/arduino_nano_33_ble/board.h index d29fc368..79e32fce 100644 --- a/src/boards/arduino_nano_33_ble/board.h +++ b/src/boards/arduino_nano_33_ble/board.h @@ -25,39 +25,37 @@ #ifndef _ARDUINOBLE33_NRF52840_H #define _ARDUINOBLE33_NRF52840_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 // red LED -#define LED_PRIMARY_PIN _PINNUM(0, 24) -#define LED_STATE_ON 0 +#define LEDS_NUMBER 1 // red LED +#define LED_PRIMARY_PIN PINNUM(0, 24) +#define LED_STATE_ON 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(1, 11) // D2 switch -#define BUTTON_2 _PINNUM(1, 12) // D3 switch -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(1, 11) // D2 switch +#define BUTTON_DFU_OTA PINNUM(1, 12) // D3 switch +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Arduino" -#define BLEDIS_MODEL "Nano 33 BLE" +#define BLEDIS_MANUFACTURER "Arduino" +#define BLEDIS_MODEL "Nano 33 BLE" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0063 -#define USB_DESC_CDC_ONLY_PID 0x0064 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0063 +#define USB_DESC_CDC_ONLY_PID 0x0064 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Arduino Nano 33 BLE" -#define UF2_VOLUME_LABEL "NANO33BOOT" -#define UF2_BOARD_ID "nRF52840-Nano-33" -#define UF2_INDEX_URL "https://www.arduino.cc/" +#define UF2_PRODUCT_NAME "Arduino Nano 33 BLE" +#define UF2_VOLUME_LABEL "NANO33BOOT" +#define UF2_BOARD_ID "nRF52840-Nano-33" +#define UF2_INDEX_URL "https://www.arduino.cc/" #endif // _ITSY_NRF52840_H diff --git a/src/boards/bast_ble/board.h b/src/boards/bast_ble/board.h index 93a0eb68..598d2dd3 100644 --- a/src/boards/bast_ble/board.h +++ b/src/boards/bast_ble/board.h @@ -25,39 +25,39 @@ #ifndef _BASTBLE_NRF52840_H #define _BASTBLE_NRF52840_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) +#define PINNUM(port, pin) ((port) * 32 + (pin)) /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 // red LED -#define LED_PRIMARY_PIN _PINNUM(0, 24) -#define LED_STATE_ON 0 +#define LEDS_NUMBER 1 // red LED +#define LED_PRIMARY_PIN PINNUM(0, 24) +#define LED_STATE_ON 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 05) -#define BUTTON_2 _PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 05) +#define BUTTON_DFU_OTA PINNUM(0, 10) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "ElectronicCats" -#define BLEDIS_MODEL "BastBLE" +#define BLEDIS_MANUFACTURER "ElectronicCats" +#define BLEDIS_MODEL "BastBLE" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ // Shared VID/PID with Feather nRF52840, will be disabled for building in the future -#define USB_DESC_VID 0x1209 -#define USB_DESC_UF2_PID 0x805A -#define USB_DESC_CDC_ONLY_PID 0x805A - -#define UF2_PRODUCT_NAME "Bast BLE" -#define UF2_VOLUME_LABEL "BASTBLE" -#define UF2_BOARD_ID "Bast-ble-v1" -#define UF2_INDEX_URL "https://www.electroniccats.com" +#define USB_DESC_VID 0x1209 +#define USB_DESC_UF2_PID 0x805A +#define USB_DESC_CDC_ONLY_PID 0x805A + +#define UF2_PRODUCT_NAME "Bast BLE" +#define UF2_VOLUME_LABEL "BASTBLE" +#define UF2_BOARD_ID "Bast-ble-v1" +#define UF2_INDEX_URL "https://www.electroniccats.com" #endif // _BASTBLE_NRF52840_H diff --git a/src/boards/bluemicro_nrf52833/board.h b/src/boards/bluemicro_nrf52833/board.h index 763a0f4f..454d3409 100644 --- a/src/boards/bluemicro_nrf52833/board.h +++ b/src/boards/bluemicro_nrf52833/board.h @@ -25,44 +25,42 @@ #ifndef _BLUEMICRO_NRF52833_H #define _BLUEMICRO_NRF52833_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN _PINNUM(1, 4) -#define LED_SECONDARY_PIN _PINNUM(0, 25) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN PINNUM(1, 4) +#define LED_SECONDARY_PIN PINNUM(0, 25) +#define LED_STATE_ON 1 -#define LED_NEOPIXEL _PINNUM(0, 7) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 +#define LED_NEOPIXEL PINNUM(0, 7) +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 18) // unusable: RESET -#define BUTTON_2 _PINNUM(1, 1) // no connection on E73-2G4M08S1E Module -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 18) // unusable: RESET +#define BUTTON_DFU_OTA PINNUM(1, 1) // no connection on E73-2G4M08S1E Module +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "BlueMicro" -#define BLEDIS_MODEL "BlueMicro 833" +#define BLEDIS_MANUFACTURER "BlueMicro" +#define BLEDIS_MODEL "BlueMicro 833" //--------------------------------------------------------------------+ // USB PID from openmoko: // PR https://github.com/openmoko/openmoko-usb-oui/pull/34 //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1d50 -#define USB_DESC_UF2_PID 0x616f -#define USB_DESC_CDC_ONLY_PID 0x616f +#define USB_DESC_VID 0x1d50 +#define USB_DESC_UF2_PID 0x616f +#define USB_DESC_CDC_ONLY_PID 0x616f -#define UF2_PRODUCT_NAME "BlueMicro" -#define UF2_VOLUME_LABEL "BLUEMICRO" -#define UF2_BOARD_ID "nRF52833-BlueMicro-v1" -#define UF2_INDEX_URL "http://bluemicro.jpconstantineau.com/" +#define UF2_PRODUCT_NAME "BlueMicro" +#define UF2_VOLUME_LABEL "BLUEMICRO" +#define UF2_BOARD_ID "nRF52833-BlueMicro-v1" +#define UF2_INDEX_URL "http://bluemicro.jpconstantineau.com/" #endif // _BLUEMICRO_H diff --git a/src/boards/bluemicro_nrf52840/board.h b/src/boards/bluemicro_nrf52840/board.h index f590dfe0..46a2ef0e 100644 --- a/src/boards/bluemicro_nrf52840/board.h +++ b/src/boards/bluemicro_nrf52840/board.h @@ -25,40 +25,38 @@ #ifndef _BLUEMICRO_NRF52840_H #define _BLUEMICRO_NRF52840_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN _PINNUM(1, 4) -#define LED_SECONDARY_PIN _PINNUM(1, 10) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN PINNUM(1, 4) +#define LED_SECONDARY_PIN PINNUM(1, 10) +#define LED_STATE_ON 1 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 18) // unusable: RESET -#define BUTTON_2 _PINNUM(1, 1) // no connection on E73-2G4M08S1C Module -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 18) // unusable: RESET +#define BUTTON_DFU_OTA PINNUM(1, 1) // no connection on E73-2G4M08S1C Module +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "BlueMicro" -#define BLEDIS_MODEL "BlueMicro 840" +#define BLEDIS_MANUFACTURER "BlueMicro" +#define BLEDIS_MODEL "BlueMicro 840" //--------------------------------------------------------------------+ // USB PID from openmoko: // Approved PR https://github.com/openmoko/openmoko-usb-oui/pull/20 //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1d50 -#define USB_DESC_UF2_PID 0x6160 -#define USB_DESC_CDC_ONLY_PID 0x6160 - -#define UF2_PRODUCT_NAME "BlueMicro" -#define UF2_VOLUME_LABEL "BLUEMICRO" -#define UF2_BOARD_ID "nRF52840-BlueMicro-v1" -#define UF2_INDEX_URL "http://bluemicro.jpconstantineau.com/" +#define USB_DESC_VID 0x1d50 +#define USB_DESC_UF2_PID 0x6160 +#define USB_DESC_CDC_ONLY_PID 0x6160 + +#define UF2_PRODUCT_NAME "BlueMicro" +#define UF2_VOLUME_LABEL "BLUEMICRO" +#define UF2_BOARD_ID "nRF52840-BlueMicro-v1" +#define UF2_INDEX_URL "http://bluemicro.jpconstantineau.com/" #endif // _BLUEMICRO_H diff --git a/src/boards/boards.c b/src/boards/boards.c index 9f986324..c02a2786 100644 --- a/src/boards/boards.c +++ b/src/boards/boards.c @@ -50,7 +50,7 @@ void SysTick_Handler(void) { led_tick(); } -#if defined(BUTTON_DFU) || defined(BUTTON_FRESET) +#if defined(BUTTON_DFU) || defined(BUTTON_DFU_OTA) void button_init(uint32_t pin) { if (BUTTON_PULL == NRF_GPIO_PIN_PULLDOWN) { nrf_gpio_cfg_sense_input(pin, BUTTON_PULL, NRF_GPIO_PIN_SENSE_HIGH); @@ -79,8 +79,8 @@ void board_init(void) { #ifdef BUTTON_DFU button_init(BUTTON_DFU); #endif -#ifdef BUTTON_FRESET - button_init(BUTTON_FRESET); +#ifdef BUTTON_DFU_OTA + button_init(BUTTON_DFU_OTA); #endif NRFX_DELAY_US(100); // wait for the pin state is stable diff --git a/src/boards/boards.h b/src/boards/boards.h index 583e044f..87b14b15 100644 --- a/src/boards/boards.h +++ b/src/boards/boards.h @@ -31,18 +31,15 @@ #include "nrf.h" #include "nrf_gpio.h" +#define PINNUM(port, pin) ((port) * 32 + (pin)) #include "board.h" #ifndef UF2_VOLUME_LABEL #define UF2_VOLUME_LABEL "NRF52BOOT " #endif -#if !defined(BUTTON_DFU) && defined(BUTTON_1) -#define BUTTON_DFU BUTTON_1 -#endif - -#if !defined(BUTTON_FRESET) && defined(BUTTON_2) -#define BUTTON_FRESET BUTTON_2 +#if !defined(BUTTON_DFU_OTA) && defined(BUTTON_DFU_OTA) + #define BUTTON_DFU_OTA BUTTON_DFU_OTA #endif // The primary LED is usually Red but not in all cases. @@ -104,7 +101,7 @@ void led_tick(void); //--------------------------------------------------------------------+ // BUTTONS //--------------------------------------------------------------------+ -#if defined(BUTTON_DFU) || defined(BUTTON_FRESET) +#if defined(BUTTON_DFU) || defined(BUTTON_DFU_OTA) void button_init(uint32_t pin); bool button_pressed(uint32_t pin); #endif diff --git a/src/boards/challenger_840_ble/board.h b/src/boards/challenger_840_ble/board.h index 25d4c6e9..7be348c1 100644 --- a/src/boards/challenger_840_ble/board.h +++ b/src/boards/challenger_840_ble/board.h @@ -25,48 +25,46 @@ #ifndef _CHALLENGER_840_BLE_H #define _CHALLENGER_840_BLE_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 12) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 12) +#define LED_STATE_ON 1 -#define LED_NEOPIXEL _PINNUM(1, 8) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 +#define LED_NEOPIXEL PINNUM(1, 8) +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 19) -#define BUTTON_2 _PINNUM(0, 8) // Pulls flash cs high -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 19) +#define BUTTON_DFU_OTA PINNUM(0, 8) // Pulls flash cs high +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP /*------------------------------------------------------------------*/ /* On board LDO control *------------------------------------------------------------------*/ -#define LDO_CONTROL_PIN _PINNUM(1, 9) // Enables external pwr +#define LDO_CONTROL_PIN PINNUM(1, 9) // Enables external pwr //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Invector Labs" -#define BLEDIS_MODEL "Challenger 840 BLE" +#define BLEDIS_MANUFACTURER "Invector Labs" +#define BLEDIS_MODEL "Challenger 840 BLE" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1209 -#define USB_DESC_UF2_PID 0x7380 -#define USB_DESC_CDC_ONLY_PID 0x7381 +#define USB_DESC_VID 0x1209 +#define USB_DESC_UF2_PID 0x7380 +#define USB_DESC_CDC_ONLY_PID 0x7381 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "ILabs Challenger 840" -#define UF2_VOLUME_LABEL "CH840BOOT" -#define UF2_BOARD_ID "nRF52840-Challenger-840" -#define UF2_INDEX_URL "https://www.ilabs.se" +#define UF2_PRODUCT_NAME "ILabs Challenger 840" +#define UF2_VOLUME_LABEL "CH840BOOT" +#define UF2_BOARD_ID "nRF52840-Challenger-840" +#define UF2_INDEX_URL "https://www.ilabs.se" #endif // _CHALLENGER_840_BLE_H diff --git a/src/boards/circuitplayground_nrf52840/board.h b/src/boards/circuitplayground_nrf52840/board.h index 62a537e8..82a9bbf7 100644 --- a/src/boards/circuitplayground_nrf52840/board.h +++ b/src/boards/circuitplayground_nrf52840/board.h @@ -25,43 +25,41 @@ #ifndef _CPLAY_NRF52840_H #define _CPLAY_NRF52840_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(1, 14) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(1, 14) +#define LED_STATE_ON 1 -#define LED_NEOPIXEL _PINNUM(0, 13) -#define NEOPIXELS_NUMBER 10 -#define BOARD_RGB_BRIGHTNESS 0x040404 +#define LED_NEOPIXEL PINNUM(0, 13) +#define NEOPIXELS_NUMBER 10 +#define BOARD_RGB_BRIGHTNESS 0x040404 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(1, 02) // left button -#define BUTTON_2 _PINNUM(1, 15) // right button -#define BUTTON_PULL NRF_GPIO_PIN_PULLDOWN +#define BUTTON_DFU PINNUM(1, 02) // left button +#define BUTTON_DFU_OTA PINNUM(1, 15) // right button +#define BUTTON_PULL NRF_GPIO_PIN_PULLDOWN //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Circuit Playground nRF52840" +#define BLEDIS_MANUFACTURER "Adafruit Industries" +#define BLEDIS_MODEL "Circuit Playground nRF52840" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0045 -#define USB_DESC_CDC_ONLY_PID 0x0045 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0045 +#define USB_DESC_CDC_ONLY_PID 0x0045 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit Circuit Playground nRF52840" -#define UF2_VOLUME_LABEL "CPLAYBTBOOT" -#define UF2_BOARD_ID "nRF52840-CircuitPlayground-revD" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4333" +#define UF2_PRODUCT_NAME "Adafruit Circuit Playground nRF52840" +#define UF2_VOLUME_LABEL "CPLAYBTBOOT" +#define UF2_BOARD_ID "nRF52840-CircuitPlayground-revD" +#define UF2_INDEX_URL "https://www.adafruit.com/product/4333" #endif // _CPLAY_NRF52840_H diff --git a/src/boards/clue_nrf52840/board.h b/src/boards/clue_nrf52840/board.h index 61466e37..91b8f568 100644 --- a/src/boards/clue_nrf52840/board.h +++ b/src/boards/clue_nrf52840/board.h @@ -25,69 +25,67 @@ #ifndef _CLUE_NRF52840_H #define _CLUE_NRF52840_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(1, 01) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(1, 01) +#define LED_STATE_ON 1 -#define LED_NEOPIXEL _PINNUM(0, 16) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 +#define LED_NEOPIXEL PINNUM(0, 16) +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(1, 02) // left button -#define BUTTON_2 _PINNUM(1, 10) // right button -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(1, 02) // left button +#define BUTTON_DFU_OTA PINNUM(1, 10) // right button +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // Display //--------------------------------------------------------------------+ #define DISPLAY_CONTROLLER_ST7789 -#define DISPLAY_PIN_SCK _PINNUM(0, 14) -#define DISPLAY_PIN_MOSI _PINNUM(0, 15) +#define DISPLAY_PIN_SCK PINNUM(0, 14) +#define DISPLAY_PIN_MOSI PINNUM(0, 15) -#define DISPLAY_PIN_CS _PINNUM(0, 12) -#define DISPLAY_PIN_DC _PINNUM(0, 13) -#define DISPLAY_PIN_RST _PINNUM(1, 3) -#define DISPLAY_PIN_BL _PINNUM(1, 5) -#define DISPLAY_BL_ON 1 // GPIO state to enable back light +#define DISPLAY_PIN_CS PINNUM(0, 12) +#define DISPLAY_PIN_DC PINNUM(0, 13) +#define DISPLAY_PIN_RST PINNUM(1, 3) +#define DISPLAY_PIN_BL PINNUM(1, 5) +#define DISPLAY_BL_ON 1 // GPIO state to enable back light -#define DISPLAY_WIDTH 240 -#define DISPLAY_HEIGHT 240 +#define DISPLAY_WIDTH 240 +#define DISPLAY_HEIGHT 240 -#define DISPLAY_COL_OFFSET 0 -#define DISPLAY_ROW_OFFSET 80 +#define DISPLAY_COL_OFFSET 0 +#define DISPLAY_ROW_OFFSET 80 // Memory Data Access Control & // Vertical Scroll Start Address -#define DISPLAY_MADCTL (TFT_MADCTL_MY) -#define DISPLAY_VSCSAD 0 +#define DISPLAY_MADCTL (TFT_MADCTL_MY) +#define DISPLAY_VSCSAD 0 -#define DISPLAY_TITLE "CLUE" +#define DISPLAY_TITLE "CLUE" //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "CLUE nRF52840" +#define BLEDIS_MANUFACTURER "Adafruit Industries" +#define BLEDIS_MODEL "CLUE nRF52840" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0071 -#define USB_DESC_CDC_ONLY_PID 0x0071 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0071 +#define USB_DESC_CDC_ONLY_PID 0x0071 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit CLUE nRF52840" -#define UF2_VOLUME_LABEL "CLUEBOOT" -#define UF2_BOARD_ID "nRF52840-CLUE-revA" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4500" +#define UF2_PRODUCT_NAME "Adafruit CLUE nRF52840" +#define UF2_VOLUME_LABEL "CLUEBOOT" +#define UF2_BOARD_ID "nRF52840-CLUE-revA" +#define UF2_INDEX_URL "https://www.adafruit.com/product/4500" #endif // _CLUE_NRF52840_H diff --git a/src/boards/ebyte_e104_bt5032a/board.h b/src/boards/ebyte_e104_bt5032a/board.h index 3cc675d7..983f5ffa 100644 --- a/src/boards/ebyte_e104_bt5032a/board.h +++ b/src/boards/ebyte_e104_bt5032a/board.h @@ -28,30 +28,31 @@ /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 31 // Red - named "Data" -#define LED_SECONDARY_PIN 30 // Blue - named "Link" (also a red one on the Test Board) -#define LED_STATE_ON 0 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN 31 // Red - named "Data" +#define LED_SECONDARY_PIN 30 // Blue - named "Link" (also a red one on the Test Board) +#define LED_STATE_ON 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 29 // DISC Button on E104-BT5032A-TB Test Board - // BUTTON_1 is DFU. The module does not have reset circuitry. - // The Test Board from Ebyte (E104-BT5032A-TB) also does not have reset circuitry - // To be able to upload using the Arduino IDE, (To enter DFU mode) - // Press "DISC", press and release "RST", then release "DISC" -#define BUTTON_2 28 // No button - WKP pin -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU \ + 29 // DISC Button on E104-BT5032A-TB Test Board + // BUTTON_DFU is DFU. The module does not have reset circuitry. + // The Test Board from Ebyte (E104-BT5032A-TB) also does not have reset circuitry + // To be able to upload using the Arduino IDE, (To enter DFU mode) + // Press "DISC", press and release "RST", then release "DISC" +#define BUTTON_DFU_OTA 28 // No button - WKP pin +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP /*------------------------------------------------------------------*/ /* UART (only used by nRF52832) *------------------------------------------------------------------*/ -#define RX_PIN_NUMBER 14 -#define TX_PIN_NUMBER 18 -#define CTS_PIN_NUMBER 20 -#define RTS_PIN_NUMBER 19 -#define HWFC false // leaving it false to make GPIO available +#define RX_PIN_NUMBER 14 +#define TX_PIN_NUMBER 18 +#define CTS_PIN_NUMBER 20 +#define RTS_PIN_NUMBER 19 +#define HWFC false // leaving it false to make GPIO available //--------------------------------------------------------------------+ // BLE OTA diff --git a/src/boards/ebyte_e73_tbb/board.h b/src/boards/ebyte_e73_tbb/board.h index f6f4c90b..bd522c56 100644 --- a/src/boards/ebyte_e73_tbb/board.h +++ b/src/boards/ebyte_e73_tbb/board.h @@ -28,31 +28,32 @@ /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 17 // Red - named "LED1" -#define LED_SECONDARY_PIN 18 // Blue - named "LED2" (also a red one on the Test Board) -#define LED_STATE_ON 0 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN 17 // Red - named "LED1" +#define LED_SECONDARY_PIN 18 // Blue - named "LED2" (also a red one on the Test Board) +#define LED_STATE_ON 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 14 // BUTTON_1 is DFU. SW1 on E73-TBB Test Board. - // This Test board uses the E73-2G4M04S1B module which contains a nrf52832. - // The module does not have reset circuitry. - // The Test Board from Ebyte (E73-TBB) also does not have reset circuitry - // To be able to upload using the Arduino IDE, (To enter DFU mode) - // Press "SW1", momentarily ground "RST/P0.21", then release "SW1" -#define BUTTON_2 13 // SW2 on board -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU \ + 14 // BUTTON_DFU is DFU. SW1 on E73-TBB Test Board. + // This Test board uses the E73-2G4M04S1B module which contains a nrf52832. + // The module does not have reset circuitry. + // The Test Board from Ebyte (E73-TBB) also does not have reset circuitry + // To be able to upload using the Arduino IDE, (To enter DFU mode) + // Press "SW1", momentarily ground "RST/P0.21", then release "SW1" +#define BUTTON_DFU_OTA 13 // SW2 on board +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP /*------------------------------------------------------------------*/ /* UART (only used by nRF52832) *------------------------------------------------------------------*/ -#define RX_PIN_NUMBER 8 -#define TX_PIN_NUMBER 6 -#define CTS_PIN_NUMBER 5 -#define RTS_PIN_NUMBER 7 -#define HWFC false // leaving it false to make GPIO available +#define RX_PIN_NUMBER 8 +#define TX_PIN_NUMBER 6 +#define CTS_PIN_NUMBER 5 +#define RTS_PIN_NUMBER 7 +#define HWFC false // leaving it false to make GPIO available //--------------------------------------------------------------------+ // BLE OTA diff --git a/src/boards/electronut_labs_papyr/board.h b/src/boards/electronut_labs_papyr/board.h index 70fa231d..69ce72fb 100644 --- a/src/boards/electronut_labs_papyr/board.h +++ b/src/boards/electronut_labs_papyr/board.h @@ -30,10 +30,10 @@ /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 13 -#define LED_SECONDARY_PIN 14 -#define LED_STATE_ON 0 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN 13 +#define LED_SECONDARY_PIN 14 +#define LED_STATE_ON 0 /*------------------------------------------------------------------*/ /* BUTTON @@ -42,25 +42,25 @@ // App can trigger OTA bootloader by writing DFU_MAGIC_OTA_APPJUM to // GPREGRET register if softdevice is not initialized; or by // writing DFU_MAGIC_OTA_RESET in case softdevice is initialized. -#define BUTTON_1 18 // RESET also by default -#define BUTTON_2 1 // P0.1 not exposed anywhere, FRST n/a -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU 18 // RESET also by default +#define BUTTON_DFU_OTA 1 // P0.1 not exposed anywhere, FRST n/a +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Electronut Labs" -#define BLEDIS_MODEL "Papyr" +#define BLEDIS_MANUFACTURER "Electronut Labs" +#define BLEDIS_MODEL "Papyr" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define UF2_PRODUCT_NAME "Electronut Labs Papyr" -#define UF2_BOARD_ID "nRF52840-Papyr-v1" -#define UF2_INDEX_URL "https://docs.electronut.in/papyr" +#define UF2_PRODUCT_NAME "Electronut Labs Papyr" +#define UF2_BOARD_ID "nRF52840-Papyr-v1" +#define UF2_INDEX_URL "https://docs.electronut.in/papyr" -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x003B -#define USB_DESC_CDC_ONLY_PID 0x003B +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x003B +#define USB_DESC_CDC_ONLY_PID 0x003B #endif // PPAPYR_H diff --git a/src/boards/feather_nrf52832/board.h b/src/boards/feather_nrf52832/board.h index dfb6175a..5e280a1f 100644 --- a/src/boards/feather_nrf52832/board.h +++ b/src/boards/feather_nrf52832/board.h @@ -28,26 +28,26 @@ /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 17 // Red -#define LED_SECONDARY_PIN 19 // Blue -#define LED_STATE_ON 1 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN 17 // Red +#define LED_SECONDARY_PIN 19 // Blue +#define LED_STATE_ON 1 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 20 -#define BUTTON_2 22 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU 20 +#define BUTTON_DFU_OTA 22 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP /*------------------------------------------------------------------*/ /* UART (only used by nRF52832) *------------------------------------------------------------------*/ -#define RX_PIN_NUMBER 8 -#define TX_PIN_NUMBER 6 -#define CTS_PIN_NUMBER 0 -#define RTS_PIN_NUMBER 0 -#define HWFC false +#define RX_PIN_NUMBER 8 +#define TX_PIN_NUMBER 6 +#define CTS_PIN_NUMBER 0 +#define RTS_PIN_NUMBER 0 +#define HWFC false //--------------------------------------------------------------------+ // BLE OTA diff --git a/src/boards/feather_nrf52840_express/board.h b/src/boards/feather_nrf52840_express/board.h index cec7e68d..4fa7c98b 100644 --- a/src/boards/feather_nrf52840_express/board.h +++ b/src/boards/feather_nrf52840_express/board.h @@ -25,26 +25,25 @@ #ifndef _FEATHER_NRF52840_H #define _FEATHER_NRF52840_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ #define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN _PINNUM(1, 15) -#define LED_SECONDARY_PIN _PINNUM(1, 10) +#define LED_PRIMARY_PIN PINNUM(1, 15) +#define LED_SECONDARY_PIN PINNUM(1, 10) #define LED_STATE_ON 1 -#define LED_NEOPIXEL _PINNUM(0, 16) -#define NEOPIXEL_POWER_PIN _PINNUM(1, 14) +#define LED_NEOPIXEL PINNUM(0, 16) +#define NEOPIXEL_POWER_PIN PINNUM(1, 14) #define NEOPIXELS_NUMBER 1 #define BOARD_RGB_BRIGHTNESS 0x040404 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(1, 02) -#define BUTTON_2 _PINNUM(0, 10) +#define BUTTON_DFU PINNUM(1, 02) +#define BUTTON_DFU_OTA PINNUM(0, 10) #define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ diff --git a/src/boards/feather_nrf52840_sense/board.h b/src/boards/feather_nrf52840_sense/board.h index 4c9b2505..e0740fd9 100644 --- a/src/boards/feather_nrf52840_sense/board.h +++ b/src/boards/feather_nrf52840_sense/board.h @@ -25,44 +25,42 @@ #ifndef _FEATHER_NRF52840_SENSE_H #define _FEATHER_NRF52840_SENSE_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN _PINNUM(1, 9) -#define LED_SECONDARY_PIN _PINNUM(1, 10) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN PINNUM(1, 9) +#define LED_SECONDARY_PIN PINNUM(1, 10) +#define LED_STATE_ON 1 -#define LED_NEOPIXEL _PINNUM(0, 16) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 +#define LED_NEOPIXEL PINNUM(0, 16) +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(1, 02) -#define BUTTON_2 _PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(1, 02) +#define BUTTON_DFU_OTA PINNUM(0, 10) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Feather nRF52840 Sense" +#define BLEDIS_MANUFACTURER "Adafruit Industries" +#define BLEDIS_MODEL "Feather nRF52840 Sense" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0087 -#define USB_DESC_CDC_ONLY_PID 0x0088 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0087 +#define USB_DESC_CDC_ONLY_PID 0x0088 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit Feather nRF52840 Sense" -#define UF2_VOLUME_LABEL "FTHRSNSBOOT" -#define UF2_BOARD_ID "nRF52840-Feather-Sense" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4516" +#define UF2_PRODUCT_NAME "Adafruit Feather nRF52840 Sense" +#define UF2_VOLUME_LABEL "FTHRSNSBOOT" +#define UF2_BOARD_ID "nRF52840-Feather-Sense" +#define UF2_INDEX_URL "https://www.adafruit.com/product/4516" #endif // _FEATHER_NRF52840_SENSE_H diff --git a/src/boards/feather_nrf52840_sense_tft/board.h b/src/boards/feather_nrf52840_sense_tft/board.h index f46c14be..2e97e02d 100644 --- a/src/boards/feather_nrf52840_sense_tft/board.h +++ b/src/boards/feather_nrf52840_sense_tft/board.h @@ -25,75 +25,73 @@ #ifndef _FEATHER_NRF52840_SENSE_TFT_H #define _FEATHER_NRF52840_SENSE_TFT_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 11) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 11) +#define LED_STATE_ON 1 -#define LED_NEOPIXEL _PINNUM(1, 8) -#define NEOPIXEL_POWER_PIN _PINNUM(1, 2) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 +#define LED_NEOPIXEL PINNUM(1, 8) +#define NEOPIXEL_POWER_PIN PINNUM(1, 2) +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(1, 6) -#define BUTTON_2 _PINNUM(1, 10) // DFU pin -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(1, 6) +#define BUTTON_DFU_OTA PINNUM(1, 10) // DFU pin +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // Display //--------------------------------------------------------------------+ // VSensor required to power the display -#define DISPLAY_VSENSOR_PIN _PINNUM(1, 7) -#define DISPLAY_VSENSOR_ON 1 +#define DISPLAY_VSENSOR_PIN PINNUM(1, 7) +#define DISPLAY_VSENSOR_ON 1 #define DISPLAY_CONTROLLER_ST7789 -#define DISPLAY_PIN_SCK _PINNUM(0, 26) -#define DISPLAY_PIN_MOSI _PINNUM(0, 5) +#define DISPLAY_PIN_SCK PINNUM(0, 26) +#define DISPLAY_PIN_MOSI PINNUM(0, 5) -#define DISPLAY_PIN_CS _PINNUM(1, 5) -#define DISPLAY_PIN_DC _PINNUM(1, 1) -#define DISPLAY_PIN_RST _PINNUM(1, 3) -#define DISPLAY_PIN_BL _PINNUM(0, 27) -#define DISPLAY_BL_ON 1 // GPIO state to enable back light +#define DISPLAY_PIN_CS PINNUM(1, 5) +#define DISPLAY_PIN_DC PINNUM(1, 1) +#define DISPLAY_PIN_RST PINNUM(1, 3) +#define DISPLAY_PIN_BL PINNUM(0, 27) +#define DISPLAY_BL_ON 1 // GPIO state to enable back light -#define DISPLAY_WIDTH 240 -#define DISPLAY_HEIGHT 135 +#define DISPLAY_WIDTH 240 +#define DISPLAY_HEIGHT 135 -#define DISPLAY_COL_OFFSET 53 -#define DISPLAY_ROW_OFFSET 40 +#define DISPLAY_COL_OFFSET 53 +#define DISPLAY_ROW_OFFSET 40 // Memory Data Access Control & // Vertical Scroll Start Address -#define DISPLAY_MADCTL (TFT_MADCTL_MX) -#define DISPLAY_VSCSAD 0 +#define DISPLAY_MADCTL (TFT_MADCTL_MX) +#define DISPLAY_VSCSAD 0 -#define DISPLAY_TITLE "Sense TFT" +#define DISPLAY_TITLE "Sense TFT" //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Feather nRF52840 Sense TFT" +#define BLEDIS_MANUFACTURER "Adafruit Industries" +#define BLEDIS_MODEL "Feather nRF52840 Sense TFT" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0087 -#define USB_DESC_CDC_ONLY_PID 0x0088 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0087 +#define USB_DESC_CDC_ONLY_PID 0x0088 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit Feather nRF52840 Sense TFT" -#define UF2_VOLUME_LABEL "SENSTFTBOOT" -#define UF2_BOARD_ID "nRF52840-FeatherSenseTFT-revA" -#define UF2_INDEX_URL "https://www.adafruit.com/product/" +#define UF2_PRODUCT_NAME "Adafruit Feather nRF52840 Sense TFT" +#define UF2_VOLUME_LABEL "SENSTFTBOOT" +#define UF2_BOARD_ID "nRF52840-FeatherSenseTFT-revA" +#define UF2_INDEX_URL "https://www.adafruit.com/product/" #endif diff --git a/src/boards/ikigaisense_vita/board.h b/src/boards/ikigaisense_vita/board.h index afb1958c..1c953474 100644 --- a/src/boards/ikigaisense_vita/board.h +++ b/src/boards/ikigaisense_vita/board.h @@ -25,44 +25,42 @@ #ifndef _IKIGAISENSE_VITA_NRF52840_H #define _IKIGAISENSE_VITA_NRF52840_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN _PINNUM(1, 15) -#define LED_SECONDARY_PIN _PINNUM(1, 10) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN PINNUM(1, 15) +#define LED_SECONDARY_PIN PINNUM(1, 10) +#define LED_STATE_ON 1 -#define LED_NEOPIXEL _PINNUM(0, 16) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 +#define LED_NEOPIXEL PINNUM(0, 16) +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(1, 02) -#define BUTTON_2 _PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(1, 02) +#define BUTTON_DFU_OTA PINNUM(0, 10) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "IkigaiSense Technologies" -#define BLEDIS_MODEL "Vita nRF52840" +#define BLEDIS_MANUFACTURER "IkigaiSense Technologies" +#define BLEDIS_MODEL "Vita nRF52840" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0093 -#define USB_DESC_CDC_ONLY_PID 0x0093 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0093 +#define USB_DESC_CDC_ONLY_PID 0x0093 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "IkigaiSense Vita nRF52840" -#define UF2_VOLUME_LABEL "ISVITABoot" -#define UF2_BOARD_ID "nRF52840-isVITA-revA" -#define UF2_INDEX_URL "https://github.com/ikigaisense/vita" +#define UF2_PRODUCT_NAME "IkigaiSense Vita nRF52840" +#define UF2_VOLUME_LABEL "ISVITABoot" +#define UF2_BOARD_ID "nRF52840-isVITA-revA" +#define UF2_INDEX_URL "https://github.com/ikigaisense/vita" #endif // _IKIGAISENSE_VITA_NRF52840_H diff --git a/src/boards/itsybitsy_nrf52840_express/board.h b/src/boards/itsybitsy_nrf52840_express/board.h index efa92ac8..563d5d59 100644 --- a/src/boards/itsybitsy_nrf52840_express/board.h +++ b/src/boards/itsybitsy_nrf52840_express/board.h @@ -25,17 +25,15 @@ #ifndef _ITSYBITSY_NRF52840_H #define _ITSYBITSY_NRF52840_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ #define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 6) +#define LED_PRIMARY_PIN PINNUM(0, 6) #define LED_STATE_ON 1 -#define LED_APA102_CLK _PINNUM(1, 9) -#define LED_APA102_DATA _PINNUM(0, 8) +#define LED_APA102_CLK PINNUM(1, 9) +#define LED_APA102_DATA PINNUM(0, 8) #define LED_APA102_BRIGHTNESS 0x1 #define APA102_NUMBER 1 @@ -45,8 +43,8 @@ /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 29) // user switch -#define BUTTON_2 _PINNUM(1, 02) // D2 breakout +#define BUTTON_DFU PINNUM(0, 29) // user switch +#define BUTTON_DFU_OTA PINNUM(1, 02) // D2 breakout #define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ diff --git a/src/boards/ledglasses_nrf52840/board.h b/src/boards/ledglasses_nrf52840/board.h index e2ea2f01..714fe94a 100644 --- a/src/boards/ledglasses_nrf52840/board.h +++ b/src/boards/ledglasses_nrf52840/board.h @@ -25,43 +25,41 @@ #ifndef _LEDGLASSES_NRF52840_H #define _LEDGLASSES_NRF52840_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 31) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 31) +#define LED_STATE_ON 1 -#define LED_NEOPIXEL _PINNUM(1, 15) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 +#define LED_NEOPIXEL PINNUM(1, 15) +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 30) // user switch -#define BUTTON_2 _PINNUM(0, 28) // NC -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 30) // user switch +#define BUTTON_DFU_OTA PINNUM(0, 28) // NC +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "LED Glasses Driver nRF52840" +#define BLEDIS_MANUFACTURER "Adafruit Industries" +#define BLEDIS_MODEL "LED Glasses Driver nRF52840" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x010D -#define USB_DESC_CDC_ONLY_PID 0x010E +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x010D +#define USB_DESC_CDC_ONLY_PID 0x010E //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit LED Glasses Driver nRF52840" -#define UF2_VOLUME_LABEL "GLASSESBOOT" -#define UF2_BOARD_ID "nRF52840-LedGlasses-revA" -#define UF2_INDEX_URL "https://www.adafruit.com/product/5217" +#define UF2_PRODUCT_NAME "Adafruit LED Glasses Driver nRF52840" +#define UF2_VOLUME_LABEL "GLASSESBOOT" +#define UF2_BOARD_ID "nRF52840-LedGlasses-revA" +#define UF2_INDEX_URL "https://www.adafruit.com/product/5217" #endif // _LEDGLASSES_NRF52840_H diff --git a/src/boards/m60_keyboard/board.h b/src/boards/m60_keyboard/board.h index 20822b5d..057f1e35 100644 --- a/src/boards/m60_keyboard/board.h +++ b/src/boards/m60_keyboard/board.h @@ -25,51 +25,49 @@ #ifndef _MAKERDIARY_M60_KEYBOARD_H_ #define _MAKERDIARY_M60_KEYBOARD_H_ -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 30) // Red -#define LED_STATE_ON 0 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 30) // Red +#define LED_STATE_ON 0 -#define LED_RGB_RED_PIN _PINNUM(0, 30) -#define LED_RGB_GREEN_PIN _PINNUM(0, 29) -#define LED_RGB_BLUE_PIN _PINNUM(0, 31) -#define BOARD_RGB_BRIGHTNESS 0x404040 +#define LED_RGB_RED_PIN PINNUM(0, 30) +#define LED_RGB_GREEN_PIN PINNUM(0, 29) +#define LED_RGB_BLUE_PIN PINNUM(0, 31) +#define BOARD_RGB_BRIGHTNESS 0x404040 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 27) -#define BUTTON_2 _PINNUM(0, 19) // ESC -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 27) +#define BUTTON_DFU_OTA PINNUM(0, 19) // ESC +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP /*------------------------------------------------------------------*/ /* On board regulator control *------------------------------------------------------------------*/ -#define LDO_CONTROL_PIN _PINNUM(0, 28) // Enables external pwr +#define LDO_CONTROL_PIN PINNUM(0, 28) // Enables external pwr //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Makerdiary" -#define BLEDIS_MODEL "M60 Mechanical Keyboard" +#define BLEDIS_MANUFACTURER "Makerdiary" +#define BLEDIS_MODEL "M60 Mechanical Keyboard" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x2886 -#define USB_DESC_UF2_PID 0xF00F -#define USB_DESC_CDC_ONLY_PID 0xF00F +#define USB_DESC_VID 0x2886 +#define USB_DESC_UF2_PID 0xF00F +#define USB_DESC_CDC_ONLY_PID 0xF00F //--------------------------------------------------------------------+ // UF2 //--------------------------------------------------------------------+ -#define UF2_PRODUCT_NAME "MakerDiary M60 Mechanical Keyboard" -#define UF2_VOLUME_LABEL "M60KEYBOARD" -#define UF2_BOARD_ID "M60KEYBOARD" -#define UF2_INDEX_URL "https://wiki.makerdiary.com/m60/" +#define UF2_PRODUCT_NAME "MakerDiary M60 Mechanical Keyboard" +#define UF2_VOLUME_LABEL "M60KEYBOARD" +#define UF2_BOARD_ID "M60KEYBOARD" +#define UF2_INDEX_URL "https://wiki.makerdiary.com/m60/" #endif /* _MAKERDIARY_M60_KEYBOARD_H_ */ diff --git a/src/boards/m60_keyboard/pinconfig.c b/src/boards/m60_keyboard/pinconfig.c index 1df7a6f8..d831386e 100644 --- a/src/boards/m60_keyboard/pinconfig.c +++ b/src/boards/m60_keyboard/pinconfig.c @@ -2,56 +2,60 @@ #include "board.h" #include "uf2/configkeys.h" -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ +__attribute__((used, section(".bootloaderConfig"))) const uint32_t bootloaderConfig[] = { /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 + CFG_MAGIC0, + CFG_MAGIC1, // magic + 5, + 100, // used entries, total entries + + 204, + 0x100000, // FLASH_BYTES = 0x100000 + 205, + 0x40000, // RAM_BYTES = 0x40000 + 208, + (USB_DESC_VID << 16) | + USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 + 209, + 0xada52840, // UF2_FAMILY = 0xada52840 + 210, + 0x20, // PINS_PORT_SIZE = PA_32 + + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 /* CF2 END */ }; -static void power_on(void) -{ +static void power_on(void) { // Turn on the regulator nrf_gpio_cfg_output(LDO_CONTROL_PIN); nrf_gpio_pin_write(LDO_CONTROL_PIN, 1); } -void board_init2(void) -{ +void board_init2(void) { power_on(); - // configure P0.05 for ESC/BUTTON_2 + // configure P0.05 for ESC/BUTTON_DFU_OTA // P0.05 --- |<- --- / --- P0.19 // diode sw(esc) // mode: output, push-pull, low - nrf_gpio_cfg( - _PINNUM(0, 5), - NRF_GPIO_PIN_DIR_OUTPUT, - NRF_GPIO_PIN_INPUT_DISCONNECT, - NRF_GPIO_PIN_NOPULL, - NRF_GPIO_PIN_H0D1, - NRF_GPIO_PIN_NOSENSE - ); - nrf_gpio_pin_write(_PINNUM(0, 5), 0); + nrf_gpio_cfg(PINNUM(0, 5), NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, + NRF_GPIO_PIN_H0D1, NRF_GPIO_PIN_NOSENSE); + nrf_gpio_pin_write(PINNUM(0, 5), 0); // Wait the buttons stable. // This is mandatory, or the keyboard will enter bootloader whenever - // booted by pressing the button at back (same with BUTTON_1) + // booted by pressing the button at back (same with BUTTON_DFU) NRFX_DELAY_MS(300); } -void board_teardown2(void) -{ +void board_teardown2(void) { // re-enable the battery power_on(); -} \ No newline at end of file +} diff --git a/src/boards/mbn52832dk/board.h b/src/boards/mbn52832dk/board.h index b731d681..0e38c192 100644 --- a/src/boards/mbn52832dk/board.h +++ b/src/boards/mbn52832dk/board.h @@ -28,26 +28,26 @@ /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 17 // LED1 -#define LED_SECONDARY_PIN 18 // LED2 -#define LED_STATE_ON 1 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN 17 // LED1 +#define LED_SECONDARY_PIN 18 // LED2 +#define LED_STATE_ON 1 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 13 -#define BUTTON_2 14 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU 13 +#define BUTTON_DFU_OTA 14 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP /*------------------------------------------------------------------*/ /* UART (only used by nRF52832) *------------------------------------------------------------------*/ -#define RX_PIN_NUMBER 8 -#define TX_PIN_NUMBER 6 -#define CTS_PIN_NUMBER 0 -#define RTS_PIN_NUMBER 0 -#define HWFC false +#define RX_PIN_NUMBER 8 +#define TX_PIN_NUMBER 6 +#define CTS_PIN_NUMBER 0 +#define RTS_PIN_NUMBER 0 +#define HWFC false //--------------------------------------------------------------------+ // BLE OTA diff --git a/src/boards/mdk_nrf52840_dongle/board.h b/src/boards/mdk_nrf52840_dongle/board.h index 066c66b2..ab5d0e14 100644 --- a/src/boards/mdk_nrf52840_dongle/board.h +++ b/src/boards/mdk_nrf52840_dongle/board.h @@ -25,47 +25,45 @@ #ifndef _MDK_NRF52840_DONGLE_H_ #define _MDK_NRF52840_DONGLE_H_ -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 // TODO(gpshead): support 0. -#define LED_PRIMARY_PIN _PINNUM(0, 23) // Red -#define LED_SECONDARY_PIN _PINNUM(0, 24) // Blue -#define LED_STATE_ON 0 +#define LEDS_NUMBER 2 // TODO(gpshead): support 0. +#define LED_PRIMARY_PIN PINNUM(0, 23) // Red +#define LED_SECONDARY_PIN PINNUM(0, 24) // Blue +#define LED_STATE_ON 0 -//#define LED_RGB_RED_PIN _PINNUM(0, 23) -//#define LED_RGB_GREEN_PIN _PINNUM(0, 22) -//#define LED_RGB_BLUE_PIN _PINNUM(0, 24) -#define BOARD_RGB_BRIGHTNESS 0x404040 +// #define LED_RGB_RED_PIN PINNUM(0, 23) +// #define LED_RGB_GREEN_PIN PINNUM(0, 22) +// #define LED_RGB_BLUE_PIN PINNUM(0, 24) +#define BOARD_RGB_BRIGHTNESS 0x404040 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ // TODO(gpshead): simplify, have code support 0. double reset only. -#define BUTTON_1 _PINNUM(0, 18) // unusable: RESET -#define BUTTON_2 _PINNUM(0, 19) // no connection -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 18) // unusable: RESET +#define BUTTON_DFU_OTA PINNUM(0, 19) // no connection +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "MakerDiary" -#define BLEDIS_MODEL "nRF52840 Micro Dev Kit USB Dongle" +#define BLEDIS_MANUFACTURER "MakerDiary" +#define BLEDIS_MODEL "nRF52840 Micro Dev Kit USB Dongle" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ // Shared VID/PID with Feather nRF52840, will be disabled for building in the future -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0029 -#define USB_DESC_CDC_ONLY_PID 0x0029 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0029 +#define USB_DESC_CDC_ONLY_PID 0x0029 -#define UF2_PRODUCT_NAME "MDK nRF52840 USB Dongle" -#define UF2_VOLUME_LABEL "MDK840DONGL" -#define UF2_BOARD_ID "nRF52840-Dongle-v1" -#define UF2_INDEX_URL "https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/" +#define UF2_PRODUCT_NAME "MDK nRF52840 USB Dongle" +#define UF2_VOLUME_LABEL "MDK840DONGL" +#define UF2_BOARD_ID "nRF52840-Dongle-v1" +#define UF2_INDEX_URL "https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/" #endif /* _MDK_NRF52840_DONGLE_H_ */ diff --git a/src/boards/metro_nrf52840_express/board.h b/src/boards/metro_nrf52840_express/board.h index 53519ed8..0c058384 100644 --- a/src/boards/metro_nrf52840_express/board.h +++ b/src/boards/metro_nrf52840_express/board.h @@ -25,45 +25,43 @@ #ifndef _METRO_NRF52840_H_ #define _METRO_NRF52840_H_ -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN _PINNUM(1, 13) -#define LED_SECONDARY_PIN _PINNUM(1, 15) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN PINNUM(1, 13) +#define LED_SECONDARY_PIN PINNUM(1, 15) +#define LED_STATE_ON 1 -#define LED_NEOPIXEL _PINNUM(0, 13) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 +#define LED_NEOPIXEL PINNUM(0, 13) +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(1, 0) -#define BUTTON_2 _PINNUM(2, 2) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(1, 0) +#define BUTTON_DFU_OTA PINNUM(2, 2) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Metro nRF52840 Express" +#define BLEDIS_MANUFACTURER "Adafruit Industries" +#define BLEDIS_MODEL "Metro nRF52840 Express" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x003F -#define USB_DESC_CDC_ONLY_PID 0x0040 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x003F +#define USB_DESC_CDC_ONLY_PID 0x0040 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit Metro nRF52840 Express" -#define UF2_VOLUME_LABEL "METR840BOOT" -#define UF2_BOARD_ID "nRF52840-Metro-revA" +#define UF2_PRODUCT_NAME "Adafruit Metro nRF52840 Express" +#define UF2_VOLUME_LABEL "METR840BOOT" +#define UF2_BOARD_ID "nRF52840-Metro-revA" -#define UF2_INDEX_URL "https://www.adafruit.com/" +#define UF2_INDEX_URL "https://www.adafruit.com/" #endif /* _METRO_NRF52840_H_ */ diff --git a/src/boards/mikoto/board.h b/src/boards/mikoto/board.h index f46ef525..9882893d 100644 --- a/src/boards/mikoto/board.h +++ b/src/boards/mikoto/board.h @@ -25,40 +25,38 @@ #ifndef _MIKOTO_H #define _MIKOTO_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - #define UICR_REGOUT0_VALUE UICR_REGOUT0_VOUT_3V3 /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 15) // Blue -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 15) // Blue +#define LED_STATE_ON 1 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 18) // unusable: RESET -#define BUTTON_2 _PINNUM(0, 19) // no connection -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 18) // unusable: RESET +#define BUTTON_DFU_OTA PINNUM(0, 19) // no connection +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "zhiayang" -#define BLEDIS_MODEL "mikoto" +#define BLEDIS_MANUFACTURER "zhiayang" +#define BLEDIS_MODEL "mikoto" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1209 -#define USB_DESC_UF2_PID 0x7A01 -#define USB_DESC_CDC_ONLY_PID 0x7A01 - -#define UF2_PRODUCT_NAME "Mikoto nRF52840" -#define UF2_VOLUME_LABEL "MIKOTO-BOOT" -#define UF2_BOARD_ID "nRF52840-mikoto-rev520" -#define UF2_INDEX_URL "https://github.com/zhiayang/mikoto" +#define USB_DESC_VID 0x1209 +#define USB_DESC_UF2_PID 0x7A01 +#define USB_DESC_CDC_ONLY_PID 0x7A01 + +#define UF2_PRODUCT_NAME "Mikoto nRF52840" +#define UF2_VOLUME_LABEL "MIKOTO-BOOT" +#define UF2_BOARD_ID "nRF52840-mikoto-rev520" +#define UF2_INDEX_URL "https://github.com/zhiayang/mikoto" #endif // _MIKOTO_H diff --git a/src/boards/nRF52840_connect_kit/board.h b/src/boards/nRF52840_connect_kit/board.h index 2cdaa240..9bb02ab9 100644 --- a/src/boards/nRF52840_connect_kit/board.h +++ b/src/boards/nRF52840_connect_kit/board.h @@ -25,46 +25,44 @@ #ifndef _MAKERDIARY_NRF82540_CONNECT_KIT_H_ #define _MAKERDIARY_NRF82540_CONNECT_KIT_H_ -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(1, 15) // Green -#define LED_STATE_ON 0 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(1, 15) // Green +#define LED_STATE_ON 0 -#define LED_RGB_RED_PIN _PINNUM(1, 10) -#define LED_RGB_GREEN_PIN _PINNUM(1, 11) -#define LED_RGB_BLUE_PIN _PINNUM(1, 12) -#define BOARD_RGB_BRIGHTNESS 0x404040 +#define LED_RGB_RED_PIN PINNUM(1, 10) +#define LED_RGB_GREEN_PIN PINNUM(1, 11) +#define LED_RGB_BLUE_PIN PINNUM(1, 12) +#define BOARD_RGB_BRIGHTNESS 0x404040 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(1, 0) // P1.00: User Button -#define BUTTON_2 _PINNUM(1, 7) // P1.07: NC -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(1, 0) // P1.00: User Button +#define BUTTON_DFU_OTA PINNUM(1, 7) // P1.07: NC +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Makerdiary" -#define BLEDIS_MODEL "nRF52840 Connect Kit" +#define BLEDIS_MANUFACTURER "Makerdiary" +#define BLEDIS_MODEL "nRF52840 Connect Kit" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x2886 -#define USB_DESC_UF2_PID 0xF00F -#define USB_DESC_CDC_ONLY_PID 0xF00F +#define USB_DESC_VID 0x2886 +#define USB_DESC_UF2_PID 0xF00F +#define USB_DESC_CDC_ONLY_PID 0xF00F //--------------------------------------------------------------------+ // UF2 //--------------------------------------------------------------------+ -#define UF2_PRODUCT_NAME "nRF52840 Connect Kit" -#define UF2_VOLUME_LABEL "CONNECTKIT" -#define UF2_BOARD_ID "nRF52840-Connect-Kit" -#define UF2_INDEX_URL "https://wiki.makerdiary.com/nrf52840-connectkit/" +#define UF2_PRODUCT_NAME "nRF52840 Connect Kit" +#define UF2_VOLUME_LABEL "CONNECTKIT" +#define UF2_BOARD_ID "nRF52840-Connect-Kit" +#define UF2_INDEX_URL "https://wiki.makerdiary.com/nrf52840-connectkit/" #endif /* _MAKERDIARY_NRF82540_CONNECT_KIT_H_ */ diff --git a/src/boards/nice_nano/board.h b/src/boards/nice_nano/board.h index c0825182..55109c0e 100644 --- a/src/boards/nice_nano/board.h +++ b/src/boards/nice_nano/board.h @@ -25,40 +25,38 @@ #ifndef _NICENANO_H #define _NICENANO_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - #define UICR_REGOUT0_VALUE UICR_REGOUT0_VOUT_3V3 /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 15) // Blue -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 15) // Blue +#define LED_STATE_ON 1 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 18) // unusable: RESET -#define BUTTON_2 _PINNUM(0, 19) // no connection -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 18) // unusable: RESET +#define BUTTON_DFU_OTA PINNUM(0, 19) // no connection +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Nice Keyboards" -#define BLEDIS_MODEL "nice!nano" +#define BLEDIS_MANUFACTURER "Nice Keyboards" +#define BLEDIS_MODEL "nice!nano" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00B3 -#define USB_DESC_CDC_ONLY_PID 0x00B3 - -#define UF2_PRODUCT_NAME "nice!nano" -#define UF2_VOLUME_LABEL "NICENANO" -#define UF2_BOARD_ID "nRF52840-nicenano" -#define UF2_INDEX_URL "https://nicekeyboards.com/docs/nice-nano" +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x00B3 +#define USB_DESC_CDC_ONLY_PID 0x00B3 + +#define UF2_PRODUCT_NAME "nice!nano" +#define UF2_VOLUME_LABEL "NICENANO" +#define UF2_BOARD_ID "nRF52840-nicenano" +#define UF2_INDEX_URL "https://nicekeyboards.com/docs/nice-nano" #endif // _NICENANO_H diff --git a/src/boards/nrf52840_bboard/board.h b/src/boards/nrf52840_bboard/board.h index 43844751..2c1977ed 100644 --- a/src/boards/nrf52840_bboard/board.h +++ b/src/boards/nrf52840_bboard/board.h @@ -25,14 +25,12 @@ #ifndef _NRF52840_BBOARD_H #define _NRF52840_BBOARD_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 7) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 7) +#define LED_STATE_ON 1 /*------------------------------------------------------------------*/ /* BUTTON @@ -41,27 +39,27 @@ // App can trigger OTA bootloader by writing DFU_MAGIC_OTA_APPJUM to // GPREGRET register if softdevice is not initialized; or by // writing DFU_MAGIC_OTA_RESET in case softdevice is initialized. -#define BUTTON_1 18 // RESET also by default -#define BUTTON_2 1 // P0.1 not exposed anywhere, FRST n/a -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU 18 // RESET also by default +#define BUTTON_DFU_OTA 1 // P0.1 not exposed anywhere, FRST n/a +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "ddB0515" -#define BLEDIS_MODEL "nRF52840 BBoard" +#define BLEDIS_MANUFACTURER "ddB0515" +#define BLEDIS_MODEL "nRF52840 BBoard" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1d50 -#define USB_DESC_UF2_PID 0x6157 -#define USB_DESC_CDC_ONLY_PID 0x6157 +#define USB_DESC_VID 0x1d50 +#define USB_DESC_UF2_PID 0x6157 +#define USB_DESC_CDC_ONLY_PID 0x6157 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "nRF52840 BBoard" -#define UF2_VOLUME_LABEL "BBOARDBOOT" -#define UF2_BOARD_ID "nRF52840-BBoard-rev2" -#define UF2_INDEX_URL "https://github.com/ddB0515/nRF52840-BBoard" +#define UF2_PRODUCT_NAME "nRF52840 BBoard" +#define UF2_VOLUME_LABEL "BBOARDBOOT" +#define UF2_BOARD_ID "nRF52840-BBoard-rev2" +#define UF2_INDEX_URL "https://github.com/ddB0515/nRF52840-BBoard" #endif // _NRF52840_BBOARD_H diff --git a/src/boards/nrf52840_m2/board.h b/src/boards/nrf52840_m2/board.h index 27fc7605..c093f5dc 100644 --- a/src/boards/nrf52840_m2/board.h +++ b/src/boards/nrf52840_m2/board.h @@ -25,47 +25,45 @@ #ifndef _MAKERDIARY_NRF52840_M2_H_ #define _MAKERDIARY_NRF52840_M2_H_ -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 30) // Red -#define LED_DOCK_GREEN_PIN _PINNUM(1, 7) // Only available on the M.2 Dock -#define LED_STATE_ON 0 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 30) // Red +#define LED_DOCK_GREEN_PIN PINNUM(1, 7) // Only available on the M.2 Dock +#define LED_STATE_ON 0 -#define LED_RGB_RED_PIN _PINNUM(0, 30) -#define LED_RGB_GREEN_PIN _PINNUM(0, 29) -#define LED_RGB_BLUE_PIN _PINNUM(0, 31) -#define BOARD_RGB_BRIGHTNESS 0x404040 +#define LED_RGB_RED_PIN PINNUM(0, 30) +#define LED_RGB_GREEN_PIN PINNUM(0, 29) +#define LED_RGB_BLUE_PIN PINNUM(0, 31) +#define BOARD_RGB_BRIGHTNESS 0x404040 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 19) -#define BUTTON_2 _PINNUM(1, 7) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 19) +#define BUTTON_DFU_OTA PINNUM(1, 7) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Makerdiary" -#define BLEDIS_MODEL "nRF52840 M.2" +#define BLEDIS_MANUFACTURER "Makerdiary" +#define BLEDIS_MODEL "nRF52840 M.2" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x2886 -#define USB_DESC_UF2_PID 0xF00F -#define USB_DESC_CDC_ONLY_PID 0xF00F +#define USB_DESC_VID 0x2886 +#define USB_DESC_UF2_PID 0xF00F +#define USB_DESC_CDC_ONLY_PID 0xF00F //--------------------------------------------------------------------+ // UF2 //--------------------------------------------------------------------+ -#define UF2_PRODUCT_NAME "MakerDiary nRF52840 M.2 Module" -#define UF2_VOLUME_LABEL "nRF52840M2" -#define UF2_BOARD_ID "nRF52840M2" -#define UF2_INDEX_URL "https://wiki.makerdiary.com/nrf52840-m2" +#define UF2_PRODUCT_NAME "MakerDiary nRF52840 M.2 Module" +#define UF2_VOLUME_LABEL "nRF52840M2" +#define UF2_BOARD_ID "nRF52840M2" +#define UF2_INDEX_URL "https://wiki.makerdiary.com/nrf52840-m2" #endif /* _MAKERDIARY_NRF52840_M2_H_ */ diff --git a/src/boards/nrfmicro/board.h b/src/boards/nrfmicro/board.h index f3bdb4e9..78507e30 100644 --- a/src/boards/nrfmicro/board.h +++ b/src/boards/nrfmicro/board.h @@ -25,41 +25,39 @@ #ifndef _NRFMICRO_NRF52840 #define _NRFMICRO_NRF52840 -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(1, 10) -#define LED_STATE_ON 1 // State when LED is lit (active HIGH) +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(1, 10) +#define LED_STATE_ON 1 // State when LED is lit (active HIGH) -#define NEOPIXELS_NUMBER 0 +#define NEOPIXELS_NUMBER 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(1, 2) // DFU -#define BUTTON_2 _PINNUM(0, 12) // FRST -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(1, 2) // DFU +#define BUTTON_DFU_OTA PINNUM(0, 12) // FRST +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "nRFMicro" -#define BLEDIS_MODEL "nRFMicro nRF52840" +#define BLEDIS_MANUFACTURER "nRFMicro" +#define BLEDIS_MODEL "nRFMicro nRF52840" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1209 -#define USB_DESC_UF2_PID 0x5284 -#define USB_DESC_CDC_ONLY_PID 0x5284 +#define USB_DESC_VID 0x1209 +#define USB_DESC_UF2_PID 0x5284 +#define USB_DESC_CDC_ONLY_PID 0x5284 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "nRFMicro" -#define UF2_VOLUME_LABEL "NRFMICRO" -#define UF2_BOARD_ID "nRF52840-nRFMicro-v0" -#define UF2_INDEX_URL "https://github.com/joric/nrfmicro/wiki" +#define UF2_PRODUCT_NAME "nRFMicro" +#define UF2_VOLUME_LABEL "NRFMICRO" +#define UF2_BOARD_ID "nRF52840-nRFMicro-v0" +#define UF2_INDEX_URL "https://github.com/joric/nrfmicro/wiki" #endif // _NRFMICRO_NRF52840 diff --git a/src/boards/ohs2020_badge/board.h b/src/boards/ohs2020_badge/board.h index 0172e6db..a6cc9430 100644 --- a/src/boards/ohs2020_badge/board.h +++ b/src/boards/ohs2020_badge/board.h @@ -26,39 +26,37 @@ #ifndef _OHS2020_BADGE_H #define _OHS2020_BADGE_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 04) // fake LED since we have none -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 04) // fake LED since we have none +#define LED_STATE_ON 1 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 29) // SW2 -#define BUTTON_2 _PINNUM(0, 03) // SW1 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 29) // SW2 +#define BUTTON_DFU_OTA PINNUM(0, 03) // SW1 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "OSHWA" -#define BLEDIS_MODEL "OHS2020 Badge" +#define BLEDIS_MANUFACTURER "OSHWA" +#define BLEDIS_MODEL "OHS2020 Badge" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x007F -#define USB_DESC_CDC_ONLY_PID 0x007F +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x007F +#define USB_DESC_CDC_ONLY_PID 0x007F //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "OHS2020 Badge" -#define UF2_VOLUME_LABEL "BADGEBOOT" -#define UF2_BOARD_ID "OHS2020-Badge-revA" -#define UF2_INDEX_URL "https://2020.oshwa.org/" +#define UF2_PRODUCT_NAME "OHS2020 Badge" +#define UF2_VOLUME_LABEL "BADGEBOOT" +#define UF2_BOARD_ID "OHS2020-Badge-revA" +#define UF2_INDEX_URL "https://2020.oshwa.org/" #endif // _OHS2020_BADGE_H diff --git a/src/boards/omnimo_nrf52840/board.h b/src/boards/omnimo_nrf52840/board.h index f591a557..c26c811a 100644 --- a/src/boards/omnimo_nrf52840/board.h +++ b/src/boards/omnimo_nrf52840/board.h @@ -25,47 +25,45 @@ #ifndef _OMNIMO_NRF52840_H #define _OMNIMO_NRF52840_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN _PINNUM(1, 15) -#define LED_SECONDARY_PIN _PINNUM(1, 10) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN PINNUM(1, 15) +#define LED_SECONDARY_PIN PINNUM(1, 10) +#define LED_STATE_ON 1 -#define LED_NEOPIXEL _PINNUM(0, 16) +#define LED_NEOPIXEL PINNUM(0, 16) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 +#define NEOPIXELS_NUMBER 1 +#define BOARD_RGB_BRIGHTNESS 0x040404 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(1, 02) -#define BUTTON_2 _PINNUM(1, 07) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(1, 02) +#define BUTTON_DFU_OTA PINNUM(1, 07) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "eAFAQ" -#define BLEDIS_MODEL "OMNIMO nRF52840" +#define BLEDIS_MANUFACTURER "eAFAQ" +#define BLEDIS_MODEL "OMNIMO nRF52840" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -//Shared VID/PID with pca10056 -#define USB_DESC_VID 0x1209 -#define USB_DESC_UF2_PID 0xCECE -#define USB_DESC_CDC_ONLY_PID 0xCECE +// Shared VID/PID with pca10056 +#define USB_DESC_VID 0x1209 +#define USB_DESC_UF2_PID 0xCECE +#define USB_DESC_CDC_ONLY_PID 0xCECE //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Omnimo nRF52840" -#define UF2_VOLUME_LABEL "OMNIn52BOOT" -#define UF2_BOARD_ID "nRF52840-Omnimo" -#define UF2_INDEX_URL "https://www.crowdsupply.com/eafaq/omnimo-nrf52840" +#define UF2_PRODUCT_NAME "Omnimo nRF52840" +#define UF2_VOLUME_LABEL "OMNIn52BOOT" +#define UF2_BOARD_ID "nRF52840-Omnimo" +#define UF2_INDEX_URL "https://www.crowdsupply.com/eafaq/omnimo-nrf52840" #endif // _OMNIMO_NRF52840_H diff --git a/src/boards/particle_argon/board.h b/src/boards/particle_argon/board.h index c8ce5d6f..b210606c 100644 --- a/src/boards/particle_argon/board.h +++ b/src/boards/particle_argon/board.h @@ -25,25 +25,23 @@ #ifndef _PARTICLE_ARGON_H #define _PARTICLE_ARGON_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ #define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(1, 12) +#define LED_PRIMARY_PIN PINNUM(1, 12) #define LED_STATE_ON 1 -#define LED_RGB_RED_PIN _PINNUM(0, 13) -#define LED_RGB_GREEN_PIN _PINNUM(0, 14) -#define LED_RGB_BLUE_PIN _PINNUM(0, 15) +#define LED_RGB_RED_PIN PINNUM(0, 13) +#define LED_RGB_GREEN_PIN PINNUM(0, 14) +#define LED_RGB_BLUE_PIN PINNUM(0, 15) #define BOARD_RGB_BRIGHTNESS 0x202020 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_DFU _PINNUM(0, 11) -#define BUTTON_FRESET _PINNUM(0, 03) // A0 +#define BUTTON_DFU PINNUM(0, 11) +#define BUTTON_DFU_OTA PINNUM(0, 03) // A0 #define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ diff --git a/src/boards/particle_boron/board.h b/src/boards/particle_boron/board.h index a4fa749b..49cc6d50 100644 --- a/src/boards/particle_boron/board.h +++ b/src/boards/particle_boron/board.h @@ -25,45 +25,43 @@ #ifndef _PARTICLE_BORON_H #define _PARTICLE_BORON_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(1, 12) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(1, 12) +#define LED_STATE_ON 1 -#define LED_RGB_RED_PIN _PINNUM(0, 13) -#define LED_RGB_GREEN_PIN _PINNUM(0, 14) -#define LED_RGB_BLUE_PIN _PINNUM(0, 15) +#define LED_RGB_RED_PIN PINNUM(0, 13) +#define LED_RGB_GREEN_PIN PINNUM(0, 14) +#define LED_RGB_BLUE_PIN PINNUM(0, 15) #define BOARD_RGB_BRIGHTNESS 0x202020 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_DFU _PINNUM(0, 11) -#define BUTTON_FRESET _PINNUM(0, 03) // A0 +#define BUTTON_DFU PINNUM(0, 11) +#define BUTTON_DFU_OTA PINNUM(0, 03) // A0 #define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Particle Industries" -#define BLEDIS_MODEL "Boron" +#define BLEDIS_MANUFACTURER "Particle Industries" +#define BLEDIS_MODEL "Boron" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ // Shared VID/PID with pca10056 -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00DA -#define USB_DESC_CDC_ONLY_PID 0x00DA +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x00DA +#define USB_DESC_CDC_ONLY_PID 0x00DA -#define UF2_PRODUCT_NAME "Particle Boron" -#define UF2_VOLUME_LABEL "BORONBOOT " -#define UF2_BOARD_ID "nRF52840-Boron-v1" -#define UF2_INDEX_URL "https://www.particle.io/mesh/" +#define UF2_PRODUCT_NAME "Particle Boron" +#define UF2_VOLUME_LABEL "BORONBOOT " +#define UF2_BOARD_ID "nRF52840-Boron-v1" +#define UF2_INDEX_URL "https://www.particle.io/mesh/" #endif // _PARTICLE_BORON_H diff --git a/src/boards/particle_xenon/board.h b/src/boards/particle_xenon/board.h index 01da00ae..5584bad4 100644 --- a/src/boards/particle_xenon/board.h +++ b/src/boards/particle_xenon/board.h @@ -25,45 +25,43 @@ #ifndef _PARTICLE_XENON_H #define _PARTICLE_XENON_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(1, 12) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(1, 12) +#define LED_STATE_ON 1 -#define LED_RGB_RED_PIN _PINNUM(0, 13) -#define LED_RGB_GREEN_PIN _PINNUM(0, 14) -#define LED_RGB_BLUE_PIN _PINNUM(0, 15) +#define LED_RGB_RED_PIN PINNUM(0, 13) +#define LED_RGB_GREEN_PIN PINNUM(0, 14) +#define LED_RGB_BLUE_PIN PINNUM(0, 15) #define BOARD_RGB_BRIGHTNESS 0x202020 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_DFU _PINNUM(0, 11) -#define BUTTON_FRESET _PINNUM(0, 03) // A0 +#define BUTTON_DFU PINNUM(0, 11) +#define BUTTON_DFU_OTA PINNUM(0, 03) // A0 #define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Particle Industries" -#define BLEDIS_MODEL "Xenon" +#define BLEDIS_MANUFACTURER "Particle Industries" +#define BLEDIS_MODEL "Xenon" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ // Shared VID/PID with pca10056 -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00DA -#define USB_DESC_CDC_ONLY_PID 0x00DA +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x00DA +#define USB_DESC_CDC_ONLY_PID 0x00DA -#define UF2_PRODUCT_NAME "Particle Xenon" -#define UF2_VOLUME_LABEL "XENONBOOT " -#define UF2_BOARD_ID "nRF52840-Xenon-v1" -#define UF2_INDEX_URL "https://www.particle.io/mesh/" +#define UF2_PRODUCT_NAME "Particle Xenon" +#define UF2_VOLUME_LABEL "XENONBOOT " +#define UF2_BOARD_ID "nRF52840-Xenon-v1" +#define UF2_INDEX_URL "https://www.particle.io/mesh/" #endif // _PARTICLE_XENON_H diff --git a/src/boards/pca10056/board.h b/src/boards/pca10056/board.h index 0fea16d1..08269bf6 100644 --- a/src/boards/pca10056/board.h +++ b/src/boards/pca10056/board.h @@ -28,35 +28,35 @@ /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 13 -#define LED_SECONDARY_PIN 14 -#define LED_STATE_ON 0 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN 13 +#define LED_SECONDARY_PIN 14 +#define LED_STATE_ON 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 11 -#define BUTTON_2 12 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU 11 +#define BUTTON_DFU_OTA 12 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Nordic" -#define BLEDIS_MODEL "PCA10056" +#define BLEDIS_MANUFACTURER "Nordic" +#define BLEDIS_MODEL "PCA10056" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ // Shared VID/PID with Feather nRF52840, will be disabled for building in the future -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00DA -#define USB_DESC_CDC_ONLY_PID 0x00DA +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x00DA +#define USB_DESC_CDC_ONLY_PID 0x00DA -#define UF2_PRODUCT_NAME "Nordic nRF52840 DK" -#define UF2_BOARD_ID "nRF52840-pca10056-v1" -#define UF2_INDEX_URL "https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK" +#define UF2_PRODUCT_NAME "Nordic nRF52840 DK" +#define UF2_BOARD_ID "nRF52840-pca10056-v1" +#define UF2_INDEX_URL "https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK" #endif // PCA10056_H diff --git a/src/boards/pca10059/board.h b/src/boards/pca10059/board.h index 47e6bc2c..88c2f41a 100644 --- a/src/boards/pca10059/board.h +++ b/src/boards/pca10059/board.h @@ -25,13 +25,11 @@ #ifndef PCA10059_H #define PCA10059_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ #define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 6 // Red +#define LED_PRIMARY_PIN 6 // Red #define LED_SECONDARY_PIN 12 // Blue #define LED_STATE_ON 0 @@ -39,27 +37,27 @@ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(1, 6) -#define BUTTON_2 _PINNUM(1, 10) +#define BUTTON_DFU PINNUM(1, 6) +#define BUTTON_DFU_OTA PINNUM(1, 10) #define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Nordic" -#define BLEDIS_MODEL "PCA10059" +#define BLEDIS_MANUFACTURER "Nordic" +#define BLEDIS_MODEL "PCA10059" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ // Shared VID/PID with Feather nRF52840, will be disabled for building in the future -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0029 -#define USB_DESC_CDC_ONLY_PID 0x0029 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0029 +#define USB_DESC_CDC_ONLY_PID 0x0029 -#define UF2_PRODUCT_NAME "Nordic nRF52840 Dongle" -#define UF2_BOARD_ID "nRF52840-pca10059-v1" -#define UF2_INDEX_URL "https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-Dongle" +#define UF2_PRODUCT_NAME "Nordic nRF52840 Dongle" +#define UF2_BOARD_ID "nRF52840-pca10059-v1" +#define UF2_INDEX_URL "https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-Dongle" #endif // PCA10059_H diff --git a/src/boards/pca10100/board.h b/src/boards/pca10100/board.h index 1df02003..5be1366e 100644 --- a/src/boards/pca10100/board.h +++ b/src/boards/pca10100/board.h @@ -28,34 +28,34 @@ /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 13 -#define LED_SECONDARY_PIN 14 -#define LED_STATE_ON 0 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN 13 +#define LED_SECONDARY_PIN 14 +#define LED_STATE_ON 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 11 -#define BUTTON_2 12 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU 11 +#define BUTTON_DFU_OTA 12 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Nordic" -#define BLEDIS_MODEL "PCA10100" +#define BLEDIS_MANUFACTURER "Nordic" +#define BLEDIS_MODEL "PCA10100" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00D8 -#define USB_DESC_CDC_ONLY_PID 0x00D8 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x00D8 +#define USB_DESC_CDC_ONLY_PID 0x00D8 -#define UF2_PRODUCT_NAME "Nordic nRF52833 DK" -#define UF2_VOLUME_LABEL "NRF833BOOT" -#define UF2_BOARD_ID "nRF52833-pca10100-v1" -#define UF2_INDEX_URL "https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52833-DK" +#define UF2_PRODUCT_NAME "Nordic nRF52833 DK" +#define UF2_VOLUME_LABEL "NRF833BOOT" +#define UF2_BOARD_ID "nRF52833-pca10100-v1" +#define UF2_INDEX_URL "https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52833-DK" #endif // PCA10100_H diff --git a/src/boards/pillbug/board.h b/src/boards/pillbug/board.h index 8c50985c..1d442c5c 100644 --- a/src/boards/pillbug/board.h +++ b/src/boards/pillbug/board.h @@ -25,40 +25,38 @@ #ifndef _PillBug_H #define _PillBug_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - #define UICR_REGOUT0_VALUE UICR_REGOUT0_VOUT_3V3 /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 20) // Blue -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 20) // Blue +#define LED_STATE_ON 1 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 18) // unusable: RESET -#define BUTTON_2 _PINNUM(0, 21) // no connection -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 18) // unusable: RESET +#define BUTTON_DFU_OTA PINNUM(0, 21) // no connection +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "MechWild" -#define BLEDIS_MODEL "PillBug" +#define BLEDIS_MANUFACTURER "MechWild" +#define BLEDIS_MODEL "PillBug" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x16D0 -#define USB_DESC_UF2_PID 0x10ED -#define USB_DESC_CDC_ONLY_PID 0x10ED - -#define UF2_PRODUCT_NAME "PillBug" -#define UF2_VOLUME_LABEL "PillBug" -#define UF2_BOARD_ID "nRF52840-PillBug" -#define UF2_INDEX_URL "https://mechwild.com" +#define USB_DESC_VID 0x16D0 +#define USB_DESC_UF2_PID 0x10ED +#define USB_DESC_CDC_ONLY_PID 0x10ED + +#define UF2_PRODUCT_NAME "PillBug" +#define UF2_VOLUME_LABEL "PillBug" +#define UF2_BOARD_ID "nRF52840-PillBug" +#define UF2_INDEX_URL "https://mechwild.com" #endif // _PillBug_H diff --git a/src/boards/pitaya_go/board.h b/src/boards/pitaya_go/board.h index 8daf1f1d..fd0b9944 100644 --- a/src/boards/pitaya_go/board.h +++ b/src/boards/pitaya_go/board.h @@ -25,46 +25,44 @@ #ifndef _MAKERDIARY_PITAYA_GO_H_ #define _MAKERDIARY_PITAYA_GO_H_ -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(1, 10) // Red -#define LED_STATE_ON 0 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(1, 10) // Red +#define LED_STATE_ON 0 -#define LED_RGB_RED_PIN _PINNUM(1, 10) -#define LED_RGB_GREEN_PIN _PINNUM(1, 11) -#define LED_RGB_BLUE_PIN _PINNUM(1, 12) -#define BOARD_RGB_BRIGHTNESS 0x404040 +#define LED_RGB_RED_PIN PINNUM(1, 10) +#define LED_RGB_GREEN_PIN PINNUM(1, 11) +#define LED_RGB_BLUE_PIN PINNUM(1, 12) +#define BOARD_RGB_BRIGHTNESS 0x404040 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(1, 0) // P1.00: User Button -#define BUTTON_2 _PINNUM(1, 7) // P1.07: NC -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(1, 0) // P1.00: User Button +#define BUTTON_DFU_OTA PINNUM(1, 7) // P1.07: NC +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Makerdiary" -#define BLEDIS_MODEL "Pitaya Go" +#define BLEDIS_MANUFACTURER "Makerdiary" +#define BLEDIS_MODEL "Pitaya Go" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x2886 -#define USB_DESC_UF2_PID 0xF00E -#define USB_DESC_CDC_ONLY_PID 0xF00E +#define USB_DESC_VID 0x2886 +#define USB_DESC_UF2_PID 0xF00E +#define USB_DESC_CDC_ONLY_PID 0xF00E //--------------------------------------------------------------------+ // UF2 //--------------------------------------------------------------------+ -#define UF2_PRODUCT_NAME "Pitaya Go" -#define UF2_VOLUME_LABEL "PITAYAGO" -#define UF2_BOARD_ID "PITAYAGO" -#define UF2_INDEX_URL "https://wiki.makerdiary.com/pitaya-go" +#define UF2_PRODUCT_NAME "Pitaya Go" +#define UF2_VOLUME_LABEL "PITAYAGO" +#define UF2_BOARD_ID "PITAYAGO" +#define UF2_INDEX_URL "https://wiki.makerdiary.com/pitaya-go" #endif /* _MAKERDIARY_PITAYA_GO_H_ */ diff --git a/src/boards/raytac_mdbt50q_db_40/board.h b/src/boards/raytac_mdbt50q_db_40/board.h index 86763e38..838c90e7 100644 --- a/src/boards/raytac_mdbt50q_db_40/board.h +++ b/src/boards/raytac_mdbt50q_db_40/board.h @@ -25,18 +25,18 @@ #ifndef RAYTAC_MDBT50Q_DB_40_H #define RAYTAC_MDBT50Q_DB_40_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) +#define PINNUM(port, pin) ((port) * 32 + (pin)) /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ #define LEDS_NUMBER 3 /* This is the Green led marked D1 on the board */ -#define LED_PRIMARY_PIN _PINNUM(1, 13) +#define LED_PRIMARY_PIN PINNUM(1, 13) /* This is the Red led marked D2 on the board */ -#define LED_SECONDARY_PIN _PINNUM(1, 14) +#define LED_SECONDARY_PIN PINNUM(1, 14) /* This is the Blue led marked D3 on the board */ -#define LED_TERTIARY_PIN _PINNUM(1, 15) +#define LED_TERTIARY_PIN PINNUM(1, 15) /* There are one more unpopulated LEDs, but for the purposes of bootloader that * doesn't matter. */ #define LED_STATE_ON 0 @@ -45,10 +45,10 @@ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 11) -#define BUTTON_2 _PINNUM(0, 12) -#define BUTTON_3 _PINNUM(0, 24) -#define BUTTON_4 _PINNUM(0, 25) +#define BUTTON_DFU PINNUM(0, 11) +#define BUTTON_DFU_OTA PINNUM(0, 12) +#define BUTTON_3 PINNUM(0, 24) +#define BUTTON_4 PINNUM(0, 25) #define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ diff --git a/src/boards/raytac_mdbt50q_rx/board.h b/src/boards/raytac_mdbt50q_rx/board.h index ac3f771e..76a178bb 100644 --- a/src/boards/raytac_mdbt50q_rx/board.h +++ b/src/boards/raytac_mdbt50q_rx/board.h @@ -25,41 +25,39 @@ #ifndef RAYTAC_MDBT50Q_RX_H #define RAYTAC_MDBT50Q_RX_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(1, 13) -#define LED_STATE_ON 0 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(1, 13) +#define LED_STATE_ON 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 15) -#define BUTTON_2 _PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 15) +#define BUTTON_DFU_OTA PINNUM(0, 10) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Raytac" -#define BLEDIS_MODEL "MDBT50Q-RX" +#define BLEDIS_MANUFACTURER "Raytac" +#define BLEDIS_MODEL "MDBT50Q-RX" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x010B -#define USB_DESC_CDC_ONLY_PID 0x010B +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x010B +#define USB_DESC_CDC_ONLY_PID 0x010B //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Raytac MDBT50Q-RX" -#define UF2_VOLUME_LABEL "MDBT50QBOOT" -#define UF2_BOARD_ID "nRF52840-MDBT50Q_RX-verD" -#define UF2_INDEX_URL "https://www.adafruit.com/product/5199" +#define UF2_PRODUCT_NAME "Raytac MDBT50Q-RX" +#define UF2_VOLUME_LABEL "MDBT50QBOOT" +#define UF2_BOARD_ID "nRF52840-MDBT50Q_RX-verD" +#define UF2_INDEX_URL "https://www.adafruit.com/product/5199" #endif // RAYTAC_MDBT50Q_RX_H diff --git a/src/boards/sparkfun_nrf52840_micromod/board.h b/src/boards/sparkfun_nrf52840_micromod/board.h index c889354a..11692a42 100644 --- a/src/boards/sparkfun_nrf52840_micromod/board.h +++ b/src/boards/sparkfun_nrf52840_micromod/board.h @@ -26,40 +26,38 @@ #ifndef _SPARKFUN_NRF52840_MICROMOD_H_ #define _SPARKFUN_NRF52840_MICROMOD_H_ -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 13) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 13) +#define LED_STATE_ON 1 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 7) -#define BUTTON_2 _PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 7) +#define BUTTON_DFU_OTA PINNUM(0, 10) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "SparkFun" -#define BLEDIS_MODEL "MicroMod nRF52840" +#define BLEDIS_MANUFACTURER "SparkFun" +#define BLEDIS_MODEL "MicroMod nRF52840" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1B4F -#define USB_DESC_UF2_PID 0x0022 -#define USB_DESC_CDC_ONLY_PID 0x0023 +#define USB_DESC_VID 0x1B4F +#define USB_DESC_UF2_PID 0x0022 +#define USB_DESC_CDC_ONLY_PID 0x0023 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "SparkFun MicroMod nRF52840" -#define UF2_VOLUME_LABEL "SFMM852BOOT" -#define UF2_BOARD_ID "nRF52840-micromod-v10" +#define UF2_PRODUCT_NAME "SparkFun MicroMod nRF52840" +#define UF2_VOLUME_LABEL "SFMM852BOOT" +#define UF2_BOARD_ID "nRF52840-micromod-v10" -#define UF2_INDEX_URL "https://www.sparkfun.com/products/16984" +#define UF2_INDEX_URL "https://www.sparkfun.com/products/16984" #endif /* _SPARKFUN_NRF52840_MICROMOD_H_ */ diff --git a/src/boards/t1000_e/board.h b/src/boards/t1000_e/board.h index a8eae88e..58a52c9f 100644 --- a/src/boards/t1000_e/board.h +++ b/src/boards/t1000_e/board.h @@ -25,42 +25,40 @@ #ifndef _T1000_E_H #define _T1000_E_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 24) // Green -#define LED_STATE_ON 0 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 24) // Green +#define LED_STATE_ON 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 6) // Primary Button -#define BUTTON_2 _PINNUM(0, 18) // unusable: RESET -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 6) // Primary Button +#define BUTTON_DFU_OTA PINNUM(0, 18) // unusable: RESET +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Seeed Studio" -#define BLEDIS_MODEL "T1000-E" +#define BLEDIS_MANUFACTURER "Seeed Studio" +#define BLEDIS_MODEL "T1000-E" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x2886 -#define USB_DESC_UF2_PID 0x0057 -#define USB_DESC_CDC_ONLY_PID 0x0057 +#define USB_DESC_VID 0x2886 +#define USB_DESC_UF2_PID 0x0057 +#define USB_DESC_CDC_ONLY_PID 0x0057 //--------------------------------------------------------------------+ // UF2 //--------------------------------------------------------------------+ -#define UF2_PRODUCT_NAME "Seeed T1000-E for Meshtastic" -#define UF2_VOLUME_LABEL "T1000-E" -#define UF2_BOARD_ID "nRF52840-T1000-E-v1" -#define UF2_INDEX_URL "https://www.seeedstudio.com/SenseCAP-Card-Tracker-T1000-E-for-Meshtastic-p-5913.html" +#define UF2_PRODUCT_NAME "Seeed T1000-E for Meshtastic" +#define UF2_VOLUME_LABEL "T1000-E" +#define UF2_BOARD_ID "nRF52840-T1000-E-v1" +#define UF2_INDEX_URL "https://www.seeedstudio.com/SenseCAP-Card-Tracker-T1000-E-for-Meshtastic-p-5913.html" #endif /* _T1000_E_H */ diff --git a/src/boards/waveshare_nrf52840_eval/board.h b/src/boards/waveshare_nrf52840_eval/board.h index 81a5fc17..d66a7852 100644 --- a/src/boards/waveshare_nrf52840_eval/board.h +++ b/src/boards/waveshare_nrf52840_eval/board.h @@ -23,49 +23,47 @@ #ifndef _WAVESHARE_NRF52840_H #define _WAVESHARE_NRF52840_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 +#define LEDS_NUMBER 2 /* This is led marked LED1 on the board */ -#define LED_PRIMARY_PIN _PINNUM(0, 13) +#define LED_PRIMARY_PIN PINNUM(0, 13) /* This is led marked LED2 on the board */ -#define LED_SECONDARY_PIN _PINNUM(0, 14) +#define LED_SECONDARY_PIN PINNUM(0, 14) /* There are two more LEDs, but for the purposes of bootloader that * doesn't matter. */ -#define LED_STATE_ON 0 +#define LED_STATE_ON 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ /* This is the 'KEY1' in top left of the board under RESET key. * It is used to remain in bootloader mode after reset. */ -#define BUTTON_1 _PINNUM(0, 11) +#define BUTTON_DFU PINNUM(0, 11) /* Pin below is in the bottom left of PCB, and can be shorted to GND * with a regular 2.54mm jumper or tweezers. This pin is used to * clear user firmware and remain in bootloader mode. */ -#define BUTTON_2 _PINNUM(0, 29) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU_OTA PINNUM(0, 29) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Waveshare" -#define BLEDIS_MODEL "nRF52840 Eval" +#define BLEDIS_MANUFACTURER "Waveshare" +#define BLEDIS_MODEL "nRF52840 Eval" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0029 -#define USB_DESC_CDC_ONLY_PID 0x002A +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0029 +#define USB_DESC_CDC_ONLY_PID 0x002A //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Waveshare nRF52840 Eval" -#define UF2_VOLUME_LABEL "WS52840EVK" -#define UF2_BOARD_ID "WS-nRF52840-EVK" -#define UF2_INDEX_URL "https://www.waveshare.com/wiki/NRF52840_Eval_Kit" +#define UF2_PRODUCT_NAME "Waveshare nRF52840 Eval" +#define UF2_VOLUME_LABEL "WS52840EVK" +#define UF2_BOARD_ID "WS-nRF52840-EVK" +#define UF2_INDEX_URL "https://www.waveshare.com/wiki/NRF52840_Eval_Kit" #endif // _WAVESHARE_NRF52840_H diff --git a/src/boards/xiao_nrf52840_ble/board.h b/src/boards/xiao_nrf52840_ble/board.h index 1543189b..eacabd20 100644 --- a/src/boards/xiao_nrf52840_ble/board.h +++ b/src/boards/xiao_nrf52840_ble/board.h @@ -25,43 +25,41 @@ #ifndef _XIAO_BLE #define _XIAO_BLE -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ // The board has 3 leds, but changing the number here causes OTA issues. -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 26) -#define LED_STATE_ON 0 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 26) +#define LED_STATE_ON 0 -#define NEOPIXELS_NUMBER 0 +#define NEOPIXELS_NUMBER 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 18) +#define BUTTON_DFU PINNUM(0, 18) // Button 2 is defined as D1 from expansion board. -#define BUTTON_2 _PINNUM(0, 3) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU_OTA PINNUM(0, 3) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Seeed" -#define BLEDIS_MODEL "XIAO nRF52840" +#define BLEDIS_MANUFACTURER "Seeed" +#define BLEDIS_MODEL "XIAO nRF52840" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x2886 -#define USB_DESC_UF2_PID 0x0044 -#define USB_DESC_CDC_ONLY_PID 0x0044 +#define USB_DESC_VID 0x2886 +#define USB_DESC_UF2_PID 0x0044 +#define USB_DESC_CDC_ONLY_PID 0x0044 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Seeed XIAO nRF52840" -#define UF2_VOLUME_LABEL "XIAO-BOOT" -#define UF2_BOARD_ID "nRF52840-SeeedXiao-v1" -#define UF2_INDEX_URL "https://www.seeedstudio.com/" +#define UF2_PRODUCT_NAME "Seeed XIAO nRF52840" +#define UF2_VOLUME_LABEL "XIAO-BOOT" +#define UF2_BOARD_ID "nRF52840-SeeedXiao-v1" +#define UF2_INDEX_URL "https://www.seeedstudio.com/" #endif // _XIAO_BLE diff --git a/src/boards/xiao_nrf52840_ble_sense/board.h b/src/boards/xiao_nrf52840_ble_sense/board.h index fd30e148..ff0c204a 100644 --- a/src/boards/xiao_nrf52840_ble_sense/board.h +++ b/src/boards/xiao_nrf52840_ble_sense/board.h @@ -25,43 +25,41 @@ #ifndef _XIAO_BLE_SENSE #define _XIAO_BLE_SENSE -#define _PINNUM(port, pin) ((port)*32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ // The board has 3 leds, but changing the number here causes OTA issues. -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 26) -#define LED_STATE_ON 0 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 26) +#define LED_STATE_ON 0 -#define NEOPIXELS_NUMBER 0 +#define NEOPIXELS_NUMBER 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_1 _PINNUM(0, 18) +#define BUTTON_DFU PINNUM(0, 18) // Button 2 is defined as D1 from expansion board. -#define BUTTON_2 _PINNUM(0, 3) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU_OTA PINNUM(0, 3) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Seeed" -#define BLEDIS_MODEL "XIAO nRF52840 Sense" +#define BLEDIS_MANUFACTURER "Seeed" +#define BLEDIS_MODEL "XIAO nRF52840 Sense" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x2886 -#define USB_DESC_UF2_PID 0x0045 -#define USB_DESC_CDC_ONLY_PID 0x0045 +#define USB_DESC_VID 0x2886 +#define USB_DESC_UF2_PID 0x0045 +#define USB_DESC_CDC_ONLY_PID 0x0045 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Seeed XIAO nRF52840" -#define UF2_VOLUME_LABEL "XIAO-SENSE" -#define UF2_BOARD_ID "nRF52840-SeeedXiaoSense-v1" -#define UF2_INDEX_URL "https://www.seeedstudio.com/" +#define UF2_PRODUCT_NAME "Seeed XIAO nRF52840" +#define UF2_VOLUME_LABEL "XIAO-SENSE" +#define UF2_BOARD_ID "nRF52840-SeeedXiaoSense-v1" +#define UF2_INDEX_URL "https://www.seeedstudio.com/" #endif // _XIAO_BLE_SENSE diff --git a/src/main.c b/src/main.c index 2e2fc0c2..703bdb8c 100644 --- a/src/main.c +++ b/src/main.c @@ -263,8 +263,8 @@ static void check_dfu_mode(void) { #endif // DFU + FRESET are pressed --> OTA -#if defined(BUTTON_DFU) && defined(BUTTON_FRESET) - _ota_dfu = _ota_dfu || (button_pressed(BUTTON_DFU) && button_pressed(BUTTON_FRESET)); +#if defined(BUTTON_DFU) && defined(BUTTON_DFU_OTA) + _ota_dfu = _ota_dfu || (button_pressed(BUTTON_DFU) && button_pressed(BUTTON_DFU_OTA)); #endif bool const valid_app = bootloader_app_is_valid(); From a6f668ece4cdab8e3b3733c9921f829ed08a6636 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 2 Feb 2026 17:16:03 +0700 Subject: [PATCH 56/84] clean up --- .../board.cmake | 1 - .../arcade_feather_nrf52840_express/board.h | 66 ------------------- .../arcade_feather_nrf52840_express/board.mk | 1 - .../pinconfig.c | 62 ----------------- src/boards/bast_ble/board.h | 1 - src/boards/boards.h | 4 -- src/boards/raytac_mdbt50q_db_40/board.h | 15 ++--- supported_boards.md | 1 - 8 files changed, 5 insertions(+), 146 deletions(-) delete mode 100644 src/boards/arcade_feather_nrf52840_express/board.cmake delete mode 100644 src/boards/arcade_feather_nrf52840_express/board.h delete mode 100644 src/boards/arcade_feather_nrf52840_express/board.mk delete mode 100644 src/boards/arcade_feather_nrf52840_express/pinconfig.c diff --git a/src/boards/arcade_feather_nrf52840_express/board.cmake b/src/boards/arcade_feather_nrf52840_express/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/arcade_feather_nrf52840_express/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/arcade_feather_nrf52840_express/board.h b/src/boards/arcade_feather_nrf52840_express/board.h deleted file mode 100644 index c33b644f..00000000 --- a/src/boards/arcade_feather_nrf52840_express/board.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _ARCARDE_FEATHER_NRF52840_H -#define _ARCARDE_FEATHER_NRF52840_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN PINNUM(1, 15) -#define LED_SECONDARY_PIN PINNUM(1, 10) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL PINNUM(0, 16) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(1, 02) -#define BUTTON_DFU_OTA PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Feather nRF52840 Express" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0029 -#define USB_DESC_CDC_ONLY_PID 0x002A - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit Feather nRF52840 Express" -#define UF2_VOLUME_LABEL "ARCADE-N4" -#define UF2_BOARD_ID "nRF52840-Feather-revD" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4062" - -#endif // _ARCARDE_FEATHER_NRF52840_H diff --git a/src/boards/arcade_feather_nrf52840_express/board.mk b/src/boards/arcade_feather_nrf52840_express/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/arcade_feather_nrf52840_express/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/arcade_feather_nrf52840_express/pinconfig.c b/src/boards/arcade_feather_nrf52840_express/pinconfig.c deleted file mode 100644 index 5424d86e..00000000 --- a/src/boards/arcade_feather_nrf52840_express/pinconfig.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "boards.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = { - /* CF2 START */ - 513675505, 539130489, // magic - 49, 100, // used entries, total entries - 4, 0x18, // PIN_BTN_A = PIN_RX - 5, 0xa, // PIN_BTN_B = PIN_D2 - 13, 0x29, // PIN_LED = PIN_D13 - 18, 0xf, // PIN_MISO = PA15 - 19, 0xd, // PIN_MOSI = PA13 - 20, 0x23, // PIN_NEOPIXEL = PB03 - 21, 0x18, // PIN_RX = PA24 - 23, 0xe, // PIN_SCK = PA14 - 24, 0xb, // PIN_SCL = PA11 - 25, 0xc, // PIN_SDA = PA12 - 28, 0x19, // PIN_TX = PA25 - 32, 0xe, // PIN_DISPLAY_SCK = PIN_SCK - 34, 0xd, // PIN_DISPLAY_MOSI = PIN_MOSI - 35, 0x1e, // PIN_DISPLAY_CS = PIN_A2 - 36, 0x1c, // PIN_DISPLAY_DC = PIN_A3 - 37, 0xa0, // DISPLAY_WIDTH = 160 - 38, 0x80, // DISPLAY_HEIGHT = 128 - 39, 0x80, // DISPLAY_CFG0 = 0x80 - 40, 0x603, // DISPLAY_CFG1 = 0x603 - 41, 0x20, // DISPLAY_CFG2 = 0x20 - 43, 0x2, // PIN_DISPLAY_RST = PIN_A4 - 44, 0x3, // PIN_DISPLAY_BL = PIN_A5 - 47, 0x6, // PIN_BTN_LEFT = PIN_D11 - 48, 0x1a, // PIN_BTN_RIGHT = PIN_D9 - 49, 0x1b, // PIN_BTN_UP = PIN_D10 - 50, 0x7, // PIN_BTN_DOWN = PIN_D6 - 51, 0x8, // PIN_BTN_MENU = PIN_D12 - 56, 0x2a, // PIN_LED2 = PB10 - 60, 0x19, // PIN_JACK_TX = PIN_TX - 100, 0x4, // PIN_A0 = PA04 - 101, 0x5, // PIN_A1 = PA05 - 102, 0x1e, // PIN_A2 = PA30 - 103, 0x1c, // PIN_A3 = PA28 - 104, 0x2, // PIN_A4 = PA02 - 105, 0x3, // PIN_A5 = PA03 - 152, 0xa, // PIN_D2 = PA10 - 155, 0x28, // PIN_D5 = PB08 - 156, 0x7, // PIN_D6 = PA07 - 159, 0x1a, // PIN_D9 = PA26 - 160, 0x1b, // PIN_D10 = PA27 - 161, 0x6, // PIN_D11 = PA06 - 162, 0x8, // PIN_D12 = PA08 - 163, 0x29, // PIN_D13 = PB09 - 200, 0x1, // NUM_NEOPIXELS = 1 - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, 0xd7688ea1, // BOOTLOADER_BOARD_ID = 0xd7688ea1 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - /* CF2 END */ -}; diff --git a/src/boards/bast_ble/board.h b/src/boards/bast_ble/board.h index 598d2dd3..68a1ea76 100644 --- a/src/boards/bast_ble/board.h +++ b/src/boards/bast_ble/board.h @@ -25,7 +25,6 @@ #ifndef _BASTBLE_NRF52840_H #define _BASTBLE_NRF52840_H -#define PINNUM(port, pin) ((port) * 32 + (pin)) /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ diff --git a/src/boards/boards.h b/src/boards/boards.h index 87b14b15..8600119e 100644 --- a/src/boards/boards.h +++ b/src/boards/boards.h @@ -38,10 +38,6 @@ #define UF2_VOLUME_LABEL "NRF52BOOT " #endif -#if !defined(BUTTON_DFU_OTA) && defined(BUTTON_DFU_OTA) - #define BUTTON_DFU_OTA BUTTON_DFU_OTA -#endif - // The primary LED is usually Red but not in all cases. #define LED_PRIMARY 0 // The secondary LED, when available, is usually blue. diff --git a/src/boards/raytac_mdbt50q_db_40/board.h b/src/boards/raytac_mdbt50q_db_40/board.h index 838c90e7..2d26a3c6 100644 --- a/src/boards/raytac_mdbt50q_db_40/board.h +++ b/src/boards/raytac_mdbt50q_db_40/board.h @@ -25,20 +25,15 @@ #ifndef RAYTAC_MDBT50Q_DB_40_H #define RAYTAC_MDBT50Q_DB_40_H -#define PINNUM(port, pin) ((port) * 32 + (pin)) - /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ #define LEDS_NUMBER 3 -/* This is the Green led marked D1 on the board */ -#define LED_PRIMARY_PIN PINNUM(1, 13) -/* This is the Red led marked D2 on the board */ -#define LED_SECONDARY_PIN PINNUM(1, 14) -/* This is the Blue led marked D3 on the board */ -#define LED_TERTIARY_PIN PINNUM(1, 15) -/* There are one more unpopulated LEDs, but for the purposes of bootloader that - * doesn't matter. */ +#define LED_PRIMARY_PIN PINNUM(1, 13) /* Green led marked D1 on the board */ +#define LED_SECONDARY_PIN PINNUM(1, 14) /* Red led marked D2 on the board */ +#define LED_TERTIARY_PIN PINNUM(1, 15) /* Blue led marked D3 on the board */ + +/* There are one more unpopulated LEDs, but for the purposes of bootloader that doesn't matter. */ #define LED_STATE_ON 0 /*------------------------------------------------------------------*/ diff --git a/supported_boards.md b/supported_boards.md index f733ac3d..82484fdd 100644 --- a/supported_boards.md +++ b/supported_boards.md @@ -4,7 +4,6 @@ | Board | Name | VID PID | URL | | --- | --- | --- | --- | -| arcade_feather_nrf52840_express | Adafruit Feather nRF52840 Express | 0x239A:0x0029 | https://www.adafruit.com/product/4062 | | circuitplayground_nrf52840 | Adafruit Circuit Playground nRF52840 | 0x239A:0x0045 | https://www.adafruit.com/product/4333 | | clue_nrf52840 | Adafruit CLUE nRF52840 | 0x239A:0x0071 | https://www.adafruit.com/product/4500 | | feather_nrf52832 | Adafruit Feather nRF52832 | N/A | https://www.adafruit.com/product/3406 | From 5bb68e612548690de7204fc4308b4bf736fe8579 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 2 Feb 2026 19:12:21 +0700 Subject: [PATCH 57/84] sync master --- src/boards/syldra/board.h | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/boards/syldra/board.h b/src/boards/syldra/board.h index 4afa78d8..b48024a5 100644 --- a/src/boards/syldra/board.h +++ b/src/boards/syldra/board.h @@ -25,32 +25,27 @@ #ifndef _SYLDRA_H #define _SYLDRA_H -#define _PINNUM(port, pin) ((port)*32 + (pin)) - //------------------------------------------------------------------// // LED //------------------------------------------------------------------// #define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN _PINNUM(0, 17) // AD10 LED +#define LED_PRIMARY_PIN PINNUM(0, 17) // AD10 LED #define LED_STATE_ON 1 //------------------------------------------------------------------// // BUTTON //------------------------------------------------------------------// -#define BUTTONS_NUMBER 2 -#define BUTTON_1 _PINNUM(0, 24) // AD20 -- DFU -#define BUTTON_2 _PINNUM(1, 00) // AD22 -- FRST +#define BUTTON_DFU PINNUM(0, 24) // AD20 -- DFU +#define BUTTON_DFU_OTA PINNUM(1, 00) // AD22 -- OTA #define BUTTON_PULL NRF_GPIO_PIN_PULLUP - //-------------------------------------------------------------------// // BLE OTA //-------------------------------------------------------------------// #define BLEDIS_MANUFACTURER "Ambraglow-&-localcc" #define BLEDIS_MODEL "Syldra_dev_r1" - //--------------------------------------------------------------------+ // USB - using test PIDs //--------------------------------------------------------------------+ From cef2651b0d08cdf7ec86d7eeb53ac03d88f201a7 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 2 Feb 2026 19:14:56 +0700 Subject: [PATCH 58/84] correct uf2 board id --- src/boards/syldra/board.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boards/syldra/board.h b/src/boards/syldra/board.h index b48024a5..61539f8a 100644 --- a/src/boards/syldra/board.h +++ b/src/boards/syldra/board.h @@ -55,7 +55,7 @@ #define UF2_PRODUCT_NAME "Syldra nrf52840 Development Board" #define UF2_VOLUME_LABEL "Syldra-boot" -#define UF2_BOARD_ID "Syldra-dev-v1" +#define UF2_BOARD_ID "nRF52840-Syldra-dev-v1" #define UF2_INDEX_URL "https://github.com/ambraglow/nonorf" #endif \ No newline at end of file From 1307fe7f57c99f9d187b1c5f16626a3ecc79b4ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Mon, 2 Feb 2026 14:28:12 -0300 Subject: [PATCH 59/84] Enable FLASH overwrite protection from user applications for MBR and bootloader --- CMakeLists.txt | 5 ++ Makefile | 5 ++ .../bootloader_dfu/bootloader_util.c | 89 ++++++++++++++++++- 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e17d10e..7b635f16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ set(MBR_HEX ${SOFTDEVICE_DIR}/mbr/hex/mbr_nrf52_2.4.1_mbr.hex) set(TINYUSB_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/tinyusb/src) set(TCRYPT_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/tinycrypt/lib) +option(SKIP_FLASH_PROT "Skip enabling MBR/BL FLASH overwrite protection from user application" OFF) option(DEFAULT_TO_OTA_DFU "Default to OTA DFU instead of Serial DFU" OFF) option(SIGNED_FW "Enable signed firmware verification" OFF) option(DUALBANK_FW "Enable dual bank DFU support" OFF) @@ -201,6 +202,10 @@ if (DEFAULT_TO_OTA_DFU) target_compile_definitions(bootloader PUBLIC DEFAULT_TO_OTA_DFU) endif () +if (SKIP_FLASH_PROT) + target_compile_definitions(bootloader PUBLIC SKIP_FLASH_PROT) +endif () + if (TRACE_ETM STREQUAL "1") # ENABLE_TRACE will cause system_nrf5x.c to set up ETM trace target_compile_definitions(bootloader PUBLIC ENABLE_TRACE) diff --git a/Makefile b/Makefile index 108ae15f..ed1d6859 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ # - DUALBANK_FW : If bootloader will implement a dual bank feature to allow autorecover from failed # - FORCE_UF2 : if SIGNED_FW is 1, will force to include UF2 support (UNSECURE, UF2 does NOT validate signature!) # - DEFAULT_TO_OTA_DFU : if entering DFU, by default enter OTA DFU instead of Serial DFU +# - SKIP_FLASH_PROT : Skip enabling protection against user application writing to FLASH MBR/Bootloader #------------------------------------------------------------------------------ PYTHON = python @@ -376,6 +377,10 @@ ifeq ($(DEFAULT_TO_OTA_DFU), 1) CFLAGS += -DDEFAULT_TO_OTA_DFU endif +ifeq ($(SKIP_FLASH_PROT), 1) +CFLAGS += -DSKIP_FLASH_PROT +endif + _VER = $(subst ., ,$(word 1, $(subst -, ,$(GIT_VERSION)))) CFLAGS += -DMK_BOOTLOADER_VERSION='($(word 1,$(_VER)) << 16) + ($(word 2,$(_VER)) << 8) + $(word 3,$(_VER))' diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c b/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c index 62cb2856..b692accf 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c @@ -11,10 +11,11 @@ */ #include "bootloader_util.h" +#include "dfu_types.h" #include +#include #include - /** * @brief Function for aborting current application/bootloader jump to to other app/bootloader. * @@ -164,8 +165,94 @@ static inline void bootloader_util_reset(uint32_t start_addr) #error Compiler not supported. #endif +#ifndef SKIP_FLASH_PROT +uint32_t bootloader_util_flash_protect(uint32_t address, uint32_t size, bool read_protect) +{ + if ((size & (CODE_PAGE_SIZE - 1)) || (address > BOOTLOADER_SETTINGS_ADDRESS)) + { + return NRF_ERROR_INVALID_PARAM; + } + +#if defined(ACL_PRESENT) + + // Protect using ACL. + static uint32_t acl_instance = 0; + + uint32_t const wmask = (ACL_ACL_PERM_WRITE_Disable << ACL_ACL_PERM_WRITE_Pos); + uint32_t const rwmask = wmask | (ACL_ACL_PERM_READ_Disable << ACL_ACL_PERM_READ_Pos); + uint32_t const mask = read_protect ? rwmask: wmask; + + do + { + if (acl_instance >= ACL_REGIONS_COUNT) + { + return NRF_ERROR_NO_MEM; + } + + NRF_ACL->ACL[acl_instance].ADDR = address; + NRF_ACL->ACL[acl_instance].SIZE = size; + NRF_ACL->ACL[acl_instance].PERM = mask; + + acl_instance++; + + } while (NRF_ACL->ACL[acl_instance - 1].ADDR != address + || NRF_ACL->ACL[acl_instance - 1].SIZE != size + || NRF_ACL->ACL[acl_instance - 1].PERM != mask); // Check whether the acl_instance has been used before. + +#elif defined (BPROT_PRESENT) + + // Protect using BPROT. BPROT does not support read protection. + uint32_t pagenum_start = address / CODE_PAGE_SIZE; + uint32_t pagenum_end = pagenum_start + ((size - 1) / CODE_PAGE_SIZE); + + for (uint32_t i = pagenum_start; i <= pagenum_end; i++) + { + uint32_t config_index = i / 32; + uint32_t mask = (1 << (i - config_index * 32)); + + switch (config_index) + { + case 0: + NRF_BPROT->CONFIG0 = mask; + break; + case 1: + NRF_BPROT->CONFIG1 = mask; + break; +#if BPROT_REGIONS_NUM > 64 + case 2: + NRF_BPROT->CONFIG2 = mask; + break; + case 3: + NRF_BPROT->CONFIG3 = mask; + break; +#endif + } + } + +#endif + + return NRF_SUCCESS; +} +#endif void bootloader_util_app_start(uint32_t start_addr) { + +#ifndef SKIP_FLASH_PROT + uint32_t area_size; + + // Protect MBR against user writes + bootloader_util_flash_protect(0, MBR_SIZE, false); + + // Size of the bootloader flash area to protect (Bootloader + MBR params + Bootloader settings) + area_size = BOOTLOADER_SETTINGS_ADDRESS + CODE_PAGE_SIZE - BOOTLOADER_REGION_START; + + // Protect bootloader code and params pages against user writes + bootloader_util_flash_protect(BOOTLOADER_REGION_START, + area_size, + false); +#endif + + // Jump to user application bootloader_util_reset(start_addr); } From 6b24be5f69e03a25aacc41f06e2364a1576c8141 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 3 Feb 2026 14:14:08 +0700 Subject: [PATCH 60/84] - add GCC diagnostic ignored "-Warray-bounds" to affected code - correct usage of __has_attribute(nonstring) and rename to ATTR_NOSTRING --- Makefile | 6 ---- .../bootloader_dfu/bootloader_settings.c | 31 ++++++++++++------- src/usb/uf2/ghostfat.c | 22 ++++++------- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/Makefile b/Makefile index 9d2d4df3..f1fb137f 100644 --- a/Makefile +++ b/Makefile @@ -392,12 +392,6 @@ endif CFLAGS += -DDFU_APP_DATA_RESERVED=$(DFU_APP_DATA_RESERVED) -# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105523 -# Fixes for gcc version 12, 13, 14 and 15. -ifneq (,$(filter 12.% 13.% 14.% 15.%,$(shell $(CC) -dumpversion 2>$(NULL_DEVICE)))) - CFLAGS += --param=min-pagesize=0 -endif - #------------------------------------------------------------------------------ # Linker Flags #------------------------------------------------------------------------------ diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader_settings.c b/lib/sdk11/components/libraries/bootloader_dfu/bootloader_settings.c index c0710207..1febf3fb 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader_settings.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader_settings.c @@ -40,15 +40,22 @@ void bootloader_util_settings_get(const bootloader_settings_t ** pp_bootloader_s *pp_bootloader_settings = p_bootloader_settings; } -void bootloader_mbr_addrs_populate(void) -{ - if (*(const uint32_t *)MBR_BOOTLOADER_ADDR == 0xFFFFFFFF) - { - nrfx_nvmc_word_write(MBR_BOOTLOADER_ADDR, BOOTLOADER_REGION_START); - } - - if (*(const uint32_t *)MBR_PARAM_PAGE_ADDR == 0xFFFFFFFF) - { - nrfx_nvmc_word_write(MBR_PARAM_PAGE_ADDR, BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS); - } -} +void bootloader_mbr_addrs_populate(void) +{ +#if defined(__GNUC__) && (__GNUC__ >= 12) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif + if (*(const uint32_t *)MBR_BOOTLOADER_ADDR == 0xFFFFFFFF) + { + nrfx_nvmc_word_write(MBR_BOOTLOADER_ADDR, BOOTLOADER_REGION_START); + } + + if (*(const uint32_t *)MBR_PARAM_PAGE_ADDR == 0xFFFFFFFF) + { + nrfx_nvmc_word_write(MBR_PARAM_PAGE_ADDR, BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS); + } +#if defined(__GNUC__) && (__GNUC__ >= 12) +#pragma GCC diagnostic pop +#endif +} diff --git a/src/usb/uf2/ghostfat.c b/src/usb/uf2/ghostfat.c index 0564182a..97753ef2 100644 --- a/src/usb/uf2/ghostfat.c +++ b/src/usb/uf2/ghostfat.c @@ -41,18 +41,18 @@ //--------------------------------------------------------------------+ // Add nonstring attribute if supported to avoid errors in GCC 15 -#ifdef __has_attribute - #define __NONSTRING__ __attribute__((nonstring)) +#if defined(__has_attribute) && __has_attribute(nonstring) + #define ATTR_NONSTRING __attribute__((nonstring)) #else - #define __NONSTRING__ + #define ATTR_NONSTRING #endif typedef struct { uint8_t JumpInstruction[3]; - uint8_t OEMInfo[8] __NONSTRING__; - uint16_t SectorSize; - uint8_t SectorsPerCluster; - uint16_t ReservedSectors; + uint8_t OEMInfo[8] ATTR_NONSTRING; + uint16_t SectorSize; + uint8_t SectorsPerCluster; + uint16_t ReservedSectors; uint8_t FATCopies; uint16_t RootDirectoryEntries; uint16_t TotalSectors16; @@ -66,8 +66,8 @@ typedef struct { uint8_t Reserved; uint8_t ExtendedBootSig; uint32_t VolumeSerialNumber; - uint8_t VolumeLabel[11] __NONSTRING__; - uint8_t FilesystemIdentifier[8] __NONSTRING__; + uint8_t VolumeLabel[11] ATTR_NONSTRING; + uint8_t FilesystemIdentifier[8] ATTR_NONSTRING; } __attribute__((packed)) FAT_BootBlock; typedef struct { @@ -88,8 +88,8 @@ typedef struct { STATIC_ASSERT(sizeof(DirEntry) == 32); struct TextFile { - char const name[11] __NONSTRING__; - char const *content; + const char name[11] ATTR_NONSTRING; + const char *content; }; From 8c45bf843cd1bbcd1bc148fba07ea6e30c89183e Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 3 Feb 2026 18:47:04 +0700 Subject: [PATCH 61/84] - always enable flash protect - clean up code - add illustrated memory layout for quick ref --- CMakeLists.txt | 5 - Makefile | 4 - .../bootloader_dfu/bootloader_util.c | 140 ++++++++---------- .../libraries/bootloader_dfu/dfu_types.h | 31 ++++ 4 files changed, 93 insertions(+), 87 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b635f16..1e17d10e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,6 @@ set(MBR_HEX ${SOFTDEVICE_DIR}/mbr/hex/mbr_nrf52_2.4.1_mbr.hex) set(TINYUSB_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/tinyusb/src) set(TCRYPT_DIR ${CMAKE_CURRENT_LIST_DIR}/lib/tinycrypt/lib) -option(SKIP_FLASH_PROT "Skip enabling MBR/BL FLASH overwrite protection from user application" OFF) option(DEFAULT_TO_OTA_DFU "Default to OTA DFU instead of Serial DFU" OFF) option(SIGNED_FW "Enable signed firmware verification" OFF) option(DUALBANK_FW "Enable dual bank DFU support" OFF) @@ -202,10 +201,6 @@ if (DEFAULT_TO_OTA_DFU) target_compile_definitions(bootloader PUBLIC DEFAULT_TO_OTA_DFU) endif () -if (SKIP_FLASH_PROT) - target_compile_definitions(bootloader PUBLIC SKIP_FLASH_PROT) -endif () - if (TRACE_ETM STREQUAL "1") # ENABLE_TRACE will cause system_nrf5x.c to set up ETM trace target_compile_definitions(bootloader PUBLIC ENABLE_TRACE) diff --git a/Makefile b/Makefile index ed1d6859..d5d06103 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,6 @@ # - DUALBANK_FW : If bootloader will implement a dual bank feature to allow autorecover from failed # - FORCE_UF2 : if SIGNED_FW is 1, will force to include UF2 support (UNSECURE, UF2 does NOT validate signature!) # - DEFAULT_TO_OTA_DFU : if entering DFU, by default enter OTA DFU instead of Serial DFU -# - SKIP_FLASH_PROT : Skip enabling protection against user application writing to FLASH MBR/Bootloader #------------------------------------------------------------------------------ PYTHON = python @@ -377,9 +376,6 @@ ifeq ($(DEFAULT_TO_OTA_DFU), 1) CFLAGS += -DDEFAULT_TO_OTA_DFU endif -ifeq ($(SKIP_FLASH_PROT), 1) -CFLAGS += -DSKIP_FLASH_PROT -endif _VER = $(subst ., ,$(word 1, $(subst -, ,$(GIT_VERSION)))) CFLAGS += -DMK_BOOTLOADER_VERSION='($(word 1,$(_VER)) << 16) + ($(word 2,$(_VER)) << 8) + $(word 3,$(_VER))' diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c b/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c index b692accf..13af757e 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c @@ -12,6 +12,8 @@ #include "bootloader_util.h" #include "dfu_types.h" +#include "nrf_peripherals.h" + #include #include #include @@ -165,94 +167,76 @@ static inline void bootloader_util_reset(uint32_t start_addr) #error Compiler not supported. #endif -#ifndef SKIP_FLASH_PROT -uint32_t bootloader_util_flash_protect(uint32_t address, uint32_t size, bool read_protect) -{ - if ((size & (CODE_PAGE_SIZE - 1)) || (address > BOOTLOADER_SETTINGS_ADDRESS)) - { - return NRF_ERROR_INVALID_PARAM; - } +// protect flash region from write access +static uint32_t bootloader_util_flash_protect(uint32_t address, uint32_t size) { + if ((size & (CODE_PAGE_SIZE - 1)) || (address > BOOTLOADER_SETTINGS_ADDRESS)) { + return NRF_ERROR_INVALID_PARAM; + } #if defined(ACL_PRESENT) + // Protect using ACL. + static uint32_t acl_instance = 0; - // Protect using ACL. - static uint32_t acl_instance = 0; - - uint32_t const wmask = (ACL_ACL_PERM_WRITE_Disable << ACL_ACL_PERM_WRITE_Pos); - uint32_t const rwmask = wmask | (ACL_ACL_PERM_READ_Disable << ACL_ACL_PERM_READ_Pos); - uint32_t const mask = read_protect ? rwmask: wmask; - - do - { - if (acl_instance >= ACL_REGIONS_COUNT) - { - return NRF_ERROR_NO_MEM; - } - - NRF_ACL->ACL[acl_instance].ADDR = address; - NRF_ACL->ACL[acl_instance].SIZE = size; - NRF_ACL->ACL[acl_instance].PERM = mask; - - acl_instance++; - - } while (NRF_ACL->ACL[acl_instance - 1].ADDR != address - || NRF_ACL->ACL[acl_instance - 1].SIZE != size - || NRF_ACL->ACL[acl_instance - 1].PERM != mask); // Check whether the acl_instance has been used before. - -#elif defined (BPROT_PRESENT) - - // Protect using BPROT. BPROT does not support read protection. - uint32_t pagenum_start = address / CODE_PAGE_SIZE; - uint32_t pagenum_end = pagenum_start + ((size - 1) / CODE_PAGE_SIZE); - - for (uint32_t i = pagenum_start; i <= pagenum_end; i++) - { - uint32_t config_index = i / 32; - uint32_t mask = (1 << (i - config_index * 32)); - - switch (config_index) - { - case 0: - NRF_BPROT->CONFIG0 = mask; - break; - case 1: - NRF_BPROT->CONFIG1 = mask; - break; -#if BPROT_REGIONS_NUM > 64 - case 2: - NRF_BPROT->CONFIG2 = mask; - break; - case 3: - NRF_BPROT->CONFIG3 = mask; - break; -#endif - } + volatile ACL_ACL_Type *p_acl; + const uint32_t mask = (ACL_ACL_PERM_WRITE_Disable << ACL_ACL_PERM_WRITE_Pos); + + do { + if (acl_instance >= ACL_REGIONS_COUNT) { + return NRF_ERROR_NO_MEM; } -#endif + p_acl = &NRF_ACL->ACL[acl_instance]; + p_acl->ADDR = address; + p_acl->SIZE = size; + p_acl->PERM = mask; + + acl_instance++; + + } while (p_acl->ADDR != address || p_acl->SIZE != size || + p_acl->PERM != mask); // Check whether the acl_instance has been used before. + +#elif defined(BPROT_PRESENT) + // Protect using BPROT. BPROT does not support read protection. + uint32_t pagenum_start = address / CODE_PAGE_SIZE; + uint32_t pagenum_end = pagenum_start + ((size - 1) / CODE_PAGE_SIZE); + + for (uint32_t i = pagenum_start; i <= pagenum_end; i++) { + uint32_t config_index = i / 32; + uint32_t mask = (1 << (i - config_index * 32)); + + switch (config_index) { + case 0: + NRF_BPROT->CONFIG0 = mask; + break; + case 1: + NRF_BPROT->CONFIG1 = mask; + break; + #if BPROT_REGIONS_NUM > 64 + case 2: + NRF_BPROT->CONFIG2 = mask; + break; + case 3: + NRF_BPROT->CONFIG3 = mask; + break; + #endif + } + } - return NRF_SUCCESS; -} #endif -void bootloader_util_app_start(uint32_t start_addr) -{ - -#ifndef SKIP_FLASH_PROT - uint32_t area_size; + return NRF_SUCCESS; +} - // Protect MBR against user writes - bootloader_util_flash_protect(0, MBR_SIZE, false); +void bootloader_util_app_start(uint32_t start_addr) { + // Protect MBR against user writes + bootloader_util_flash_protect(0, MBR_SIZE); - // Size of the bootloader flash area to protect (Bootloader + MBR params + Bootloader settings) - area_size = BOOTLOADER_SETTINGS_ADDRESS + CODE_PAGE_SIZE - BOOTLOADER_REGION_START; + // Size of the bootloader flash area to protect (Bootloader + MBR params + Bootloader settings) + const uint32_t area_size = BOOTLOADER_SETTINGS_ADDRESS + CODE_PAGE_SIZE - BOOTLOADER_REGION_START; - // Protect bootloader code and params pages against user writes - bootloader_util_flash_protect(BOOTLOADER_REGION_START, - area_size, - false); -#endif + // Protect bootloader code and params pages against user writes + bootloader_util_flash_protect(BOOTLOADER_REGION_START, area_size); - // Jump to user application - bootloader_util_reset(start_addr); + // Jump to user application + bootloader_util_reset(start_addr); } diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h b/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h index e186e055..665f9c48 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h @@ -20,6 +20,37 @@ * @brief Device Firmware Update module type and definitions. */ +/* + * Flash layout (nrf52840, see linker/nrf52840.ld): + * + * ------------------------- + * | Bootloader Settings | + * | 4 KB | + * |-------------------------| 0xFF000 BOOTLOADER_SETTINGS_ADDRESS + * | MBR Params Page | + * | 4 KB | + * |-------------------------| 0xFE000 + * | Bootloader Config | + * | 2 KB | + * |-------------------------| 0xFD800 + * | Bootloader | + * | 38 KB | + * |-------------------------| 0xF4000 BOOTLOADER_REGION_START + * | App Data Reserved | + * | 40 KB | + * |-------------------------| 0xEA000 + * | Application | + * | Single/ Dual Bank | + * | | + * |-------------------------| CODE_REGION_1_START + * | SoftDevice | + * | 0x26000 (optional) | + * |-------------------------| 0x01000 + * | MBR | + * | 4 KB | + * ------------------------- 0x00000 + */ + #ifndef DFU_TYPES_H__ #define DFU_TYPES_H__ From 48cfda2a2942a47a27261e5f08a692a52384d85f Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 3 Feb 2026 19:01:45 +0700 Subject: [PATCH 62/84] check page size alignment for address as well --- lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c b/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c index 13af757e..a1fb9be3 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader_util.c @@ -169,7 +169,7 @@ static inline void bootloader_util_reset(uint32_t start_addr) // protect flash region from write access static uint32_t bootloader_util_flash_protect(uint32_t address, uint32_t size) { - if ((size & (CODE_PAGE_SIZE - 1)) || (address > BOOTLOADER_SETTINGS_ADDRESS)) { + if ((size & (CODE_PAGE_SIZE - 1)) || (address & (CODE_PAGE_SIZE - 1)) || (address > BOOTLOADER_SETTINGS_ADDRESS)) { return NRF_ERROR_INVALID_PARAM; } From c2ae4183aa36b4ef6007126775af547b120ba8d2 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 3 Feb 2026 20:38:21 +0700 Subject: [PATCH 63/84] minor --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index d5d06103..108ae15f 100644 --- a/Makefile +++ b/Makefile @@ -376,7 +376,6 @@ ifeq ($(DEFAULT_TO_OTA_DFU), 1) CFLAGS += -DDEFAULT_TO_OTA_DFU endif - _VER = $(subst ., ,$(word 1, $(subst -, ,$(GIT_VERSION)))) CFLAGS += -DMK_BOOTLOADER_VERSION='($(word 1,$(_VER)) << 16) + ($(word 2,$(_VER)) << 8) + $(word 3,$(_VER))' From dd816e511ab9801a1d33c0734df4bc8817c1bf25 Mon Sep 17 00:00:00 2001 From: mestrode <35630715+mestrode@users.noreply.github.com> Date: Mon, 16 Feb 2026 17:32:07 +0100 Subject: [PATCH 64/84] Seeed Xiao BLE boards: enable 2nd LED --- src/boards/xiao_nrf52840_ble/board.h | 8 ++++---- src/boards/xiao_nrf52840_ble_sense/board.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/boards/xiao_nrf52840_ble/board.h b/src/boards/xiao_nrf52840_ble/board.h index eacabd20..b04dc39b 100644 --- a/src/boards/xiao_nrf52840_ble/board.h +++ b/src/boards/xiao_nrf52840_ble/board.h @@ -28,10 +28,10 @@ /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -// The board has 3 leds, but changing the number here causes OTA issues. -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 26) -#define LED_STATE_ON 0 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN PINNUM(0, 26) // red +#define LED_SECONDARY_PIN PINNUM(0, 6) // blue +#define LED_STATE_ON 0 #define NEOPIXELS_NUMBER 0 diff --git a/src/boards/xiao_nrf52840_ble_sense/board.h b/src/boards/xiao_nrf52840_ble_sense/board.h index ff0c204a..cd74bc92 100644 --- a/src/boards/xiao_nrf52840_ble_sense/board.h +++ b/src/boards/xiao_nrf52840_ble_sense/board.h @@ -28,10 +28,10 @@ /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -// The board has 3 leds, but changing the number here causes OTA issues. -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 26) -#define LED_STATE_ON 0 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN PINNUM(0, 26) // red +#define LED_SECONDARY_PIN PINNUM(0, 6) // blue +#define LED_STATE_ON 0 #define NEOPIXELS_NUMBER 0 From 57ce8b2e9353e0115e09c49e417d04439e7e0a2d Mon Sep 17 00:00:00 2001 From: jesuslg123 Date: Sun, 8 Mar 2026 21:20:03 +0100 Subject: [PATCH 65/84] When in bootloader mode and USB is disconnected the system now jumpt to app state to avoid leave the device stuck on bootloader --- .gitignore | 2 +- .../libraries/bootloader_dfu/bootloader.c | 130 +++++++++++------- .../libraries/bootloader_dfu/bootloader.h | 17 ++- .../bootloader_dfu/dfu_transport_serial.c | 14 +- src/usb/msc_uf2.c | 12 +- 5 files changed, 109 insertions(+), 66 deletions(-) diff --git a/.gitignore b/.gitignore index e7b13c9f..3180a079 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,7 @@ dkms.conf segger/Output _build/ +cmake-build-*/ bin/ *.emSession *.jlink @@ -68,4 +69,3 @@ Makefile.user # Exclude all SoftDevice S340 files lib/softdevice/s340* - diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c index bd9b8cce..059b6fdd 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c @@ -48,12 +48,29 @@ typedef enum BOOTLOADER_RESET_TO_SELF, /**< Bootloader status field for indicating that a reset has been requested and current update process should be aborted and the bootloader must be reentered. */ } bootloader_status_t; -static pstorage_handle_t m_bootsettings_handle; /**< Pstorage handle to use for registration and identifying the bootloader module on subsequent calls to the pstorage module for load and store of bootloader setting in flash. */ -static bootloader_status_t m_update_status; /**< Current update status for the bootloader module to ensure correct behaviour when updating settings and when update completes. */ -static bool m_cancel_timeout_on_usb; /**< If set the timeout is cancelled when USB is enumerated. Otherwise, the timeout is only cancelled when DFU update is started. */ - -APP_TIMER_DEF( _dfu_startup_timer ); -volatile bool dfu_startup_packet_received = false; +static pstorage_handle_t m_bootsettings_handle; /**< Pstorage handle to use for registration and identifying the bootloader module on subsequent calls to the pstorage module for load and store of bootloader setting in flash. */ +static bootloader_status_t m_update_status; /**< Current update status for the bootloader module to ensure correct behaviour when updating settings and when update completes. */ +static bool m_cancel_timeout_on_usb; /**< If set the timeout is cancelled when USB is enumerated. Otherwise, the timeout is only cancelled when DFU update is started. */ +static bool m_usb_was_mounted; /**< Tracks whether the USB DFU session was ever enumerated so we can exit when cable is later removed. */ +static bool m_startup_dfu_has_activity; /**< Tracks whether any valid serial or UF2 update traffic has started. */ + +APP_TIMER_DEF( _dfu_startup_timer ); + +static void bootloader_timeout_startup_dfu(void) +{ + if (!m_startup_dfu_has_activity) + { + dfu_update_status_t update_status; + update_status.status_code = DFU_TIMEOUT; + + bootloader_dfu_update_process(update_status); + } +} + +void bootloader_dfu_activity_mark(void) +{ + m_startup_dfu_has_activity = true; +} /**@brief Function for handling callbacks from pstorage module. * @@ -79,26 +96,20 @@ static void pstorage_callback_handler(pstorage_handle_t * p_handle, /* Terminate the forced DFU mode on startup if no packets is received * by put an terminal handler to scheduler */ -static void dfu_startup_timer_handler(void * p_context) -{ -#ifdef NRF_USBD - if (m_cancel_timeout_on_usb && tud_mounted()) - { - return; - } -#endif - - // nRF52832 forced DFU on startup - // No packets are received within timeout, exit DFU mode - // dfu_startup_packet_received is set by process_dfu_packet() in dfu_transport_serial.c - if (!dfu_startup_packet_received) - { - dfu_update_status_t update_status; - update_status.status_code = DFU_TIMEOUT; - - bootloader_dfu_update_process(update_status); - } -} +static void dfu_startup_timer_handler(void * p_context) +{ +#ifdef NRF_USBD + if (m_cancel_timeout_on_usb && tud_mounted()) + { + m_usb_was_mounted = true; + return; + } +#endif + + // nRF52832 forced DFU on startup + // No update activity is received within timeout, exit DFU mode + bootloader_timeout_startup_dfu(); +} /**@brief Function for waiting for events. * @@ -126,14 +137,29 @@ static void wait_for_events(void) // Event received. Process it from the scheduler. app_sched_execute(); -#ifdef NRF_USBD - // skip if usb is not inited ( e.g OTA / finializing sd/bootloader ) - if ( tusb_inited() ) - { - tud_task(); - tud_cdc_write_flush(); - } -#endif +#ifdef NRF_USBD + // skip if usb is not inited ( e.g OTA / finializing sd/bootloader ) + if ( tusb_inited() ) + { + tud_task(); + tud_cdc_write_flush(); + } + + if (m_cancel_timeout_on_usb) + { + if (tusb_inited() && tud_mounted()) + { + m_usb_was_mounted = true; + } + else if (m_usb_was_mounted && + !(NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk)) + { + // Only exit startup DFU after USB was actually unplugged, not on a host-side + // re-enumeration or temporary unmount. + bootloader_timeout_startup_dfu(); + } + } +#endif if ((m_update_status == BOOTLOADER_COMPLETE) || (m_update_status == BOOTLOADER_TIMEOUT) || @@ -353,15 +379,17 @@ uint32_t bootloader_init(void) } -uint32_t bootloader_dfu_start(bool ota, uint32_t timeout_ms, bool cancel_timeout_on_usb) -{ - uint32_t err_code; - - m_cancel_timeout_on_usb = cancel_timeout_on_usb && !ota; - - // Clear swap if banked update is used. - err_code = dfu_init(); - VERIFY_SUCCESS(err_code); +uint32_t bootloader_dfu_start(bool ota, uint32_t timeout_ms, bool cancel_timeout_on_usb) +{ + uint32_t err_code; + + m_cancel_timeout_on_usb = cancel_timeout_on_usb && !ota; + m_usb_was_mounted = false; + m_startup_dfu_has_activity = false; + + // Clear swap if banked update is used. + err_code = dfu_init(); + VERIFY_SUCCESS(err_code); if ( ota ) { @@ -369,15 +397,13 @@ uint32_t bootloader_dfu_start(bool ota, uint32_t timeout_ms, bool cancel_timeout }else { // DFU mode with timeout can be - // - Forced startup DFU for nRF52832 or - // - Makecode single tap reset but no enumerated (battery power) - if ( timeout_ms ) - { - dfu_startup_packet_received = false; - - app_timer_create(&_dfu_startup_timer, APP_TIMER_MODE_SINGLE_SHOT, dfu_startup_timer_handler); - app_timer_start(_dfu_startup_timer, APP_TIMER_TICKS(timeout_ms), NULL); - } + // - Forced startup DFU for nRF52832 or + // - Makecode single tap reset but no enumerated (battery power) + if ( timeout_ms ) + { + app_timer_create(&_dfu_startup_timer, APP_TIMER_MODE_SINGLE_SHOT, dfu_startup_timer_handler); + app_timer_start(_dfu_startup_timer, APP_TIMER_TICKS(timeout_ms), NULL); + } err_code = dfu_transport_serial_update_start(); } diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h index 9b6f7540..5c68e9bc 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h @@ -68,11 +68,18 @@ void bootloader_app_start(void); */ void bootloader_settings_get(bootloader_settings_t * const p_settings); -/**@brief Function for processing DFU status update. - * - * @param[in] update_status DFU update status. - */ -void bootloader_dfu_update_process(dfu_update_status_t update_status); +/**@brief Function for processing DFU status update. + * + * @param[in] update_status DFU update status. + */ +void bootloader_dfu_update_process(dfu_update_status_t update_status); + +/**@brief Mark that startup DFU has received valid update activity. + * + * @details This suppresses the startup timeout or USB unplug fallback that is only + * meant for the "entered bootloader, but never started an update" case. + */ +void bootloader_dfu_activity_mark(void); /**@brief Function getting state of SoftDevice update in progress. * After a successfull SoftDevice transfer the system restarts in orderto disable SoftDevice diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c index 71f08526..b8c65ae3 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c @@ -10,10 +10,11 @@ * */ -#include "dfu_transport.h" -#include -#include "dfu.h" -#include +#include "dfu_transport.h" +#include +#include "bootloader.h" +#include "dfu.h" +#include #include "app_error.h" #include "app_util.h" #include "hci_transport.h" @@ -197,9 +198,8 @@ static void process_dfu_packet(void * p_event_data, uint16_t event_size) uint32_t index; dfu_update_packet_t * packet; - // Adafruit modification for startup dfu - extern bool dfu_startup_packet_received; - dfu_startup_packet_received = true; + // Mark that startup DFU has transitioned into a real update flow. + bootloader_dfu_activity_mark(); while (false == DATA_QUEUE_EMPTY()) { diff --git a/src/usb/msc_uf2.c b/src/usb/msc_uf2.c index 96d705bc..f4a60787 100644 --- a/src/usb/msc_uf2.c +++ b/src/usb/msc_uf2.c @@ -145,9 +145,19 @@ int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* uint32_t count = 0; while ( count < bufsize ) { + int written; + // Consider non-uf2 block write as successful // only break if write_block is busy with flashing (return 0) - if ( 0 == write_block(lba, buffer, &_wr_state) ) break; + written = write_block(lba, buffer, &_wr_state); + if ( written > 0 ) + { + bootloader_dfu_activity_mark(); + } + else if ( written == 0 ) + { + break; + } lba++; buffer += 512; From 96c57447c6c617e5733203e41a603e1c91225547 Mon Sep 17 00:00:00 2001 From: jesuslg123 Date: Sun, 8 Mar 2026 21:20:18 +0100 Subject: [PATCH 66/84] Ignore files --- src/boards/xiao_nrf52840_ble/board.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boards/xiao_nrf52840_ble/board.h b/src/boards/xiao_nrf52840_ble/board.h index b04dc39b..ee350757 100644 --- a/src/boards/xiao_nrf52840_ble/board.h +++ b/src/boards/xiao_nrf52840_ble/board.h @@ -58,7 +58,7 @@ //------------- UF2 -------------// #define UF2_PRODUCT_NAME "Seeed XIAO nRF52840" -#define UF2_VOLUME_LABEL "XIAO-BOOT" +#define UF2_VOLUME_LABEL "PADEL-ON" #define UF2_BOARD_ID "nRF52840-SeeedXiao-v1" #define UF2_INDEX_URL "https://www.seeedstudio.com/" From 1f17efd6116a9480f4e2bd8e1d2d28425ad77baf Mon Sep 17 00:00:00 2001 From: jesuslg123 Date: Sun, 8 Mar 2026 22:35:48 +0100 Subject: [PATCH 67/84] Revert unwanted change --- src/boards/xiao_nrf52840_ble/board.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boards/xiao_nrf52840_ble/board.h b/src/boards/xiao_nrf52840_ble/board.h index ee350757..b04dc39b 100644 --- a/src/boards/xiao_nrf52840_ble/board.h +++ b/src/boards/xiao_nrf52840_ble/board.h @@ -58,7 +58,7 @@ //------------- UF2 -------------// #define UF2_PRODUCT_NAME "Seeed XIAO nRF52840" -#define UF2_VOLUME_LABEL "PADEL-ON" +#define UF2_VOLUME_LABEL "XIAO-BOOT" #define UF2_BOARD_ID "nRF52840-SeeedXiao-v1" #define UF2_INDEX_URL "https://www.seeedstudio.com/" From 545fc0534d125a3f3e9d030430f0b9907c7df67b Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 18 Mar 2026 21:07:05 +0700 Subject: [PATCH 68/84] update AGENTS.md --- AGENTS.md | 91 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 82 insertions(+), 9 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 4df910ee..67f8947f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,25 +1,98 @@ # Agent Instructions +This file provides guidance to AI agents when working with code in this repository. + +## Project Overview + +CDC/DFU/UF2 bootloader for Nordic nRF52 microcontrollers (nRF52832, nRF52833, nRF52840). Supports DFU over Serial, BLE OTA, and USB mass storage (UF2). Runs on 60+ boards. + ## Style -- Follow the repo `.clang-format` when making changes. + +Follow the repo `.clang-format` when making changes. ## Build / Verify -- Prefer CMake for testing/verification. -- Always build both boards during verification: - - `feather_nrf52832` - - `feather_nrf52840_express` + +Default board: `feather_nrf52840_express`. Always verify changes against both boards: +- `feather_nrf52840_express` (nRF52840) +- `feather_nrf52832` (nRF52832) ### CMake (preferred) ```bash -cmake -S . -B cmake-build-feather_nrf52832 -DBOARD=feather_nrf52832 -cmake --build cmake-build-feather_nrf52832 - cmake -S . -B cmake-build-feather_nrf52840_express -DBOARD=feather_nrf52840_express cmake --build cmake-build-feather_nrf52840_express + +cmake -S . -B cmake-build-feather_nrf52832 -DBOARD=feather_nrf52832 +cmake --build cmake-build-feather_nrf52832 ``` ### Make (alternate) ```bash -make BOARD=feather_nrf52832 all make BOARD=feather_nrf52840_express all +make BOARD=feather_nrf52832 all +``` + +### Flashing +```bash +make BOARD={board} flash # Flash via JLink +make BOARD={board} flash-dfu # Flash via Serial/CDC DFU +make BOARD={board} flash-sd # Flash SoftDevice only ``` + +### Build all boards +```bash +python3 tools/build_all.py +``` + +## Architecture + +### MCU Variants and SoftDevices +- **nrf52** (nRF52832): UART-only bootloader, default SoftDevice s132 v6.1.1 +- **nrf52833**: USB support, default SoftDevice s140 v7.3.0 +- **nrf52840**: Full USB + OTA, default SoftDevice s140 v6.1.1 +- Boards may override `SD_NAME`/`SD_VERSION` in their `board.mk` + +### Key Source Structure +- `src/main.c` — Bootloader entry point, DFU mode detection, LED/button init +- `src/dfu_init.c` — DFU packet validation, CRC/signature verification +- `src/dfu_ble_svc.c` — BLE DFU service +- `src/flash_nrf5x.c` — Flash memory operations +- `src/boards/boards.c` — Board abstraction (LED control, buttons, timing) +- `src/usb/` — USB stack (nRF52833/nRF52840): CDC serial, MSC storage, UF2 handler +- `src/usb/uf2/ghostfat.c` — Virtual FAT filesystem for UF2 drag-and-drop + +### Board Definition System + +Each board lives in `src/boards/{board_name}/` with: +- `board.h` — Pin definitions, LED/button assignments, USB VID/PID, UF2 metadata +- `board.mk` — Makefile variable `MCU_SUB_VARIANT` (nrf52, nrf52833, or nrf52840) +- `board.cmake` — CMake variable `MCU_VARIANT` +- `pinconfig.c` — CF2 bootloader configuration (flash/RAM size, UF2 family ID) + +### Memory Layout (linker scripts in `linker/`) +- Bootloader occupies ~38KB near end of flash (e.g., 0xF4000–0xFD800 on nRF52840) +- No heap (`__HEAP_SIZE=0`), static allocation only +- Special sections: double-reset detection word, bond info for OTA, MBR params, bootloader settings + +### Submodules (`lib/`) +- `tinyusb` — USB device stack +- `nrfx` — Nordic HAL drivers +- `uf2` — UF2 format tools +- `tinycrypt` — Crypto (only when `SIGNED_FW=1`) +- `sdk/`, `sdk11/` — Nordic SDK libraries +- `softdevice/` — Precompiled Bluetooth stack binaries + +### Compile-Time Feature Flags +- `SIGNED_FW` — Require signed firmware (disables UF2 unless `FORCE_UF2=1`) +- `DUALBANK_FW` — Dual-bank updates +- `DEFAULT_TO_OTA_DFU` — Default to BLE OTA instead of serial DFU +- `DEBUG` — Enable RTT debugging, larger bootloader region + +## CI + +GitHub Actions (`.github/workflows/githubci.yml`) builds all boards in parallel using a matrix generated from `src/boards/` directory names. On release, artifacts (zip, hex, uf2) are uploaded as release assets. + +## Required Toolchain + +- `arm-none-eabi-gcc` (tested with 12.3.1) +- Python 3 with: `adafruit-nrfutil`, `intelhex` +- `nrfjprog` (for JLink flashing) From ac086326fec3d55b16f4d2152d576b6a87618186 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 19 Mar 2026 10:34:13 +0700 Subject: [PATCH 69/84] restrict GitHub Actions to master branch and add workflow_dispatch trigger --- .github/workflows/githubci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/githubci.yml b/.github/workflows/githubci.yml index 68b4f7f2..b3686dd3 100644 --- a/.github/workflows/githubci.yml +++ b/.github/workflows/githubci.yml @@ -1,13 +1,18 @@ name: Build on: + workflow_dispatch: pull_request: push: - repository_dispatch: + branches: [ master ] release: types: - created +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: set-matrix: runs-on: ubuntu-latest From 67105f8db3279c54898be42fb44548bb7adf5a87 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 15 May 2026 00:05:00 +0700 Subject: [PATCH 70/84] validate app presence more robustly, allow debugger flashing e.g with jlink without flashing the setting page --- AGENTS.md | 4 ++ .../libraries/bootloader_dfu/bootloader.c | 37 +++++++++++++------ 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 67f8947f..aaebd02f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -87,6 +87,10 @@ Each board lives in `src/boards/{board_name}/` with: - `DEFAULT_TO_OTA_DFU` — Default to BLE OTA instead of serial DFU - `DEBUG` — Enable RTT debugging, larger bootloader region +## PR Review Checklist + +- **VID/PID assignment**: When a PR adds or modifies a board, verify `USB_DESC_VID`/`USB_DESC_UF2_PID` in `board.h`. New boards should use unique values. The Adafruit VID `0x239A` is reserved for Adafruit-allocated boards; third-party boards must use their own VID. Some legacy boards intentionally share a VID/PID with a reference board (see `// Shared VID/PID with ...` comments) — these are pending cleanup, not a template for new boards. + ## CI GitHub Actions (`.github/workflows/githubci.yml`) builds all boards in parallel using a matrix generated from `src/boards/` directory names. On release, artifacts (zip, hex, uf2) are uploaded as release assets. diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c index bd9b8cce..500e229b 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c @@ -149,23 +149,22 @@ static void wait_for_events(void) bool bootloader_app_is_valid(void) { - bool success = false; uint32_t const app_addr = DFU_BANK_0_REGION_START; - - bootloader_settings_t const *p_bootloader_settings; - bootloader_util_settings_get(&p_bootloader_settings); + uint32_t const sp_addr = *((uint32_t*)app_addr); + uint32_t const reset_addr = *((uint32_t*)(app_addr + 4)); enum { EMPTY_FLASH = 0xFFFFFFFFUL }; - // Application is invalid if first 2 words are all 0xFFFFFFF - if ( *((uint32_t *)app_addr ) == EMPTY_FLASH && - *((uint32_t *)(app_addr+4)) == EMPTY_FLASH ) + if (sp_addr == EMPTY_FLASH && reset_addr == EMPTY_FLASH) { return false; } - // The application in CODE region 1 is flagged as valid during update. - if ( p_bootloader_settings->bank_0 == BANK_VALID_APP ) + bootloader_settings_t const * p_bootloader_settings; + bootloader_util_settings_get(&p_bootloader_settings); + + // Primary path: a prior DFU flagged bank 0 as valid. + if (p_bootloader_settings->bank_0 == BANK_VALID_APP) { uint16_t image_crc = 0; @@ -177,10 +176,26 @@ bool bootloader_app_is_valid(void) NULL); } - success = (image_crc == p_bootloader_settings->bank_0_crc); + return (image_crc == p_bootloader_settings->bank_0_crc); } - return success; + // Fallback for debugger-flashed apps: settings page is erased (bank_0 = 0xFFFF) + // but an app may still have been written to bank 0 directly. Accept it if its + // Cortex-M vector table is plausible: initial SP within RAM, reset vector + // within app flash region with the Thumb bit set. The SP upper bound is + // inclusive because the empty-descending stack initializes one past RAM end. + if (p_bootloader_settings->bank_0 == 0xFFFF) + { + uint32_t const ram_start = 0x20000000UL; + uint32_t const ram_end = ram_start + (NRF_FICR->INFO.RAM << 10u); + bool const sp_valid = (sp_addr >= ram_start) && (sp_addr <= ram_end) && ((sp_addr & 3U) == 0); + bool const reset_in_app = (reset_addr & 1U) && + (reset_addr >= app_addr) && + (reset_addr < BOOTLOADER_REGION_START); + return sp_valid && reset_in_app; + } + + return false; } From 64f1b70922de014e9bce81ab52daff037ddc8910 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 20 May 2026 18:07:12 +0700 Subject: [PATCH 71/84] usb tud_mount_cb() to set m_usb_was_mounted --- .../libraries/bootloader_dfu/bootloader.c | 155 +++++++++--------- .../libraries/bootloader_dfu/bootloader.h | 31 ++-- src/usb/usb.c | 2 + 3 files changed, 98 insertions(+), 90 deletions(-) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c index 059b6fdd..2acbb091 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c @@ -48,29 +48,34 @@ typedef enum BOOTLOADER_RESET_TO_SELF, /**< Bootloader status field for indicating that a reset has been requested and current update process should be aborted and the bootloader must be reentered. */ } bootloader_status_t; -static pstorage_handle_t m_bootsettings_handle; /**< Pstorage handle to use for registration and identifying the bootloader module on subsequent calls to the pstorage module for load and store of bootloader setting in flash. */ -static bootloader_status_t m_update_status; /**< Current update status for the bootloader module to ensure correct behaviour when updating settings and when update completes. */ -static bool m_cancel_timeout_on_usb; /**< If set the timeout is cancelled when USB is enumerated. Otherwise, the timeout is only cancelled when DFU update is started. */ -static bool m_usb_was_mounted; /**< Tracks whether the USB DFU session was ever enumerated so we can exit when cable is later removed. */ -static bool m_startup_dfu_has_activity; /**< Tracks whether any valid serial or UF2 update traffic has started. */ - -APP_TIMER_DEF( _dfu_startup_timer ); - -static void bootloader_timeout_startup_dfu(void) -{ - if (!m_startup_dfu_has_activity) - { - dfu_update_status_t update_status; - update_status.status_code = DFU_TIMEOUT; - - bootloader_dfu_update_process(update_status); - } -} - -void bootloader_dfu_activity_mark(void) -{ - m_startup_dfu_has_activity = true; -} +static pstorage_handle_t m_bootsettings_handle; /**< Pstorage handle to use for registration and identifying the bootloader module on subsequent calls to the pstorage module for load and store of bootloader setting in flash. */ +static bootloader_status_t m_update_status; /**< Current update status for the bootloader module to ensure correct behaviour when updating settings and when update completes. */ +static bool m_cancel_timeout_on_usb; /**< If set the timeout is cancelled when USB is enumerated. Otherwise, the timeout is only cancelled when DFU update is started. */ +static bool m_usb_was_mounted; /**< Tracks whether the USB DFU session was ever enumerated so we can exit when cable is later removed. */ +static bool m_startup_dfu_has_activity; /**< Tracks whether any valid serial or UF2 update traffic has started. */ + +APP_TIMER_DEF( _dfu_startup_timer ); + +static void bootloader_timeout_startup_dfu(void) +{ + if (!m_startup_dfu_has_activity) + { + dfu_update_status_t update_status; + update_status.status_code = DFU_TIMEOUT; + + bootloader_dfu_update_process(update_status); + } +} + +void bootloader_dfu_activity_mark(void) +{ + m_startup_dfu_has_activity = true; +} + +void bootloader_mark_usb_mounted(void) +{ + m_usb_was_mounted = true; +} /**@brief Function for handling callbacks from pstorage module. * @@ -96,20 +101,21 @@ static void pstorage_callback_handler(pstorage_handle_t * p_handle, /* Terminate the forced DFU mode on startup if no packets is received * by put an terminal handler to scheduler */ -static void dfu_startup_timer_handler(void * p_context) -{ -#ifdef NRF_USBD - if (m_cancel_timeout_on_usb && tud_mounted()) - { - m_usb_was_mounted = true; - return; - } -#endif - - // nRF52832 forced DFU on startup - // No update activity is received within timeout, exit DFU mode - bootloader_timeout_startup_dfu(); -} +static void dfu_startup_timer_handler(void * p_context) +{ +#ifdef NRF_USBD + // If host enumerated the device, keep waiting — m_usb_was_mounted was set + // by tud_mount_cb() and the unplug path in wait_for_events() will handle exit. + if (m_cancel_timeout_on_usb && m_usb_was_mounted) + { + return; + } +#endif + + // nRF52832 forced DFU on startup + // No update activity is received within timeout, exit DFU mode + bootloader_timeout_startup_dfu(); +} /**@brief Function for waiting for events. * @@ -137,29 +143,22 @@ static void wait_for_events(void) // Event received. Process it from the scheduler. app_sched_execute(); -#ifdef NRF_USBD - // skip if usb is not inited ( e.g OTA / finializing sd/bootloader ) - if ( tusb_inited() ) - { - tud_task(); - tud_cdc_write_flush(); - } - - if (m_cancel_timeout_on_usb) - { - if (tusb_inited() && tud_mounted()) - { - m_usb_was_mounted = true; - } - else if (m_usb_was_mounted && - !(NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk)) - { - // Only exit startup DFU after USB was actually unplugged, not on a host-side - // re-enumeration or temporary unmount. - bootloader_timeout_startup_dfu(); - } - } -#endif +#ifdef NRF_USBD + // skip if usb is not inited ( e.g OTA / finializing sd/bootloader ) + if ( tusb_inited() ) + { + tud_task(); + tud_cdc_write_flush(); + } + + // Exit startup DFU once USB was actually unplugged (VBUS gone), not on a + // host-side re-enumeration or temporary unmount. + if (m_cancel_timeout_on_usb && m_usb_was_mounted && + !(NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk)) + { + bootloader_timeout_startup_dfu(); + } +#endif if ((m_update_status == BOOTLOADER_COMPLETE) || (m_update_status == BOOTLOADER_TIMEOUT) || @@ -379,17 +378,17 @@ uint32_t bootloader_init(void) } -uint32_t bootloader_dfu_start(bool ota, uint32_t timeout_ms, bool cancel_timeout_on_usb) -{ - uint32_t err_code; - - m_cancel_timeout_on_usb = cancel_timeout_on_usb && !ota; - m_usb_was_mounted = false; - m_startup_dfu_has_activity = false; - - // Clear swap if banked update is used. - err_code = dfu_init(); - VERIFY_SUCCESS(err_code); +uint32_t bootloader_dfu_start(bool ota, uint32_t timeout_ms, bool cancel_timeout_on_usb) +{ + uint32_t err_code; + + m_cancel_timeout_on_usb = cancel_timeout_on_usb && !ota; + m_usb_was_mounted = false; + m_startup_dfu_has_activity = false; + + // Clear swap if banked update is used. + err_code = dfu_init(); + VERIFY_SUCCESS(err_code); if ( ota ) { @@ -397,13 +396,13 @@ uint32_t bootloader_dfu_start(bool ota, uint32_t timeout_ms, bool cancel_timeout }else { // DFU mode with timeout can be - // - Forced startup DFU for nRF52832 or - // - Makecode single tap reset but no enumerated (battery power) - if ( timeout_ms ) - { - app_timer_create(&_dfu_startup_timer, APP_TIMER_MODE_SINGLE_SHOT, dfu_startup_timer_handler); - app_timer_start(_dfu_startup_timer, APP_TIMER_TICKS(timeout_ms), NULL); - } + // - Forced startup DFU for nRF52832 or + // - Makecode single tap reset but no enumerated (battery power) + if ( timeout_ms ) + { + app_timer_create(&_dfu_startup_timer, APP_TIMER_MODE_SINGLE_SHOT, dfu_startup_timer_handler); + app_timer_start(_dfu_startup_timer, APP_TIMER_TICKS(timeout_ms), NULL); + } err_code = dfu_transport_serial_update_start(); } diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h index 5c68e9bc..952a95fe 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h @@ -68,18 +68,25 @@ void bootloader_app_start(void); */ void bootloader_settings_get(bootloader_settings_t * const p_settings); -/**@brief Function for processing DFU status update. - * - * @param[in] update_status DFU update status. - */ -void bootloader_dfu_update_process(dfu_update_status_t update_status); - -/**@brief Mark that startup DFU has received valid update activity. - * - * @details This suppresses the startup timeout or USB unplug fallback that is only - * meant for the "entered bootloader, but never started an update" case. - */ -void bootloader_dfu_activity_mark(void); +/**@brief Function for processing DFU status update. + * + * @param[in] update_status DFU update status. + */ +void bootloader_dfu_update_process(dfu_update_status_t update_status); + +/**@brief Mark that startup DFU has received valid update activity. + * + * @details This suppresses the startup timeout or USB unplug fallback that is only + * meant for the "entered bootloader, but never started an update" case. + */ +void bootloader_dfu_activity_mark(void); + +/**@brief Mark that the USB DFU session was enumerated by a host. + * + * @details Called from tud_mount_cb(). Enables the USB-unplug exit path in + * wait_for_events(), which exits startup DFU once VBUS is removed. + */ +void bootloader_mark_usb_mounted(void); /**@brief Function getting state of SoftDevice update in progress. * After a successfull SoftDevice transfer the system restarts in orderto disable SoftDevice diff --git a/src/usb/usb.c b/src/usb/usb.c index ecc881b7..b89e9e96 100644 --- a/src/usb/usb.c +++ b/src/usb/usb.c @@ -33,6 +33,7 @@ #include "uf2/uf2.h" #include "boards.h" +#include "bootloader.h" //--------------------------------------------------------------------+ // MACRO TYPEDEF CONSTANT ENUM DECLARATION @@ -115,6 +116,7 @@ void usb_teardown(void) { //--------------------------------------------------------------------+ void tud_mount_cb(void) { led_state(STATE_USB_MOUNTED); + bootloader_mark_usb_mounted(); } void tud_umount_cb(void) { From 2f272950fc0a9a5df5756e07449b27ca109afe21 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 20 May 2026 22:38:07 +0700 Subject: [PATCH 72/84] dfu_transport_serial: normalize new lines to CRLF PR introduced LF on the new #include and the activity_mark call inside an otherwise-CRLF file, leaving it mixed-EOL. Convert those lines back to CRLF to match the rest of the file. Co-Authored-By: Claude Opus 4.7 --- .../bootloader_dfu/dfu_transport_serial.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c index b8c65ae3..e27d600c 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c @@ -10,11 +10,11 @@ * */ -#include "dfu_transport.h" -#include -#include "bootloader.h" -#include "dfu.h" -#include +#include "dfu_transport.h" +#include +#include "bootloader.h" +#include "dfu.h" +#include #include "app_error.h" #include "app_util.h" #include "hci_transport.h" @@ -198,8 +198,8 @@ static void process_dfu_packet(void * p_event_data, uint16_t event_size) uint32_t index; dfu_update_packet_t * packet; - // Mark that startup DFU has transitioned into a real update flow. - bootloader_dfu_activity_mark(); + // Mark that startup DFU has transitioned into a real update flow. + bootloader_dfu_activity_mark(); while (false == DATA_QUEUE_EMPTY()) { From dc42c2a7c0fcecbf8d6e52b053457d94324665d1 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 21 May 2026 11:49:01 +0700 Subject: [PATCH 73/84] update cmake version as well --- CMakeLists.txt | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e17d10e..58238bf2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -316,13 +316,22 @@ endif() execute_process(COMMAND git describe --dirty --always --tags OUTPUT_VARIABLE GIT_VERSION) string(STRIP ${GIT_VERSION} GIT_VERSION) -string(REPLACE "-" ";" RELEASE_VERSION ${GIT_VERSION}) -list(GET RELEASE_VERSION 0 RELEASE_VERSION) -string(REPLACE "." ";" RELEASE_VERSION ${RELEASE_VERSION}) -list(GET RELEASE_VERSION 0 RELEASE_VERSION_MAJOR) -list(GET RELEASE_VERSION 1 RELEASE_VERSION_MINOR) -list(GET RELEASE_VERSION 2 RELEASE_VERSION_PATCH) -math(EXPR MK_BOOTLOADER_VERSION "(${RELEASE_VERSION_MAJOR} << 16) + (${RELEASE_VERSION_MINOR} << 8) + ${RELEASE_VERSION_PATCH}") +# Extract semantic version numbers (MAJOR.MINOR.PATCH) from GIT_VERSION. +# Supports tags like: +# v1.2.3 +# 1.2.3 +# 1.2.3-147-gd71abcd +# If the version string does not match MAJOR.MINOR.PATCH, defaults to 0.0.0. +if(GIT_VERSION MATCHES "^v?([0-9]+)\\.([0-9]+)\\.([0-9]+)") + set(RELEASE_VERSION_MAJOR ${CMAKE_MATCH_1}) + set(RELEASE_VERSION_MINOR ${CMAKE_MATCH_2}) + set(RELEASE_VERSION_PATCH ${CMAKE_MATCH_3}) +else() + set(RELEASE_VERSION_MAJOR 0) + set(RELEASE_VERSION_MINOR 0) + set(RELEASE_VERSION_PATCH 0) +endif() +math(EXPR MK_BOOTLOADER_VERSION "((${RELEASE_VERSION_MAJOR} << 16) | (${RELEASE_VERSION_MINOR} << 8) | ${RELEASE_VERSION_PATCH})") cmake_print_variables(GIT_VERSION MK_BOOTLOADER_VERSION) From dfabf13967eb216c3dc9b16e51ccd7d2966becab Mon Sep 17 00:00:00 2001 From: James Rich Date: Thu, 20 Aug 2026 16:48:17 -0500 Subject: [PATCH 74/84] feat: prune the board matrix to the 17 Meshtastic boards, on the mainline board.h contract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mainline renamed BUTTON_1/BUTTON_2 to BUTTON_DFU/BUTTON_DFU_OTA, dropped BUTTONS_NUMBER, and provides PINNUM() from boards.h — the 14 boards that only exist here are translated mechanically. The 3 boards mainline also ships (t1000_e, both xiao) keep mainline's board.h and only gain the per-board DFU advertising name in board.mk. board.cmake files dropped: the CMake path was never wired here. --- src/boards/adm_b_nrf52840_1/board.cmake | 1 - src/boards/adm_b_nrf52840_1/board.mk | 1 - src/boards/ae_bl652_bo/board.cmake | 1 - src/boards/ae_bl652_bo/board.h | 58 --------- src/boards/ae_bl652_bo/board.mk | 2 - src/boards/aramcon2_badge/board.cmake | 1 - src/boards/aramcon2_badge/board.h | 62 --------- src/boards/aramcon2_badge/board.mk | 1 - src/boards/aramcon_badge_2019/board.cmake | 1 - src/boards/aramcon_badge_2019/board.h | 65 ---------- src/boards/aramcon_badge_2019/board.mk | 1 - src/boards/arduino_nano_33_ble/board.cmake | 1 - src/boards/arduino_nano_33_ble/board.h | 61 --------- src/boards/arduino_nano_33_ble/board.mk | 1 - src/boards/bast_ble/board.cmake | 1 - src/boards/bast_ble/board.h | 62 --------- src/boards/bast_ble/board.mk | 1 - src/boards/bluemicro_nrf52833/board.cmake | 1 - src/boards/bluemicro_nrf52833/board.h | 66 ---------- src/boards/bluemicro_nrf52833/board.mk | 2 - src/boards/bluemicro_nrf52833/pinconfig.c | 19 --- src/boards/bluemicro_nrf52840/board.cmake | 1 - src/boards/bluemicro_nrf52840/board.h | 62 --------- src/boards/bluemicro_nrf52840/board.mk | 1 - src/boards/challenger_840_ble/board.cmake | 1 - src/boards/challenger_840_ble/board.h | 70 ---------- src/boards/challenger_840_ble/board.mk | 1 - src/boards/challenger_840_ble/pinconfig.c | 27 ---- .../circuitplayground_nrf52840/board.cmake | 1 - src/boards/circuitplayground_nrf52840/board.h | 65 ---------- .../circuitplayground_nrf52840/board.mk | 1 - src/boards/clue_nrf52840/board.cmake | 1 - src/boards/clue_nrf52840/board.h | 91 ------------- src/boards/clue_nrf52840/board.mk | 1 - src/boards/ebyte_e104_bt5032a/board.cmake | 1 - src/boards/ebyte_e104_bt5032a/board.h | 66 ---------- src/boards/ebyte_e104_bt5032a/board.mk | 2 - src/boards/ebyte_e73_tbb/board.cmake | 1 - src/boards/ebyte_e73_tbb/board.h | 67 ---------- src/boards/ebyte_e73_tbb/board.mk | 2 - src/boards/electronut_labs_papyr/board.cmake | 1 - src/boards/electronut_labs_papyr/board.h | 66 ---------- src/boards/electronut_labs_papyr/board.mk | 1 - src/boards/feather_nrf52832/board.cmake | 1 - src/boards/feather_nrf52832/board.h | 61 --------- src/boards/feather_nrf52832/board.mk | 2 - .../feather_nrf52840_express/board.cmake | 1 - src/boards/feather_nrf52840_express/board.mk | 1 - src/boards/feather_nrf52840_sense/board.cmake | 1 - src/boards/feather_nrf52840_sense/board.mk | 1 - .../feather_nrf52840_sense_tft/board.cmake | 1 - .../feather_nrf52840_sense_tft/board.mk | 1 - .../feather_nrf52840_sense_tft/pinconfig.c | 19 --- src/boards/heltec_t096/board.h | 120 ++++++++++++++++++ src/boards/heltec_t096/board.mk | 3 + .../pinconfig.c | 0 src/boards/heltec_t1/board.h | 119 +++++++++++++++++ src/boards/heltec_t1/board.mk | 3 + .../{aramcon2_badge => heltec_t1}/pinconfig.c | 0 .../board.h | 81 ++++++------ src/boards/heltec_t114/board.mk | 3 + .../pinconfig.c | 0 src/boards/ikigaisense_vita/board.cmake | 1 - src/boards/ikigaisense_vita/board.h | 66 ---------- src/boards/ikigaisense_vita/board.mk | 1 - src/boards/ikigaisense_vita/pinconfig.c | 19 --- .../itsybitsy_nrf52840_express/board.cmake | 1 - .../itsybitsy_nrf52840_express/board.mk | 1 - .../itsybitsy_nrf52840_express/pinconfig.c | 47 ------- src/boards/ledglasses_nrf52840/board.cmake | 1 - src/boards/ledglasses_nrf52840/board.h | 65 ---------- src/boards/ledglasses_nrf52840/board.mk | 1 - src/boards/ledglasses_nrf52840/pinconfig.c | 19 --- .../{nice_nano => lilygo_techo}/board.h | 36 +++--- src/boards/lilygo_techo/board.mk | 2 + .../pinconfig.c | 0 src/boards/m60_keyboard/board.cmake | 1 - src/boards/m60_keyboard/board.h | 73 ----------- src/boards/m60_keyboard/board.mk | 2 - src/boards/m60_keyboard/pinconfig.c | 61 --------- src/boards/mbn52832dk/board.cmake | 1 - src/boards/mbn52832dk/board.h | 62 --------- src/boards/mbn52832dk/board.mk | 2 - src/boards/mdk_nrf52840_dongle/board.cmake | 1 - src/boards/mdk_nrf52840_dongle/board.h | 69 ---------- src/boards/mdk_nrf52840_dongle/board.mk | 1 - src/boards/mdk_nrf52840_dongle/pinconfig.c | 19 --- src/boards/metro_nrf52840_express/board.cmake | 1 - src/boards/metro_nrf52840_express/board.h | 67 ---------- src/boards/metro_nrf52840_express/board.mk | 1 - src/boards/metro_nrf52840_express/pinconfig.c | 19 --- src/boards/mikoto/board.cmake | 1 - src/boards/mikoto/board.h | 62 --------- src/boards/mikoto/board.mk | 1 - src/boards/mikoto/pinconfig.c | 19 --- .../{nrfmicro => minewsemi_mx25le01}/board.h | 39 +++--- src/boards/minewsemi_mx25le01/board.mk | 3 + .../pinconfig.c | 0 src/boards/nRF52840_connect_kit/board.cmake | 1 - src/boards/nRF52840_connect_kit/board.h | 68 ---------- src/boards/nRF52840_connect_kit/board.mk | 2 - src/boards/nRF52840_connect_kit/pinconfig.c | 19 --- src/boards/nice_nano/board.cmake | 1 - src/boards/nice_nano/board.mk | 1 - src/boards/nice_nano/pinconfig.c | 19 --- src/boards/nrf52840_bboard/board.cmake | 1 - src/boards/nrf52840_bboard/board.h | 65 ---------- src/boards/nrf52840_bboard/board.mk | 1 - src/boards/nrf52840_m2/board.cmake | 1 - src/boards/nrf52840_m2/board.h | 69 ---------- src/boards/nrf52840_m2/board.mk | 2 - src/boards/nrf52840_m2/pinconfig.c | 19 --- src/boards/nrfmicro/board.cmake | 1 - src/boards/nrfmicro/board.mk | 2 - src/boards/ohs2020_badge/board.cmake | 1 - src/boards/ohs2020_badge/board.h | 62 --------- src/boards/ohs2020_badge/board.mk | 1 - src/boards/ohs2020_badge/pinconfig.c | 19 --- src/boards/omnimo_nrf52840/board.cmake | 1 - src/boards/omnimo_nrf52840/board.h | 69 ---------- src/boards/omnimo_nrf52840/board.mk | 1 - src/boards/omnimo_nrf52840/pinconfig.c | 19 --- src/boards/particle_argon/board.cmake | 1 - src/boards/particle_argon/board.h | 67 ---------- src/boards/particle_argon/board.mk | 1 - src/boards/particle_argon/pinconfig.c | 19 --- src/boards/particle_boron/board.cmake | 1 - src/boards/particle_boron/board.h | 67 ---------- src/boards/particle_boron/board.mk | 1 - src/boards/particle_boron/pinconfig.c | 19 --- src/boards/particle_xenon/board.cmake | 1 - src/boards/particle_xenon/board.h | 67 ---------- src/boards/particle_xenon/board.mk | 1 - src/boards/particle_xenon/pinconfig.c | 19 --- src/boards/pca10056/board.cmake | 1 - src/boards/pca10056/board.mk | 1 - src/boards/pca10056/pinconfig.c | 19 --- src/boards/pca10059/board.cmake | 1 - src/boards/pca10059/board.h | 63 --------- src/boards/pca10059/board.mk | 1 - src/boards/pca10059/pinconfig.c | 19 --- src/boards/pca10100/board.cmake | 1 - src/boards/pca10100/board.mk | 1 - src/boards/pca10100/pinconfig.c | 19 --- src/boards/pillbug/board.cmake | 1 - src/boards/pillbug/board.mk | 1 - src/boards/pillbug/pinconfig.c | 19 --- src/boards/pitaya_go/board.cmake | 1 - src/boards/pitaya_go/board.h | 68 ---------- src/boards/pitaya_go/board.mk | 2 - src/boards/pitaya_go/pinconfig.c | 19 --- .../{pillbug => promicro_nrf52840}/board.h | 38 +++--- src/boards/promicro_nrf52840/board.mk | 2 + .../pinconfig.c | 0 src/boards/raytac_mdbt50q_db_40/board.cmake | 1 - src/boards/raytac_mdbt50q_db_40/board.h | 69 ---------- src/boards/raytac_mdbt50q_db_40/board.mk | 1 - src/boards/raytac_mdbt50q_db_40/pinconfig.c | 19 --- src/boards/raytac_mdbt50q_rx/board.cmake | 1 - src/boards/raytac_mdbt50q_rx/board.mk | 1 - src/boards/raytac_mdbt50q_rx/pinconfig.c | 19 --- .../board.h | 41 +++--- src/boards/sensecap_solar_p1/board.mk | 3 + .../pinconfig.c | 0 .../sparkfun_nrf52840_micromod/board.cmake | 1 - src/boards/sparkfun_nrf52840_micromod/board.h | 63 --------- .../sparkfun_nrf52840_micromod/board.mk | 1 - .../sparkfun_nrf52840_micromod/pinconfig.c | 19 --- src/boards/syldra/board.cmake | 1 - src/boards/syldra/board.h | 61 --------- src/boards/syldra/board.mk | 2 - src/boards/t1000_e/board.mk | 1 + src/boards/{pca10056 => thinknode_m1}/board.h | 39 +++--- src/boards/thinknode_m1/board.mk | 2 + .../{nrfmicro => thinknode_m1}/pinconfig.c | 0 src/boards/{pca10100 => thinknode_m3}/board.h | 39 +++--- src/boards/thinknode_m3/board.mk | 2 + .../pinconfig.c | 0 src/boards/thinknode_m6/board.h | 58 +++++++++ src/boards/thinknode_m6/board.mk | 2 + .../pinconfig.c | 0 .../waveshare_nrf52840_eval/board.cmake | 1 - src/boards/waveshare_nrf52840_eval/board.h | 69 ---------- src/boards/waveshare_nrf52840_eval/board.mk | 1 - .../waveshare_nrf52840_eval/pinconfig.c | 19 --- .../board.h | 40 +++--- src/boards/wio_tracker_l1/board.mk | 3 + .../{syldra => wio_tracker_l1}/pinconfig.c | 0 .../board.h | 42 +++--- src/boards/wiscore_rak3401/board.mk | 2 + .../pinconfig.c | 0 .../board.h | 45 ++++--- src/boards/wiscore_rak4631_board/board.mk | 2 + .../pinconfig.c | 0 .../board.h | 33 +++-- src/boards/wismesh_tag/board.mk | 2 + .../pinconfig.c | 0 src/boards/xiao_nrf52840_ble/board.mk | 3 +- src/boards/xiao_nrf52840_ble_sense/board.mk | 3 +- 199 files changed, 567 insertions(+), 3252 deletions(-) delete mode 100644 src/boards/adm_b_nrf52840_1/board.cmake delete mode 100644 src/boards/adm_b_nrf52840_1/board.mk delete mode 100644 src/boards/ae_bl652_bo/board.cmake delete mode 100644 src/boards/ae_bl652_bo/board.h delete mode 100644 src/boards/ae_bl652_bo/board.mk delete mode 100644 src/boards/aramcon2_badge/board.cmake delete mode 100644 src/boards/aramcon2_badge/board.h delete mode 100644 src/boards/aramcon2_badge/board.mk delete mode 100644 src/boards/aramcon_badge_2019/board.cmake delete mode 100644 src/boards/aramcon_badge_2019/board.h delete mode 100644 src/boards/aramcon_badge_2019/board.mk delete mode 100644 src/boards/arduino_nano_33_ble/board.cmake delete mode 100644 src/boards/arduino_nano_33_ble/board.h delete mode 100644 src/boards/arduino_nano_33_ble/board.mk delete mode 100644 src/boards/bast_ble/board.cmake delete mode 100644 src/boards/bast_ble/board.h delete mode 100644 src/boards/bast_ble/board.mk delete mode 100644 src/boards/bluemicro_nrf52833/board.cmake delete mode 100644 src/boards/bluemicro_nrf52833/board.h delete mode 100644 src/boards/bluemicro_nrf52833/board.mk delete mode 100644 src/boards/bluemicro_nrf52833/pinconfig.c delete mode 100644 src/boards/bluemicro_nrf52840/board.cmake delete mode 100644 src/boards/bluemicro_nrf52840/board.h delete mode 100644 src/boards/bluemicro_nrf52840/board.mk delete mode 100644 src/boards/challenger_840_ble/board.cmake delete mode 100644 src/boards/challenger_840_ble/board.h delete mode 100644 src/boards/challenger_840_ble/board.mk delete mode 100644 src/boards/challenger_840_ble/pinconfig.c delete mode 100644 src/boards/circuitplayground_nrf52840/board.cmake delete mode 100644 src/boards/circuitplayground_nrf52840/board.h delete mode 100644 src/boards/circuitplayground_nrf52840/board.mk delete mode 100644 src/boards/clue_nrf52840/board.cmake delete mode 100644 src/boards/clue_nrf52840/board.h delete mode 100644 src/boards/clue_nrf52840/board.mk delete mode 100644 src/boards/ebyte_e104_bt5032a/board.cmake delete mode 100644 src/boards/ebyte_e104_bt5032a/board.h delete mode 100644 src/boards/ebyte_e104_bt5032a/board.mk delete mode 100644 src/boards/ebyte_e73_tbb/board.cmake delete mode 100644 src/boards/ebyte_e73_tbb/board.h delete mode 100644 src/boards/ebyte_e73_tbb/board.mk delete mode 100644 src/boards/electronut_labs_papyr/board.cmake delete mode 100644 src/boards/electronut_labs_papyr/board.h delete mode 100644 src/boards/electronut_labs_papyr/board.mk delete mode 100644 src/boards/feather_nrf52832/board.cmake delete mode 100644 src/boards/feather_nrf52832/board.h delete mode 100644 src/boards/feather_nrf52832/board.mk delete mode 100644 src/boards/feather_nrf52840_express/board.cmake delete mode 100644 src/boards/feather_nrf52840_express/board.mk delete mode 100644 src/boards/feather_nrf52840_sense/board.cmake delete mode 100644 src/boards/feather_nrf52840_sense/board.mk delete mode 100644 src/boards/feather_nrf52840_sense_tft/board.cmake delete mode 100644 src/boards/feather_nrf52840_sense_tft/board.mk delete mode 100644 src/boards/feather_nrf52840_sense_tft/pinconfig.c create mode 100644 src/boards/heltec_t096/board.h create mode 100644 src/boards/heltec_t096/board.mk rename src/boards/{adm_b_nrf52840_1 => heltec_t096}/pinconfig.c (100%) create mode 100644 src/boards/heltec_t1/board.h create mode 100644 src/boards/heltec_t1/board.mk rename src/boards/{aramcon2_badge => heltec_t1}/pinconfig.c (100%) rename src/boards/{feather_nrf52840_sense_tft => heltec_t114}/board.h (55%) create mode 100644 src/boards/heltec_t114/board.mk rename src/boards/{aramcon_badge_2019 => heltec_t114}/pinconfig.c (100%) delete mode 100644 src/boards/ikigaisense_vita/board.cmake delete mode 100644 src/boards/ikigaisense_vita/board.h delete mode 100644 src/boards/ikigaisense_vita/board.mk delete mode 100644 src/boards/ikigaisense_vita/pinconfig.c delete mode 100644 src/boards/itsybitsy_nrf52840_express/board.cmake delete mode 100644 src/boards/itsybitsy_nrf52840_express/board.mk delete mode 100644 src/boards/itsybitsy_nrf52840_express/pinconfig.c delete mode 100644 src/boards/ledglasses_nrf52840/board.cmake delete mode 100644 src/boards/ledglasses_nrf52840/board.h delete mode 100644 src/boards/ledglasses_nrf52840/board.mk delete mode 100644 src/boards/ledglasses_nrf52840/pinconfig.c rename src/boards/{nice_nano => lilygo_techo}/board.h (71%) create mode 100644 src/boards/lilygo_techo/board.mk rename src/boards/{arduino_nano_33_ble => lilygo_techo}/pinconfig.c (100%) delete mode 100644 src/boards/m60_keyboard/board.cmake delete mode 100644 src/boards/m60_keyboard/board.h delete mode 100644 src/boards/m60_keyboard/board.mk delete mode 100644 src/boards/m60_keyboard/pinconfig.c delete mode 100644 src/boards/mbn52832dk/board.cmake delete mode 100644 src/boards/mbn52832dk/board.h delete mode 100644 src/boards/mbn52832dk/board.mk delete mode 100644 src/boards/mdk_nrf52840_dongle/board.cmake delete mode 100644 src/boards/mdk_nrf52840_dongle/board.h delete mode 100644 src/boards/mdk_nrf52840_dongle/board.mk delete mode 100644 src/boards/mdk_nrf52840_dongle/pinconfig.c delete mode 100644 src/boards/metro_nrf52840_express/board.cmake delete mode 100644 src/boards/metro_nrf52840_express/board.h delete mode 100644 src/boards/metro_nrf52840_express/board.mk delete mode 100644 src/boards/metro_nrf52840_express/pinconfig.c delete mode 100644 src/boards/mikoto/board.cmake delete mode 100644 src/boards/mikoto/board.h delete mode 100644 src/boards/mikoto/board.mk delete mode 100644 src/boards/mikoto/pinconfig.c rename src/boards/{nrfmicro => minewsemi_mx25le01}/board.h (69%) create mode 100644 src/boards/minewsemi_mx25le01/board.mk rename src/boards/{bast_ble => minewsemi_mx25le01}/pinconfig.c (100%) delete mode 100644 src/boards/nRF52840_connect_kit/board.cmake delete mode 100644 src/boards/nRF52840_connect_kit/board.h delete mode 100644 src/boards/nRF52840_connect_kit/board.mk delete mode 100644 src/boards/nRF52840_connect_kit/pinconfig.c delete mode 100644 src/boards/nice_nano/board.cmake delete mode 100644 src/boards/nice_nano/board.mk delete mode 100644 src/boards/nice_nano/pinconfig.c delete mode 100644 src/boards/nrf52840_bboard/board.cmake delete mode 100644 src/boards/nrf52840_bboard/board.h delete mode 100644 src/boards/nrf52840_bboard/board.mk delete mode 100644 src/boards/nrf52840_m2/board.cmake delete mode 100644 src/boards/nrf52840_m2/board.h delete mode 100644 src/boards/nrf52840_m2/board.mk delete mode 100644 src/boards/nrf52840_m2/pinconfig.c delete mode 100644 src/boards/nrfmicro/board.cmake delete mode 100644 src/boards/nrfmicro/board.mk delete mode 100644 src/boards/ohs2020_badge/board.cmake delete mode 100644 src/boards/ohs2020_badge/board.h delete mode 100644 src/boards/ohs2020_badge/board.mk delete mode 100644 src/boards/ohs2020_badge/pinconfig.c delete mode 100644 src/boards/omnimo_nrf52840/board.cmake delete mode 100644 src/boards/omnimo_nrf52840/board.h delete mode 100644 src/boards/omnimo_nrf52840/board.mk delete mode 100644 src/boards/omnimo_nrf52840/pinconfig.c delete mode 100644 src/boards/particle_argon/board.cmake delete mode 100644 src/boards/particle_argon/board.h delete mode 100644 src/boards/particle_argon/board.mk delete mode 100644 src/boards/particle_argon/pinconfig.c delete mode 100644 src/boards/particle_boron/board.cmake delete mode 100644 src/boards/particle_boron/board.h delete mode 100644 src/boards/particle_boron/board.mk delete mode 100644 src/boards/particle_boron/pinconfig.c delete mode 100644 src/boards/particle_xenon/board.cmake delete mode 100644 src/boards/particle_xenon/board.h delete mode 100644 src/boards/particle_xenon/board.mk delete mode 100644 src/boards/particle_xenon/pinconfig.c delete mode 100644 src/boards/pca10056/board.cmake delete mode 100644 src/boards/pca10056/board.mk delete mode 100644 src/boards/pca10056/pinconfig.c delete mode 100644 src/boards/pca10059/board.cmake delete mode 100644 src/boards/pca10059/board.h delete mode 100644 src/boards/pca10059/board.mk delete mode 100644 src/boards/pca10059/pinconfig.c delete mode 100644 src/boards/pca10100/board.cmake delete mode 100644 src/boards/pca10100/board.mk delete mode 100644 src/boards/pca10100/pinconfig.c delete mode 100644 src/boards/pillbug/board.cmake delete mode 100644 src/boards/pillbug/board.mk delete mode 100644 src/boards/pillbug/pinconfig.c delete mode 100644 src/boards/pitaya_go/board.cmake delete mode 100644 src/boards/pitaya_go/board.h delete mode 100644 src/boards/pitaya_go/board.mk delete mode 100644 src/boards/pitaya_go/pinconfig.c rename src/boards/{pillbug => promicro_nrf52840}/board.h (69%) create mode 100644 src/boards/promicro_nrf52840/board.mk rename src/boards/{bluemicro_nrf52840 => promicro_nrf52840}/pinconfig.c (100%) delete mode 100644 src/boards/raytac_mdbt50q_db_40/board.cmake delete mode 100644 src/boards/raytac_mdbt50q_db_40/board.h delete mode 100644 src/boards/raytac_mdbt50q_db_40/board.mk delete mode 100644 src/boards/raytac_mdbt50q_db_40/pinconfig.c delete mode 100644 src/boards/raytac_mdbt50q_rx/board.cmake delete mode 100644 src/boards/raytac_mdbt50q_rx/board.mk delete mode 100644 src/boards/raytac_mdbt50q_rx/pinconfig.c rename src/boards/{adm_b_nrf52840_1 => sensecap_solar_p1}/board.h (67%) create mode 100644 src/boards/sensecap_solar_p1/board.mk rename src/boards/{nrf52840_bboard => sensecap_solar_p1}/pinconfig.c (100%) delete mode 100644 src/boards/sparkfun_nrf52840_micromod/board.cmake delete mode 100644 src/boards/sparkfun_nrf52840_micromod/board.h delete mode 100644 src/boards/sparkfun_nrf52840_micromod/board.mk delete mode 100644 src/boards/sparkfun_nrf52840_micromod/pinconfig.c delete mode 100644 src/boards/syldra/board.cmake delete mode 100644 src/boards/syldra/board.h delete mode 100644 src/boards/syldra/board.mk rename src/boards/{pca10056 => thinknode_m1}/board.h (68%) create mode 100644 src/boards/thinknode_m1/board.mk rename src/boards/{nrfmicro => thinknode_m1}/pinconfig.c (100%) rename src/boards/{pca10100 => thinknode_m3}/board.h (69%) create mode 100644 src/boards/thinknode_m3/board.mk rename src/boards/{circuitplayground_nrf52840 => thinknode_m3}/pinconfig.c (100%) create mode 100644 src/boards/thinknode_m6/board.h create mode 100644 src/boards/thinknode_m6/board.mk rename src/boards/{clue_nrf52840 => thinknode_m6}/pinconfig.c (100%) delete mode 100644 src/boards/waveshare_nrf52840_eval/board.cmake delete mode 100644 src/boards/waveshare_nrf52840_eval/board.h delete mode 100644 src/boards/waveshare_nrf52840_eval/board.mk delete mode 100644 src/boards/waveshare_nrf52840_eval/pinconfig.c rename src/boards/{raytac_mdbt50q_rx => wio_tracker_l1}/board.h (70%) create mode 100644 src/boards/wio_tracker_l1/board.mk rename src/boards/{syldra => wio_tracker_l1}/pinconfig.c (100%) rename src/boards/{itsybitsy_nrf52840_express => wiscore_rak3401}/board.h (66%) create mode 100644 src/boards/wiscore_rak3401/board.mk rename src/boards/{electronut_labs_papyr => wiscore_rak3401}/pinconfig.c (100%) rename src/boards/{feather_nrf52840_sense => wiscore_rak4631_board}/board.h (62%) create mode 100644 src/boards/wiscore_rak4631_board/board.mk rename src/boards/{feather_nrf52840_express => wiscore_rak4631_board}/pinconfig.c (100%) rename src/boards/{feather_nrf52840_express => wismesh_tag}/board.h (74%) create mode 100644 src/boards/wismesh_tag/board.mk rename src/boards/{feather_nrf52840_sense => wismesh_tag}/pinconfig.c (100%) diff --git a/src/boards/adm_b_nrf52840_1/board.cmake b/src/boards/adm_b_nrf52840_1/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/adm_b_nrf52840_1/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/adm_b_nrf52840_1/board.mk b/src/boards/adm_b_nrf52840_1/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/adm_b_nrf52840_1/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/ae_bl652_bo/board.cmake b/src/boards/ae_bl652_bo/board.cmake deleted file mode 100644 index e1b9ded7..00000000 --- a/src/boards/ae_bl652_bo/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52) diff --git a/src/boards/ae_bl652_bo/board.h b/src/boards/ae_bl652_bo/board.h deleted file mode 100644 index 77232d89..00000000 --- a/src/boards/ae_bl652_bo/board.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _AE_BL652_BO_H -#define _AE_BL652_BO_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 17 // LED1 -#define LED_SECONDARY_PIN 19 // LED2 -#define LED_STATE_ON 1 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU 11 // BTN1 -#define BUTTON_DFU_OTA 15 // BTN2 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -/*------------------------------------------------------------------*/ -/* UART (only used by nRF52832) - *------------------------------------------------------------------*/ -#define RX_PIN_NUMBER 8 -#define TX_PIN_NUMBER 6 -#define CTS_PIN_NUMBER 0 -#define RTS_PIN_NUMBER 0 -#define HWFC false - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Akizuki Denshi Tsusho" -#define BLEDIS_MODEL "AE-BL652-BO" - -#endif // _AE_BL652_BO_H diff --git a/src/boards/ae_bl652_bo/board.mk b/src/boards/ae_bl652_bo/board.mk deleted file mode 100644 index f932bb80..00000000 --- a/src/boards/ae_bl652_bo/board.mk +++ /dev/null @@ -1,2 +0,0 @@ -# nrf52 is nrf52832 -MCU_SUB_VARIANT = nrf52 diff --git a/src/boards/aramcon2_badge/board.cmake b/src/boards/aramcon2_badge/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/aramcon2_badge/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/aramcon2_badge/board.h b/src/boards/aramcon2_badge/board.h deleted file mode 100644 index cf61be98..00000000 --- a/src/boards/aramcon2_badge/board.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 Benjamin Meisels - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _ARAMCON2_BADGE_H -#define _ARAMCON2_BADGE_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(1, 11) // Red - -#define LED_NEOPIXEL PINNUM(0, 8) -#define NEOPIXELS_NUMBER 2 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 2) // left Button -#define BUTTON_DFU_OTA PINNUM(0, 29) // middle button -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -// Used as model string in OTA mode -#define BLEDIS_MANUFACTURER "ARAMCON Badge Team" -#define BLEDIS_MODEL "ARAMCON2 Badge" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x007B -#define USB_DESC_CDC_ONLY_PID 0x007B - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "ARAMCON2 Badge" -#define UF2_VOLUME_LABEL "ARAMBOOT" -#define UF2_BOARD_ID "nrf52840-ARAMCON2-Badge" -#define UF2_INDEX_URL "https://github.com/aramcon-badge" - -#endif // _ARAMCON2_BADGE_H diff --git a/src/boards/aramcon2_badge/board.mk b/src/boards/aramcon2_badge/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/aramcon2_badge/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/aramcon_badge_2019/board.cmake b/src/boards/aramcon_badge_2019/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/aramcon_badge_2019/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/aramcon_badge_2019/board.h b/src/boards/aramcon_badge_2019/board.h deleted file mode 100644 index b81fdbd9..00000000 --- a/src/boards/aramcon_badge_2019/board.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Uri Shaked - * Copyright (c) 2019 Benjamin Meisels - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _ARAMCON_BADGE_2019_H -#define _ARAMCON_BADGE_2019_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(1, 11) // Red - -#define LED_NEOPIXEL PINNUM(0, 8) -#define NEOPIXELS_NUMBER 4 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 2) // left Button -#define BUTTON_DFU_OTA PINNUM(0, 29) // middle button -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "ARAMCON Badge Team" -#define BLEDIS_MODEL "ARAMCON Badge 2019" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0079 -#define USB_DESC_CDC_ONLY_PID 0x0079 - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "ARAMCON Badge 2019" -#define UF2_VOLUME_LABEL "ARAMBOOT" -#define UF2_BOARD_ID "nrf52840-ARAMCON-Badge-2019" -#define UF2_INDEX_URL "https://github.com/aramcon-badge" - -#endif // _ARAMCON_BADGE_2019_H diff --git a/src/boards/aramcon_badge_2019/board.mk b/src/boards/aramcon_badge_2019/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/aramcon_badge_2019/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/arduino_nano_33_ble/board.cmake b/src/boards/arduino_nano_33_ble/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/arduino_nano_33_ble/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/arduino_nano_33_ble/board.h b/src/boards/arduino_nano_33_ble/board.h deleted file mode 100644 index 79e32fce..00000000 --- a/src/boards/arduino_nano_33_ble/board.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _ARDUINOBLE33_NRF52840_H -#define _ARDUINOBLE33_NRF52840_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 // red LED -#define LED_PRIMARY_PIN PINNUM(0, 24) -#define LED_STATE_ON 0 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(1, 11) // D2 switch -#define BUTTON_DFU_OTA PINNUM(1, 12) // D3 switch -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Arduino" -#define BLEDIS_MODEL "Nano 33 BLE" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0063 -#define USB_DESC_CDC_ONLY_PID 0x0064 - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Arduino Nano 33 BLE" -#define UF2_VOLUME_LABEL "NANO33BOOT" -#define UF2_BOARD_ID "nRF52840-Nano-33" -#define UF2_INDEX_URL "https://www.arduino.cc/" - -#endif // _ITSY_NRF52840_H diff --git a/src/boards/arduino_nano_33_ble/board.mk b/src/boards/arduino_nano_33_ble/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/arduino_nano_33_ble/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/bast_ble/board.cmake b/src/boards/bast_ble/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/bast_ble/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/bast_ble/board.h b/src/boards/bast_ble/board.h deleted file mode 100644 index 68a1ea76..00000000 --- a/src/boards/bast_ble/board.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _BASTBLE_NRF52840_H -#define _BASTBLE_NRF52840_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 // red LED -#define LED_PRIMARY_PIN PINNUM(0, 24) -#define LED_STATE_ON 0 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 05) -#define BUTTON_DFU_OTA PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "ElectronicCats" -#define BLEDIS_MODEL "BastBLE" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ - -// Shared VID/PID with Feather nRF52840, will be disabled for building in the future -#define USB_DESC_VID 0x1209 -#define USB_DESC_UF2_PID 0x805A -#define USB_DESC_CDC_ONLY_PID 0x805A - -#define UF2_PRODUCT_NAME "Bast BLE" -#define UF2_VOLUME_LABEL "BASTBLE" -#define UF2_BOARD_ID "Bast-ble-v1" -#define UF2_INDEX_URL "https://www.electroniccats.com" - -#endif // _BASTBLE_NRF52840_H diff --git a/src/boards/bast_ble/board.mk b/src/boards/bast_ble/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/bast_ble/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/bluemicro_nrf52833/board.cmake b/src/boards/bluemicro_nrf52833/board.cmake deleted file mode 100644 index cb4f2f9e..00000000 --- a/src/boards/bluemicro_nrf52833/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52833) diff --git a/src/boards/bluemicro_nrf52833/board.h b/src/boards/bluemicro_nrf52833/board.h deleted file mode 100644 index 454d3409..00000000 --- a/src/boards/bluemicro_nrf52833/board.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Pierre Constantineau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _BLUEMICRO_NRF52833_H -#define _BLUEMICRO_NRF52833_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN PINNUM(1, 4) -#define LED_SECONDARY_PIN PINNUM(0, 25) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL PINNUM(0, 7) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 18) // unusable: RESET -#define BUTTON_DFU_OTA PINNUM(1, 1) // no connection on E73-2G4M08S1E Module -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "BlueMicro" -#define BLEDIS_MODEL "BlueMicro 833" - -//--------------------------------------------------------------------+ -// USB PID from openmoko: -// PR https://github.com/openmoko/openmoko-usb-oui/pull/34 -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1d50 -#define USB_DESC_UF2_PID 0x616f -#define USB_DESC_CDC_ONLY_PID 0x616f - -#define UF2_PRODUCT_NAME "BlueMicro" -#define UF2_VOLUME_LABEL "BLUEMICRO" -#define UF2_BOARD_ID "nRF52833-BlueMicro-v1" -#define UF2_INDEX_URL "http://bluemicro.jpconstantineau.com/" - -#endif // _BLUEMICRO_H diff --git a/src/boards/bluemicro_nrf52833/board.mk b/src/boards/bluemicro_nrf52833/board.mk deleted file mode 100644 index be281d49..00000000 --- a/src/boards/bluemicro_nrf52833/board.mk +++ /dev/null @@ -1,2 +0,0 @@ -MCU_SUB_VARIANT = nrf52833 -SD_VERSION=7.2.0 diff --git a/src/boards/bluemicro_nrf52833/pinconfig.c b/src/boards/bluemicro_nrf52833/pinconfig.c deleted file mode 100644 index 47a268ef..00000000 --- a/src/boards/bluemicro_nrf52833/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x80000, // FLASH_BYTES - 205, 0x20000, // RAM_BYTES - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0x621e937a, // UF2_FAMILY = 0x621e937a - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/bluemicro_nrf52840/board.cmake b/src/boards/bluemicro_nrf52840/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/bluemicro_nrf52840/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/bluemicro_nrf52840/board.h b/src/boards/bluemicro_nrf52840/board.h deleted file mode 100644 index 46a2ef0e..00000000 --- a/src/boards/bluemicro_nrf52840/board.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Pierre Constantineau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _BLUEMICRO_NRF52840_H -#define _BLUEMICRO_NRF52840_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN PINNUM(1, 4) -#define LED_SECONDARY_PIN PINNUM(1, 10) -#define LED_STATE_ON 1 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 18) // unusable: RESET -#define BUTTON_DFU_OTA PINNUM(1, 1) // no connection on E73-2G4M08S1C Module -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "BlueMicro" -#define BLEDIS_MODEL "BlueMicro 840" - -//--------------------------------------------------------------------+ -// USB PID from openmoko: -// Approved PR https://github.com/openmoko/openmoko-usb-oui/pull/20 -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1d50 -#define USB_DESC_UF2_PID 0x6160 -#define USB_DESC_CDC_ONLY_PID 0x6160 - -#define UF2_PRODUCT_NAME "BlueMicro" -#define UF2_VOLUME_LABEL "BLUEMICRO" -#define UF2_BOARD_ID "nRF52840-BlueMicro-v1" -#define UF2_INDEX_URL "http://bluemicro.jpconstantineau.com/" - -#endif // _BLUEMICRO_H diff --git a/src/boards/bluemicro_nrf52840/board.mk b/src/boards/bluemicro_nrf52840/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/bluemicro_nrf52840/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/challenger_840_ble/board.cmake b/src/boards/challenger_840_ble/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/challenger_840_ble/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/challenger_840_ble/board.h b/src/boards/challenger_840_ble/board.h deleted file mode 100644 index 7be348c1..00000000 --- a/src/boards/challenger_840_ble/board.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries, 2022 Invector Labs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _CHALLENGER_840_BLE_H -#define _CHALLENGER_840_BLE_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 12) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL PINNUM(1, 8) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 19) -#define BUTTON_DFU_OTA PINNUM(0, 8) // Pulls flash cs high -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -/*------------------------------------------------------------------*/ -/* On board LDO control - *------------------------------------------------------------------*/ -#define LDO_CONTROL_PIN PINNUM(1, 9) // Enables external pwr - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Invector Labs" -#define BLEDIS_MODEL "Challenger 840 BLE" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1209 -#define USB_DESC_UF2_PID 0x7380 -#define USB_DESC_CDC_ONLY_PID 0x7381 - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "ILabs Challenger 840" -#define UF2_VOLUME_LABEL "CH840BOOT" -#define UF2_BOARD_ID "nRF52840-Challenger-840" -#define UF2_INDEX_URL "https://www.ilabs.se" - -#endif // _CHALLENGER_840_BLE_H diff --git a/src/boards/challenger_840_ble/board.mk b/src/boards/challenger_840_ble/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/challenger_840_ble/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/challenger_840_ble/pinconfig.c b/src/boards/challenger_840_ble/pinconfig.c deleted file mode 100644 index 601844c1..00000000 --- a/src/boards/challenger_840_ble/pinconfig.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "boards.h" -#include "board.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; - -void board_init2(void) -{ - // Turn LDO on - nrf_gpio_cfg_output(LDO_CONTROL_PIN); - nrf_gpio_pin_write(LDO_CONTROL_PIN, 1); -} diff --git a/src/boards/circuitplayground_nrf52840/board.cmake b/src/boards/circuitplayground_nrf52840/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/circuitplayground_nrf52840/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/circuitplayground_nrf52840/board.h b/src/boards/circuitplayground_nrf52840/board.h deleted file mode 100644 index 82a9bbf7..00000000 --- a/src/boards/circuitplayground_nrf52840/board.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _CPLAY_NRF52840_H -#define _CPLAY_NRF52840_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(1, 14) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL PINNUM(0, 13) -#define NEOPIXELS_NUMBER 10 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(1, 02) // left button -#define BUTTON_DFU_OTA PINNUM(1, 15) // right button -#define BUTTON_PULL NRF_GPIO_PIN_PULLDOWN - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Circuit Playground nRF52840" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0045 -#define USB_DESC_CDC_ONLY_PID 0x0045 - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit Circuit Playground nRF52840" -#define UF2_VOLUME_LABEL "CPLAYBTBOOT" -#define UF2_BOARD_ID "nRF52840-CircuitPlayground-revD" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4333" - -#endif // _CPLAY_NRF52840_H diff --git a/src/boards/circuitplayground_nrf52840/board.mk b/src/boards/circuitplayground_nrf52840/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/circuitplayground_nrf52840/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/clue_nrf52840/board.cmake b/src/boards/clue_nrf52840/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/clue_nrf52840/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/clue_nrf52840/board.h b/src/boards/clue_nrf52840/board.h deleted file mode 100644 index 91b8f568..00000000 --- a/src/boards/clue_nrf52840/board.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _CLUE_NRF52840_H -#define _CLUE_NRF52840_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(1, 01) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL PINNUM(0, 16) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(1, 02) // left button -#define BUTTON_DFU_OTA PINNUM(1, 10) // right button -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// Display -//--------------------------------------------------------------------+ -#define DISPLAY_CONTROLLER_ST7789 - -#define DISPLAY_PIN_SCK PINNUM(0, 14) -#define DISPLAY_PIN_MOSI PINNUM(0, 15) - -#define DISPLAY_PIN_CS PINNUM(0, 12) -#define DISPLAY_PIN_DC PINNUM(0, 13) -#define DISPLAY_PIN_RST PINNUM(1, 3) -#define DISPLAY_PIN_BL PINNUM(1, 5) -#define DISPLAY_BL_ON 1 // GPIO state to enable back light - -#define DISPLAY_WIDTH 240 -#define DISPLAY_HEIGHT 240 - -#define DISPLAY_COL_OFFSET 0 -#define DISPLAY_ROW_OFFSET 80 - -// Memory Data Access Control & // Vertical Scroll Start Address -#define DISPLAY_MADCTL (TFT_MADCTL_MY) -#define DISPLAY_VSCSAD 0 - -#define DISPLAY_TITLE "CLUE" - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "CLUE nRF52840" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0071 -#define USB_DESC_CDC_ONLY_PID 0x0071 - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit CLUE nRF52840" -#define UF2_VOLUME_LABEL "CLUEBOOT" -#define UF2_BOARD_ID "nRF52840-CLUE-revA" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4500" - -#endif // _CLUE_NRF52840_H diff --git a/src/boards/clue_nrf52840/board.mk b/src/boards/clue_nrf52840/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/clue_nrf52840/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/ebyte_e104_bt5032a/board.cmake b/src/boards/ebyte_e104_bt5032a/board.cmake deleted file mode 100644 index e1b9ded7..00000000 --- a/src/boards/ebyte_e104_bt5032a/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52) diff --git a/src/boards/ebyte_e104_bt5032a/board.h b/src/boards/ebyte_e104_bt5032a/board.h deleted file mode 100644 index 983f5ffa..00000000 --- a/src/boards/ebyte_e104_bt5032a/board.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 Pierre Constantineau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _EBYTE_E104_BT5032A_H -#define _EBYTE_E104_BT5032A_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 31 // Red - named "Data" -#define LED_SECONDARY_PIN 30 // Blue - named "Link" (also a red one on the Test Board) -#define LED_STATE_ON 0 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU \ - 29 // DISC Button on E104-BT5032A-TB Test Board - // BUTTON_DFU is DFU. The module does not have reset circuitry. - // The Test Board from Ebyte (E104-BT5032A-TB) also does not have reset circuitry - // To be able to upload using the Arduino IDE, (To enter DFU mode) - // Press "DISC", press and release "RST", then release "DISC" -#define BUTTON_DFU_OTA 28 // No button - WKP pin -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -/*------------------------------------------------------------------*/ -/* UART (only used by nRF52832) - *------------------------------------------------------------------*/ -#define RX_PIN_NUMBER 14 -#define TX_PIN_NUMBER 18 -#define CTS_PIN_NUMBER 20 -#define RTS_PIN_NUMBER 19 -#define HWFC false // leaving it false to make GPIO available - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "CDEBYTE" -#define BLEDIS_MODEL "E104-BT5032A" - -#define UF2_PRODUCT_NAME "Ebyte E104-BT5032A nRF52832" -#define UF2_INDEX_URL "https://www.ebyte.com/en/product-view-news.html?id=756" - -#endif // _EBYTE_E104_BT5032A_H diff --git a/src/boards/ebyte_e104_bt5032a/board.mk b/src/boards/ebyte_e104_bt5032a/board.mk deleted file mode 100644 index f932bb80..00000000 --- a/src/boards/ebyte_e104_bt5032a/board.mk +++ /dev/null @@ -1,2 +0,0 @@ -# nrf52 is nrf52832 -MCU_SUB_VARIANT = nrf52 diff --git a/src/boards/ebyte_e73_tbb/board.cmake b/src/boards/ebyte_e73_tbb/board.cmake deleted file mode 100644 index e1b9ded7..00000000 --- a/src/boards/ebyte_e73_tbb/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52) diff --git a/src/boards/ebyte_e73_tbb/board.h b/src/boards/ebyte_e73_tbb/board.h deleted file mode 100644 index bd522c56..00000000 --- a/src/boards/ebyte_e73_tbb/board.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 Pierre Constantineau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _EBYTE_E73_TBB_H -#define _EBYTE_E73_TBB_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 17 // Red - named "LED1" -#define LED_SECONDARY_PIN 18 // Blue - named "LED2" (also a red one on the Test Board) -#define LED_STATE_ON 0 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU \ - 14 // BUTTON_DFU is DFU. SW1 on E73-TBB Test Board. - // This Test board uses the E73-2G4M04S1B module which contains a nrf52832. - // The module does not have reset circuitry. - // The Test Board from Ebyte (E73-TBB) also does not have reset circuitry - // To be able to upload using the Arduino IDE, (To enter DFU mode) - // Press "SW1", momentarily ground "RST/P0.21", then release "SW1" -#define BUTTON_DFU_OTA 13 // SW2 on board -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -/*------------------------------------------------------------------*/ -/* UART (only used by nRF52832) - *------------------------------------------------------------------*/ -#define RX_PIN_NUMBER 8 -#define TX_PIN_NUMBER 6 -#define CTS_PIN_NUMBER 5 -#define RTS_PIN_NUMBER 7 -#define HWFC false // leaving it false to make GPIO available - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "CDEBYTE" -#define BLEDIS_MODEL "E73-TBB" - -#define UF2_PRODUCT_NAME "Ebyte E73-TBB nRF52832" -#define UF2_INDEX_URL "https://www.ebyte.com/en/product-view-news.aspx?id=889" - -#endif // _EBYTE_E73_TBB_H diff --git a/src/boards/ebyte_e73_tbb/board.mk b/src/boards/ebyte_e73_tbb/board.mk deleted file mode 100644 index f932bb80..00000000 --- a/src/boards/ebyte_e73_tbb/board.mk +++ /dev/null @@ -1,2 +0,0 @@ -# nrf52 is nrf52832 -MCU_SUB_VARIANT = nrf52 diff --git a/src/boards/electronut_labs_papyr/board.cmake b/src/boards/electronut_labs_papyr/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/electronut_labs_papyr/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/electronut_labs_papyr/board.h b/src/boards/electronut_labs_papyr/board.h deleted file mode 100644 index 69ce72fb..00000000 --- a/src/boards/electronut_labs_papyr/board.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * The MIT License (MIT) - * - * @author Tavish Naruka - * - * Copyright (c) 2018 Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef PAPYR_H -#define PAPYR_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 13 -#define LED_SECONDARY_PIN 14 -#define LED_STATE_ON 0 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -// NOTE: USB UF2 bootloader can be triggered by double pressing RESET -// App can trigger OTA bootloader by writing DFU_MAGIC_OTA_APPJUM to -// GPREGRET register if softdevice is not initialized; or by -// writing DFU_MAGIC_OTA_RESET in case softdevice is initialized. -#define BUTTON_DFU 18 // RESET also by default -#define BUTTON_DFU_OTA 1 // P0.1 not exposed anywhere, FRST n/a -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Electronut Labs" -#define BLEDIS_MODEL "Papyr" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define UF2_PRODUCT_NAME "Electronut Labs Papyr" -#define UF2_BOARD_ID "nRF52840-Papyr-v1" -#define UF2_INDEX_URL "https://docs.electronut.in/papyr" - -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x003B -#define USB_DESC_CDC_ONLY_PID 0x003B - -#endif // PPAPYR_H diff --git a/src/boards/electronut_labs_papyr/board.mk b/src/boards/electronut_labs_papyr/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/electronut_labs_papyr/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/feather_nrf52832/board.cmake b/src/boards/feather_nrf52832/board.cmake deleted file mode 100644 index e1b9ded7..00000000 --- a/src/boards/feather_nrf52832/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52) diff --git a/src/boards/feather_nrf52832/board.h b/src/boards/feather_nrf52832/board.h deleted file mode 100644 index 5e280a1f..00000000 --- a/src/boards/feather_nrf52832/board.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _FEATHER_NRF52832_H -#define _FEATHER_NRF52832_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 17 // Red -#define LED_SECONDARY_PIN 19 // Blue -#define LED_STATE_ON 1 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU 20 -#define BUTTON_DFU_OTA 22 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -/*------------------------------------------------------------------*/ -/* UART (only used by nRF52832) - *------------------------------------------------------------------*/ -#define RX_PIN_NUMBER 8 -#define TX_PIN_NUMBER 6 -#define CTS_PIN_NUMBER 0 -#define RTS_PIN_NUMBER 0 -#define HWFC false - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Feather nRF52832" - -#define UF2_PRODUCT_NAME "Adafruit Feather nRF52832" -#define UF2_INDEX_URL "https://www.adafruit.com/product/3406" - -#endif // _FEATHER_NRF52832_H diff --git a/src/boards/feather_nrf52832/board.mk b/src/boards/feather_nrf52832/board.mk deleted file mode 100644 index f932bb80..00000000 --- a/src/boards/feather_nrf52832/board.mk +++ /dev/null @@ -1,2 +0,0 @@ -# nrf52 is nrf52832 -MCU_SUB_VARIANT = nrf52 diff --git a/src/boards/feather_nrf52840_express/board.cmake b/src/boards/feather_nrf52840_express/board.cmake deleted file mode 100644 index 2715f360..00000000 --- a/src/boards/feather_nrf52840_express/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/feather_nrf52840_express/board.mk b/src/boards/feather_nrf52840_express/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/feather_nrf52840_express/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/feather_nrf52840_sense/board.cmake b/src/boards/feather_nrf52840_sense/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/feather_nrf52840_sense/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/feather_nrf52840_sense/board.mk b/src/boards/feather_nrf52840_sense/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/feather_nrf52840_sense/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/feather_nrf52840_sense_tft/board.cmake b/src/boards/feather_nrf52840_sense_tft/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/feather_nrf52840_sense_tft/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/feather_nrf52840_sense_tft/board.mk b/src/boards/feather_nrf52840_sense_tft/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/feather_nrf52840_sense_tft/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/feather_nrf52840_sense_tft/pinconfig.c b/src/boards/feather_nrf52840_sense_tft/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/feather_nrf52840_sense_tft/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/heltec_t096/board.h b/src/boards/heltec_t096/board.h new file mode 100644 index 00000000..930d6033 --- /dev/null +++ b/src/boards/heltec_t096/board.h @@ -0,0 +1,120 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _HELTEC_T096_H +#define _HELTEC_T096_H + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 28) // White +#define LED_STATE_ON 0 + +// #define LED_NEOPIXEL PINNUM(0, 14) +// #define NEOPIXEL_POWER_PIN PINNUM(0, 21) +// #define NEOPIXELS_NUMBER 2 +#define BOARD_RGB_BRIGHTNESS 0x040404 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_DFU PINNUM(1, 10) +#define BUTTON_DFU_OTA PINNUM(1, 2) // stock bootloader has this set to 34 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +//--------------------------------------------------------------------+ +// Display +//--------------------------------------------------------------------+ + +// VSensor required to power the display +#define DISPLAY_VSENSOR_PIN PINNUM(0, 26) +#define DISPLAY_VSENSOR_ON 1 + +// #define DISPLAY_CONTROLLER_ST7789 +#define DISPLAY_CONTROLLER_ST7735 + +#define DISPLAY_PIN_SCK PINNUM(0, 20) +#define DISPLAY_PIN_MOSI PINNUM(0, 17) + +#define DISPLAY_PIN_CS PINNUM(0, 22) +#define DISPLAY_PIN_DC PINNUM(0, 15) +#define DISPLAY_PIN_RST PINNUM(0, 13) +#define DISPLAY_PIN_BL PINNUM(1, 12) +#define DISPLAY_BL_ON 0 // GPIO state to enable back light + +#define DISPLAY_WIDTH 160 +#define DISPLAY_HEIGHT 80 + +// USB FACING LEFT +#define DISPLAY_COL_OFFSET 24 +#define DISPLAY_ROW_OFFSET 0 +#define DISPLAY_MADCTL (ST77XX_MADCTL_MY | ST7735_MADCTL_BGR) + +// USB FACING RIGHT +//#define DISPLAY_COL_OFFSET 52 +//#define DISPLAY_ROW_OFFSET 40 +//#define DISPLAY_MADCTL (ST77XX_MADCTL_MX | ST7735_MADCTL_BGR) + +#define DISPLAY_VSCSAD 0 + +#define DISPLAY_TITLE "T096" +#define BANNER_TEXT "meshtastic.org - OTAFIX" +#define FONT_SIZE_LARGE 3 +#define NOLABELS + +#define SCREEN_BAR1_Y 0 +#define SCREEN_BAR1_H 32 +#define SCREEN_BAR2_Y 32 +#define SCREEN_BAR2_H 36 +#define SCREEN_BAR3_Y 68 +#define SCREEN_BAR3_H 12 +#define BANNER_TEXT_X 0 +#define BANNER_TEXT_Y 70 +#define DISPLAY_TITLE_Y 1 +#define UF2_VERSION_BASE_Y 24 +#define BLE_OTA_Y 38 +#define DRAG 34 +#define DRAGX 4 + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "Heltec AutoMation" +#define BLEDIS_MODEL "Tracker" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0071 +#define USB_DESC_CDC_ONLY_PID 0x0071 + +//------------- UF2 -------------// +#define UF2_PRODUCT_NAME "Heltec-T096" +#define UF2_VOLUME_LABEL "HeltecT096" +#define UF2_BOARD_ID "Heltec-T096-v1" +#define UF2_INDEX_URL "http://heltec.org/" + +#endif diff --git a/src/boards/heltec_t096/board.mk b/src/boards/heltec_t096/board.mk new file mode 100644 index 00000000..e89d22d5 --- /dev/null +++ b/src/boards/heltec_t096/board.mk @@ -0,0 +1,3 @@ +MCU_SUB_VARIANT = nrf52840 +CFLAGS += -DDEVICE_NAME='"T096_DFU"' + diff --git a/src/boards/adm_b_nrf52840_1/pinconfig.c b/src/boards/heltec_t096/pinconfig.c similarity index 100% rename from src/boards/adm_b_nrf52840_1/pinconfig.c rename to src/boards/heltec_t096/pinconfig.c diff --git a/src/boards/heltec_t1/board.h b/src/boards/heltec_t1/board.h new file mode 100644 index 00000000..a542d906 --- /dev/null +++ b/src/boards/heltec_t1/board.h @@ -0,0 +1,119 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Ha Thach for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _HELTEC_T1_H +#define _HELTEC_T1_H + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 16) +#define LED_STATE_ON 0 + +// #define LED_NEOPIXEL PINNUM(0, 14) +// #define NEOPIXEL_POWER_PIN PINNUM(0, 21) +// #define NEOPIXELS_NUMBER 2 +// #define BOARD_RGB_BRIGHTNESS 0x040404 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_DFU PINNUM(1, 10) +#define BUTTON_DFU_OTA PINNUM(1, 2) // stock bootloader has this set to 34 +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +//--------------------------------------------------------------------+ +// Display +//--------------------------------------------------------------------+ + +// VSensor required to power the display +#define DISPLAY_VSENSOR_PIN PINNUM(0, 13) +#define DISPLAY_VSENSOR_ON 0 + +#define DISPLAY_CONTROLLER_ST7735 + +#define DISPLAY_PIN_SCK PINNUM(1, 0) +#define DISPLAY_PIN_MOSI PINNUM(0, 24) + +#define DISPLAY_PIN_CS PINNUM(0, 12) +#define DISPLAY_PIN_DC PINNUM(0, 22) +#define DISPLAY_PIN_RST PINNUM(0, 20) +#define DISPLAY_PIN_BL PINNUM(0, 15) +#define DISPLAY_BL_ON 0 // GPIO state to enable back light + +#define DISPLAY_WIDTH 160 +#define DISPLAY_HEIGHT 80 + +// // USB FACING DOWN +// #define DISPLAY_COL_OFFSET 24 +// #define DISPLAY_ROW_OFFSET 0 +// #define DISPLAY_MADCTL (ST77XX_MADCTL_MY | ST7735_MADCTL_BGR) + +// USB FACING UP +#define DISPLAY_COL_OFFSET 24 +#define DISPLAY_ROW_OFFSET 0 +#define DISPLAY_MADCTL (ST77XX_MADCTL_MX | ST7735_MADCTL_BGR) + +#define DISPLAY_VSCSAD 0 + +#define DISPLAY_TITLE "T1" +#define BANNER_TEXT "meshtastic.org - OTAFIX" +#define FONT_SIZE_LARGE 3 +#define NOLABELS + +#define SCREEN_BAR1_Y 0 +#define SCREEN_BAR1_H 32 +#define SCREEN_BAR2_Y 32 +#define SCREEN_BAR2_H 36 +#define SCREEN_BAR3_Y 68 +#define SCREEN_BAR3_H 12 +#define BANNER_TEXT_X 0 +#define BANNER_TEXT_Y 70 +#define DISPLAY_TITLE_Y 1 +#define UF2_VERSION_BASE_Y 24 +#define BLE_OTA_Y 38 +#define DRAG 34 +#define DRAGX 4 + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "Heltec AutoMation" +#define BLEDIS_MODEL "T1" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0071 +#define USB_DESC_CDC_ONLY_PID 0x0071 + +//------------- UF2 -------------// +#define UF2_PRODUCT_NAME "Heltec-T1" +#define UF2_VOLUME_LABEL "HeltecT1" +#define UF2_BOARD_ID "Heltec-T1" +#define UF2_INDEX_URL "http://heltec.org/" + +#endif diff --git a/src/boards/heltec_t1/board.mk b/src/boards/heltec_t1/board.mk new file mode 100644 index 00000000..bd6b8084 --- /dev/null +++ b/src/boards/heltec_t1/board.mk @@ -0,0 +1,3 @@ +MCU_SUB_VARIANT = nrf52840 +CFLAGS += -DDEVICE_NAME='"T1_DFU"' + diff --git a/src/boards/aramcon2_badge/pinconfig.c b/src/boards/heltec_t1/pinconfig.c similarity index 100% rename from src/boards/aramcon2_badge/pinconfig.c rename to src/boards/heltec_t1/pinconfig.c diff --git a/src/boards/feather_nrf52840_sense_tft/board.h b/src/boards/heltec_t114/board.h similarity index 55% rename from src/boards/feather_nrf52840_sense_tft/board.h rename to src/boards/heltec_t114/board.h index 2e97e02d..677badc6 100644 --- a/src/boards/feather_nrf52840_sense_tft/board.h +++ b/src/boards/heltec_t114/board.h @@ -22,76 +22,81 @@ * THE SOFTWARE. */ -#ifndef _FEATHER_NRF52840_SENSE_TFT_H -#define _FEATHER_NRF52840_SENSE_TFT_H +#ifndef _HELTEC_T114_H +#define _HELTEC_T114_H /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 11) -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(1, 3) // Green +#define LED_STATE_ON 0 -#define LED_NEOPIXEL PINNUM(1, 8) -#define NEOPIXEL_POWER_PIN PINNUM(1, 2) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 +#define LED_NEOPIXEL PINNUM(0, 14) +#define NEOPIXEL_POWER_PIN PINNUM(0, 21) +#define NEOPIXELS_NUMBER 2 +#define BOARD_RGB_BRIGHTNESS 0x040404 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(1, 6) -#define BUTTON_DFU_OTA PINNUM(1, 10) // DFU pin -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(1, 10) +#define BUTTON_DFU_OTA PINNUM(1, 10) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // Display //--------------------------------------------------------------------+ // VSensor required to power the display -#define DISPLAY_VSENSOR_PIN PINNUM(1, 7) -#define DISPLAY_VSENSOR_ON 1 +#define DISPLAY_VSENSOR_PIN PINNUM(0, 3) +#define DISPLAY_VSENSOR_ON 0 #define DISPLAY_CONTROLLER_ST7789 -#define DISPLAY_PIN_SCK PINNUM(0, 26) -#define DISPLAY_PIN_MOSI PINNUM(0, 5) +#define DISPLAY_PIN_SCK PINNUM(1, 8) +#define DISPLAY_PIN_MOSI PINNUM(1, 9) -#define DISPLAY_PIN_CS PINNUM(1, 5) -#define DISPLAY_PIN_DC PINNUM(1, 1) -#define DISPLAY_PIN_RST PINNUM(1, 3) -#define DISPLAY_PIN_BL PINNUM(0, 27) -#define DISPLAY_BL_ON 1 // GPIO state to enable back light +#define DISPLAY_PIN_CS PINNUM(0, 11) +#define DISPLAY_PIN_DC PINNUM(0, 12) +#define DISPLAY_PIN_RST PINNUM(0, 2) +#define DISPLAY_PIN_BL PINNUM(0, 15) +#define DISPLAY_BL_ON 0 // GPIO state to enable back light -#define DISPLAY_WIDTH 240 -#define DISPLAY_HEIGHT 135 +#define DISPLAY_WIDTH 240 +#define DISPLAY_HEIGHT 135 -#define DISPLAY_COL_OFFSET 53 -#define DISPLAY_ROW_OFFSET 40 +// USB FACING LEFT +#define DISPLAY_COL_OFFSET 53 +#define DISPLAY_ROW_OFFSET 40 +#define DISPLAY_MADCTL (TFT_MADCTL_MX | TFT_MADCTL_RGB) -// Memory Data Access Control & // Vertical Scroll Start Address -#define DISPLAY_MADCTL (TFT_MADCTL_MX) -#define DISPLAY_VSCSAD 0 +// USB FACING RIGHT +//#define DISPLAY_COL_OFFSET 52 +//#define DISPLAY_ROW_OFFSET 40 +//#define DISPLAY_MADCTL (TFT_MADCTL_MY | TFT_MADCTL_RGB) -#define DISPLAY_TITLE "Sense TFT" +#define DISPLAY_VSCSAD 0 + +#define DISPLAY_TITLE "HT-n5262" //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Feather nRF52840 Sense TFT" +#define BLEDIS_MANUFACTURER "Heltec AutoMation" +#define BLEDIS_MODEL "HT-n5262" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0087 -#define USB_DESC_CDC_ONLY_PID 0x0088 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0071 +#define USB_DESC_CDC_ONLY_PID 0x0071 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit Feather nRF52840 Sense TFT" -#define UF2_VOLUME_LABEL "SENSTFTBOOT" -#define UF2_BOARD_ID "nRF52840-FeatherSenseTFT-revA" -#define UF2_INDEX_URL "https://www.adafruit.com/product/" +#define UF2_PRODUCT_NAME "HT-n5262" +#define UF2_VOLUME_LABEL "HT-n5262" +#define UF2_BOARD_ID "HT-n5262" +#define UF2_INDEX_URL "HT-n5262" #endif diff --git a/src/boards/heltec_t114/board.mk b/src/boards/heltec_t114/board.mk new file mode 100644 index 00000000..2122d880 --- /dev/null +++ b/src/boards/heltec_t114/board.mk @@ -0,0 +1,3 @@ +MCU_SUB_VARIANT = nrf52840 +CFLAGS += -DDEVICE_NAME='"T114_DFU"' + diff --git a/src/boards/aramcon_badge_2019/pinconfig.c b/src/boards/heltec_t114/pinconfig.c similarity index 100% rename from src/boards/aramcon_badge_2019/pinconfig.c rename to src/boards/heltec_t114/pinconfig.c diff --git a/src/boards/ikigaisense_vita/board.cmake b/src/boards/ikigaisense_vita/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/ikigaisense_vita/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/ikigaisense_vita/board.h b/src/boards/ikigaisense_vita/board.h deleted file mode 100644 index 1c953474..00000000 --- a/src/boards/ikigaisense_vita/board.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _IKIGAISENSE_VITA_NRF52840_H -#define _IKIGAISENSE_VITA_NRF52840_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN PINNUM(1, 15) -#define LED_SECONDARY_PIN PINNUM(1, 10) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL PINNUM(0, 16) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(1, 02) -#define BUTTON_DFU_OTA PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "IkigaiSense Technologies" -#define BLEDIS_MODEL "Vita nRF52840" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0093 -#define USB_DESC_CDC_ONLY_PID 0x0093 - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "IkigaiSense Vita nRF52840" -#define UF2_VOLUME_LABEL "ISVITABoot" -#define UF2_BOARD_ID "nRF52840-isVITA-revA" -#define UF2_INDEX_URL "https://github.com/ikigaisense/vita" - -#endif // _IKIGAISENSE_VITA_NRF52840_H diff --git a/src/boards/ikigaisense_vita/board.mk b/src/boards/ikigaisense_vita/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/ikigaisense_vita/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/ikigaisense_vita/pinconfig.c b/src/boards/ikigaisense_vita/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/ikigaisense_vita/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/itsybitsy_nrf52840_express/board.cmake b/src/boards/itsybitsy_nrf52840_express/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/itsybitsy_nrf52840_express/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/itsybitsy_nrf52840_express/board.mk b/src/boards/itsybitsy_nrf52840_express/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/itsybitsy_nrf52840_express/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/itsybitsy_nrf52840_express/pinconfig.c b/src/boards/itsybitsy_nrf52840_express/pinconfig.c deleted file mode 100644 index 4217f9c1..00000000 --- a/src/boards/itsybitsy_nrf52840_express/pinconfig.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 31, 100, // used entries, total entries - 4, 0x1d, // PIN_BTN_A = PA29 - 7, 0x29, // PIN_DOTSTAR_CLOCK = PB09 - 8, 0x8, // PIN_DOTSTAR_DATA = PA08 - 13, 0x6, // PIN_LED = PA06 - 18, 0x14, // PIN_MISO = PA20 - 19, 0xf, // PIN_MOSI = PA15 - 21, 0x19, // PIN_RX = PA25 - 23, 0xd, // PIN_SCK = PA13 - 24, 0xe, // PIN_SCL = PA14 - 25, 0x10, // PIN_SDA = PA16 - 28, 0x18, // PIN_TX = PA24 - 100, 0x4, // PIN_A0 = PA04 - 101, 0x1e, // PIN_A1 = PA30 - 102, 0x1c, // PIN_A2 = PA28 - 103, 0x1f, // PIN_A3 = PA31 - 104, 0x2, // PIN_A4 = PA02 - 105, 0x3, // PIN_A5 = PA03 - 152, 0x22, // PIN_D2 = PB02 - 155, 0x1b, // PIN_D5 = PA27 - 157, 0x28, // PIN_D7 = PB08 - 159, 0x7, // PIN_D9 = PA07 - 160, 0x5, // PIN_D10 = PA05 - 161, 0x1a, // PIN_D11 = PA26 - 162, 0xb, // PIN_D12 = PA11 - 163, 0xc, // PIN_D13 = PA12 - 201, 0x1, // NUM_DOTSTARS = 1 - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* CF2 END */ -}; diff --git a/src/boards/ledglasses_nrf52840/board.cmake b/src/boards/ledglasses_nrf52840/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/ledglasses_nrf52840/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/ledglasses_nrf52840/board.h b/src/boards/ledglasses_nrf52840/board.h deleted file mode 100644 index 714fe94a..00000000 --- a/src/boards/ledglasses_nrf52840/board.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _LEDGLASSES_NRF52840_H -#define _LEDGLASSES_NRF52840_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 31) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL PINNUM(1, 15) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 30) // user switch -#define BUTTON_DFU_OTA PINNUM(0, 28) // NC -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "LED Glasses Driver nRF52840" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x010D -#define USB_DESC_CDC_ONLY_PID 0x010E - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit LED Glasses Driver nRF52840" -#define UF2_VOLUME_LABEL "GLASSESBOOT" -#define UF2_BOARD_ID "nRF52840-LedGlasses-revA" -#define UF2_INDEX_URL "https://www.adafruit.com/product/5217" - -#endif // _LEDGLASSES_NRF52840_H diff --git a/src/boards/ledglasses_nrf52840/board.mk b/src/boards/ledglasses_nrf52840/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/ledglasses_nrf52840/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/ledglasses_nrf52840/pinconfig.c b/src/boards/ledglasses_nrf52840/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/ledglasses_nrf52840/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/nice_nano/board.h b/src/boards/lilygo_techo/board.h similarity index 71% rename from src/boards/nice_nano/board.h rename to src/boards/lilygo_techo/board.h index 3196ae1f..50d45c1f 100644 --- a/src/boards/nice_nano/board.h +++ b/src/boards/lilygo_techo/board.h @@ -1,8 +1,6 @@ /* * The MIT License (MIT) * - * Copyright (c) 2020 Nick Winans - * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -22,39 +20,41 @@ * THE SOFTWARE. */ -#ifndef _NICENANO_H -#define _NICENANO_H +#ifndef _LILYGO_TECHO_H +#define _LILYGO_TECHO_H #define UICR_REGOUT0_VALUE UICR_REGOUT0_VOUT_3V3 /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 15) // Blue -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 14) // Blue +#define LED_STATE_ON 1 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -// This board has no buttons +#define BUTTON_DFU PINNUM(1, 10) // user button +#define BUTTON_DFU_OTA PINNUM(1, 10) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Nice Keyboards" -#define BLEDIS_MODEL "nice!nano" +#define BLEDIS_MANUFACTURER "LilyGo" +#define BLEDIS_MODEL "T-Echo v1.0" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00B3 -#define USB_DESC_CDC_ONLY_PID 0x00B3 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0029 +#define USB_DESC_CDC_ONLY_PID 0x0029 -#define UF2_PRODUCT_NAME "nice!nano" -#define UF2_VOLUME_LABEL "NICENANO" -#define UF2_BOARD_ID "nRF52840-nicenano" -#define UF2_INDEX_URL "https://nicekeyboards.com/docs/nice-nano" +#define UF2_PRODUCT_NAME "LilyGo T-Echo" +#define UF2_VOLUME_LABEL "TECHOBOOT" +#define UF2_BOARD_ID "nRF52840-TEcho-v1" +#define UF2_INDEX_URL "https://lilygo.cc/products/t-echo-lilygo" -#endif // _NICENANO_H +#endif // _LILYGO_TECHO_H diff --git a/src/boards/lilygo_techo/board.mk b/src/boards/lilygo_techo/board.mk new file mode 100644 index 00000000..4395cc8f --- /dev/null +++ b/src/boards/lilygo_techo/board.mk @@ -0,0 +1,2 @@ +MCU_SUB_VARIANT = nrf52840 +CFLAGS += -DDEVICE_NAME='"LGTE_DFU"' diff --git a/src/boards/arduino_nano_33_ble/pinconfig.c b/src/boards/lilygo_techo/pinconfig.c similarity index 100% rename from src/boards/arduino_nano_33_ble/pinconfig.c rename to src/boards/lilygo_techo/pinconfig.c diff --git a/src/boards/m60_keyboard/board.cmake b/src/boards/m60_keyboard/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/m60_keyboard/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/m60_keyboard/board.h b/src/boards/m60_keyboard/board.h deleted file mode 100644 index 057f1e35..00000000 --- a/src/boards/m60_keyboard/board.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Yihui Xiong for Makerdiary - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _MAKERDIARY_M60_KEYBOARD_H_ -#define _MAKERDIARY_M60_KEYBOARD_H_ - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 30) // Red -#define LED_STATE_ON 0 - -#define LED_RGB_RED_PIN PINNUM(0, 30) -#define LED_RGB_GREEN_PIN PINNUM(0, 29) -#define LED_RGB_BLUE_PIN PINNUM(0, 31) -#define BOARD_RGB_BRIGHTNESS 0x404040 -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 27) -#define BUTTON_DFU_OTA PINNUM(0, 19) // ESC -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -/*------------------------------------------------------------------*/ -/* On board regulator control - *------------------------------------------------------------------*/ -#define LDO_CONTROL_PIN PINNUM(0, 28) // Enables external pwr - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Makerdiary" -#define BLEDIS_MODEL "M60 Mechanical Keyboard" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x2886 -#define USB_DESC_UF2_PID 0xF00F -#define USB_DESC_CDC_ONLY_PID 0xF00F - -//--------------------------------------------------------------------+ -// UF2 -//--------------------------------------------------------------------+ -#define UF2_PRODUCT_NAME "MakerDiary M60 Mechanical Keyboard" -#define UF2_VOLUME_LABEL "M60KEYBOARD" -#define UF2_BOARD_ID "M60KEYBOARD" -#define UF2_INDEX_URL "https://wiki.makerdiary.com/m60/" - - -#endif /* _MAKERDIARY_M60_KEYBOARD_H_ */ diff --git a/src/boards/m60_keyboard/board.mk b/src/boards/m60_keyboard/board.mk deleted file mode 100644 index bea74cc3..00000000 --- a/src/boards/m60_keyboard/board.mk +++ /dev/null @@ -1,2 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 -USE_NFCT = yes diff --git a/src/boards/m60_keyboard/pinconfig.c b/src/boards/m60_keyboard/pinconfig.c deleted file mode 100644 index d831386e..00000000 --- a/src/boards/m60_keyboard/pinconfig.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "boards.h" -#include "board.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) const uint32_t bootloaderConfig[] = { - /* CF2 START */ - CFG_MAGIC0, - CFG_MAGIC1, // magic - 5, - 100, // used entries, total entries - - 204, - 0x100000, // FLASH_BYTES = 0x100000 - 205, - 0x40000, // RAM_BYTES = 0x40000 - 208, - (USB_DESC_VID << 16) | - USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, - 0xada52840, // UF2_FAMILY = 0xada52840 - 210, - 0x20, // PINS_PORT_SIZE = PA_32 - - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - /* CF2 END */ -}; - -static void power_on(void) { - // Turn on the regulator - nrf_gpio_cfg_output(LDO_CONTROL_PIN); - nrf_gpio_pin_write(LDO_CONTROL_PIN, 1); -} - -void board_init2(void) { - power_on(); - - // configure P0.05 for ESC/BUTTON_DFU_OTA - // P0.05 --- |<- --- / --- P0.19 - // diode sw(esc) - // mode: output, push-pull, low - nrf_gpio_cfg(PINNUM(0, 5), NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, - NRF_GPIO_PIN_H0D1, NRF_GPIO_PIN_NOSENSE); - nrf_gpio_pin_write(PINNUM(0, 5), 0); - - // Wait the buttons stable. - // This is mandatory, or the keyboard will enter bootloader whenever - // booted by pressing the button at back (same with BUTTON_DFU) - NRFX_DELAY_MS(300); -} - -void board_teardown2(void) { - // re-enable the battery - power_on(); -} diff --git a/src/boards/mbn52832dk/board.cmake b/src/boards/mbn52832dk/board.cmake deleted file mode 100644 index e1b9ded7..00000000 --- a/src/boards/mbn52832dk/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52) diff --git a/src/boards/mbn52832dk/board.h b/src/boards/mbn52832dk/board.h deleted file mode 100644 index 0e38c192..00000000 --- a/src/boards/mbn52832dk/board.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _MBN52832DK_H -#define _MBN52832DK_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 17 // LED1 -#define LED_SECONDARY_PIN 18 // LED2 -#define LED_STATE_ON 1 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU 13 -#define BUTTON_DFU_OTA 14 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -/*------------------------------------------------------------------*/ -/* UART (only used by nRF52832) - *------------------------------------------------------------------*/ -#define RX_PIN_NUMBER 8 -#define TX_PIN_NUMBER 6 -#define CTS_PIN_NUMBER 0 -#define RTS_PIN_NUMBER 0 -#define HWFC false - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Murata" -#define BLEDIS_MODEL "MBN52832DK" - -#define UF2_BOARD_ID "nRF52832-MBN52832DK-v1" -#define UF2_PRODUCT_NAME "Murata MBN52832DK" -#define UF2_INDEX_URL "https://www.murata.com/ja-jp/products/connectivitymodule/bluetooth/overview/lineup/mbn52832" - -#endif // _MBN52832DK_H diff --git a/src/boards/mbn52832dk/board.mk b/src/boards/mbn52832dk/board.mk deleted file mode 100644 index f932bb80..00000000 --- a/src/boards/mbn52832dk/board.mk +++ /dev/null @@ -1,2 +0,0 @@ -# nrf52 is nrf52832 -MCU_SUB_VARIANT = nrf52 diff --git a/src/boards/mdk_nrf52840_dongle/board.cmake b/src/boards/mdk_nrf52840_dongle/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/mdk_nrf52840_dongle/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/mdk_nrf52840_dongle/board.h b/src/boards/mdk_nrf52840_dongle/board.h deleted file mode 100644 index ab5d0e14..00000000 --- a/src/boards/mdk_nrf52840_dongle/board.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 gpshead (krypto.org) for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _MDK_NRF52840_DONGLE_H_ -#define _MDK_NRF52840_DONGLE_H_ - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 // TODO(gpshead): support 0. -#define LED_PRIMARY_PIN PINNUM(0, 23) // Red -#define LED_SECONDARY_PIN PINNUM(0, 24) // Blue -#define LED_STATE_ON 0 - -// #define LED_RGB_RED_PIN PINNUM(0, 23) -// #define LED_RGB_GREEN_PIN PINNUM(0, 22) -// #define LED_RGB_BLUE_PIN PINNUM(0, 24) -#define BOARD_RGB_BRIGHTNESS 0x404040 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -// TODO(gpshead): simplify, have code support 0. double reset only. -#define BUTTON_DFU PINNUM(0, 18) // unusable: RESET -#define BUTTON_DFU_OTA PINNUM(0, 19) // no connection -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "MakerDiary" -#define BLEDIS_MODEL "nRF52840 Micro Dev Kit USB Dongle" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ - -// Shared VID/PID with Feather nRF52840, will be disabled for building in the future -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0029 -#define USB_DESC_CDC_ONLY_PID 0x0029 - -#define UF2_PRODUCT_NAME "MDK nRF52840 USB Dongle" -#define UF2_VOLUME_LABEL "MDK840DONGL" -#define UF2_BOARD_ID "nRF52840-Dongle-v1" -#define UF2_INDEX_URL "https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/" - -#endif /* _MDK_NRF52840_DONGLE_H_ */ diff --git a/src/boards/mdk_nrf52840_dongle/board.mk b/src/boards/mdk_nrf52840_dongle/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/mdk_nrf52840_dongle/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/mdk_nrf52840_dongle/pinconfig.c b/src/boards/mdk_nrf52840_dongle/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/mdk_nrf52840_dongle/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/metro_nrf52840_express/board.cmake b/src/boards/metro_nrf52840_express/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/metro_nrf52840_express/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/metro_nrf52840_express/board.h b/src/boards/metro_nrf52840_express/board.h deleted file mode 100644 index 0c058384..00000000 --- a/src/boards/metro_nrf52840_express/board.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _METRO_NRF52840_H_ -#define _METRO_NRF52840_H_ - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN PINNUM(1, 13) -#define LED_SECONDARY_PIN PINNUM(1, 15) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL PINNUM(0, 13) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(1, 0) -#define BUTTON_DFU_OTA PINNUM(2, 2) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Metro nRF52840 Express" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x003F -#define USB_DESC_CDC_ONLY_PID 0x0040 - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit Metro nRF52840 Express" -#define UF2_VOLUME_LABEL "METR840BOOT" -#define UF2_BOARD_ID "nRF52840-Metro-revA" - -#define UF2_INDEX_URL "https://www.adafruit.com/" - -#endif /* _METRO_NRF52840_H_ */ diff --git a/src/boards/metro_nrf52840_express/board.mk b/src/boards/metro_nrf52840_express/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/metro_nrf52840_express/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/metro_nrf52840_express/pinconfig.c b/src/boards/metro_nrf52840_express/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/metro_nrf52840_express/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/mikoto/board.cmake b/src/boards/mikoto/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/mikoto/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/mikoto/board.h b/src/boards/mikoto/board.h deleted file mode 100644 index 9882893d..00000000 --- a/src/boards/mikoto/board.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 Hai-Ninh Dang - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _MIKOTO_H -#define _MIKOTO_H - -#define UICR_REGOUT0_VALUE UICR_REGOUT0_VOUT_3V3 - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 15) // Blue -#define LED_STATE_ON 1 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 18) // unusable: RESET -#define BUTTON_DFU_OTA PINNUM(0, 19) // no connection -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "zhiayang" -#define BLEDIS_MODEL "mikoto" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1209 -#define USB_DESC_UF2_PID 0x7A01 -#define USB_DESC_CDC_ONLY_PID 0x7A01 - -#define UF2_PRODUCT_NAME "Mikoto nRF52840" -#define UF2_VOLUME_LABEL "MIKOTO-BOOT" -#define UF2_BOARD_ID "nRF52840-mikoto-rev520" -#define UF2_INDEX_URL "https://github.com/zhiayang/mikoto" - -#endif // _MIKOTO_H diff --git a/src/boards/mikoto/board.mk b/src/boards/mikoto/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/mikoto/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/mikoto/pinconfig.c b/src/boards/mikoto/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/mikoto/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/nrfmicro/board.h b/src/boards/minewsemi_mx25le01/board.h similarity index 69% rename from src/boards/nrfmicro/board.h rename to src/boards/minewsemi_mx25le01/board.h index 78507e30..ba353c5c 100644 --- a/src/boards/nrfmicro/board.h +++ b/src/boards/minewsemi_mx25le01/board.h @@ -22,42 +22,41 @@ * THE SOFTWARE. */ -#ifndef _NRFMICRO_NRF52840 -#define _NRFMICRO_NRF52840 +#ifndef _MINEWSEMI_MX25LE01_H +#define _MINEWSEMI_MX25LE01_H /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(1, 10) -#define LED_STATE_ON 1 // State when LED is lit (active HIGH) - -#define NEOPIXELS_NUMBER 0 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN PINNUM(1, 7) +#define LED_SECONDARY_PIN PINNUM(1, 5) +#define LED_STATE_ON 1 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(1, 2) // DFU -#define BUTTON_DFU_OTA PINNUM(0, 12) // FRST -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 27) +#define BUTTON_DFU_OTA PINNUM(0, 11) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "nRFMicro" -#define BLEDIS_MODEL "nRFMicro nRF52840" +#define BLEDIS_MANUFACTURER "MinewSemi" +#define BLEDIS_MODEL "MX25LE01" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1209 -#define USB_DESC_UF2_PID 0x5284 -#define USB_DESC_CDC_ONLY_PID 0x5284 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0029 +#define USB_DESC_CDC_ONLY_PID 0x002A //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "nRFMicro" -#define UF2_VOLUME_LABEL "NRFMICRO" -#define UF2_BOARD_ID "nRF52840-nRFMicro-v0" -#define UF2_INDEX_URL "https://github.com/joric/nrfmicro/wiki" +#define UF2_PRODUCT_NAME "MinewSemi MX25LE01" +#define UF2_VOLUME_LABEL "MX25LE01" +#define UF2_BOARD_ID "MinewSemi-MX25LE01" +#define UF2_INDEX_URL "https://www.minewsemi.com" -#endif // _NRFMICRO_NRF52840 +#endif // _MINEWSEMI_MX25LE01_H diff --git a/src/boards/minewsemi_mx25le01/board.mk b/src/boards/minewsemi_mx25le01/board.mk new file mode 100644 index 00000000..7dbe0bcc --- /dev/null +++ b/src/boards/minewsemi_mx25le01/board.mk @@ -0,0 +1,3 @@ +MCU_SUB_VARIANT = nrf52840 +SD_VERSION = 7.3.0 +CFLAGS += -DDEVICE_NAME='"MX25_DFU"' \ No newline at end of file diff --git a/src/boards/bast_ble/pinconfig.c b/src/boards/minewsemi_mx25le01/pinconfig.c similarity index 100% rename from src/boards/bast_ble/pinconfig.c rename to src/boards/minewsemi_mx25le01/pinconfig.c diff --git a/src/boards/nRF52840_connect_kit/board.cmake b/src/boards/nRF52840_connect_kit/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/nRF52840_connect_kit/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/nRF52840_connect_kit/board.h b/src/boards/nRF52840_connect_kit/board.h deleted file mode 100644 index 9bb02ab9..00000000 --- a/src/boards/nRF52840_connect_kit/board.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Yihui Xiong for Makerdiary - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _MAKERDIARY_NRF82540_CONNECT_KIT_H_ -#define _MAKERDIARY_NRF82540_CONNECT_KIT_H_ - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(1, 15) // Green -#define LED_STATE_ON 0 - -#define LED_RGB_RED_PIN PINNUM(1, 10) -#define LED_RGB_GREEN_PIN PINNUM(1, 11) -#define LED_RGB_BLUE_PIN PINNUM(1, 12) -#define BOARD_RGB_BRIGHTNESS 0x404040 -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(1, 0) // P1.00: User Button -#define BUTTON_DFU_OTA PINNUM(1, 7) // P1.07: NC -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Makerdiary" -#define BLEDIS_MODEL "nRF52840 Connect Kit" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x2886 -#define USB_DESC_UF2_PID 0xF00F -#define USB_DESC_CDC_ONLY_PID 0xF00F - -//--------------------------------------------------------------------+ -// UF2 -//--------------------------------------------------------------------+ -#define UF2_PRODUCT_NAME "nRF52840 Connect Kit" -#define UF2_VOLUME_LABEL "CONNECTKIT" -#define UF2_BOARD_ID "nRF52840-Connect-Kit" -#define UF2_INDEX_URL "https://wiki.makerdiary.com/nrf52840-connectkit/" - - -#endif /* _MAKERDIARY_NRF82540_CONNECT_KIT_H_ */ diff --git a/src/boards/nRF52840_connect_kit/board.mk b/src/boards/nRF52840_connect_kit/board.mk deleted file mode 100644 index bea74cc3..00000000 --- a/src/boards/nRF52840_connect_kit/board.mk +++ /dev/null @@ -1,2 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 -USE_NFCT = yes diff --git a/src/boards/nRF52840_connect_kit/pinconfig.c b/src/boards/nRF52840_connect_kit/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/nRF52840_connect_kit/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/nice_nano/board.cmake b/src/boards/nice_nano/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/nice_nano/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/nice_nano/board.mk b/src/boards/nice_nano/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/nice_nano/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/nice_nano/pinconfig.c b/src/boards/nice_nano/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/nice_nano/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/nrf52840_bboard/board.cmake b/src/boards/nrf52840_bboard/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/nrf52840_bboard/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/nrf52840_bboard/board.h b/src/boards/nrf52840_bboard/board.h deleted file mode 100644 index 2c1977ed..00000000 --- a/src/boards/nrf52840_bboard/board.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 ddB0515(Dario Budimir) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _NRF52840_BBOARD_H -#define _NRF52840_BBOARD_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 7) -#define LED_STATE_ON 1 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -// NOTE: USB UF2 bootloader can be triggered by double pressing RESET -// App can trigger OTA bootloader by writing DFU_MAGIC_OTA_APPJUM to -// GPREGRET register if softdevice is not initialized; or by -// writing DFU_MAGIC_OTA_RESET in case softdevice is initialized. -#define BUTTON_DFU 18 // RESET also by default -#define BUTTON_DFU_OTA 1 // P0.1 not exposed anywhere, FRST n/a -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "ddB0515" -#define BLEDIS_MODEL "nRF52840 BBoard" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1d50 -#define USB_DESC_UF2_PID 0x6157 -#define USB_DESC_CDC_ONLY_PID 0x6157 - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "nRF52840 BBoard" -#define UF2_VOLUME_LABEL "BBOARDBOOT" -#define UF2_BOARD_ID "nRF52840-BBoard-rev2" -#define UF2_INDEX_URL "https://github.com/ddB0515/nRF52840-BBoard" - -#endif // _NRF52840_BBOARD_H diff --git a/src/boards/nrf52840_bboard/board.mk b/src/boards/nrf52840_bboard/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/nrf52840_bboard/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/nrf52840_m2/board.cmake b/src/boards/nrf52840_m2/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/nrf52840_m2/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/nrf52840_m2/board.h b/src/boards/nrf52840_m2/board.h deleted file mode 100644 index c093f5dc..00000000 --- a/src/boards/nrf52840_m2/board.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Yihui Xiong for Makerdiary - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _MAKERDIARY_NRF52840_M2_H_ -#define _MAKERDIARY_NRF52840_M2_H_ - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 30) // Red -#define LED_DOCK_GREEN_PIN PINNUM(1, 7) // Only available on the M.2 Dock -#define LED_STATE_ON 0 - -#define LED_RGB_RED_PIN PINNUM(0, 30) -#define LED_RGB_GREEN_PIN PINNUM(0, 29) -#define LED_RGB_BLUE_PIN PINNUM(0, 31) -#define BOARD_RGB_BRIGHTNESS 0x404040 -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 19) -#define BUTTON_DFU_OTA PINNUM(1, 7) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Makerdiary" -#define BLEDIS_MODEL "nRF52840 M.2" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x2886 -#define USB_DESC_UF2_PID 0xF00F -#define USB_DESC_CDC_ONLY_PID 0xF00F - -//--------------------------------------------------------------------+ -// UF2 -//--------------------------------------------------------------------+ -#define UF2_PRODUCT_NAME "MakerDiary nRF52840 M.2 Module" -#define UF2_VOLUME_LABEL "nRF52840M2" -#define UF2_BOARD_ID "nRF52840M2" -#define UF2_INDEX_URL "https://wiki.makerdiary.com/nrf52840-m2" - - -#endif /* _MAKERDIARY_NRF52840_M2_H_ */ diff --git a/src/boards/nrf52840_m2/board.mk b/src/boards/nrf52840_m2/board.mk deleted file mode 100644 index bea74cc3..00000000 --- a/src/boards/nrf52840_m2/board.mk +++ /dev/null @@ -1,2 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 -USE_NFCT = yes diff --git a/src/boards/nrf52840_m2/pinconfig.c b/src/boards/nrf52840_m2/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/nrf52840_m2/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/nrfmicro/board.cmake b/src/boards/nrfmicro/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/nrfmicro/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/nrfmicro/board.mk b/src/boards/nrfmicro/board.mk deleted file mode 100644 index 8a77053b..00000000 --- a/src/boards/nrfmicro/board.mk +++ /dev/null @@ -1,2 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 -SD_VERSION = 7.3.0 \ No newline at end of file diff --git a/src/boards/ohs2020_badge/board.cmake b/src/boards/ohs2020_badge/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/ohs2020_badge/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/ohs2020_badge/board.h b/src/boards/ohs2020_badge/board.h deleted file mode 100644 index a6cc9430..00000000 --- a/src/boards/ohs2020_badge/board.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * Copyright (c) 2020 Michael Welling - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _OHS2020_BADGE_H -#define _OHS2020_BADGE_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 04) // fake LED since we have none -#define LED_STATE_ON 1 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 29) // SW2 -#define BUTTON_DFU_OTA PINNUM(0, 03) // SW1 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "OSHWA" -#define BLEDIS_MODEL "OHS2020 Badge" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x007F -#define USB_DESC_CDC_ONLY_PID 0x007F - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "OHS2020 Badge" -#define UF2_VOLUME_LABEL "BADGEBOOT" -#define UF2_BOARD_ID "OHS2020-Badge-revA" -#define UF2_INDEX_URL "https://2020.oshwa.org/" - -#endif // _OHS2020_BADGE_H diff --git a/src/boards/ohs2020_badge/board.mk b/src/boards/ohs2020_badge/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/ohs2020_badge/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/ohs2020_badge/pinconfig.c b/src/boards/ohs2020_badge/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/ohs2020_badge/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/omnimo_nrf52840/board.cmake b/src/boards/omnimo_nrf52840/board.cmake deleted file mode 100644 index 2715f360..00000000 --- a/src/boards/omnimo_nrf52840/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/omnimo_nrf52840/board.h b/src/boards/omnimo_nrf52840/board.h deleted file mode 100644 index c26c811a..00000000 --- a/src/boards/omnimo_nrf52840/board.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _OMNIMO_NRF52840_H -#define _OMNIMO_NRF52840_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN PINNUM(1, 15) -#define LED_SECONDARY_PIN PINNUM(1, 10) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL PINNUM(0, 16) - -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(1, 02) -#define BUTTON_DFU_OTA PINNUM(1, 07) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "eAFAQ" -#define BLEDIS_MODEL "OMNIMO nRF52840" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ - -// Shared VID/PID with pca10056 -#define USB_DESC_VID 0x1209 -#define USB_DESC_UF2_PID 0xCECE -#define USB_DESC_CDC_ONLY_PID 0xCECE - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Omnimo nRF52840" -#define UF2_VOLUME_LABEL "OMNIn52BOOT" -#define UF2_BOARD_ID "nRF52840-Omnimo" -#define UF2_INDEX_URL "https://www.crowdsupply.com/eafaq/omnimo-nrf52840" - -#endif // _OMNIMO_NRF52840_H diff --git a/src/boards/omnimo_nrf52840/board.mk b/src/boards/omnimo_nrf52840/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/omnimo_nrf52840/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/omnimo_nrf52840/pinconfig.c b/src/boards/omnimo_nrf52840/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/omnimo_nrf52840/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/particle_argon/board.cmake b/src/boards/particle_argon/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/particle_argon/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/particle_argon/board.h b/src/boards/particle_argon/board.h deleted file mode 100644 index b210606c..00000000 --- a/src/boards/particle_argon/board.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _PARTICLE_ARGON_H -#define _PARTICLE_ARGON_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(1, 12) -#define LED_STATE_ON 1 - -#define LED_RGB_RED_PIN PINNUM(0, 13) -#define LED_RGB_GREEN_PIN PINNUM(0, 14) -#define LED_RGB_BLUE_PIN PINNUM(0, 15) -#define BOARD_RGB_BRIGHTNESS 0x202020 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 11) -#define BUTTON_DFU_OTA PINNUM(0, 03) // A0 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Particle Industries" -#define BLEDIS_MODEL "Argon" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ - -// Shared VID/PID with pca10056 -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00DA -#define USB_DESC_CDC_ONLY_PID 0x00DA - -#define UF2_PRODUCT_NAME "Particle Argon" -#define UF2_VOLUME_LABEL "ARGONBOOT " -#define UF2_BOARD_ID "nRF52840-Argon-v1" -#define UF2_INDEX_URL "https://www.particle.io/mesh/" - -#endif // _PARTICLE_ARGON_H diff --git a/src/boards/particle_argon/board.mk b/src/boards/particle_argon/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/particle_argon/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/particle_argon/pinconfig.c b/src/boards/particle_argon/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/particle_argon/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/particle_boron/board.cmake b/src/boards/particle_boron/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/particle_boron/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/particle_boron/board.h b/src/boards/particle_boron/board.h deleted file mode 100644 index 49cc6d50..00000000 --- a/src/boards/particle_boron/board.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _PARTICLE_BORON_H -#define _PARTICLE_BORON_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(1, 12) -#define LED_STATE_ON 1 - -#define LED_RGB_RED_PIN PINNUM(0, 13) -#define LED_RGB_GREEN_PIN PINNUM(0, 14) -#define LED_RGB_BLUE_PIN PINNUM(0, 15) -#define BOARD_RGB_BRIGHTNESS 0x202020 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 11) -#define BUTTON_DFU_OTA PINNUM(0, 03) // A0 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Particle Industries" -#define BLEDIS_MODEL "Boron" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ - -// Shared VID/PID with pca10056 -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00DA -#define USB_DESC_CDC_ONLY_PID 0x00DA - -#define UF2_PRODUCT_NAME "Particle Boron" -#define UF2_VOLUME_LABEL "BORONBOOT " -#define UF2_BOARD_ID "nRF52840-Boron-v1" -#define UF2_INDEX_URL "https://www.particle.io/mesh/" - -#endif // _PARTICLE_BORON_H diff --git a/src/boards/particle_boron/board.mk b/src/boards/particle_boron/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/particle_boron/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/particle_boron/pinconfig.c b/src/boards/particle_boron/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/particle_boron/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/particle_xenon/board.cmake b/src/boards/particle_xenon/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/particle_xenon/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/particle_xenon/board.h b/src/boards/particle_xenon/board.h deleted file mode 100644 index 5584bad4..00000000 --- a/src/boards/particle_xenon/board.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _PARTICLE_XENON_H -#define _PARTICLE_XENON_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(1, 12) -#define LED_STATE_ON 1 - -#define LED_RGB_RED_PIN PINNUM(0, 13) -#define LED_RGB_GREEN_PIN PINNUM(0, 14) -#define LED_RGB_BLUE_PIN PINNUM(0, 15) -#define BOARD_RGB_BRIGHTNESS 0x202020 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 11) -#define BUTTON_DFU_OTA PINNUM(0, 03) // A0 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Particle Industries" -#define BLEDIS_MODEL "Xenon" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ - -// Shared VID/PID with pca10056 -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00DA -#define USB_DESC_CDC_ONLY_PID 0x00DA - -#define UF2_PRODUCT_NAME "Particle Xenon" -#define UF2_VOLUME_LABEL "XENONBOOT " -#define UF2_BOARD_ID "nRF52840-Xenon-v1" -#define UF2_INDEX_URL "https://www.particle.io/mesh/" - -#endif // _PARTICLE_XENON_H diff --git a/src/boards/particle_xenon/board.mk b/src/boards/particle_xenon/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/particle_xenon/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/particle_xenon/pinconfig.c b/src/boards/particle_xenon/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/particle_xenon/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/pca10056/board.cmake b/src/boards/pca10056/board.cmake deleted file mode 100644 index 2715f360..00000000 --- a/src/boards/pca10056/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/pca10056/board.mk b/src/boards/pca10056/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/pca10056/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/pca10056/pinconfig.c b/src/boards/pca10056/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/pca10056/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/pca10059/board.cmake b/src/boards/pca10059/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/pca10059/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/pca10059/board.h b/src/boards/pca10059/board.h deleted file mode 100644 index 88c2f41a..00000000 --- a/src/boards/pca10059/board.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef PCA10059_H -#define PCA10059_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 6 // Red -#define LED_SECONDARY_PIN 12 // Blue -#define LED_STATE_ON 0 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ - -#define BUTTON_DFU PINNUM(1, 6) -#define BUTTON_DFU_OTA PINNUM(1, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Nordic" -#define BLEDIS_MODEL "PCA10059" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ - -// Shared VID/PID with Feather nRF52840, will be disabled for building in the future -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0029 -#define USB_DESC_CDC_ONLY_PID 0x0029 - -#define UF2_PRODUCT_NAME "Nordic nRF52840 Dongle" -#define UF2_BOARD_ID "nRF52840-pca10059-v1" -#define UF2_INDEX_URL "https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-Dongle" - -#endif // PCA10059_H diff --git a/src/boards/pca10059/board.mk b/src/boards/pca10059/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/pca10059/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/pca10059/pinconfig.c b/src/boards/pca10059/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/pca10059/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/pca10100/board.cmake b/src/boards/pca10100/board.cmake deleted file mode 100644 index cb4f2f9e..00000000 --- a/src/boards/pca10100/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52833) diff --git a/src/boards/pca10100/board.mk b/src/boards/pca10100/board.mk deleted file mode 100644 index a979f7e5..00000000 --- a/src/boards/pca10100/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52833 \ No newline at end of file diff --git a/src/boards/pca10100/pinconfig.c b/src/boards/pca10100/pinconfig.c deleted file mode 100644 index 47a268ef..00000000 --- a/src/boards/pca10100/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x80000, // FLASH_BYTES - 205, 0x20000, // RAM_BYTES - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0x621e937a, // UF2_FAMILY = 0x621e937a - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/pillbug/board.cmake b/src/boards/pillbug/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/pillbug/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/pillbug/board.mk b/src/boards/pillbug/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/pillbug/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/pillbug/pinconfig.c b/src/boards/pillbug/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/pillbug/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/pitaya_go/board.cmake b/src/boards/pitaya_go/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/pitaya_go/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/pitaya_go/board.h b/src/boards/pitaya_go/board.h deleted file mode 100644 index fd0b9944..00000000 --- a/src/boards/pitaya_go/board.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Yihui Xiong for Makerdiary - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _MAKERDIARY_PITAYA_GO_H_ -#define _MAKERDIARY_PITAYA_GO_H_ - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(1, 10) // Red -#define LED_STATE_ON 0 - -#define LED_RGB_RED_PIN PINNUM(1, 10) -#define LED_RGB_GREEN_PIN PINNUM(1, 11) -#define LED_RGB_BLUE_PIN PINNUM(1, 12) -#define BOARD_RGB_BRIGHTNESS 0x404040 -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(1, 0) // P1.00: User Button -#define BUTTON_DFU_OTA PINNUM(1, 7) // P1.07: NC -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Makerdiary" -#define BLEDIS_MODEL "Pitaya Go" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x2886 -#define USB_DESC_UF2_PID 0xF00E -#define USB_DESC_CDC_ONLY_PID 0xF00E - -//--------------------------------------------------------------------+ -// UF2 -//--------------------------------------------------------------------+ -#define UF2_PRODUCT_NAME "Pitaya Go" -#define UF2_VOLUME_LABEL "PITAYAGO" -#define UF2_BOARD_ID "PITAYAGO" -#define UF2_INDEX_URL "https://wiki.makerdiary.com/pitaya-go" - - -#endif /* _MAKERDIARY_PITAYA_GO_H_ */ diff --git a/src/boards/pitaya_go/board.mk b/src/boards/pitaya_go/board.mk deleted file mode 100644 index bea74cc3..00000000 --- a/src/boards/pitaya_go/board.mk +++ /dev/null @@ -1,2 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 -USE_NFCT = yes diff --git a/src/boards/pitaya_go/pinconfig.c b/src/boards/pitaya_go/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/pitaya_go/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/pillbug/board.h b/src/boards/promicro_nrf52840/board.h similarity index 69% rename from src/boards/pillbug/board.h rename to src/boards/promicro_nrf52840/board.h index 1d442c5c..12a156d7 100644 --- a/src/boards/pillbug/board.h +++ b/src/boards/promicro_nrf52840/board.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2022 Kyle McCreery + * Copyright (c) 2020 Nick Winans * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,41 +22,41 @@ * THE SOFTWARE. */ -#ifndef _PillBug_H -#define _PillBug_H +#ifndef _PROMICRO_NRF52840_H +#define _PROMICRO_NRF52840_H #define UICR_REGOUT0_VALUE UICR_REGOUT0_VOUT_3V3 /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 20) // Blue -#define LED_STATE_ON 1 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 15) // Blue +#define LED_STATE_ON 1 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 18) // unusable: RESET -#define BUTTON_DFU_OTA PINNUM(0, 21) // no connection -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(1, 0) // user button +#define BUTTON_DFU_OTA PINNUM(1, 0) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "MechWild" -#define BLEDIS_MODEL "PillBug" +#define BLEDIS_MANUFACTURER "Nologo" +#define BLEDIS_MODEL "ProMicro NRF52840" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x16D0 -#define USB_DESC_UF2_PID 0x10ED -#define USB_DESC_CDC_ONLY_PID 0x10ED +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x00B3 +#define USB_DESC_CDC_ONLY_PID 0x00B3 -#define UF2_PRODUCT_NAME "PillBug" -#define UF2_VOLUME_LABEL "PillBug" -#define UF2_BOARD_ID "nRF52840-PillBug" -#define UF2_INDEX_URL "https://mechwild.com" +#define UF2_PRODUCT_NAME "ProMicro NRF52840" +#define UF2_VOLUME_LABEL "PROMICRO" +#define UF2_BOARD_ID "nRF52840-promicro" +#define UF2_INDEX_URL "https://www.nologo.tech/product/otherboard/NRF52840.html" -#endif // _PillBug_H +#endif // _PROMICRO_NRF52840_H diff --git a/src/boards/promicro_nrf52840/board.mk b/src/boards/promicro_nrf52840/board.mk new file mode 100644 index 00000000..7feb66b8 --- /dev/null +++ b/src/boards/promicro_nrf52840/board.mk @@ -0,0 +1,2 @@ +MCU_SUB_VARIANT = nrf52840 +CFLAGS += -DDEVICE_NAME='"PROM_DFU"' diff --git a/src/boards/bluemicro_nrf52840/pinconfig.c b/src/boards/promicro_nrf52840/pinconfig.c similarity index 100% rename from src/boards/bluemicro_nrf52840/pinconfig.c rename to src/boards/promicro_nrf52840/pinconfig.c diff --git a/src/boards/raytac_mdbt50q_db_40/board.cmake b/src/boards/raytac_mdbt50q_db_40/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/raytac_mdbt50q_db_40/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/raytac_mdbt50q_db_40/board.h b/src/boards/raytac_mdbt50q_db_40/board.h deleted file mode 100644 index 2d26a3c6..00000000 --- a/src/boards/raytac_mdbt50q_db_40/board.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef RAYTAC_MDBT50Q_DB_40_H -#define RAYTAC_MDBT50Q_DB_40_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 3 -#define LED_PRIMARY_PIN PINNUM(1, 13) /* Green led marked D1 on the board */ -#define LED_SECONDARY_PIN PINNUM(1, 14) /* Red led marked D2 on the board */ -#define LED_TERTIARY_PIN PINNUM(1, 15) /* Blue led marked D3 on the board */ - -/* There are one more unpopulated LEDs, but for the purposes of bootloader that doesn't matter. */ -#define LED_STATE_ON 0 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ - -#define BUTTON_DFU PINNUM(0, 11) -#define BUTTON_DFU_OTA PINNUM(0, 12) -#define BUTTON_3 PINNUM(0, 24) -#define BUTTON_4 PINNUM(0, 25) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Raytac" -#define BLEDIS_MODEL "MDBT50Q-DB-40" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ - -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00BB -#define USB_DESC_CDC_ONLY_PID 0x00BB - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Raytac MDBT50Q Demo Board 40" -#define UF2_VOLUME_LABEL "MDBT50QBOOT" -#define UF2_BOARD_ID "nRF52840-MDBT50Q-1MV2" -#define UF2_INDEX_URL "https://www.raytac.com/product/ins.php?index_id=81" - -#endif // RAYTAC_MDBT50Q_DB_40_H diff --git a/src/boards/raytac_mdbt50q_db_40/board.mk b/src/boards/raytac_mdbt50q_db_40/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/raytac_mdbt50q_db_40/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/raytac_mdbt50q_db_40/pinconfig.c b/src/boards/raytac_mdbt50q_db_40/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/raytac_mdbt50q_db_40/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/raytac_mdbt50q_rx/board.cmake b/src/boards/raytac_mdbt50q_rx/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/raytac_mdbt50q_rx/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/raytac_mdbt50q_rx/board.mk b/src/boards/raytac_mdbt50q_rx/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/raytac_mdbt50q_rx/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/raytac_mdbt50q_rx/pinconfig.c b/src/boards/raytac_mdbt50q_rx/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/raytac_mdbt50q_rx/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/adm_b_nrf52840_1/board.h b/src/boards/sensecap_solar_p1/board.h similarity index 67% rename from src/boards/adm_b_nrf52840_1/board.h rename to src/boards/sensecap_solar_p1/board.h index 34dfa54f..d8c5d631 100644 --- a/src/boards/adm_b_nrf52840_1/board.h +++ b/src/boards/sensecap_solar_p1/board.h @@ -22,43 +22,42 @@ * THE SOFTWARE. */ -#ifndef ADM_B_NRF52840_1_H -#define ADM_B_NRF52840_1_H +#ifndef _SEEED_SOLAR_NODE_P1_H +#define _SEEED_SOLAR_NODE_P1_H /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 19) // Red -// #define LED_SECONDARY_PIN 12 // Blue -#define LED_STATE_ON 0 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 19) +#define LED_STATE_ON 1 + +#define NEOPIXELS_NUMBER 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ - -#define BUTTON_DFU PINNUM(0, 11) -#define BUTTON_DFU_OTA PINNUM(0, 12) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(1, 1) // Hold user button while pressing reset to enter OTA DFU mode +#define BUTTON_DFU_OTA PINNUM(1, 1) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Atelier-Du-Maker" -#define BLEDIS_MODEL "ADM_B_NRF52840_1" +#define BLEDIS_MANUFACTURER "Seeed" +#define BLEDIS_MODEL "SenseCAP Solar Node P1" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ - -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x009F -#define USB_DESC_CDC_ONLY_PID 0x009F +#define USB_DESC_VID 0x2886 +#define USB_DESC_UF2_PID 0x0044 +#define USB_DESC_CDC_ONLY_PID 0x0044 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "AtelierDuMaker NRF52840 Breakout" -#define UF2_VOLUME_LABEL "ADM840BOOT" -#define UF2_BOARD_ID "ADM_B_NRF52840_1" -#define UF2_INDEX_URL "https://github.com/Atelier-Du-Maker/HolyIoT_NRF52840" +#define UF2_PRODUCT_NAME "Seeed Solar Node P1" +#define UF2_VOLUME_LABEL "SENSECAP" +#define UF2_BOARD_ID "nRF52840-SeeedSenseCAPSolarP1-v1" +#define UF2_INDEX_URL "https://www.seeedstudio.com/" -#endif // ADM_B_NRF52840_1_H +#endif // _SEEED_SOLAR_NODE_P1_H diff --git a/src/boards/sensecap_solar_p1/board.mk b/src/boards/sensecap_solar_p1/board.mk new file mode 100644 index 00000000..9ebb0acd --- /dev/null +++ b/src/boards/sensecap_solar_p1/board.mk @@ -0,0 +1,3 @@ +MCU_SUB_VARIANT = nrf52840 +SD_VERSION = 7.3.0 +CFLAGS += -DDEVICE_NAME='"SCAP_DFU"' \ No newline at end of file diff --git a/src/boards/nrf52840_bboard/pinconfig.c b/src/boards/sensecap_solar_p1/pinconfig.c similarity index 100% rename from src/boards/nrf52840_bboard/pinconfig.c rename to src/boards/sensecap_solar_p1/pinconfig.c diff --git a/src/boards/sparkfun_nrf52840_micromod/board.cmake b/src/boards/sparkfun_nrf52840_micromod/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/sparkfun_nrf52840_micromod/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/sparkfun_nrf52840_micromod/board.h b/src/boards/sparkfun_nrf52840_micromod/board.h deleted file mode 100644 index 11692a42..00000000 --- a/src/boards/sparkfun_nrf52840_micromod/board.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * Copyright (c) 2021 Chris Marc Dailey - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _SPARKFUN_NRF52840_MICROMOD_H_ -#define _SPARKFUN_NRF52840_MICROMOD_H_ - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 13) -#define LED_STATE_ON 1 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 7) -#define BUTTON_DFU_OTA PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "SparkFun" -#define BLEDIS_MODEL "MicroMod nRF52840" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1B4F -#define USB_DESC_UF2_PID 0x0022 -#define USB_DESC_CDC_ONLY_PID 0x0023 - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "SparkFun MicroMod nRF52840" -#define UF2_VOLUME_LABEL "SFMM852BOOT" -#define UF2_BOARD_ID "nRF52840-micromod-v10" - -#define UF2_INDEX_URL "https://www.sparkfun.com/products/16984" - -#endif /* _SPARKFUN_NRF52840_MICROMOD_H_ */ diff --git a/src/boards/sparkfun_nrf52840_micromod/board.mk b/src/boards/sparkfun_nrf52840_micromod/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/sparkfun_nrf52840_micromod/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/sparkfun_nrf52840_micromod/pinconfig.c b/src/boards/sparkfun_nrf52840_micromod/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/sparkfun_nrf52840_micromod/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/syldra/board.cmake b/src/boards/syldra/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/syldra/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/syldra/board.h b/src/boards/syldra/board.h deleted file mode 100644 index 61539f8a..00000000 --- a/src/boards/syldra/board.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2026 Ambraglow - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _SYLDRA_H -#define _SYLDRA_H - -//------------------------------------------------------------------// -// LED -//------------------------------------------------------------------// -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 17) // AD10 LED -#define LED_STATE_ON 1 - -//------------------------------------------------------------------// -// BUTTON -//------------------------------------------------------------------// -#define BUTTON_DFU PINNUM(0, 24) // AD20 -- DFU -#define BUTTON_DFU_OTA PINNUM(1, 00) // AD22 -- OTA - -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//-------------------------------------------------------------------// -// BLE OTA -//-------------------------------------------------------------------// -#define BLEDIS_MANUFACTURER "Ambraglow-&-localcc" -#define BLEDIS_MODEL "Syldra_dev_r1" - -//--------------------------------------------------------------------+ -// USB - using test PIDs -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x1209 -#define USB_DESC_UF2_PID 0x0001 -#define USB_DESC_CDC_ONLY_PID 0x7A01 - -#define UF2_PRODUCT_NAME "Syldra nrf52840 Development Board" -#define UF2_VOLUME_LABEL "Syldra-boot" -#define UF2_BOARD_ID "nRF52840-Syldra-dev-v1" -#define UF2_INDEX_URL "https://github.com/ambraglow/nonorf" - -#endif \ No newline at end of file diff --git a/src/boards/syldra/board.mk b/src/boards/syldra/board.mk deleted file mode 100644 index befd7cba..00000000 --- a/src/boards/syldra/board.mk +++ /dev/null @@ -1,2 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 -SD_NAME = s140 \ No newline at end of file diff --git a/src/boards/t1000_e/board.mk b/src/boards/t1000_e/board.mk index 0fa44906..554e02ef 100644 --- a/src/boards/t1000_e/board.mk +++ b/src/boards/t1000_e/board.mk @@ -1,3 +1,4 @@ MCU_SUB_VARIANT = nrf52840 SD_VERSION = 7.3.0 +CFLAGS += -DDEVICE_NAME='"T1KE_DFU"' diff --git a/src/boards/pca10056/board.h b/src/boards/thinknode_m1/board.h similarity index 68% rename from src/boards/pca10056/board.h rename to src/boards/thinknode_m1/board.h index 08269bf6..e97703ad 100644 --- a/src/boards/pca10056/board.h +++ b/src/boards/thinknode_m1/board.h @@ -1,8 +1,6 @@ /* * The MIT License (MIT) * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -22,41 +20,40 @@ * THE SOFTWARE. */ -#ifndef PCA10056_H -#define PCA10056_H +#ifndef _THINKNODE_M1_H +#define _THINKNODE_M1_H /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 13 -#define LED_SECONDARY_PIN 14 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 14) // Blue LED #define LED_STATE_ON 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_DFU 11 -#define BUTTON_DFU_OTA 12 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +// #define BUTTON_DFU PINNUM(1, 7) // Function button (TBN) +#define BUTTON_DFU PINNUM(1, 10) // Hold page turn to enter OTA DFU mode. +#define BUTTON_DFU_OTA PINNUM(1, 10) // Page Turn button +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Nordic" -#define BLEDIS_MODEL "PCA10056" +#define BLEDIS_MANUFACTURER "ELECROW" +#define BLEDIS_MODEL "ThinkNodeM1" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x00DA +#define USB_DESC_CDC_ONLY_PID 0x00DA -// Shared VID/PID with Feather nRF52840, will be disabled for building in the future -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00DA -#define USB_DESC_CDC_ONLY_PID 0x00DA - -#define UF2_PRODUCT_NAME "Nordic nRF52840 DK" -#define UF2_BOARD_ID "nRF52840-pca10056-v1" -#define UF2_INDEX_URL "https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK" +#define UF2_PRODUCT_NAME "ELECROW ThinkNodeM1" +#define UF2_VOLUME_LABEL "ThinkNodeM1" +#define UF2_BOARD_ID "nRF52840-ThinkNodeM1-v1" +#define UF2_INDEX_URL "https://www.elecrow.com" -#endif // PCA10056_H +#endif // _THINKNODE_M1_H \ No newline at end of file diff --git a/src/boards/thinknode_m1/board.mk b/src/boards/thinknode_m1/board.mk new file mode 100644 index 00000000..fa6ab3b1 --- /dev/null +++ b/src/boards/thinknode_m1/board.mk @@ -0,0 +1,2 @@ +MCU_SUB_VARIANT = nrf52840 +CFLAGS += -DDEVICE_NAME='"TNM1_DFU"' \ No newline at end of file diff --git a/src/boards/nrfmicro/pinconfig.c b/src/boards/thinknode_m1/pinconfig.c similarity index 100% rename from src/boards/nrfmicro/pinconfig.c rename to src/boards/thinknode_m1/pinconfig.c diff --git a/src/boards/pca10100/board.h b/src/boards/thinknode_m3/board.h similarity index 69% rename from src/boards/pca10100/board.h rename to src/boards/thinknode_m3/board.h index 5be1366e..8950bac9 100644 --- a/src/boards/pca10100/board.h +++ b/src/boards/thinknode_m3/board.h @@ -1,8 +1,6 @@ /* * The MIT License (MIT) * - * Copyright (c) 2018 Ha Thach for Adafruit Industries - * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -22,40 +20,39 @@ * THE SOFTWARE. */ -#ifndef PCA10100_H -#define PCA10100_H +#ifndef _THINKNODE_M3_H +#define _THINKNODE_M3_H /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN 13 -#define LED_SECONDARY_PIN 14 -#define LED_STATE_ON 0 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(1, 5) // Blue +#define LED_STATE_ON 1 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_DFU 11 -#define BUTTON_DFU_OTA 12 -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 11) // probably unconnected? +#define BUTTON_DFU_OTA PINNUM(0, 12) // user button +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Nordic" -#define BLEDIS_MODEL "PCA10100" +#define BLEDIS_MANUFACTURER "ELECROW" +#define BLEDIS_MODEL "ThinkNode-M3" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x00D8 -#define USB_DESC_CDC_ONLY_PID 0x00D8 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x00DA +#define USB_DESC_CDC_ONLY_PID 0x00DA -#define UF2_PRODUCT_NAME "Nordic nRF52833 DK" -#define UF2_VOLUME_LABEL "NRF833BOOT" -#define UF2_BOARD_ID "nRF52833-pca10100-v1" -#define UF2_INDEX_URL "https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52833-DK" +#define UF2_PRODUCT_NAME "ELECROW ThinkNode-M3" +#define UF2_VOLUME_LABEL "ThinkNodeM3" +#define UF2_BOARD_ID "nRF52840-ThinkNode-M3-v1" +#define UF2_INDEX_URL "https://www.elecrow.com" -#endif // PCA10100_H +#endif // _THINKNODE_M3_H diff --git a/src/boards/thinknode_m3/board.mk b/src/boards/thinknode_m3/board.mk new file mode 100644 index 00000000..6e3404a7 --- /dev/null +++ b/src/boards/thinknode_m3/board.mk @@ -0,0 +1,2 @@ +MCU_SUB_VARIANT = nrf52840 +CFLAGS += -DDEVICE_NAME='"TNM3_DFU"' diff --git a/src/boards/circuitplayground_nrf52840/pinconfig.c b/src/boards/thinknode_m3/pinconfig.c similarity index 100% rename from src/boards/circuitplayground_nrf52840/pinconfig.c rename to src/boards/thinknode_m3/pinconfig.c diff --git a/src/boards/thinknode_m6/board.h b/src/boards/thinknode_m6/board.h new file mode 100644 index 00000000..faaf6a9f --- /dev/null +++ b/src/boards/thinknode_m6/board.h @@ -0,0 +1,58 @@ +/* + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _THINKNODE_M6_H +#define _THINKNODE_M6_H + +/*------------------------------------------------------------------*/ +/* LED + *------------------------------------------------------------------*/ +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(0, 7) // Blue +#define LED_STATE_ON 1 + +/*------------------------------------------------------------------*/ +/* BUTTON + *------------------------------------------------------------------*/ +#define BUTTON_DFU PINNUM(0, 17) // user button +#define BUTTON_DFU_OTA PINNUM(0, 17) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP + +//--------------------------------------------------------------------+ +// BLE OTA +//--------------------------------------------------------------------+ +#define BLEDIS_MANUFACTURER "ELECROW" +#define BLEDIS_MODEL "ThinkNodeM6" + +//--------------------------------------------------------------------+ +// USB +//--------------------------------------------------------------------+ +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x00DA +#define USB_DESC_CDC_ONLY_PID 0x00DA + +#define UF2_PRODUCT_NAME "ELECROW ThinkNodeM6" +#define UF2_VOLUME_LABEL "ThinkNodeM6" +#define UF2_BOARD_ID "nRF52840-ThinkNodeM6-v1" +#define UF2_INDEX_URL "https://www.elecrow.com" + +#endif // _THINKNODE_M6_H \ No newline at end of file diff --git a/src/boards/thinknode_m6/board.mk b/src/boards/thinknode_m6/board.mk new file mode 100644 index 00000000..633412f8 --- /dev/null +++ b/src/boards/thinknode_m6/board.mk @@ -0,0 +1,2 @@ +MCU_SUB_VARIANT = nrf52840 +CFLAGS += -DDEVICE_NAME='"TNM6_DFU"' diff --git a/src/boards/clue_nrf52840/pinconfig.c b/src/boards/thinknode_m6/pinconfig.c similarity index 100% rename from src/boards/clue_nrf52840/pinconfig.c rename to src/boards/thinknode_m6/pinconfig.c diff --git a/src/boards/waveshare_nrf52840_eval/board.cmake b/src/boards/waveshare_nrf52840_eval/board.cmake deleted file mode 100644 index 25daf71d..00000000 --- a/src/boards/waveshare_nrf52840_eval/board.cmake +++ /dev/null @@ -1 +0,0 @@ -set(MCU_VARIANT nrf52840) diff --git a/src/boards/waveshare_nrf52840_eval/board.h b/src/boards/waveshare_nrf52840_eval/board.h deleted file mode 100644 index d66a7852..00000000 --- a/src/boards/waveshare_nrf52840_eval/board.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * The MIT License (MIT) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef _WAVESHARE_NRF52840_H -#define _WAVESHARE_NRF52840_H - -/*------------------------------------------------------------------*/ -/* LED - *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -/* This is led marked LED1 on the board */ -#define LED_PRIMARY_PIN PINNUM(0, 13) -/* This is led marked LED2 on the board */ -#define LED_SECONDARY_PIN PINNUM(0, 14) -/* There are two more LEDs, but for the purposes of bootloader that - * doesn't matter. */ -#define LED_STATE_ON 0 - -/*------------------------------------------------------------------*/ -/* BUTTON - *------------------------------------------------------------------*/ -/* This is the 'KEY1' in top left of the board under RESET key. - * It is used to remain in bootloader mode after reset. */ -#define BUTTON_DFU PINNUM(0, 11) -/* Pin below is in the bottom left of PCB, and can be shorted to GND - * with a regular 2.54mm jumper or tweezers. This pin is used to - * clear user firmware and remain in bootloader mode. */ -#define BUTTON_DFU_OTA PINNUM(0, 29) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP - -//--------------------------------------------------------------------+ -// BLE OTA -//--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Waveshare" -#define BLEDIS_MODEL "nRF52840 Eval" - -//--------------------------------------------------------------------+ -// USB -//--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0029 -#define USB_DESC_CDC_ONLY_PID 0x002A - -//------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Waveshare nRF52840 Eval" -#define UF2_VOLUME_LABEL "WS52840EVK" -#define UF2_BOARD_ID "WS-nRF52840-EVK" -#define UF2_INDEX_URL "https://www.waveshare.com/wiki/NRF52840_Eval_Kit" - -#endif // _WAVESHARE_NRF52840_H diff --git a/src/boards/waveshare_nrf52840_eval/board.mk b/src/boards/waveshare_nrf52840_eval/board.mk deleted file mode 100644 index 9d29ac69..00000000 --- a/src/boards/waveshare_nrf52840_eval/board.mk +++ /dev/null @@ -1 +0,0 @@ -MCU_SUB_VARIANT = nrf52840 diff --git a/src/boards/waveshare_nrf52840_eval/pinconfig.c b/src/boards/waveshare_nrf52840_eval/pinconfig.c deleted file mode 100644 index a983e4ab..00000000 --- a/src/boards/waveshare_nrf52840_eval/pinconfig.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "boards.h" -#include "uf2/configkeys.h" - -__attribute__((used, section(".bootloaderConfig"))) -const uint32_t bootloaderConfig[] = -{ - /* CF2 START */ - CFG_MAGIC0, CFG_MAGIC1, // magic - 5, 100, // used entries, total entries - - 204, 0x100000, // FLASH_BYTES = 0x100000 - 205, 0x40000, // RAM_BYTES = 0x40000 - 208, (USB_DESC_VID << 16) | USB_DESC_UF2_PID, // BOOTLOADER_BOARD_ID = USB VID+PID, used for verification when updating bootloader via uf2 - 209, 0xada52840, // UF2_FAMILY = 0xada52840 - 210, 0x20, // PINS_PORT_SIZE = PA_32 - - 0, 0, 0, 0, 0, 0, 0, 0 - /* CF2 END */ -}; diff --git a/src/boards/raytac_mdbt50q_rx/board.h b/src/boards/wio_tracker_l1/board.h similarity index 70% rename from src/boards/raytac_mdbt50q_rx/board.h rename to src/boards/wio_tracker_l1/board.h index 76a178bb..ed5b9f60 100644 --- a/src/boards/raytac_mdbt50q_rx/board.h +++ b/src/boards/wio_tracker_l1/board.h @@ -22,42 +22,42 @@ * THE SOFTWARE. */ -#ifndef RAYTAC_MDBT50Q_RX_H -#define RAYTAC_MDBT50Q_RX_H +#ifndef _TRACKER_L1 +#define _TRACKER_L1 /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(1, 13) -#define LED_STATE_ON 0 +#define LEDS_NUMBER 1 +#define LED_PRIMARY_PIN PINNUM(1, 1) +#define LED_STATE_ON 1 + +#define NEOPIXELS_NUMBER 0 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ - -#define BUTTON_DFU PINNUM(0, 15) -#define BUTTON_DFU_OTA PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 8) // user button +#define BUTTON_DFU_OTA PINNUM(0, 8) +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Raytac" -#define BLEDIS_MODEL "MDBT50Q-RX" +#define BLEDIS_MANUFACTURER "Seeed" +#define BLEDIS_MODEL "Wio Tracker L1" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ - -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x010B -#define USB_DESC_CDC_ONLY_PID 0x010B +#define USB_DESC_VID 0x2886 +#define USB_DESC_UF2_PID 0x1667 +#define USB_DESC_CDC_ONLY_PID 0x1667 //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Raytac MDBT50Q-RX" -#define UF2_VOLUME_LABEL "MDBT50QBOOT" -#define UF2_BOARD_ID "nRF52840-MDBT50Q_RX-verD" -#define UF2_INDEX_URL "https://www.adafruit.com/product/5199" +#define UF2_PRODUCT_NAME "Seeed TRACKER L1" +#define UF2_VOLUME_LABEL "TRACKER L1" +#define UF2_BOARD_ID "TRACKER L1" +#define UF2_INDEX_URL "https://www.seeedstudio.com/" -#endif // RAYTAC_MDBT50Q_RX_H +#endif // _TRACKER_L1 diff --git a/src/boards/wio_tracker_l1/board.mk b/src/boards/wio_tracker_l1/board.mk new file mode 100644 index 00000000..f255dd1a --- /dev/null +++ b/src/boards/wio_tracker_l1/board.mk @@ -0,0 +1,3 @@ +MCU_SUB_VARIANT = nrf52840 +SD_VERSION = 7.3.0 +CFLAGS += -DDEVICE_NAME='"WTL1_DFU"' \ No newline at end of file diff --git a/src/boards/syldra/pinconfig.c b/src/boards/wio_tracker_l1/pinconfig.c similarity index 100% rename from src/boards/syldra/pinconfig.c rename to src/boards/wio_tracker_l1/pinconfig.c diff --git a/src/boards/itsybitsy_nrf52840_express/board.h b/src/boards/wiscore_rak3401/board.h similarity index 66% rename from src/boards/itsybitsy_nrf52840_express/board.h rename to src/boards/wiscore_rak3401/board.h index 563d5d59..11a08d21 100644 --- a/src/boards/itsybitsy_nrf52840_express/board.h +++ b/src/boards/wiscore_rak3401/board.h @@ -22,48 +22,44 @@ * THE SOFTWARE. */ -#ifndef _ITSYBITSY_NRF52840_H -#define _ITSYBITSY_NRF52840_H +#ifndef _WISCORE_RAK3401_BOARD_H +#define _WISCORE_RAK3401_BOARD_H + +// change default 1.8v to 3.3v, add by taylor +#define UICR_REGOUT0_VALUE UICR_REGOUT0_VOUT_3V3 /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 1 -#define LED_PRIMARY_PIN PINNUM(0, 6) +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN PINNUM(1, 3) +#define LED_SECONDARY_PIN PINNUM(1, 4) #define LED_STATE_ON 1 -#define LED_APA102_CLK PINNUM(1, 9) -#define LED_APA102_DATA PINNUM(0, 8) -#define LED_APA102_BRIGHTNESS 0x1 -#define APA102_NUMBER 1 - -// For dotstart set to max for colour information is not masked off -#define BOARD_RGB_BRIGHTNESS 0xffffffff - /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(0, 29) // user switch -#define BUTTON_DFU_OTA PINNUM(1, 02) // D2 breakout +#define BUTTON_DFU PINNUM(0, 8) // change default 0.0 to 0.8, add by Michael. This pin is not connected on 4631. +#define BUTTON_DFU_OTA PINNUM(0, 8) // change default 0.0 to 0.8, add by Michael. This pin is not connected on 4631. #define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "ItsyBitsy nRF52840 Express" +#define BLEDIS_MANUFACTURER "RAKWireless" +#define BLEDIS_MODEL "WisBlock RAK3401" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ #define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0051 -#define USB_DESC_CDC_ONLY_PID 0x0052 +#define USB_DESC_UF2_PID 0x0029 +#define USB_DESC_CDC_ONLY_PID 0x002A //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit ItsyBitsy nRF52840 Express" -#define UF2_VOLUME_LABEL "ITSY840BOOT" -#define UF2_BOARD_ID "nRF52840-ItsyBitsy-revA" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4481" +#define UF2_PRODUCT_NAME "WisBlock RAK3401 Board" +#define UF2_VOLUME_LABEL "RAK3401" +#define UF2_BOARD_ID "WisBlock-RAK3401-Board" +#define UF2_INDEX_URL "https://store.rakwireless.com/collections/wisblock-core" -#endif +#endif // _WISCORE_RAK3401_BOARD_H diff --git a/src/boards/wiscore_rak3401/board.mk b/src/boards/wiscore_rak3401/board.mk new file mode 100644 index 00000000..db0c809b --- /dev/null +++ b/src/boards/wiscore_rak3401/board.mk @@ -0,0 +1,2 @@ +MCU_SUB_VARIANT = nrf52840 +CFLAGS += -DDEVICE_NAME='"3401_DFU"' \ No newline at end of file diff --git a/src/boards/electronut_labs_papyr/pinconfig.c b/src/boards/wiscore_rak3401/pinconfig.c similarity index 100% rename from src/boards/electronut_labs_papyr/pinconfig.c rename to src/boards/wiscore_rak3401/pinconfig.c diff --git a/src/boards/feather_nrf52840_sense/board.h b/src/boards/wiscore_rak4631_board/board.h similarity index 62% rename from src/boards/feather_nrf52840_sense/board.h rename to src/boards/wiscore_rak4631_board/board.h index e0740fd9..40a974df 100644 --- a/src/boards/feather_nrf52840_sense/board.h +++ b/src/boards/wiscore_rak4631_board/board.h @@ -22,45 +22,44 @@ * THE SOFTWARE. */ -#ifndef _FEATHER_NRF52840_SENSE_H -#define _FEATHER_NRF52840_SENSE_H +#ifndef _WISCORE_RAK4631_BOARD_H +#define _WISCORE_RAK4631_BOARD_H + +// change default 1.8v to 3.3v, add by taylor +#define UICR_REGOUT0_VALUE UICR_REGOUT0_VOUT_3V3 /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ -#define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN PINNUM(1, 9) -#define LED_SECONDARY_PIN PINNUM(1, 10) -#define LED_STATE_ON 1 - -#define LED_NEOPIXEL PINNUM(0, 16) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 +#define LEDS_NUMBER 2 +#define LED_PRIMARY_PIN PINNUM(1, 3) +#define LED_SECONDARY_PIN PINNUM(1, 4) +#define LED_STATE_ON 1 /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(1, 02) -#define BUTTON_DFU_OTA PINNUM(0, 10) -#define BUTTON_PULL NRF_GPIO_PIN_PULLUP +#define BUTTON_DFU PINNUM(0, 8) // change default 0.0 to 0.8, add by Michael. This pin is not connected on 4631. +#define BUTTON_DFU_OTA PINNUM(0, 8) // change default 0.0 to 0.8, add by Michael. This pin is not connected on 4631. +#define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Feather nRF52840 Sense" +#define BLEDIS_MANUFACTURER "RAKWireless" +#define BLEDIS_MODEL "WisBlock RAK4631" //--------------------------------------------------------------------+ // USB //--------------------------------------------------------------------+ -#define USB_DESC_VID 0x239A -#define USB_DESC_UF2_PID 0x0087 -#define USB_DESC_CDC_ONLY_PID 0x0088 +#define USB_DESC_VID 0x239A +#define USB_DESC_UF2_PID 0x0029 +#define USB_DESC_CDC_ONLY_PID 0x002A //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit Feather nRF52840 Sense" -#define UF2_VOLUME_LABEL "FTHRSNSBOOT" -#define UF2_BOARD_ID "nRF52840-Feather-Sense" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4516" +#define UF2_PRODUCT_NAME "WisBlock RAK4631 Board" +#define UF2_VOLUME_LABEL "RAK4631" +#define UF2_BOARD_ID "WisBlock-RAK4631-Board" +#define UF2_INDEX_URL "https://store.rakwireless.com/collections/wisblock-core" -#endif // _FEATHER_NRF52840_SENSE_H +#endif // _WISCORE_RAK4631_BOARD_H diff --git a/src/boards/wiscore_rak4631_board/board.mk b/src/boards/wiscore_rak4631_board/board.mk new file mode 100644 index 00000000..a63f4c4c --- /dev/null +++ b/src/boards/wiscore_rak4631_board/board.mk @@ -0,0 +1,2 @@ +MCU_SUB_VARIANT = nrf52840 +CFLAGS += -DDEVICE_NAME='"4631_DFU"' \ No newline at end of file diff --git a/src/boards/feather_nrf52840_express/pinconfig.c b/src/boards/wiscore_rak4631_board/pinconfig.c similarity index 100% rename from src/boards/feather_nrf52840_express/pinconfig.c rename to src/boards/wiscore_rak4631_board/pinconfig.c diff --git a/src/boards/feather_nrf52840_express/board.h b/src/boards/wismesh_tag/board.h similarity index 74% rename from src/boards/feather_nrf52840_express/board.h rename to src/boards/wismesh_tag/board.h index 4fa7c98b..c2206df5 100644 --- a/src/boards/feather_nrf52840_express/board.h +++ b/src/boards/wismesh_tag/board.h @@ -22,35 +22,32 @@ * THE SOFTWARE. */ -#ifndef _FEATHER_NRF52840_H -#define _FEATHER_NRF52840_H +#ifndef _WISMESH_TAG_H +#define _WISMESH_TAG_H +// change default 1.8v to 3.3v, add by taylor +#define UICR_REGOUT0_VALUE UICR_REGOUT0_VOUT_3V3 /*------------------------------------------------------------------*/ /* LED *------------------------------------------------------------------*/ #define LEDS_NUMBER 2 -#define LED_PRIMARY_PIN PINNUM(1, 15) -#define LED_SECONDARY_PIN PINNUM(1, 10) +#define LED_PRIMARY_PIN PINNUM(1, 4) +#define LED_SECONDARY_PIN PINNUM(1, 3) #define LED_STATE_ON 1 -#define LED_NEOPIXEL PINNUM(0, 16) -#define NEOPIXEL_POWER_PIN PINNUM(1, 14) -#define NEOPIXELS_NUMBER 1 -#define BOARD_RGB_BRIGHTNESS 0x040404 - /*------------------------------------------------------------------*/ /* BUTTON *------------------------------------------------------------------*/ -#define BUTTON_DFU PINNUM(1, 02) -#define BUTTON_DFU_OTA PINNUM(0, 10) +#define BUTTON_DFU PINNUM(0, 9) // Allow booting to OTA DFU from button +#define BUTTON_DFU_OTA PINNUM(0, 9) // Allow booting to OTA DFU from button #define BUTTON_PULL NRF_GPIO_PIN_PULLUP //--------------------------------------------------------------------+ // BLE OTA //--------------------------------------------------------------------+ -#define BLEDIS_MANUFACTURER "Adafruit Industries" -#define BLEDIS_MODEL "Feather nRF52840 Express" +#define BLEDIS_MANUFACTURER "RAKWireless" +#define BLEDIS_MODEL "WisMesh Tag" //--------------------------------------------------------------------+ // USB @@ -60,9 +57,9 @@ #define USB_DESC_CDC_ONLY_PID 0x002A //------------- UF2 -------------// -#define UF2_PRODUCT_NAME "Adafruit Feather nRF52840 Express" -#define UF2_VOLUME_LABEL "FTHR840BOOT" -#define UF2_BOARD_ID "nRF52840-Feather-revE" -#define UF2_INDEX_URL "https://www.adafruit.com/product/4062" +#define UF2_PRODUCT_NAME "WisMesh Tag" +#define UF2_VOLUME_LABEL "RAK4631" +#define UF2_BOARD_ID "WisMesh-Tag" +#define UF2_INDEX_URL "https://store.rakwireless.com/products/wismesh-tag-meshtastic-gps-lora-tracker-ip66" -#endif // _FEATHER_NRF52840_H +#endif // _WISMESH_TAG_H diff --git a/src/boards/wismesh_tag/board.mk b/src/boards/wismesh_tag/board.mk new file mode 100644 index 00000000..9a38b697 --- /dev/null +++ b/src/boards/wismesh_tag/board.mk @@ -0,0 +1,2 @@ +MCU_SUB_VARIANT = nrf52840 +CFLAGS += -DDEVICE_NAME='"RTAG_DFU"' \ No newline at end of file diff --git a/src/boards/feather_nrf52840_sense/pinconfig.c b/src/boards/wismesh_tag/pinconfig.c similarity index 100% rename from src/boards/feather_nrf52840_sense/pinconfig.c rename to src/boards/wismesh_tag/pinconfig.c diff --git a/src/boards/xiao_nrf52840_ble/board.mk b/src/boards/xiao_nrf52840_ble/board.mk index 8a77053b..f4577d07 100644 --- a/src/boards/xiao_nrf52840_ble/board.mk +++ b/src/boards/xiao_nrf52840_ble/board.mk @@ -1,2 +1,3 @@ MCU_SUB_VARIANT = nrf52840 -SD_VERSION = 7.3.0 \ No newline at end of file +SD_VERSION = 7.3.0 +CFLAGS += -DDEVICE_NAME='"XIAO_DFU"' diff --git a/src/boards/xiao_nrf52840_ble_sense/board.mk b/src/boards/xiao_nrf52840_ble_sense/board.mk index 8a77053b..f4577d07 100644 --- a/src/boards/xiao_nrf52840_ble_sense/board.mk +++ b/src/boards/xiao_nrf52840_ble_sense/board.mk @@ -1,2 +1,3 @@ MCU_SUB_VARIANT = nrf52840 -SD_VERSION = 7.3.0 \ No newline at end of file +SD_VERSION = 7.3.0 +CFLAGS += -DDEVICE_NAME='"XIAO_DFU"' From 3c7c298edf1c78e3ddefb4333e3ac15f7623713b Mon Sep 17 00:00:00 2001 From: James Rich Date: Thu, 20 Aug 2026 16:48:17 -0500 Subject: [PATCH 75/84] feat: port the OTAFIX display layer and version string screen.c/images.c are OTAFIX's size-agnostic, Meshtastic-branded versions (OLED UF2 + BLE-OTA screens); boards.c gains the ST7735 controller init next to mainline's ST7789; UF2_VERSION carries submodule versions, with UF2_VERSION_BASE for the screen. --- Makefile | 4 +- src/boards/boards.c | 144 ++++++++++++++++++++++++++++++++ src/boards/boards.h | 1 + src/screen.c | 194 ++++++++++++++++++++++++++------------------ 4 files changed, 261 insertions(+), 82 deletions(-) diff --git a/Makefile b/Makefile index 5866d155..8f1bb721 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,7 @@ else LD_FILE = linker/$(MCU_SUB_VARIANT).ld endif +GIT_SUBMODULE_VERSIONS := $(shell git submodule status | cut -d" " -f3,4 | paste -s -d" " -) GIT_VERSION := $(shell git describe --dirty --always --tags) # compiled file name @@ -359,7 +360,8 @@ ifneq ($(USE_NFCT),yes) endif CFLAGS += -DSOFTDEVICE_PRESENT -CFLAGS += -DUF2_VERSION='"$(GIT_VERSION)"' +CFLAGS += -DUF2_VERSION_BASE='"$(GIT_VERSION)"' +CFLAGS += -DUF2_VERSION='"$(GIT_VERSION) $(GIT_SUBMODULE_VERSIONS)"' CFLAGS += -DBLEDIS_FW_VERSION='"$(GIT_VERSION) $(SD_NAME) $(SD_VERSION)"' ifeq ($(SIGNED_FW), 1) diff --git a/src/boards/boards.c b/src/boards/boards.c index c02a2786..ff88e8f1 100644 --- a/src/boards/boards.c +++ b/src/boards/boards.c @@ -791,3 +791,147 @@ static void tft_controller_init(void) { } #endif + +#ifdef DISPLAY_CONTROLLER_ST7735 + +#define ST_CMD_DELAY 0x80 // special signifier for command lists + +// ST77XX common commands (shared with ST7789) +#define ST77XX_NOP 0x00 +#define ST77XX_SWRESET 0x01 +#define ST77XX_RDDID 0x04 +#define ST77XX_RDDST 0x09 + +#define ST77XX_SLPIN 0x10 +#define ST77XX_SLPOUT 0x11 +#define ST77XX_PTLON 0x12 +#define ST77XX_NORON 0x13 + +#define ST77XX_INVOFF 0x20 +#define ST77XX_INVON 0x21 +#define ST77XX_DISPOFF 0x28 +#define ST77XX_DISPON 0x29 +#define ST77XX_CASET 0x2A +#define ST77XX_RASET 0x2B +#define ST77XX_RAMWR 0x2C +#define ST77XX_RAMRD 0x2E + +#define ST77XX_PTLAR 0x30 +#define ST77XX_TEOFF 0x34 +#define ST77XX_TEON 0x35 +#define ST77XX_MADCTL 0x36 +#define ST77XX_VSCSAD 0x37 +#define ST77XX_COLMOD 0x3A + +#define ST77XX_MADCTL_MY 0x80 +#define ST77XX_MADCTL_MX 0x40 +#define ST77XX_MADCTL_MV 0x20 +#define ST77XX_MADCTL_ML 0x10 +#define ST77XX_MADCTL_RGB 0x00 + +#define ST77XX_RDID1 0xDA +#define ST77XX_RDID2 0xDB +#define ST77XX_RDID3 0xDC +#define ST77XX_RDID4 0xDD + +// ST7735-specific commands +#define ST7735_FRMCTR1 0xB1 +#define ST7735_FRMCTR2 0xB2 +#define ST7735_FRMCTR3 0xB3 +#define ST7735_INVCTR 0xB4 +#define ST7735_PWCTR1 0xC0 +#define ST7735_PWCTR2 0xC1 +#define ST7735_PWCTR3 0xC2 +#define ST7735_PWCTR4 0xC3 +#define ST7735_PWCTR5 0xC4 +#define ST7735_VMCTR1 0xC5 +#define ST7735_GMCTRP1 0xE0 +#define ST7735_GMCTRN1 0xE1 +#define ST7735_MADCTL_BGR 0x08 + +// Some ready-made 16-bit ('565') color settings: +#define ST77XX_BLACK 0x0000 +#define ST77XX_WHITE 0xFFFF +#define ST77XX_RED 0xF800 +#define ST77XX_GREEN 0x07E0 +#define ST77XX_BLUE 0x001F +#define ST77XX_CYAN 0x07FF +#define ST77XX_MAGENTA 0xF81F +#define ST77XX_YELLOW 0xFFE0 +#define ST77XX_ORANGE 0xFC00 + +static void tft_controller_init(void) { + // Init commands for ST7735 mini 160x80 screens + // Based on Adafruit INITR_MINI160x80 sequence + uint8_t cmdinit_st7735[] = { + #if !defined(DISPLAY_PIN_RST) || (DISPLAY_PIN_RST < 0) + // Software reset if rst pin not available + ST77XX_SWRESET, ST_CMD_DELAY, 150, + #endif + // Out of sleep mode, w/delay 255 = 500ms + ST77XX_SLPOUT, ST_CMD_DELAY, 255, + // Frame rate control - normal mode (3 args) + ST7735_FRMCTR1, 3, 0x01, 0x2C, 0x2D, + // Frame rate control - idle mode (3 args) + ST7735_FRMCTR2, 3, 0x01, 0x2C, 0x2D, + // Frame rate control - partial mode (6 args) + ST7735_FRMCTR3, 6, 0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D, + // Display inversion control (1 arg) + ST7735_INVCTR, 1, 0x07, + // Power control 1 (3 args) + ST7735_PWCTR1, 3, 0xA2, 0x02, 0x84, + // Power control 2 (1 arg) + ST7735_PWCTR2, 1, 0xC5, + // Power control 3 - normal mode (2 args) + ST7735_PWCTR3, 2, 0x0A, 0x00, + // Power control 4 - idle mode (2 args) + ST7735_PWCTR4, 2, 0x8A, 0x2A, + // Power control 5 - partial mode (2 args) + ST7735_PWCTR5, 2, 0x8A, 0xEE, + // VCOM control (1 arg) + ST7735_VMCTR1, 1, 0x0E, + // Inversion off (required for Heltec T096) + ST77XX_INVOFF, 0, + // Set color mode, 1 arg: 16-bit color (0x05 for ST7735) + ST77XX_COLMOD, 1 + ST_CMD_DELAY, 0x05, 10, + // Mem access ctrl, 1 arg: row/col addr, color order + ST77XX_MADCTL, 1, DISPLAY_MADCTL, + // Column addr set, 4 args: XSTART/XEND (132 wide framebuffer) + ST77XX_CASET, 4, 0x00, 0x00, 0x00, 0x83, + // Row addr set, 4 args: YSTART/YEND (162 tall framebuffer) + ST77XX_RASET, 4, 0x00, 0x00, 0x00, 0xA1, + // Positive gamma correction (16 args) + ST7735_GMCTRP1, 16, + 0x02, 0x1C, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2D, + 0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10, + // Negative gamma correction (16 args) + ST7735_GMCTRN1, 16, + 0x03, 0x1D, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D, + 0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10, + // Normal display on, w/delay 10ms + ST77XX_NORON, ST_CMD_DELAY, 10, + // Main screen turn on, w/delay 100ms + ST77XX_DISPON, ST_CMD_DELAY, 100 + }; + + size_t count = 0; + while (count < sizeof(cmdinit_st7735)) { + uint8_t const cmd = cmdinit_st7735[count++]; + uint8_t const cmd_arg = cmdinit_st7735[count++]; + uint8_t const has_delay = cmd_arg & ST_CMD_DELAY; + uint8_t const narg = cmd_arg & ~ST_CMD_DELAY; + + tft_cmd(cmd, cmdinit_st7735 + count, narg); + count += narg; + + if (has_delay) { + uint16_t delay = (uint16_t) cmdinit_st7735[count++]; + if (delay == 255) { + delay = 500; // If 255, delay for 500 ms + } + NRFX_DELAY_MS(delay); + } + } +} + +#endif \ No newline at end of file diff --git a/src/boards/boards.h b/src/boards/boards.h index 8600119e..6db378dd 100644 --- a/src/boards/boards.h +++ b/src/boards/boards.h @@ -112,6 +112,7 @@ void board_display_init(void); void board_display_teardown(void); void board_display_draw_line(uint16_t y, uint8_t const* buf, size_t nbytes); void screen_draw_drag(void); +void screen_draw_ble(void); #endif //--------------------------------------------------------------------+ diff --git a/src/screen.c b/src/screen.c index f51330a3..a1f82e44 100644 --- a/src/screen.c +++ b/src/screen.c @@ -26,14 +26,17 @@ #if defined(DISPLAY_PIN_SCK) +#ifndef BANNER_TEXT +#define BANNER_TEXT "meshtastic.org - OTAFIX by oltaco" +#endif //BANNER_TEXT + #include #include -// Overlap 4x chars by this much. -#define CHAR4_KERNING 3 - -// Width of a single 4x char, adjusted by kerning -#define CHAR4_KERNED_WIDTH (6 * 4 - CHAR4_KERNING) +// Size independent kerning +#define CHAR_ADV(n) (5 * (n) + 1) // cursor step between chars +#define CHAR_INK(n) (6 * (n)) // columns printch actually writes +#define TEXT_WIDTH(n, len) ((len) ? CHAR_ADV(n) * ((len) - 1) + CHAR_INK(n) : 0) #define COL0(r, g, b) ((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3)) #define COL(c) COL0((c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff) @@ -73,6 +76,56 @@ const uint16_t palette[] = { COL(0x000000), // 15 }; +// inital display-size agnostic hack +#ifndef SCREEN_BAR1_Y +#define SCREEN_BAR1_Y 0 +#endif +#ifndef SCREEN_BAR1_H +#define SCREEN_BAR1_H 52 +#endif + +#ifndef SCREEN_BAR2_Y +#define SCREEN_BAR2_Y 52 +#endif +#ifndef SCREEN_BAR2_H +#define SCREEN_BAR2_H 55 +#endif + +#ifndef SCREEN_BAR3_Y +#define SCREEN_BAR3_Y 107 +#endif +#ifndef SCREEN_BAR3_H +#define SCREEN_BAR3_H 14 +#endif +#ifndef fileLogo_X +#define fileLogo_X 0 +#endif +#ifndef arrowLogo_X +#define arrowLogo_X 65 +#endif +#ifndef pendriveLogo_X +#define pendriveLogo_X 129 +#endif +#ifndef DISPLAY_TITLE_Y +#define DISPLAY_TITLE_Y 5 +#endif +#ifndef UF2_VERSION_BASE_Y +#define UF2_VERSION_BASE_Y 40 +#endif +#ifndef BANNER_TEXT_X +#define BANNER_TEXT_X 23 +#endif +#ifndef BANNER_TEXT_Y +#define BANNER_TEXT_Y 110 +#endif +#ifndef BLE_OTA_Y +#define BLE_OTA_Y 65 +#endif +#ifndef FONT_SIZE_LARGE +#define FONT_SIZE_LARGE 4 +#endif + + // TODO only buffer partial screen to save SRAM // ESP32s2 can only statically allocated DRAM up to 160KB. // the remaining 160KB can only be allocated at runtime as heap. @@ -88,38 +141,16 @@ extern const uint8_t arrowLogo[]; // //--------------------------------------------------------------------+ -// print character with font size = 1 -static void printch(int x, int y, int color, const uint8_t* fnt) { - for (int i = 0; i < 6; ++i) { - uint8_t* p = frame_buf + (x + i) * DISPLAY_HEIGHT + y; - uint8_t mask = 0x01; - for (int j = 0; j < 8; ++j) { - if (*fnt & mask) { - *p = color; - } - p++; - mask <<= 1; - } - fnt++; - } -} - -// print character with font size = 4 -static void printch4(int x, int y, int color, const uint8_t* fnt) { - for (int i = 0; i < 6 * 4; ++i) { +// print character +static void printch(int x, int y, int color, const uint8_t* fnt, int size) { + for (int i = 0; i < 6 * size; ++i) { uint8_t* p = frame_buf + (x + i) * DISPLAY_HEIGHT + y; - uint8_t mask = 0x01; + uint8_t b = fnt[i / size]; for (int j = 0; j < 8; ++j) { - for (int k = 0; k < 4; ++k) { - if (*fnt & mask) { - *p = color; - } - p++; + if (b & (1u << j)) { + for (int k = 0; k < size; ++k) p[k] = color; } - mask <<= 1; - } - if ((i & 3) == 3) { - fnt++; + p += size; } } } @@ -171,45 +202,31 @@ static void printicon(int x, int y, int color, const uint8_t* icon) { } } -// print text with font size = 1 -static void print(int x, int y, int col, const char* text) { - int x0 = x; - while (*text) { - char c = *text++; - if (c == '\r') continue; - if (c == '\n') { - x = x0; - y += 10; - continue; - } - /* - if (x + 8 > DISPLAY_WIDTH) { - x = x0; - y += 10; - } - */ - if (c < ' ') c = '?'; - if (c >= 0x7f) c = '?'; - c -= ' '; - printch(x, y, col, &font8[c * 6]); - x += 6; +static void print(int x, int y, int color, const char* text, int size) { + // Glyphs are written straight into frame_buf with no bounds check of + // their own, so reject anything that would land outside it. + if (y < 0 || y + 8 * size > DISPLAY_HEIGHT) { + return; } -} - -// Print text with font size = 4 -static void print4(int x, int y, int color, const char* text) { while (*text) { char c = *text++; + if (c < ' ' || c >= 0x7f) c = '?'; c -= ' '; - printch4(x, y, color, &font8[c * 6]); - x += CHAR4_KERNED_WIDTH; - if (x + CHAR4_KERNED_WIDTH > DISPLAY_WIDTH) { - // Next char won't fit. + if (x < 0 || x + CHAR_INK(size) > DISPLAY_WIDTH) { + // This char won't fit. return; } + printch(x, y, color, &font8[c * 6], size); + x += CHAR_ADV(size); } } +static inline void print_centered(int y, int color, const char* text, int size) { + int len = (int) strlen(text); + int text_x = (DISPLAY_WIDTH - TEXT_WIDTH(size, len)) / 2; + print(text_x >= 0 ? text_x : 0, y, color, text, size); +} + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ @@ -238,27 +255,42 @@ static void drawBar(int y, int h, int color) { // draw drag & drop screen void screen_draw_drag(void) { - drawBar(0, 52, COLOR_GREEN); - drawBar(52, 55, COLOR_BLUE); - drawBar(107, 14, COLOR_ORANGE); + drawBar(SCREEN_BAR1_Y, SCREEN_BAR1_H, COLOR_GREEN); + drawBar(SCREEN_BAR2_Y, SCREEN_BAR2_H, COLOR_BLUE); + drawBar(SCREEN_BAR3_Y, SCREEN_BAR3_H, COLOR_ORANGE); + + // Print title, version, banner. + print_centered(DISPLAY_TITLE_Y, COLOR_WHITE, DISPLAY_TITLE, FONT_SIZE_LARGE); + print_centered(UF2_VERSION_BASE_Y, COLOR_PURPLE, UF2_VERSION_BASE, 1); + print_centered(BANNER_TEXT_Y, COLOR_WHITE, BANNER_TEXT, 1); - // Center UF2_PRODUCT_NAME and UF2_VERSION. - int name_x = (DISPLAY_WIDTH - CHAR4_KERNED_WIDTH * (int) strlen(DISPLAY_TITLE)) / 2; - print4(name_x >= 0 ? name_x : 0, 5, COLOR_WHITE, DISPLAY_TITLE); +#ifndef DRAG +#define DRAG 70 +#endif +#ifndef DRAGX +#define DRAGX 47 +#endif + printicon(DRAGX + fileLogo_X, DRAG + 5, COLOR_WHITE, fileLogo); + printicon(DRAGX + arrowLogo_X, DRAG, COLOR_WHITE, arrowLogo); + printicon(DRAGX + pendriveLogo_X, DRAG, COLOR_WHITE, pendriveLogo); + #ifndef NOLABELS + print(22, DRAG - 12, COLOR_WHITE, "firmware.uf2", 1); + print(160, DRAG - 12, COLOR_WHITE, UF2_VOLUME_LABEL, 1); + #endif // NOLABELS - int version_x = (DISPLAY_WIDTH - 6 * (int) strlen(UF2_VERSION)) / 2; - print(version_x >= 0 ? version_x : 0, 40, COLOR_PURPLE, UF2_VERSION); + draw_screen(frame_buf); +} - // TODO the reset should be center as well - print(23, 110, 1, "circuitpython.org"); +void screen_draw_ble(void) { + drawBar(SCREEN_BAR1_Y, SCREEN_BAR1_H, COLOR_GREEN); + drawBar(SCREEN_BAR2_Y, SCREEN_BAR2_H, COLOR_BLUE); + drawBar(SCREEN_BAR3_Y, SCREEN_BAR3_H, COLOR_ORANGE); -#define DRAG 70 -#define DRAGX 10 - printicon(DRAGX + 20, DRAG + 5, COLOR_WHITE, fileLogo); - printicon(DRAGX + 66, DRAG, COLOR_WHITE, arrowLogo); - printicon(DRAGX + 108, DRAG, COLOR_WHITE, pendriveLogo); - print(10, DRAG - 12, COLOR_WHITE, "firmware.uf2"); - print(90, DRAG - 12, COLOR_WHITE, UF2_VOLUME_LABEL); + // Print title, version, OTA, banner + print_centered(DISPLAY_TITLE_Y, COLOR_WHITE, DISPLAY_TITLE, FONT_SIZE_LARGE); + print_centered(UF2_VERSION_BASE_Y, COLOR_PURPLE, UF2_VERSION_BASE, 1); + print_centered(BLE_OTA_Y, COLOR_WHITE, "BLE OTA", FONT_SIZE_LARGE); + print_centered(BANNER_TEXT_Y, COLOR_WHITE, BANNER_TEXT, 1); draw_screen(frame_buf); } From f8bc6d5c540f75cd53df7697da4e3c5acd418499 Mon Sep 17 00:00:00 2001 From: taco Date: Tue, 16 Dec 2025 09:06:26 +1100 Subject: [PATCH 76/84] default to ota dfu when booting into dfu mode, the default will ota dfu unless specifically directed to enter a different mode (cherry picked from commit db8dc4d72063f9d2117ded7d02c09a2da5a18e6f) --- src/main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index c988ee43..fbff955c 100644 --- a/src/main.c +++ b/src/main.c @@ -253,6 +253,7 @@ static void check_dfu_mode(void) { bool const dfu_skip = (gpregret == DFU_MAGIC_SKIP); bool const reason_reset_pin = (NRF_POWER->RESETREAS & POWER_RESETREAS_RESETPIN_Msk) ? true : false; + bool const double_reset = ((*dbl_reset_mem) == DFU_DBL_RESET_MAGIC) && reason_reset_pin; // start either serial, uf2 or ble bool dfu_start = _ota_dfu || serial_only_dfu || uf2_dfu || @@ -297,7 +298,7 @@ static void check_dfu_mode(void) { #endif // App mode: Double Reset detection or DFU startup for nrf52832 - if (!(just_start_app || dfu_start || !valid_app)) { + if (!(just_start_app || dfu_start)) { // removed !valid_app check to allow double reset even when no app #ifdef NRF52832_XXAA /* Even DFU is not active, we still force an 1000 ms dfu serial mode when startup * to support auto programming from Arduino IDE @@ -321,6 +322,10 @@ static void check_dfu_mode(void) { (*dbl_reset_mem) = 0; } + if ((dfu_start || !valid_app) && !serial_only_dfu && !uf2_dfu && !double_reset) { + _ota_dfu = true; // set default to OTA only when no explicit UF2/serial/dbl-reset + } + // Enter DFU mode accordingly to input if (dfu_start || !valid_app) { if (_ota_dfu) { From 7914aadde18f6073b22fb92b0973b5972e12b171 Mon Sep 17 00:00:00 2001 From: taco Date: Mon, 13 Apr 2026 10:06:40 +1000 Subject: [PATCH 77/84] use maximum ble tx power for nrf52840 Co-authored-by: Rastislav Vysoky (cherry picked from commit 1d8194f44fb83d70b3e4a4241b2f927cbace5333) --- .../components/libraries/bootloader_dfu/dfu_transport_ble.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c index be6b6726..8ed8c2c4 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c @@ -744,8 +744,12 @@ static void advertising_start(void) advertising_init(&gap_adv.adv_data, adv_flag); APP_ERROR_CHECK( sd_ble_gap_adv_set_configure(&_adv_handle, &gap_adv, &m_adv_params) ); - // maximum power for nrf52832, nrf52840 max power is +8 + // maximum power for nrf52832 is +4, nrf52840 max power is +8 + #if defined(NRF52840_XXAA) + APP_ERROR_CHECK( sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, _adv_handle, 8) ); + #else APP_ERROR_CHECK( sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, _adv_handle, 4) ); + #endif APP_ERROR_CHECK( sd_ble_gap_adv_start(_adv_handle, BLE_CONN_CFG_HIGH_BANDWIDTH) ); m_is_advertising = true; From c55a7fa5840c20a85043bfe397407163afe1c2d7 Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Wed, 19 Aug 2026 05:38:11 -0700 Subject: [PATCH 78/84] fix: size CURRENT.UF2 to the real installed app, not the max region (#20) * fix: size CURRENT.UF2 to the real installed app, not the max region CURRENT.UF2 (the on-the-fly dump of whatever's currently flashed, generated by read_block()) has reported and generated content sized to TRUE_USER_FLASH_SIZE - the max possible app+SoftDevice region - rather than how big the actually-installed app is. On a RAK4631 running Meshtastic 2.7.26, that meant CURRENT.UF2 was ~390KB larger than the official firmware image (3728 vs 2966 UF2 blocks), padding out with whatever's physically sitting in flash past the real app+SD boundary. Copying CURRENT.UF2 back onto the drive to restore the app it came from - a workaround documented as working by users hitting stuck-in- DFU-mode bugs (adafruit/Adafruit_nRF52_Bootloader#201) - reproducibly hung the device on real RAK4631 hardware while investigating the nrfx/tinyusb bump in #19, on both this repo's current bootloader and the bumped one (a control test on the unmodified bootloader hung the same way, ruling out the bump as the cause). adafruit/Adafruit_nRF52_Bootloader#38 (2018) fixed a related CURRENT.UF2 problem by sizing it off bootloader_settings.bank_0_size - the real recorded size of the currently-installed app - instead of a fixed constant. That fix has no trace left in the current file; this restores the same idea (current_flash_size(), with the same zero/erased-flash fallback to the max region) grafted onto the current SoftDevice-decoupled code from #128, which computes the max bound differently (TRUE_USER_FLASH_SIZE) than the 2018 code did. Verified: all 14 boards still build via tools/build_all.py. Hardware retest of the exact CURRENT.UF2 dump-and-restore sequence that hung before is next. Co-Authored-By: Claude Sonnet 5 * fix: record real app size on UF2 app-flash completion ghostfat.c's current_flash_size() (this branch's other commit) reads bootloader_settings.bank_0_size to size CURRENT.UF2 to the real installed app. But tud_msc_write10_complete_cb()'s app-completion branch never set update_status.app_size before calling bootloader_dfu_update_process() - it stayed 0 from the initial memset, so bank_0_size was always recorded as 0 for any app flashed via plain UF2 drag-and-drop (as opposed to the DFU-serial protocol, which transmits size upfront and was unaffected). Verified on hardware: after this fix, CURRENT.UF2 exactly matches the real firmware size (1518592 bytes, matching the official release UF2 byte for byte) instead of the previous 1908736 bytes of padding. The CURRENT.UF2 dump-and-restore sequence that reproducibly hung the device before now completes in ~2 seconds with no hang. Co-Authored-By: Claude Sonnet 5 --------- Co-authored-by: Claude Sonnet 5 (cherry picked from commit 18758d957cc697e31dd3e1be26f863e215c3048a) --- src/usb/msc_uf2.c | 7 ++++++ src/usb/uf2/ghostfat.c | 54 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/usb/msc_uf2.c b/src/usb/msc_uf2.c index f4a60787..7329d70a 100644 --- a/src/usb/msc_uf2.c +++ b/src/usb/msc_uf2.c @@ -235,6 +235,13 @@ void tud_msc_write10_complete_cb(uint8_t lun) // update App update_status.status_code = DFU_UPDATE_APP_COMPLETE; + // Record the real written size so bootloader_settings.bank_0_size + // reflects the actual app, not 0 -- ghostfat.c's CURRENT.UF2 uses + // this to size its dump to the real app instead of the max region. + // 256 mirrors ghostfat.c's UF2_FIRMWARE_BYTES_PER_SECTOR (the UF2 + // format's fixed payload-per-block size, not board-specific). + update_status.app_size = _wr_state.numWritten * 256; + PRINTF("Application update complete\r\n"); } diff --git a/src/usb/uf2/ghostfat.c b/src/usb/uf2/ghostfat.c index 97753ef2..e097df60 100644 --- a/src/usb/uf2/ghostfat.c +++ b/src/usb/uf2/ghostfat.c @@ -183,6 +183,49 @@ STATIC_ASSERT(UF2_SECTORS == ((UF2_SIZE/2) / 256)); // Not a requirement ... ens #define UF2_FIRST_SECTOR ((NUM_FILES + 1) * BPB_SECTORS_PER_CLUSTER) // WARNING -- code presumes each non-UF2 file content fits in single sector #define UF2_LAST_SECTOR ((UF2_FIRST_SECTOR + UF2_SECTORS - 1) * BPB_SECTORS_PER_CLUSTER) +// CURRENT.UF2 (the on-the-fly dump of the currently-flashed app) reports and +// generates content sized to the REAL installed app, not the fixed max +// TRUE_USER_FLASH_SIZE region above. Otherwise every read/write of +// CURRENT.UF2 covers however much of the app-flash region is left over from +// whatever previously occupied it, well past the real app+SD boundary. +// Mirrors the size adafruit/Adafruit_nRF52_Bootloader#38 (2018) used before +// this file's current.uf2 handling was rewritten in #128. +static uint32_t current_flash_size(void) +{ + static uint32_t flash_sz = 0; + + if ( flash_sz == 0 ) + { + bootloader_settings_t const * boot_setting; + bootloader_util_settings_get(&boot_setting); + + flash_sz = boot_setting->bank_0_size; + + // Round up to a whole UF2 payload chunk, else the last real chunk of + // the app would get truncated out of CURRENT.UF2. + if ( flash_sz % UF2_FIRMWARE_BYTES_PER_SECTOR ) + { + flash_sz = (flash_sz - (flash_sz % UF2_FIRMWARE_BYTES_PER_SECTOR)) + UF2_FIRMWARE_BYTES_PER_SECTOR; + } + + // bank_0_size is 0 or erased-flash when the app was written by a tool + // that bypasses the bootloader's own settings page (e.g. a debug + // probe) -- fall back to the max region so CURRENT.UF2 still covers + // the whole possible app instead of reporting a bogus small size. + if ( (flash_sz == 0) || (flash_sz == 0xFFFFFFFFUL) || (flash_sz > TRUE_USER_FLASH_SIZE) ) + { + flash_sz = TRUE_USER_FLASH_SIZE; + } + } + + return flash_sz; +} + +#define CURRENT_UF2_SECTORS ( (current_flash_size() / UF2_FIRMWARE_BYTES_PER_SECTOR) + \ + ((current_flash_size() % UF2_FIRMWARE_BYTES_PER_SECTOR) ? 1 : 0)) +#define CURRENT_UF2_SIZE (CURRENT_UF2_SECTORS * BPB_SECTOR_SIZE) +#define CURRENT_UF2_LAST_SECTOR ((UF2_FIRST_SECTOR + CURRENT_UF2_SECTORS - 1) * BPB_SECTORS_PER_CLUSTER) + #define FS_START_FAT0_SECTOR BPB_RESERVED_SECTORS #define FS_START_FAT1_SECTOR (FS_START_FAT0_SECTOR + BPB_SECTORS_PER_FAT) #define FS_START_ROOTDIR_SECTOR (FS_START_FAT1_SECTOR + BPB_SECTORS_PER_FAT) @@ -330,10 +373,11 @@ void read_block(uint32_t block_no, uint8_t *data) { data[i] = 0xff; } } + uint32_t const current_uf2_last_sector = CURRENT_UF2_LAST_SECTOR; for (uint32_t i = 0; i < FAT_ENTRIES_PER_SECTOR; ++i) { // Generate the FAT chain for the firmware "file" uint32_t v = (sectionIdx * FAT_ENTRIES_PER_SECTOR) + i; - if (UF2_FIRST_SECTOR <= v && v <= UF2_LAST_SECTOR) - ((uint16_t *)(void *)data)[i] = v == UF2_LAST_SECTOR ? 0xffff : v + 1; + if (UF2_FIRST_SECTOR <= v && v <= current_uf2_last_sector) + ((uint16_t *)(void *)data)[i] = v == current_uf2_last_sector ? 0xffff : v + 1; } } else if (block_no < FS_START_CLUSTERS_SECTOR) { // Requested root directory sector @@ -367,7 +411,7 @@ void read_block(uint32_t block_no, uint8_t *data) { d->updateTime = __DOSTIME__; d->updateDate = __DOSDATE__; d->startCluster = startCluster & 0xFFFF; - d->size = (inf->content ? strlen(inf->content) : UF2_SIZE); + d->size = (inf->content ? strlen(inf->content) : CURRENT_UF2_SIZE); } } else if (block_no < BPB_TOTAL_SECTORS) { @@ -378,13 +422,13 @@ void read_block(uint32_t block_no, uint8_t *data) { } else { // generate the UF2 file data on-the-fly sectionIdx -= NUM_FILES - 1; uint32_t addr = USER_FLASH_START + (sectionIdx * UF2_FIRMWARE_BYTES_PER_SECTOR); - if (addr < CFG_UF2_FLASH_SIZE) { + if (addr < USER_FLASH_START + current_flash_size()) { UF2_Block *bl = (void *)data; bl->magicStart0 = UF2_MAGIC_START0; bl->magicStart1 = UF2_MAGIC_START1; bl->magicEnd = UF2_MAGIC_END; bl->blockNo = sectionIdx; - bl->numBlocks = UF2_SECTORS; + bl->numBlocks = CURRENT_UF2_SECTORS; bl->targetAddr = addr; bl->payloadSize = UF2_FIRMWARE_BYTES_PER_SECTOR; bl->flags = UF2_FLAG_FAMILYID; From 91fc5fac856ebc412c82775b3d1441d7e2b9ea94 Mon Sep 17 00:00:00 2001 From: taco Date: Thu, 4 Sep 2025 17:41:18 +1000 Subject: [PATCH 79/84] add display support to OTA DFU mode (cherry picked from commit f9f9f514242b100329b1e130f2bda459fcfc4f0a) --- src/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main.c b/src/main.c index fbff955c..f553301c 100644 --- a/src/main.c +++ b/src/main.c @@ -329,6 +329,10 @@ static void check_dfu_mode(void) { // Enter DFU mode accordingly to input if (dfu_start || !valid_app) { if (_ota_dfu) { + #ifdef DISPLAY_PIN_SCK + board_display_init(); + screen_draw_ble(); + #endif led_state(STATE_BLE_DISCONNECTED); if (!_sd_inited) mbr_init_sd(); _sd_inited = true; From 6f88993bc73226e347fde305997cc2f857c11370 Mon Sep 17 00:00:00 2001 From: James Rich Date: Thu, 20 Aug 2026 16:50:32 -0500 Subject: [PATCH 80/84] add usb_teardown after ota dfu this fixes the issue where boards wouldn't automatically boot to app after successful ota update when connected to usb host (cherry picked from commit 02fc9bd, applied by hand: mainline's block calls disable_softdevice()) --- src/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.c b/src/main.c index f553301c..9c52809c 100644 --- a/src/main.c +++ b/src/main.c @@ -353,6 +353,7 @@ static void check_dfu_mode(void) { if (_ota_dfu) { disable_softdevice(); + usb_teardown(); // allow booting to app after ota even if usb is connected } else { usb_teardown(); } From 1458940577515ecfa3d444238a1026f88fb8cb24 Mon Sep 17 00:00:00 2001 From: taco Date: Sun, 14 Dec 2025 22:06:18 +1100 Subject: [PATCH 81/84] lazy erase (cherry picked from commit 7e69127005a7cc65aa1596a4889056bb33630c12) --- .../drivers_nrf/pstorage/pstorage.h | 4 + .../drivers_nrf/pstorage/pstorage_raw.c | 350 ++++++++++++++++-- .../bootloader_dfu/dfu_single_bank.c | 35 +- .../bootloader_dfu/dfu_transport_ble.c | 8 +- 4 files changed, 360 insertions(+), 37 deletions(-) diff --git a/lib/sdk11/components/drivers_nrf/pstorage/pstorage.h b/lib/sdk11/components/drivers_nrf/pstorage/pstorage.h index f7d2f43d..dbd86715 100644 --- a/lib/sdk11/components/drivers_nrf/pstorage/pstorage.h +++ b/lib/sdk11/components/drivers_nrf/pstorage/pstorage.h @@ -27,6 +27,10 @@ #include "pstorage_platform.h" +extern uint8_t* dfu_page_erased; +extern uint32_t dfu_image_page_count; +extern int32_t dfu_pending_erase_page; +extern uint32_t dfu_base_address; /**@defgroup ps_opcode Persistent Storage Access Operation Codes * @{ diff --git a/lib/sdk11/components/drivers_nrf/pstorage/pstorage_raw.c b/lib/sdk11/components/drivers_nrf/pstorage/pstorage_raw.c index a6ac0676..f95fe9b3 100644 --- a/lib/sdk11/components/drivers_nrf/pstorage/pstorage_raw.c +++ b/lib/sdk11/components/drivers_nrf/pstorage/pstorage_raw.c @@ -47,6 +47,15 @@ #error No target defined #endif +#define PENDING_PACKET_SIZE 256 /**< Maximum size of a single pending packet data buffer. */ +#define PENDING_PACKET_COUNT 8 /**< Maximum number of packets that can be buffered during lazy erase. */ + +uint8_t * dfu_page_erased; /**< Pointer to page erased status array, set by DFU layer. */ +uint32_t dfu_image_page_count; /**< Number of pages in DFU image, set by DFU layer. */ +uint32_t dfu_base_address; /**< Base address for DFU image, set by DFU layer. */ + +static bool lazy_erase_check_and_trigger(pstorage_handle_t * p_dest, pstorage_size_t size, pstorage_size_t offset); +static void process_pending_packets(void); /** * @brief Application registration information. @@ -93,10 +102,29 @@ typedef struct cmd_queue_element_t cmd[PSTORAGE_CMD_QUEUE_SIZE]; /**< Array to maintain flash access operation details */ }cmd_queue_t; -static cmd_queue_t m_cmd_queue; /**< Flash operation request queue. */ -static pstorage_module_table_t m_app_table[PSTORAGE_NUM_OF_PAGES]; /**< Registered application information table. */ -static pstorage_size_t m_next_app_instance; /**< Points to the application module instance that can be allocated next */ -static pstorage_size_t m_round_val; /**< Round value for multiple round operations. For erase operations, the round value will contain current round counter which is identical to number of pages erased. For store operations, the round value contains current round of operation * SOC_MAX_WRITE_SIZE to ensure each store to the SoC Flash API is within the SoC limit. */ +/**@brief Structure for buffering packets during lazy page erase. */ +typedef struct +{ + bool valid; /**< Whether this slot contains valid data. */ + pstorage_handle_t handle; /**< Storage handle for destination. */ + uint32_t offset; /**< Offset within the storage block. */ + uint32_t size; /**< Size of data in bytes. */ + uint8_t * p_data_orig; /**< Original data pointer for callback. */ + uint8_t data[PENDING_PACKET_SIZE] __attribute__((aligned(4))); /**< Buffered packet data. */ +} pending_packet_t; + +static cmd_queue_t m_cmd_queue; /**< Flash operation request queue. */ +static pstorage_module_table_t m_app_table[PSTORAGE_NUM_OF_PAGES]; /**< Registered application information table. */ +static pstorage_size_t m_next_app_instance; /**< Points to the application module instance that can be allocated next */ +static pstorage_size_t m_round_val; /**< Round value for multiple round operations. For erase operations, the round value will contain current round counter which is identical to number of pages erased. For store operations, the round value contains current round of operation * SOC_MAX_WRITE_SIZE to ensure each store to the SoC Flash API is within the SoC limit. */ + +static pending_packet_t m_pending_packets[PENDING_PACKET_COUNT]; /**< Ring buffer for pending packets. */ +static uint8_t m_pending_read_idx; /**< Read index for pending packet ring buffer. */ +static uint8_t m_pending_write_idx; /**< Write index for pending packet ring buffer. */ +static uint8_t m_pending_count; /**< Number of pending packets in buffer. */ +static bool m_lazy_erase_active; /**< True if a lazy erase is in progress. */ +static uint32_t m_lazy_erase_page; /**< Page index currently being erased. */ +static bool m_processing_pending; /**< True if processing a pending packet store. */ /** * @brief Function for processing of commands and issuing flash access request to the SoftDevice. @@ -160,6 +188,82 @@ static void cmd_queue_init(void) } } +/** + * @brief Function for initializing the pending packets buffer. + */ +static void pending_packets_init(void) +{ + for (int i = 0; i < PENDING_PACKET_COUNT; i++) + { + m_pending_packets[i].valid = false; + } + m_pending_read_idx = 0; + m_pending_write_idx = 0; + m_pending_count = 0; + m_lazy_erase_active = false; + m_lazy_erase_page = 0; + m_processing_pending = false; +} + +/** + * @brief Function for adding a packet to the pending buffer. + * + * @param[in] p_handle Pointer to the storage handle. + * @param[in] p_data Pointer to the data to buffer. + * @param[in] size Size of data in bytes. + * @param[in] offset Offset within the storage block. + * + * @retval true If the packet was successfully added. + * @retval false If the buffer is full or size exceeds maximum. + */ +static bool pending_packet_add(pstorage_handle_t * p_handle, uint8_t * p_data, + uint32_t size, uint32_t offset) +{ + if (m_pending_count >= PENDING_PACKET_COUNT || size > PENDING_PACKET_SIZE) + { + return false; + } + + pending_packet_t * p = &m_pending_packets[m_pending_write_idx]; + p->handle = *p_handle; + p->offset = offset; + p->size = size; + p->p_data_orig = p_data; + memcpy(p->data, p_data, size); + p->valid = true; + + m_pending_write_idx = (m_pending_write_idx + 1) % PENDING_PACKET_COUNT; + m_pending_count++; + + return true; +} + +/** + * @brief Function for getting a pointer to the next pending packet without removing it. + * + * @return Pointer to the next pending packet, or NULL if buffer is empty. + */ +static pending_packet_t * pending_packet_peek(void) +{ + if (m_pending_count == 0) + { + return NULL; + } + return &m_pending_packets[m_pending_read_idx]; +} + +/** + * @brief Function for removing the next pending packet from the buffer. + */ +static void pending_packet_pop(void) +{ + if (m_pending_count > 0) + { + m_pending_packets[m_pending_read_idx].valid = false; + m_pending_read_idx = (m_pending_read_idx + 1) % PENDING_PACKET_COUNT; + m_pending_count--; + } +} /** * @brief Function for enqueueing a flash access operation. @@ -196,6 +300,7 @@ static uint32_t cmd_queue_enqueue(uint8_t opcode, m_cmd_queue.cmd[write_index].storage_addr = (*p_storage_addr); m_cmd_queue.cmd[write_index].size = size; m_cmd_queue.cmd[write_index].offset = offset; + m_cmd_queue.count++; retval = NRF_SUCCESS; if (m_cmd_queue.flash_access == false) { @@ -206,7 +311,6 @@ static uint32_t cmd_queue_enqueue(uint8_t opcode, retval = NRF_SUCCESS; } } - m_cmd_queue.count++; } else { @@ -261,15 +365,132 @@ static void app_notify(uint32_t result, cmd_queue_element_t * p_elem) { pstorage_ntf_cb_t ntf_cb; uint8_t op_code = p_elem->op_code; + + if (p_elem->storage_addr.module_id < PSTORAGE_NUM_OF_PAGES) + { + ntf_cb = m_app_table[p_elem->storage_addr.module_id].cb; + if (ntf_cb != NULL) + { + ntf_cb(&p_elem->storage_addr, + op_code, + result, + p_elem->p_data_addr, + p_elem->size); + } + } +} + +/** + * @brief Function for notifying application of buffered store completion. + * + * @param[in] result Result of event being notified. + * @param[in] p_handle Pointer to the storage handle. + * @param[in] p_data Pointer to the data buffer. + * @param[in] size Size of data in bytes. + */ +static void app_notify_buffered(uint32_t result, pstorage_handle_t * p_handle, + uint8_t * p_data, uint32_t size) +{ + if (p_handle->module_id < PSTORAGE_NUM_OF_PAGES) + { + pstorage_ntf_cb_t ntf_cb = m_app_table[p_handle->module_id].cb; + if (ntf_cb != NULL) + { + ntf_cb(p_handle, PSTORAGE_STORE_OP_CODE, result, p_data, size); + } + } +} + +/** + * @brief Function for processing packets from the pending buffer. + * + * @details Attempts to process the next pending packet. If the target page needs + * erasing, triggers a lazy erase. Otherwise enqueues the store operation. + */ +static void process_pending_packets(void) +{ + if (m_processing_pending || m_lazy_erase_active || m_cmd_queue.count > 0) + { + return; + } + + pending_packet_t * p = pending_packet_peek(); + if (p == NULL) + { + return; + } + + // Check if this packet needs an erase first + if (lazy_erase_check_and_trigger(&p->handle, p->size, p->offset)) + { + // Erase triggered, don't process this packet yet + return; + } + + // Page is ready, enqueue the store + m_processing_pending = true; + + uint32_t ret = cmd_queue_enqueue(PSTORAGE_STORE_OP_CODE, &p->handle, p->data, p->size, p->offset); + + if (ret != NRF_SUCCESS) + { + m_processing_pending = false; + pending_packet_pop(); + } +} + + +/** + * @brief Function for checking and triggering lazy erase if needed. + * @param[in] p_dest Destination address where data is to be stored persistently. + * @param[in] size Size of data to be stored expressed in bytes. + * @param[in] offset Offset in bytes to be applied when writing to the block. + * @retval true Lazy erase was triggered. + * @retval false No lazy erase was needed. + */ +static bool lazy_erase_check_and_trigger(pstorage_handle_t * p_handle, pstorage_size_t size, pstorage_size_t offset) +{ + uint32_t target_addr = p_handle->block_id + offset; + + if (target_addr < dfu_base_address || dfu_page_erased == NULL) + { + return false; + } + + uint32_t rel_addr = target_addr - dfu_base_address; + uint32_t page_index = rel_addr / PSTORAGE_FLASH_PAGE_SIZE; + + // Also check end of packet + uint32_t end_addr = target_addr + size - 1; + uint32_t end_rel_addr = end_addr - dfu_base_address; + uint32_t end_page_index = end_rel_addr / PSTORAGE_FLASH_PAGE_SIZE; + + // Check if start page needs erase + bool start_needs_erase = (page_index < dfu_image_page_count && !dfu_page_erased[page_index]); + + // Check if end page needs erase (and is different from start page) + bool end_needs_erase = (end_page_index != page_index) && + (end_page_index < dfu_image_page_count && !dfu_page_erased[end_page_index]); + + if (!start_needs_erase && !end_needs_erase) + { + return false; + } + + // Determine which page to erase (start page takes priority) + uint32_t erase_page = start_needs_erase ? page_index : end_page_index; + + m_lazy_erase_active = true; + m_lazy_erase_page = erase_page; + + pstorage_handle_t erase_handle; + erase_handle.block_id = dfu_base_address + (erase_page * PSTORAGE_FLASH_PAGE_SIZE); + erase_handle.module_id = p_handle->module_id; + + cmd_queue_enqueue(PSTORAGE_CLEAR_OP_CODE, &erase_handle, NULL, + PSTORAGE_FLASH_PAGE_SIZE, 0); - ntf_cb = m_app_table[p_elem->storage_addr.module_id].cb; - - // Indicate result to client. - ntf_cb(&p_elem->storage_addr, - op_code, - result, - p_elem->p_data_addr, - p_elem->size); + return true; } @@ -297,13 +518,19 @@ void pstorage_sys_event_handler(uint32_t sys_evt) { p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; m_round_val++; - + bool command_finished = ((m_round_val * SOC_MAX_WRITE_SIZE) >= p_cmd->size); + if (p_cmd->op_code == PSTORAGE_CLEAR_OP_CODE) + { + command_finished = true; + } + if (command_finished) { uint8_t queue_rp = m_cmd_queue.rp; - + uint8_t completed_op = p_cmd->op_code; + m_round_val = 0; m_cmd_queue.count--; m_cmd_queue.rp++; @@ -313,24 +540,78 @@ void pstorage_sys_event_handler(uint32_t sys_evt) m_cmd_queue.rp -= PSTORAGE_CMD_QUEUE_SIZE; } - app_notify(retval, &m_cmd_queue.cmd[queue_rp]); + if (m_lazy_erase_active && completed_op == PSTORAGE_CLEAR_OP_CODE) + { + // Lazy erase completed + if (dfu_page_erased != NULL && m_lazy_erase_page < dfu_image_page_count) + { + dfu_page_erased[m_lazy_erase_page] = 1; + } + + m_lazy_erase_active = false; + cmd_queue_element_init(queue_rp); + + process_pending_packets(); + } + else if (m_processing_pending && completed_op == PSTORAGE_STORE_OP_CODE) + { + // Store from pending buffer completed + pending_packet_t * p = pending_packet_peek(); + if (p != NULL) + { + pstorage_handle_t cb_handle = p->handle; + uint8_t* cb_data = p->p_data_orig; + uint32_t cb_size = p->size; + + pending_packet_pop(); + + cmd_queue_element_init(queue_rp); + + // Clear flag BEFORE callback + m_processing_pending = false; + + // Notify application that buffered this buffered store is done + app_notify_buffered(retval, &cb_handle, cb_data, cb_size); + } + else + { + m_processing_pending = false; + cmd_queue_element_init(queue_rp); + } + } + else + { + app_notify(retval, &m_cmd_queue.cmd[queue_rp]); + + // Initialize/free the element as it is now processed. + cmd_queue_element_init(queue_rp); + } + } - // Initialize/free the element as it is now processed. - cmd_queue_element_init(queue_rp); + // Process next queued command OR next pending packet + if (m_cmd_queue.count > 0) + { + retval = cmd_queue_dequeue(); + } + else if (m_pending_count > 0 && !m_processing_pending && !m_lazy_erase_active) + { + // Queue is empty but we have pending packets - process one + process_pending_packets(); } - // Schedule any queued flash access operations. - retval = cmd_queue_dequeue(); + if (retval != NRF_SUCCESS) { app_notify(retval, &m_cmd_queue.cmd[m_cmd_queue.rp]); } } break; - + case NRF_EVT_FLASH_OPERATION_ERROR: + m_lazy_erase_active = false; + m_processing_pending = false; app_notify(NRF_ERROR_TIMEOUT, &m_cmd_queue.cmd[m_cmd_queue.rp]); break; - + default: // No implementation needed. break; @@ -353,6 +634,11 @@ static uint32_t cmd_process(void) retval = NRF_ERROR_FORBIDDEN; + if (m_cmd_queue.count == 0) + { + return NRF_SUCCESS; + } + p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; storage_addr = p_cmd->storage_addr.block_id; @@ -387,7 +673,7 @@ static uint32_t cmd_process(void) } } break; - + case PSTORAGE_CLEAR_OP_CODE: { uint32_t page_number; @@ -398,7 +684,7 @@ static uint32_t cmd_process(void) retval = sd_flash_page_erase(page_number); } break; - + default: // Should never reach here. break; @@ -418,7 +704,8 @@ static uint32_t cmd_process(void) uint32_t pstorage_init(void) { cmd_queue_init(); - + pending_packets_init(); + m_next_app_instance = 0; m_round_val = 0; @@ -460,11 +747,22 @@ uint32_t pstorage_store(pstorage_handle_t * p_dest, pstorage_size_t offset) { // Verify word alignment. - if ((!is_word_aligned(p_src)) || (!is_word_aligned(p_src+offset))) + if ((!is_word_aligned(p_src)) || (!is_word_aligned((void *)(p_dest->block_id + offset)))) { return NRF_ERROR_INVALID_ADDR; } + if (m_lazy_erase_active || lazy_erase_check_and_trigger(p_dest, size, offset)) + { + // Buffer this packet - erase in progress or just triggered + if (!pending_packet_add(p_dest, p_src, size, offset)) + { + return NRF_ERROR_NO_MEM; + } + return NRF_SUCCESS; + } + + // Page already erased, proceed with store return cmd_queue_enqueue(PSTORAGE_STORE_OP_CODE, p_dest, p_src, size, offset); } diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c index 114a57c9..b79a67a0 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c @@ -68,6 +68,12 @@ static void pstorage_callback_handler(pstorage_handle_t * p_handle, case PSTORAGE_CLEAR_OP_CODE: if (m_dfu_state == DFU_STATE_PREPARING) { + // Special case for lazy erase - mark first page as erased + if (dfu_page_erased != NULL && dfu_image_page_count > 0) + { + dfu_page_erased[0] = 1; + } + m_functions.cleared(); m_dfu_state = DFU_STATE_RDY; if (m_data_pkt_cb != NULL) @@ -124,25 +130,35 @@ static uint32_t dfu_timer_restart(void) } -/**@brief Function for preparing of flash before receiving SoftDevice image. +/**@brief Function for preparing flash before receiving image. * - * @details This function will erase current application area to ensure sufficient amount of - * storage for the SoftDevice image. Upon erase complete a callback will be done. - * See \ref dfu_bank_prepare_t for further details. + * @details For lazy erase mode (OTA), this sets up the tracking array and erases + * only the first page to signal readiness. Subsequent pages are erased + * on-demand when stores target them. + * For non-OTA mode, all pages are erased upfront. */ static void dfu_prepare_func_app_erase(uint32_t image_size) { mp_storage_handle_active = &m_storage_handle_app; - // Doing a SoftDevice update thus current application must be cleared to ensure enough space - // for new SoftDevice. m_dfu_state = DFU_STATE_PREPARING; + dfu_base_address = m_storage_handle_app.block_id; if ( is_ota() ) { + // Setup for lazy erase + dfu_image_page_count = NRFX_CEIL_DIV(m_image_size, CODE_PAGE_SIZE); + + if (dfu_image_page_count < 1) dfu_image_page_count = 1; // This should not happen + + static uint8_t dfu_page_erased_static[256]; + dfu_page_erased = dfu_page_erased_static; + memset(dfu_page_erased, 0, dfu_image_page_count); + + // Only the first page is erased up front; the rest are erased on demand by pstorage uint32_t err_code; while(1) { - err_code = pstorage_clear(&m_storage_handle_app, m_image_size); + err_code = pstorage_clear(&m_storage_handle_app, CODE_PAGE_SIZE); if (err_code != NRF_ERROR_NO_MEM) break; // No space, wait until an entry in the queue is freed @@ -279,6 +295,11 @@ uint32_t dfu_init(void) m_init_packet_length = 0; m_image_crc = 0; + + // Reset lazy erase state + dfu_page_erased = NULL; + dfu_image_page_count = 0; + dfu_base_address = 0; err_code = pstorage_register(&storage_module_param, &m_storage_handle_app); if (err_code != NRF_SUCCESS) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c index 8ed8c2c4..fed903ff 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c @@ -1154,7 +1154,7 @@ uint32_t dfu_transport_ble_close() #ifdef CFG_DEBUG -#if 0 +// #if 0 typedef struct { uint32_t key; @@ -1243,12 +1243,12 @@ lookup_table_t const _strevt_table = .items = _strevt_lookup }; -#endif +// #endif void print_ble_event(uint16_t evt_id) { - // const char * str = (const char *) lookup_find(&_strevt_table, evt_id); - const char * str = NULL; + const char * str = (const char *) lookup_find(&_strevt_table, evt_id); +// const char * str = NULL; PRINTF("BLE event: "); if ( str == NULL ) { From fecfb3faa5e9beefbc910b7be64640ee79c474b8 Mon Sep 17 00:00:00 2001 From: taco Date: Sun, 21 Dec 2025 08:27:14 +1100 Subject: [PATCH 82/84] packet accumulator for small packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes an issue introduced with lazy erase, where small packets could overflow the lazy erase buffer and flash write queue when PRNs were set above 3–4. introduces a packet accumulator at the transport layer to combine small packets before writing to flash. this reduces pressure on the lazy erase buffer and flash write queue, and also provides a modest speed boost for transfers using small packets. (cherry picked from commit 54a9e8578801eff3b1a4268aa5974d4ec1b131dd) --- .../bootloader_dfu/dfu_transport_ble.c | 175 +++++++++++++++++- 1 file changed, 166 insertions(+), 9 deletions(-) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c index fed903ff..98b8506d 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c @@ -79,6 +79,16 @@ #define BL_IMAGE_SIZE_OFFSET 4 /**< Offset in start packet for the size information for bootloader. */ #define APP_IMAGE_SIZE_OFFSET 8 /**< Offset in start packet for the size information for application. */ +// Accumulator buffer for small packets, to reduce flash write overhead +#define ACCUMULATE_THRESHOLD 64 /**< Enable accumulator only if the first packet is <= this size */ +#define ACCUMULATE_TARGET_SIZE 240 /**< Flush when accumulator reaches this size (word-aligned) */ + +static uint8_t m_accum_buf[ACCUMULATE_TARGET_SIZE] __attribute__((aligned(4))); +static uint16_t m_accum_len = 0; +static bool m_accum_active = false; +static uint32_t m_accum_bytes_pending = 0; /**< Bytes accumulated but not yet counted in m_num_of_firmware_bytes_rcvd */ +static uint32_t m_reported_image_size = 0; /**< Size of the incoming image being received over BLE DFU as reported by the host. */ + /**@brief Packet type enumeration. */ typedef enum @@ -364,6 +374,11 @@ static void start_data_process(ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt) // Prioritize FLASH writes over BLE comms prioritize_flash_writes_over_ble(); + // Store the reported image size for use by the accumulator logic + m_reported_image_size = start_packet.sd_image_size + + start_packet.bl_image_size + + start_packet.app_image_size; + err_code = dfu_start_pkt_handle(&update_packet); if (err_code != NRF_SUCCESS) { @@ -431,6 +446,94 @@ static void init_data_process(ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt) } } +/**@brief Flush the accumulator buffer to HCI pool for processing + */ +static void accum_flush(ble_dfu_t * p_dfu) +{ + if (m_accum_len == 0) + { + return; + } + + uint32_t err_code; + uint32_t length = m_accum_len; + uint32_t original_len = m_accum_len; // Before padding + + PRINTF("OTA: Flushing accumulator buffer (%ld bytes)\r\n", length); + + // Word-align the length (pad with 0xFF for flash compatibility) + uint32_t aligned_len = (length + 3) & ~3; + while (m_accum_len < aligned_len) + { + m_accum_buf[m_accum_len++] = 0xFF; + } + length = m_accum_len; + + err_code = hci_mem_pool_rx_produce(length, (void **) &mp_rx_buffer); + if (err_code != NRF_SUCCESS) + { + PRINTF("OTA: hci_mem_pool_rx_produce failed: 0x%08lX\r\n", err_code); + dfu_error_notify(p_dfu, err_code); + m_accum_len = 0; + m_accum_bytes_pending = 0; + return; + } + + memcpy(mp_rx_buffer, m_accum_buf, length); + + err_code = hci_mem_pool_rx_data_size_set(length); + if (err_code != NRF_SUCCESS) + { + PRINTF("OTA: hci_mem_pool_rx_data_size_set failed: 0x%08lX\r\n", err_code); + dfu_error_notify(p_dfu, err_code); + m_accum_len = 0; + m_accum_bytes_pending = 0; + return; + } + + err_code = hci_mem_pool_rx_extract(&mp_rx_buffer, &length); + if (err_code != NRF_SUCCESS) + { + PRINTF("OTA: hci_mem_pool_rx_extract failed: 0x%08lX\r\n", err_code); + dfu_error_notify(p_dfu, err_code); + m_accum_len = 0; + m_accum_bytes_pending = 0; + return; + } + + dfu_update_packet_t dfu_pkt; + dfu_pkt.packet_type = DATA_PACKET; + dfu_pkt.params.data_packet.packet_length = length / sizeof(uint32_t); + dfu_pkt.params.data_packet.p_data_packet = (uint32_t *)mp_rx_buffer; + + err_code = dfu_data_pkt_handle(&dfu_pkt); + + if (err_code == NRF_SUCCESS) + { + // All firmware data received - this was the final packet + m_num_of_firmware_bytes_rcvd += original_len; + mp_final_packet = mp_rx_buffer; + PRINTF("OTA: Final packet flushed, total bytes: %ld\r\n", m_num_of_firmware_bytes_rcvd); + } + else if (err_code == NRF_ERROR_INVALID_LENGTH) + { + // More data expected - this is the normal case + m_num_of_firmware_bytes_rcvd += original_len; + } + else + { + PRINTF("OTA: dfu_data_pkt_handle failed: 0x%08lX\r\n", err_code); + uint32_t hci_error = hci_mem_pool_rx_consume(mp_rx_buffer); + if (hci_error != NRF_SUCCESS) + { + dfu_error_notify(p_dfu, hci_error); + } + dfu_error_notify(p_dfu, err_code); + } + + m_accum_len = 0; + m_accum_bytes_pending = 0; +} /**@brief Function for processing application data written by the peer to the DFU Packet * Characteristic. @@ -441,8 +544,61 @@ static void init_data_process(ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt) static void app_data_process(ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt) { uint32_t err_code; + uint16_t pkt_len = p_evt->evt.ble_dfu_pkt_write.len; + uint8_t * p_data = p_evt->evt.ble_dfu_pkt_write.p_data; - if ((p_evt->evt.ble_dfu_pkt_write.len & (sizeof(uint32_t) - 1)) != 0) + // Check if this is the first data packet and decide whether to accumulate packets + if (m_num_of_firmware_bytes_rcvd == 0 && m_accum_len == 0) + { + m_accum_active = (pkt_len <= ACCUMULATE_THRESHOLD); + PRINTF("OTA: Packet accumulator %s (first packet size %d)\r\n", m_accum_active ? "enabled" : "disabled", pkt_len); + } + + if (m_accum_active) + { + // Flush the accumulator if adding this packet would overflow the buffer + if (m_accum_len + pkt_len > ACCUMULATE_TARGET_SIZE) + { + PRINTF("OTA: Pre-overflow flush of accumulator\r\n"); + accum_flush(p_dfu); + } + + // Add packet to accumulator buffer + memcpy(m_accum_buf + m_accum_len, p_data, pkt_len); + m_accum_len += pkt_len; + m_accum_bytes_pending += pkt_len; + + // Handle PRN - must be based on RECEIVED packets not flushed packets + // otherwise host might timeout waiting for notification + if (m_pkt_rcpt_notif_enabled) + { + m_pkt_notif_target_cnt--; + + if (m_pkt_notif_target_cnt == 0) + { + // Need to flush before sending PRN so byte count is accurate + PRINTF("OTA: PRN flush of accumulator\r\n"); + accum_flush(p_dfu); + + err_code = ble_dfu_pkts_rcpt_notify(p_dfu, m_num_of_firmware_bytes_rcvd); + APP_ERROR_CHECK(err_code); + + m_pkt_notif_target_cnt = m_pkt_notif_target; + } + } + + // Flush the accumulator if we've received the full image in order to trigger completion + if ((m_num_of_firmware_bytes_rcvd + m_accum_len) == m_reported_image_size) + { + accum_flush(p_dfu); + return; + } + + return; + } + + // Original logic for large packets (no accumulation) + if ((pkt_len & (sizeof(uint32_t) - 1)) != 0) { // Data length is not a multiple of 4 (word size). err_code = ble_dfu_response_send(p_dfu, @@ -452,7 +608,7 @@ static void app_data_process(ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt) return; } - uint32_t length = p_evt->evt.ble_dfu_pkt_write.len; + uint32_t length = pkt_len; err_code = hci_mem_pool_rx_produce(length, (void **) &mp_rx_buffer); if (err_code != NRF_SUCCESS) @@ -461,9 +617,7 @@ static void app_data_process(ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt) return; } - uint8_t * p_data_packet = p_evt->evt.ble_dfu_pkt_write.p_data; - - memcpy(mp_rx_buffer, p_data_packet, length); + memcpy(mp_rx_buffer, p_data, length); err_code = hci_mem_pool_rx_data_size_set(length); if (err_code != NRF_SUCCESS) @@ -1092,6 +1246,9 @@ uint32_t dfu_transport_ble_update_start(void) { uint32_t err_code; + m_accum_len = 0; + m_accum_active = false; + m_tear_down_in_progress = false; m_pkt_type = PKT_TYPE_INVALID; @@ -1154,7 +1311,7 @@ uint32_t dfu_transport_ble_close() #ifdef CFG_DEBUG -// #if 0 +#if 0 typedef struct { uint32_t key; @@ -1243,12 +1400,12 @@ lookup_table_t const _strevt_table = .items = _strevt_lookup }; -// #endif +#endif void print_ble_event(uint16_t evt_id) { - const char * str = (const char *) lookup_find(&_strevt_table, evt_id); -// const char * str = NULL; +// const char * str = (const char *) lookup_find(&_strevt_table, evt_id); + const char * str = NULL; PRINTF("BLE event: "); if ( str == NULL ) { From 83ce15b44c23755d2f86c26e84abc3410ec0ab63 Mon Sep 17 00:00:00 2001 From: James Rich Date: Thu, 20 Aug 2026 16:53:43 -0500 Subject: [PATCH 83/84] chore: carry the Meshtastic fork's repo identity onto the mainline base CI workflow (pinned actions, scoped permissions, per-board matrix), agent and community docs, changelog, renovate config, DFU screenshots. Drops the s340 SoftDevice mainline still vendors (no ANT boards here) and regenerates supported_boards.md for the 17-board matrix. Mainline's removal of segger/ and docs/oldsettings.md is kept. --- .github/FUNDING.yml | 2 + .github/ISSUE_TEMPLATE/bug_report.yml | 55 ++- .github/ISSUE_TEMPLATE/config.yml | 12 +- .github/PULL_REQUEST_TEMPLATE.md | 15 +- .github/workflows/githubci.yml | 29 +- .gitignore | 6 + AGENTS.md | 260 ++++++++----- CLAUDE.md | 6 + CODE_OF_CONDUCT.md | 76 +--- CONTRIBUTING.md | 88 +++++ README.md | 435 ++++++++++------------ SECURITY.md | 12 + changelog.md | 44 +++ docs/Screenshot_20251114_142334_DFU.jpg | Bin 0 -> 222559 bytes docs/dfu_settings_01.png | Bin 0 -> 48846 bytes docs/dfu_settings_02.png | Bin 0 -> 46259 bytes lib/softdevice/s340_nrf52_7.0.1/readme.md | 23 -- renovate.json | 19 + supported_boards.md | 59 +-- 19 files changed, 637 insertions(+), 504 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100644 CLAUDE.md create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md create mode 100644 docs/Screenshot_20251114_142334_DFU.jpg create mode 100644 docs/dfu_settings_01.png create mode 100644 docs/dfu_settings_02.png delete mode 100644 lib/softdevice/s340_nrf52_7.0.1/readme.md create mode 100644 renovate.json diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..a5c0976e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: meshtastic +ko_fi: oltaco diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index d6e53881..e4b3ffc1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -10,46 +10,77 @@ body: - type: dropdown attributes: - label: Operating System + label: Board options: - - Linux - - MacOS - - RaspberryPi OS - - Windows 7 - - Windows 10 - - Windows 11 - - Others + - Elecrow ThinkNode M1 + - Elecrow ThinkNode M3 + - Elecrow ThinkNode M6 + - Heltec Automation Mesh Node T114 + - LilyGO T-Echo + - Minewsemi MX25LE01 + - Nologo ProMicro NRF52840 (SuperMini NRF52840) + - RAK 4631 + - RAK WisMesh Tag + - Seeed Studio SenseCAP Card Tracker T1000-E + - Seeed SenseCAP Solar Node P1 + - Seeed Studio Wio Tracker L1 + - Seeed Studio XIAO nRF52840 BLE + - Seeed Studio XIAO nRF52840 BLE SENSE + - Other / not listed validations: required: true + - type: input + attributes: + label: How did you flash/update the bootloader? + placeholder: e.g. Meshtastic Android app (USB), manual UF2 copy, adafruit-nrfutil, nRF Connect OTA + validations: + required: true + + - type: dropdown + attributes: + label: Operating System (of the computer/phone used to flash, if applicable) + options: + - Linux + - macOS + - Windows 10 + - Windows 11 + - Android + - iOS + - Not applicable + - Others + validations: + required: false + - type: textarea attributes: label: INFO_UF2.TXT + description: Enter UF2 mode (double-press reset) and paste the contents of INFO_UF2.TXT from the mounted drive — it identifies the exact bootloader version and board. placeholder: Paste your INFO_UF2.TXT contents here validations: required: true - type: textarea attributes: - label: What happened ? + label: What happened? placeholder: A clear and concise description of what the bug is. validations: required: true - type: textarea attributes: - label: How to reproduce ? + label: How to reproduce? placeholder: | 1. Go to '...' 2. Click on '....' - 3. See error + 3. See error validations: required: true - type: textarea attributes: label: Debug Log - placeholder: Debug log attached txt file. + placeholder: Debug log attached as a txt file. validations: required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 2b876363..4043c58c 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,8 @@ -contact_links: - - name: Adafruit Support Forum - url: https://forums.adafruit.com - about: If you have other questions or need help, post it here. +blank_issues_enabled: false +contact_links: + - name: Meshtastic Discussions + url: https://github.com/orgs/meshtastic/discussions + about: Questions, troubleshooting, and general discussion — ask here first. + - name: Meshtastic Website + url: https://meshtastic.org/ + about: Docs and other ways to reach us. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 65233633..98a4d2ff 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,13 +1,16 @@ ## Checklist -*By completing this PR sufficiently, you help us to review this Pull Request quicker and also help improve the quality of Release Notes* +*By completing this PR sufficiently, you help us to review this Pull Request quicker and also help improve the quality of Release Notes.* +*See [`CONTRIBUTING.md`](../CONTRIBUTING.md) for the full dev setup and PR process.* -- [ ] Please provide specific title of the PR describing the change -- [ ] If you are adding an new boards, please make sure - - [ ] Provide link to your allocated VID/PID if applicable - - [ ] `UF2_BOARD_ID` in your board.h follow correct format from [uf2 specs](https://github.com/microsoft/uf2#files-exposed-by-bootloaders) +- [ ] PR title specifically describes the change +- [ ] `tools/build_all.py` (or CI's board matrix) passes +- [ ] If adding a new board, see [Adding a new board](../CONTRIBUTING.md#adding-a-new-board): + - [ ] Tested on real hardware, not just compiled + - [ ] Link to allocated VID/PID provided, if applicable + - [ ] `UF2_BOARD_ID` in `board.h` follows the [UF2 spec](https://github.com/microsoft/uf2#files-exposed-by-bootloaders) format -*This checklist items that are not applicable to your PR can be deleted.* +*Checklist items that don't apply to your PR can be deleted.* ----------- diff --git a/.github/workflows/githubci.yml b/.github/workflows/githubci.yml index b3686dd3..8ef3dc88 100644 --- a/.github/workflows/githubci.yml +++ b/.github/workflows/githubci.yml @@ -1,14 +1,22 @@ name: Build on: - workflow_dispatch: pull_request: - push: - branches: [ master ] + # push: + # repository_dispatch: release: types: - created +# The repo's default is write (checked via `gh api .../actions/permissions/workflow`), +# so every job here would otherwise get a write-capable GITHUB_TOKEN even +# though only the release-asset upload needs one — and this workflow now +# runs on every PR, including from forks. contents:write is scoped to the +# one job that needs it (the release step, gated separately by +# `if: github.event_name == 'release'`); everything else gets read-only. +permissions: + contents: read + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -20,7 +28,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Checkout Code - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set matrix id: set-matrix @@ -33,19 +41,23 @@ jobs: build: needs: set-matrix runs-on: ubuntu-latest + # Only this job's release step needs write access (softprops/action-gh-release + # creating/updating release assets); PR runs never reach that step. + permissions: + contents: write strategy: fail-fast: false matrix: board: ${{ fromJSON(needs.set-matrix.outputs.matrix) }} steps: - name: Checkout Code - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 submodules: true - name: Install ARM GCC - uses: carlosperate/arm-none-eabi-gcc-action@v1 + uses: carlosperate/arm-none-eabi-gcc-action@98c40e51546516419841748d3210c290df0d2c6c # v1.13.0 with: release: '12.3.Rel1' @@ -58,13 +70,14 @@ jobs: make BOARD=${{ matrix.board }} all make BOARD=${{ matrix.board }} copy-artifact - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ matrix.board }} path: _bin/${{ matrix.board }} + retention-days: ${{ github.event_name == 'release' && 90 || 1 }} - name: Upload Release Asset - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3 if: ${{ github.event_name == 'release' }} with: files: | diff --git a/.gitignore b/.gitignore index 3180a079..bc5ad7f6 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,9 @@ Makefile.user # Exclude all SoftDevice S340 files lib/softdevice/s340* +_bin/ +venv/ +.python-version +lib/python3.13/ +lib64 +pyvenv.cfg diff --git a/AGENTS.md b/AGENTS.md index aaebd02f..d21a5cc8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,102 +1,190 @@ -# Agent Instructions +# AGENTS.md -This file provides guidance to AI agents when working with code in this repository. +Canonical guidance for AI coding agents and maintainers working in this repo. +(`CLAUDE.md` is a pointer to this file.) -## Project Overview +## First read -CDC/DFU/UF2 bootloader for Nordic nRF52 microcontrollers (nRF52832, nRF52833, nRF52840). Supports DFU over Serial, BLE OTA, and USB mass storage (UF2). Runs on 60+ boards. +1. [README.md's "How this fits together"](./README.md#how-this-fits-together) + — the boot chain (MBR → this bootloader → SoftDevice → application), why + this repo is only the bootloader and not the Meshtastic application + itself, and the three ways a new image gets installed (UF2, serial DFU, + BLE OTA DFU). Read this before touching `main.c` or `usb/` — the + Architecture section below assumes it. +2. The rest of `README.md` — boards supported, installation, the Meshtastic + Android in-app upgrade flow, troubleshooting. +3. `CONTRIBUTING.md` — dev setup, PR process, adding a new board. +4. `changelog.md` — OTAFIX version history (2.1–2.3 at the top; everything + below predates the OTAFIX fork). +5. The design invariants and gotchas below — do not violate them. -## Style +## What this is -Follow the repo `.clang-format` when making changes. +Meshtastic's fork (org-owned since 2026-08) of +[oltaco's OTAFIX bootloader](https://github.com/oltaco/Adafruit_nRF52_Bootloader_OTAFIX), +itself a fork of `adafruit/Adafruit_nRF52_Bootloader` with OTA DFU +enhancements. It's the bootloader several nRF52-based Meshtastic devices +ship with, and the one the +[Meshtastic Android app](https://github.com/meshtastic/Meshtastic-Android) +can upgrade in-app over USB/serial (`feature/firmware` there). -## Build / Verify +Meshtastic's separate `Adafruit_nRF52_Bootloader` fork is a **different +lineage without** the OTAFIX patches — don't confuse the two. -Default board: `feather_nrf52840_express`. Always verify changes against both boards: -- `feather_nrf52840_express` (nRF52840) -- `feather_nrf52832` (nRF52832) +This is C, built with a bare Makefile (the supported path) or `CMakeLists.txt` +(**not** fully wired — see the CMake gotcha below). Not PlatformIO. -### CMake (preferred) -```bash -cmake -S . -B cmake-build-feather_nrf52840_express -DBOARD=feather_nrf52840_express -cmake --build cmake-build-feather_nrf52840_express +## Build -cmake -S . -B cmake-build-feather_nrf52832 -DBOARD=feather_nrf52832 -cmake --build cmake-build-feather_nrf52832 -``` +You need `arm-none-eabi-gcc` (CI pins **12.3.Rel1** exactly — see the +toolchain gotcha below) and Python 3. -### Make (alternate) -```bash -make BOARD=feather_nrf52840_express all -make BOARD=feather_nrf52832 all -``` +```sh +git submodule update --init --recursive # lib/nrfx, lib/tinyusb, lib/uf2 — required, not vendored inline -### Flashing -```bash -make BOARD={board} flash # Flash via JLink -make BOARD={board} flash-dfu # Flash via Serial/CDC DFU -make BOARD={board} flash-sd # Flash SoftDevice only -``` +python3 -m venv .venv && source .venv/bin/activate # or: uv venv .venv && source .venv/bin/activate +pip install adafruit-nrfutil uritemplate requests intelhex setuptools -### Build all boards -```bash -python3 tools/build_all.py +make BOARD=wiscore_rak4631_board all # board = a directory name under src/boards/ +make BOARD=wiscore_rak4631_board copy-artifact # -> _bin//*.zip, *.hex, update-*.uf2 +make BOARD=wiscore_rak4631_board clean + +tools/build_all.py # builds every board in src/boards/, prints pass/fail + flash/RAM size table ``` +No lint, no test suite — this is a bootloader; the only correctness signal is +"does it compile for every board" (CI's job) and real hardware testing. + ## Architecture -### MCU Variants and SoftDevices -- **nrf52** (nRF52832): UART-only bootloader, default SoftDevice s132 v6.1.1 -- **nrf52833**: USB support, default SoftDevice s140 v7.3.0 -- **nrf52840**: Full USB + OTA, default SoftDevice s140 v6.1.1 -- Boards may override `SD_NAME`/`SD_VERSION` in their `board.mk` - -### Key Source Structure -- `src/main.c` — Bootloader entry point, DFU mode detection, LED/button init -- `src/dfu_init.c` — DFU packet validation, CRC/signature verification -- `src/dfu_ble_svc.c` — BLE DFU service -- `src/flash_nrf5x.c` — Flash memory operations -- `src/boards/boards.c` — Board abstraction (LED control, buttons, timing) -- `src/usb/` — USB stack (nRF52833/nRF52840): CDC serial, MSC storage, UF2 handler -- `src/usb/uf2/ghostfat.c` — Virtual FAT filesystem for UF2 drag-and-drop - -### Board Definition System - -Each board lives in `src/boards/{board_name}/` with: -- `board.h` — Pin definitions, LED/button assignments, USB VID/PID, UF2 metadata -- `board.mk` — Makefile variable `MCU_SUB_VARIANT` (nrf52, nrf52833, or nrf52840) -- `board.cmake` — CMake variable `MCU_VARIANT` -- `pinconfig.c` — CF2 bootloader configuration (flash/RAM size, UF2 family ID) - -### Memory Layout (linker scripts in `linker/`) -- Bootloader occupies ~38KB near end of flash (e.g., 0xF4000–0xFD800 on nRF52840) -- No heap (`__HEAP_SIZE=0`), static allocation only -- Special sections: double-reset detection word, bond info for OTA, MBR params, bootloader settings - -### Submodules (`lib/`) -- `tinyusb` — USB device stack -- `nrfx` — Nordic HAL drivers -- `uf2` — UF2 format tools -- `tinycrypt` — Crypto (only when `SIGNED_FW=1`) -- `sdk/`, `sdk11/` — Nordic SDK libraries -- `softdevice/` — Precompiled Bluetooth stack binaries - -### Compile-Time Feature Flags -- `SIGNED_FW` — Require signed firmware (disables UF2 unless `FORCE_UF2=1`) -- `DUALBANK_FW` — Dual-bank updates -- `DEFAULT_TO_OTA_DFU` — Default to BLE OTA instead of serial DFU -- `DEBUG` — Enable RTT debugging, larger bootloader region - -## PR Review Checklist - -- **VID/PID assignment**: When a PR adds or modifies a board, verify `USB_DESC_VID`/`USB_DESC_UF2_PID` in `board.h`. New boards should use unique values. The Adafruit VID `0x239A` is reserved for Adafruit-allocated boards; third-party boards must use their own VID. Some legacy boards intentionally share a VID/PID with a reference board (see `// Shared VID/PID with ...` comments) — these are pending cleanup, not a template for new boards. - -## CI - -GitHub Actions (`.github/workflows/githubci.yml`) builds all boards in parallel using a matrix generated from `src/boards/` directory names. On release, artifacts (zip, hex, uf2) are uploaded as release assets. - -## Required Toolchain - -- `arm-none-eabi-gcc` (tested with 12.3.1) -- Python 3 with: `adafruit-nrfutil`, `intelhex` -- `nrfjprog` (for JLink flashing) +### Board abstraction: `src/boards//` + +Each board directory can carry: +- `board.h` — pin/peripheral defines (`DISPLAY_PIN_SCK` gates the OLED code + in `src/screen.c` and `src/images.c`; `BLEDIS_MANUFACTURER`/`BLEDIS_MODEL` + are the real vendor name, e.g. RAKWireless — don't rebrand these). + Defining `BANNER_TEXT` overrides the on-screen DFU banner default in + `src/screen.c`. +- `board.mk` — Makefile board config (`MCU_SUB_VARIANT`, per-board + `CFLAGS` like the BLE `DEVICE_NAME`). +- `board.cmake` — **only exists for 4 of 17 boards** (`heltec_t096`, + `heltec_t1`, `heltec_t114`, `thinknode_m1`). `cmake -DBOARD=` fails outright. Nobody + uses the CMake path in practice (CI and `tools/build_all.py` both use + `make`); don't assume feature parity between the two build systems. +- `pinconfig.c` — present on some boards, board-specific pin table. + +`Makefile` and `CMakeLists.txt` both dynamically discover the board's files +under `src/boards/$(BOARD)/` — there is no separate hardcoded board list to +keep in sync in either build system (unlike the CI matrix, see below). + +### Core bootloader (`src/`) + +`main.c` is the entry point (MBR/SoftDevice handoff, DFU state machine +dispatch). `screen.c`/`images.c` render the OLED UF2/BLE-OTA screens, active +only when the board defines `DISPLAY_PIN_SCK` — today that's `heltec_t096`, +`heltec_t1`, and `heltec_t114`. `dfu_ble_svc.c`/`dfu_init.c` are the BLE DFU +service; `flash_nrf5x.c` wraps flash writes. `usb/` is the USB MSC (UF2 +drive) + CDC stack on top of the vendored `lib/tinyusb`. `cmsis/` is ARM +CMSIS headers. + +### Vendored submodules (`lib/`) + +`lib/nrfx`, `lib/tinyusb`, `lib/uf2` are git submodules, **not** vendored +copies — `git submodule update --init --recursive` is required before any +build. `renovate.json` surfaces bump PRs on a daily schedule, but nothing +bumps them automatically — these are boot-critical, and a version jump needs +real hardware testing before merge, not a bot auto-merge. + +`lib/nrfx` is deliberately pinned to **v3.14.0**, not the latest commit +Renovate will keep proposing. nrfx 4.0 restructured the entire repo layout +(`mdk/` → `bsp/stable/mdk/`, among others) — a jump to it would need this +repo's Makefile `IPATH`s and every board's linker script reworked, not just +a digest bump. v3.14.0 is the last tag on the pre-4.0 layout, so it's the +practical ceiling for a same-day bump; going past it is a real project, not +a Renovate merge. If a future Renovate PR targets nrfx ≥4.0, that's this +gotcha firing — don't merge it without doing that rework. + +`lib/tinyusb`'s `nrf5x` USB port (`dcd_nrf5x.c`) calls nrfx's chip-specific +errata functions (e.g. `nrf52_errata_199()`) — bumping tinyusb alone, +without nrfx at a version new enough to define them, fails to compile. The +two submodules move together, not independently, which is why Renovate's +separate per-submodule PRs (#11, #12 as originally filed) each failed CI on +their own; see the `linker/nrf_common.ld` gotcha below for the other half of +what that joint bump needed. + +`lib/softdevice/` vendors Nordic's SoftDevice binaries directly (not a +submodule) — `SD_NAME`/`SD_VERSION` in `Makefile` select which one. + +### CI (`.github/workflows/githubci.yml`) + +A `set-matrix` job lists `src/boards/*/` and fans out a `build` job per +board (currently 17), on every PR and on `release: created`. Release events +additionally upload `.zip`/`.hex`/`update-*.uf2` per board as release +assets; PR runs just validate the compile and get 1-day artifact retention +(release runs keep 90). + +Branch protection on `master` requires all 18 checks (`set-matrix` + 17 +`build ()` contexts) by literal name. **Adding, removing, or renaming +a board changes those context names** — branch protection does not update +itself; a human has to edit it too. + +## Gotchas + +- **ARM GCC version matters.** CI pins exactly `12.3.Rel1`. Much newer + toolchains (verified: 15.2.Rel1) fail with `-Werror=array-bounds` in + `lib/sdk11/components/libraries/bootloader_dfu/bootloader_settings.c` + (a false positive from newer GCC's stricter analysis of a fixed + MBR-address read) — verified by actually building with it. 13.3.Rel1 + compiles clean and is the closest verified-working version if you can't + get the exact CI-pinned one. +- **MeshCore/Ripple content has been deliberately removed** from the docs + (README, changelog) — this is Meshtastic's own branded fork now, not a + place to re-add other companion-firmware documentation. If you're + porting docs changes from upstream `oltaco`, check for this before + copying anything verbatim. +- **This repo's `src/boards/*` folder names are internal-only.** Nothing + cross-repo reads them — `Meshtastic-Android`'s + `device_bootloader_ota_quirks.json` matches boards by the canonical + `HardwareModel` protobuf enum (`hwModel`/`hwModelSlug`) and by firmware's + PlatformIO target names, never by this repo's directory names. Renaming a + board directory here for "consistency" achieves nothing functionally and + risks breaking `UF2_BOARD_ID`/build-artifact filenames for no benefit. +- **`linker/nrf_common.ld` needs `__data_start`/`__sdata_*`/`__tdata_*`/`__fast_*` + symbols since nrfx 3.x.** `lib/nrfx/mdk/gcc_startup_.S`'s data-copy loop + changed from this repo's `__data_start__`-style (double underscore) names to + plain `__data_start`, plus three new RAM-loaded regions (`.sdata`/`.tdata`/ + `.fast`) nothing in this codebase actually uses. **Do not** pull in nrfx's own + updated `mdk/nrf_common.ld` to get these — it also redefines `.bss`/`.noinit` + placement, which would silently fight this repo's board `.ld` files (e.g. + `linker/nrf52840.ld`'s fixed-address `NOINIT` region used for BLE bond + exchange across a DFU reset). The fix already applied is a handful of alias + assignments plus zero-length filler for the three unused regions — extend + that pattern, don't replace the file. +- **Board gaps are tracked as issues, not guessed at.** `Meshtastic-Android` + flags `NANO_G2_ULTRA` and `NOMADSTAR_METEOR_PRO` as needing bootloader + upgrade support with no board here yet (issues #4, #5) — bringing up a + new board needs real hardware to get `UF2_BOARD_ID`/VID-PID/pin defs + right; don't fabricate a `board.h` without one. +- **`CURRENT.UF2` dump-and-restore used to hang the device — fixed in #20, + don't reintroduce it.** Root cause: `CURRENT.UF2` was sized off the max + possible app region (`TRUE_USER_FLASH_SIZE`) instead of the real + installed app, AND `msc_uf2.c`'s UF2-app-flash completion path never + recorded the real app size into `bootloader_settings.bank_0_size` (stayed + 0 from a `memset`, only the DFU-serial protocol populated it). Together + that meant restoring a `CURRENT.UF2` dump byte-for-byte could still hang + the device on boot. Both fixed together in #20 (`ghostfat.c`'s + `current_flash_size()` + `msc_uf2.c`'s `update_status.app_size`) — + verified on real RAK4631 hardware, the same dump-and-restore sequence + that hung now completes in ~2 seconds. If you change either of those two + files, check this still holds. +- **Flashing the bootloader+SoftDevice package over serial DFU zeros + `bank_0` — the device then boots into BLE-OTA wait mode, not the app, + on its own.** Confirmed on real RAK4631 hardware (2026-08-19): after + `adafruit-nrfutil dfu serial --package _bootloader-..._s140_...zip` + reports "Device programmed", the board goes USB-silent (no serial port, + no UF2 drive) because `bank_0_size` is now 0 and this bootloader's + documented OTAFIX 2.0+ behavior is to default to BLE-OTA rather than get + stuck. This is expected, not a brick: double-press reset to force UF2 + mode and drag the app's `.uf2` across, or complete a BLE OTA DFU to the + same effect. Don't reflash blind expecting the app to still be running. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..b44e359f --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,6 @@ +# CLAUDE.md + +This file is a pointer for agent runners that key off the Claude filename. The +canonical guidance lives in [`AGENTS.md`](./AGENTS.md) — read that file. + +The two files are kept in sync. If they diverge, **`AGENTS.md` wins**. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 09827eb1..f2644147 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,76 +1,4 @@ # Contributor Covenant Code of Conduct -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at . All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see -https://www.contributor-covenant.org/faq +This project is subject to the code of conduct for the parent Meshtastic project, which can be found here: +https://meshtastic.org/docs/legal/conduct/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..6bada268 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,88 @@ +# Contributing + +Thanks for considering a contribution. This is boot-critical firmware — a +broken bootloader can leave a device needing a hardware programmer to +recover — so a few things here are stricter than a typical repo. + +## Before you start + +- **Read [`AGENTS.md`](./AGENTS.md) first.** It covers the board abstraction, + the build system split (`make` vs. the incomplete `CMakeLists.txt`), the + vendored submodules, and a list of known gotchas. This file covers + process; that one covers the code. +- **New board support needs real hardware.** `UF2_BOARD_ID`, VID/PID, and + pin definitions cannot be guessed from a datasheet alone — see + [Adding a new board](#adding-a-new-board) below. +- **This repo is [MIT-licensed](./LICENSE)** (originally Adafruit + Industries). By contributing, you agree your changes are under the same + license. + +## Building locally + +You need `arm-none-eabi-gcc` and Python 3 installed. + +```sh +git submodule update --init --recursive # lib/nrfx, lib/tinyusb, lib/uf2 — required, not vendored inline + +python3 -m venv .venv && source .venv/bin/activate +pip install adafruit-nrfutil uritemplate requests intelhex setuptools + +# ARM GCC 12.3.Rel1 is what CI pins (.github/workflows/githubci.yml). +# Newer versions (13.x verified working, 15.x does not) can hit a +# -Werror=array-bounds false positive in bootloader_settings.c — see +# AGENTS.md's Gotchas section before reaching for a newer toolchain. + +make BOARD=wiscore_rak4631_board all +make BOARD=wiscore_rak4631_board copy-artifact # writes _bin// +``` + +Board names are the directory names under `src/boards/`. `tools/build_all.py` +builds every board and prints a pass/fail + size table — run it before +opening a PR; it's the same check CI's board matrix does per-PR, just local. + +**Use `make`, not `cmake`.** `CMakeLists.txt` only has a `board.cmake` for 2 +of the 14 boards; `cmake -DBOARD=` fails outright. Nobody +uses the CMake path in practice. + +There is no lint or test suite — for a bootloader, "does it compile for +every board" (CI's job) and real hardware testing are the correctness +signals that exist. + +## Adding a new board + +The [README's board list](./README.md#boards-supported) says to raise an +issue for a board you'd like supported — that's still the right first step, +and [#4](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/issues/4) +and +[#5](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/issues/5) +are open examples. To actually submit the board yourself: + +1. You need the physical hardware to test on — a bootloader that compiles + but was never flashed is not a contribution, it's a liability. +2. Add `src/boards//board.h` and `board.mk`, following an + existing board (e.g. `src/boards/wiscore_rak4631_board`) as a template. +3. `UF2_BOARD_ID` must follow the format in the + [UF2 spec](https://github.com/microsoft/uf2#files-exposed-by-bootloaders). + If your board has an allocated VID/PID, link to it in the PR. +4. Confirm `make BOARD= all` succeeds and the resulting UF2 + actually boots the board correctly — not just compiles. +5. The PR template has a checklist for exactly this; fill it in. + +## Pull requests + +- CI (`.github/workflows/githubci.yml`) builds every board in the matrix on + every PR. Branch protection on `master` requires all of those checks to + pass before merge. +- Recent commit history is + [Conventional Commits](https://www.conventionalcommits.org/)-style + (`docs:`, `ci:`, `chore:`, etc.); older history (pre-fork) is looser — + match the newer style going forward. +- Keep PRs scoped to one change. A board addition, a docs fix, and a CI + tweak are three PRs, not one. + +## Code of Conduct and security + +This project follows the +[Meshtastic Code of Conduct](./CODE_OF_CONDUCT.md). Report security +vulnerabilities privately per [`SECURITY.md`](./SECURITY.md) — not as a +public issue. diff --git a/README.md b/README.md index e66eb64b..6a0b59ef 100644 --- a/README.md +++ b/README.md @@ -1,299 +1,242 @@ -# Adafruit nRF52 Bootloader +# Meshtastic OTAFIX Bootloader + +[![Build](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/actions/workflows/githubci.yml/badge.svg)](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/actions/workflows/githubci.yml) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE) + +Adafruit nRF52 bootloader with enhanced OTA DFU, forked for [Meshtastic](https://meshtastic.org) from [oltaco's OTAFIX bootloader](https://github.com/oltaco/Adafruit_nRF52_Bootloader_OTAFIX). This is the bootloader several nRF52-based Meshtastic devices ship with, and the one the [Meshtastic Android app](https://github.com/meshtastic/Meshtastic-Android) can upgrade in-app. + +Current release: **OTAFIX 2.3** — see [changelog.md](changelog.md) for version history. + +## Contents + +- [How this fits together](#how-this-fits-together) +- [Boards supported](#boards-supported) +- [BLE advertising names](#ble-advertising-names) +- [Installation](#installation) +- [Bootloader upgrade from the Meshtastic Android app](#bootloader-upgrade-from-the-meshtastic-android-app) +- [Troubleshooting](#troubleshooting) +- [Recommended OTA DFU settings](#recommended-ota-dfu-settings) +- [Notes on Xiao NRF52840 BLE](#notes-on-xiao-nrf52840-ble) +- [Notes on RAK4631 bootloader](#notes-on-rak4631-bootloader) +- [Contributing](#contributing) +- [Getting help](#getting-help) +- [License](#license) + +--- + +## How this fits together + +This repo builds **only the bootloader** — the small program that runs +before anything else on the device. It does not contain the Meshtastic +application (the LoRa mesh, BLE, and display code); that's built entirely +separately in [`meshtastic/firmware`](https://github.com/meshtastic/firmware). +This bootloader's job is just to start that application, or, when asked, +replace it (or itself) with a new version. + +On every power-up or reset, a tiny fixed piece of code at the very start of +flash — Nordic's **MBR** — hands off to this bootloader. From there, the +bootloader either: + +- boots straight into the installed Meshtastic application, or +- if no valid application is installed, or the user has asked for it, waits + for a new one. + +A new application image — or occasionally the bootloader itself — can be +installed three different ways: + +| Method | Transport | Typical use | +|---|---|---| +| **UF2 drag-and-drop** | USB, appears as a drive | Manual flashing — see [Installation](#installation) | +| **Serial DFU** | USB, appears as a serial port | Recovery, and flashing a full bootloader+SoftDevice package with `adafruit-nrfutil` — see [Installation](#installation) | +| **BLE OTA DFU** | Bluetooth, no cable needed | The Meshtastic Android app's firmware/bootloader update, and any Nordic DFU app — see [below](#bootloader-upgrade-from-the-meshtastic-android-app) and [recommended settings](#recommended-ota-dfu-settings) | + +BLE OTA DFU is the only *wireless* path, which is what the "OTA" in +"OTAFIX" refers to — and it's also the bootloader's default fallback: since +**OTAFIX 2.0**, if no valid application is present, the device waits in BLE +OTA mode automatically rather than risk getting stuck (see +[Troubleshooting](#troubleshooting)). -[![Build Status](https://github.com/adafruit/Adafruit_nRF52_Bootloader/workflows/Build/badge.svg)](https://github.com/adafruit/Adafruit_nRF52_Bootloader/actions) +The application also relies on Nordic's **SoftDevice** — a closed-source, +precompiled Bluetooth stack (vendored here as a hex blob under +`lib/softdevice/`) that sits in flash alongside it. The bootloader and the +application both call into it for BLE, so bootloader and SoftDevice +versions are usually flashed together as a matched pair (that's why the +[Installation](#installation) instructions below mention flashing "a full +bootloader and SoftDevice zip package"). -A CDC/DFU/UF2 bootloader for Nordic nRF52 microcontroller. UF2 is an easy-to-use bootloader that appears as a flash drive. You can just copy `.uf2`-format application images to the flash drive to load new firmware. See https://github.com/Microsoft/uf2 for more information. +--- -DFU via serial/CDC requires [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil), a modified version of [Nordic nrfutil](https://github.com/NordicSemiconductor/pc-nrfutil). Install `python3` if it is not installed already and run this command to install adafruit-nrfutil from PyPi: +## Boards supported +- Elecrow ThinkNode M1 +- Elecrow ThinkNode M3 +- Elecrow ThinkNode M6 +- Heltec T096 +- Heltec T1 +- Heltec T114 / HT-nRF5262 +- LilyGO T-Echo +- Minewsemi MX25LE01 +- Nologo ProMicro NRF52840 (aka SuperMini NRF52840) +- RAK 3401 +- RAK 4631 ([See note](#notes-on-rak4631-bootloader)) +- RAK WisMesh Tag +- Seeed Studio SenseCAP Card Tracker T1000-E +- Seeed SenseCAP Solar Node P1 +- Seeed Studio Wio Tracker L1 +- Seeed Studio XIAO nRF52840 BLE ([See note](#notes-on-xiao-nrf52840-ble)) +- Seeed Studio XIAO nRF52840 BLE SENSE -``` -$ pip3 install --user adafruit-nrfutil -``` +If there is another nRF52840-based Meshtastic board you would like to see supported, please [raise a GitHub issue](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/issues/new/choose) — or see [Adding a new board](./CONTRIBUTING.md#adding-a-new-board) in `CONTRIBUTING.md` if you want to submit it yourself. -## Supported Boards +## BLE advertising names -Officially supported boards are: +When in OTA DFU mode, devices advertise using a board-specific name rather than the generic `AdaDFU`. -- [Adafruit CLUE](https://www.adafruit.com/product/4500) -- [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333) -- [Adafruit Feather nRF52832](https://www.adafruit.com/product/3406) -- [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062) -- [Adafruit Feather nRF52840 Sense](https://www.adafruit.com/product/4516) -- [Adafruit ItsyBitsy nRF52840 Express](https://www.adafruit.com/product/4481) -- [Adafruit LED Glasses Driver nRF52840](https://www.adafruit.com/product/5217) -- Adafruit Metro nRF52840 Express -- [Raytac MDBT50Q-RX Dongle](https://www.adafruit.com/product/5199) +| Board | OTA DFU advertising name | +| ---------------------------- | ------------------------ | +| Elecrow ThinkNode M1 | `TNM1_DFU` | +| Elecrow ThinkNode M3 | `TNM3_DFU` | +| Elecrow ThinkNode M6 | `TNM6_DFU` | +| Heltec T096 | `T096_DFU` | +| Heltec T1 | `T1_DFU` | +| Heltec T114 | `T114_DFU` | +| LILYGO T-Echo | `LGTE_DFU` | +| Minewsemi MX25LE01 | `MX25_DFU` | +| ProMicro NRF52840 | `PROM_DFU` | +| RAK 4631 | `4631_DFU` | +| RAK 3401 | `3401_DFU` | +| RAK WisMesh Tag | `RTAG_DFU` | +| Seeed SenseCAP Solar Node P1 | `SCAP_DFU` | +| Seeed T1000e | `T1KE_DFU` | +| Seeed WioTracker L1 | `WTL1_DFU` | +| XIAO NRF52 BLE / SENSE | `XIAO_DFU` | -In addition, there is also lots of other 3rd-party boards which are added by other makers, users and community. Check -out the [complete list of all boards here](/supported_boards.md). +--- -## Features +## Installation -- DFU over Serial and OTA (application, Bootloader+SD) -- Self-upgradable via Serial and OTA -- DFU using UF2 (https://github.com/Microsoft/uf2) (application only) -- Auto-enter DFU briefly on startup for DTR auto-reset trick (832 only) -- Supports dual bank firmware updates (disabled by default) -- Supports signed firmware updates (disabled by default) +The recommended way to install the bootloader is using the UF2 file. +Download the UF2 file for your board (they can be found in the [releases](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/releases) with filenames beginning with `update-`), enter UF2 mode (usually by double pressing the reset button within 0.5s) and copy the UF2 file across. -Note: For OTA, `Packet Receipt Notification` (PRN) must be 8 or less, which can be configured in nRF DFU. Otherwise, the -bootloader will run out of -memory. +If an incorrect bootloader has been flashed to the device, a full bootloader and SoftDevice zip package will need to be flashed using ``adafruit-nrfutil``. -## How to use +--- -There are two pins, `DFU` and `FRST` that bootloader will check upon reset/power: +## Bootloader upgrade from the Meshtastic Android app -- `Double Reset` Reset twice within 500 ms will enter DFU with UF2 and CDC support (only works with nRF52840) -- `DFU = LOW` and `FRST = HIGH`: Enter bootloader with UF2 and CDC support -- `DFU = LOW` and `FRST = LOW`: Enter bootloader with OTA, to upgrade with a mobile application such as Nordic nrfConnect/Toolbox -- `DFU = HIGH` and `FRST = LOW`: Factory Reset mode: erase firmware application and its data -- `DFU = HIGH` and `FRST = HIGH`: Go to application code if it is present, otherwise enter DFU with UF2 -- The `GPREGRET` register can also be set to force the bootloader can enter any of above modes (plus a CDC-only mode for Arduino). -`GPREGRET` is set by the application before performing a soft reset. +The [Meshtastic Android app](https://github.com/meshtastic/Meshtastic-Android) can flash this bootloader directly — no manual UF2 drag-and-drop needed. -```c -#include "nrf_nvic.h" -void reset_to_uf2(void) { - NRF_POWER->GPREGRET = 0x57; // 0xA8 OTA, 0x4e Serial - NVIC_SystemReset(); // or sd_nvic_SystemReset(); -} -``` +With the radio connected over **USB/serial** (not Bluetooth), open the connected radio's configuration, go to **Advanced → Firmware Update**, and where an upgraded bootloader is published for your board you'll see an **Upgrade bootloader** option alongside **Erase and reinstall**. The app reads `INFO_UF2.TXT` from the device's update drive first to confirm the board and Bluetooth stack before writing anything, and will ask you to select the update drive twice — once for the bootloader image, once for the firmware. See the app's [Firmware Updates guide](https://github.com/meshtastic/Meshtastic-Android/blob/main/docs/en/user/firmware.md) for the full flow. -On the Nordic PCA10056 DK board, `DFU` is connected to **Button1**, and `FRST` is connected to **Button2**. -So holding down **Button1** while clicking **RESET** will put the board into USB bootloader mode, with UF2 and CDC support. -Holding down **Button2** while clicking **RESET** will put the board into OTA (over-the-air) bootloader mode. +--- -On the Nordic PCA10059 Dongle board, `DFU` is connected to the white button. -`FRST` is connected to pin 1.10. Ground it to pull `FRST` low, as if you had pushed an `FRST` button. -There is an adjacent ground pad. +## Troubleshooting -For other boards, please check the board definition for details. +### Device does not appear as a USB drive or serial port -### Making your own UF2 +If the device does not show up on your computer after flashing the bootloader or performing an OTA update, it may be **waiting in OTA DFU mode**. -To create your own UF2 DFU update image, simply use the [Python conversion script](https://github.com/Microsoft/uf2/blob/master/utils/uf2conv.py) on a .bin file or .hex file, specifying the family as **0xADA52840** (nRF52840) or **0x621E937A** (nRF52833). +In **OTAFIX 2.0** and above, OTA DFU is the default state when no valid application is present. +In this mode: +- No UF2 drive is exposed +- No serial port is available +- The device is waiting for an OTA firmware update over BLE -``` -nRF52840 -uf2conv.py firmware.hex -c -f 0xADA52840 +**What to do:** +- Perform an OTA update using a supported DFU app, **or** +- Explicitly request UF2/serial mode using **double-reset**. -nRF52833 -uf2conv.py firmware.hex -c -f 0x621E937A -``` +This behaviour is intentional and prevents devices from getting stuck in UF2 mode after failed OTA updates. -If using a .bin file with the conversion script you must specify application address with the -b switch, this address depend on the SoftDevice size/version e.g S140 v6 is 0x26000, v7 is 0x27000 +--- -``` -nRF52840 -uf2conv.py firmware.bin -c -b 0x26000 -f 0xADA52840 +### OTA update fails with `Error: Operation Failed` -nRF52833 -uf2conv.py firmware.bin -c -b 0x27000 -f 0x621E937A -``` +If an OTA update consistently fails early with `Error: Operation Failed`, this is often caused by BLE stack incompatibilities when **Request High MTU** is enabled. -To create a UF2 image for bootloader from a .hex file using separated family of **0xd663823c** +**What to try:** +- Experiment with different PRN settings — try 12, 8, 1, or off altogether. +- Disable **Request High MTU** in the DFU app. -``` -uf2conv.py bootloader.hex -c -f 0xd663823c -``` +While high MTU significantly improves performance on supported devices, it is not required for a successful OTA update. -## Burn & Upgrade with pre-built binaries +--- -You can burn and/or upgrade the bootloader with either a J-link or DFU (serial) to a specific pre-built binary version -without the hassle of installing a toolchain and compiling the code. -This is preferred if you are not developing/customizing the bootloader. -Pre-builtin binaries are available on GitHub [releases](https://github.com/adafruit/Adafruit_nRF52_Bootloader/releases) +## Recommended OTA DFU settings -Note: The bootloader can be downgraded. Since the binary release is a merged version of -both bootloader and the Nordic SoftDevice, you can freely upgrade/downgrade to any version you like. +To perform the OTA update, use **nRF Device Firmware Update** +([Android](https://play.google.com/store/apps/details?id=no.nordicsemi.android.dfu&hl=en&gl=US) / [iOS](https://apps.apple.com/sa/app/device-firmware-update/id1624454660)) +or **nRF Connect** +([Android](https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp&hl=en&gl=US) / [iOS](https://apps.apple.com/gb/app/nrf-connect-for-mobile/id1054362403)). -## How to compile and build +**nRF Device Firmware Update** is the recommended app of the two. -You should only continue if you are looking to develop bootloader for your own. -You must have have a J-Link available to "unbrick" your device. +For **OTAFIX 2.0** and later, the following settings are recommended (these may change — feel free to experiment and report findings via a GitHub issue): -### Prerequisites + + + + +
-- ARM GCC -- Nordic's [nRF5x Command Line Tools](https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF-Command-Line-Tools) -- [Python IntelHex](https://pypi.org/project/IntelHex/) +**Packet Receipt Notification (PRN):** ON +**Number of packets:** 30 +**Reboot time:** 0ms +**Scan timeout:** 2000ms +**Request high MTU:** ON for Android (see notes below) / not available on iOS +**Disable resume:** ON +**Prepare object delay:** 0ms +**Force scanning:** ON +**Keep bond:** OFF +**External MCU DFU:** OFF -### Build: +**Notes:** +- Some Android devices and BLE stacks do not behave well with **Request high MTU** enabled. + If the transfer fails early with `ERROR: Operation Failed`, retry with **Request high MTU turned OFF**. +- For maximum speed, Packet Receipt Notification can be disabled, and the number of packets increased. + Android is generally more tolerant of higher values; on iOS and other small-packet hosts, values above ~60 are not recommended. -Firstly clone this repo including its submodules with following command: +
-``` -git clone --recurse-submodules https://github.com/adafruit/Adafruit_nRF52_Bootloader -``` +[Recommended settings for versions prior to 2.0 can be found here](docs/oldsettings.md). -For git versions before `2.13.0` you have to do that manually: -``` -git clone https://github.com/adafruit/Adafruit_nRF52_Bootloader -cd Adafruit_nRF52_Bootloader -git submodule update --init -``` +**IMPORTANT:** +On older versions of the bootloader, performing an OTA update while the device was connected to a computer USB host would complete successfully but **would not automatically boot into the new application firmware**, requiring a manual reset. +This issue is fixed in **OTAFIX 2.0**. -#### Build using `make` -Then build it with `make BOARD={board} all`, for example: +--- -``` -make BOARD=feather_nrf52840_express all -``` +## Notes on Xiao NRF52840 BLE -For the list of supported boards, run `make` without `BOARD=` : +Many of these boards are shipped with the Sense version of the bootloader installed. If your board has the Sense version installed you must use the Sense version when updating via UF2. -``` -make -You must provide a BOARD parameter with 'BOARD=' -Supported boards are: feather_nrf52840_express feather_nrf52840_express pca10056 -Makefile:90: *** BOARD not defined. Stop -``` +You can look at the INFO_UF2.TXT file on the UF2 drive to check what version is currently installed. -#### Build using `cmake` +To check: +1. Enter UF2 DFU mode (double-press reset) +2. Open the `INFO_UF2.TXT` file on the mounted drive -Firstly initialize your build environment by passing your board to `cmake` via `-DBOARD={board}`: +If the file shows: "Board-ID: nRF52840-SeeedXiaoSense-v1" then the ***SENSE*** variant must be used if updating via UF2 file. -```bash -mkdir build -cd build -cmake .. -DBOARD=feather_nrf52840_express -``` +## Notes on RAK4631 bootloader -And then build it with: +This version of the RAK4631 bootloader is based on a much newer version (0.9.2) of the Adafruit nRF52 bootloader than what RAK Wireless uses on their official bootloader (0.6.2-11). It has been tested with no problems found; whether RAK's own patches to the Adafruit bootloader introduce any behavioral difference has not been investigated. A variant of the official RAK bootloader with these patches included instead is available [here](https://github.com/oltaco/WisCore_RAK4631_Bootloader/releases). -```bash -make -``` +--- -You can also use the generator of your choice. For example adding the `-GNinja` and then you can invoke `ninja build` instead of `make`. +## Contributing -To list all supported targets, run: +Want to build from source, add a board, or submit a fix? See +[`CONTRIBUTING.md`](./CONTRIBUTING.md) for the development setup and PR +process, and [`AGENTS.md`](./AGENTS.md) for how the codebase is put +together. -```bash -cmake --build . --target help -``` +## Getting help -To build individual targets, you can specify it directly with `make` or using `cmake --build`: +- **Questions or troubleshooting:** [Meshtastic Discussions](https://github.com/orgs/meshtastic/discussions) +- **Bug reports and feature requests:** [open an issue](https://github.com/meshtastic/Adafruit_nRF52_Bootloader_OTAFIX/issues/new/choose) +- **Security vulnerabilities:** see [`SECURITY.md`](./SECURITY.md) — please do not open a public issue +- **General Meshtastic docs:** [meshtastic.org](https://meshtastic.org/) -```bash -make bootloader -cmake --build . --target bootloader -``` +## License -### Flash - -To flash the bootloader (without softdevice/mbr) using JLink: - -``` -make BOARD=feather_nrf52840_express flash -``` - -If you are using pyocd as debugger, add `FLASHER=pyocd` to make command: - -``` -make BOARD=feather_nrf52840_express FLASHER=pyocd flash -``` - -To upgrade the bootloader using DFU Serial via port /dev/ttyACM0 - -``` -make BOARD=feather_nrf52840_express SERIAL=/dev/ttyACM0 flash-dfu -``` - -To flash SoftDevice (will also erase chip): - -``` -make BOARD=feather_nrf52840_express flash-sd -``` - -To flash MBR only - -``` -make BOARD=feather_nrf52840_express flash-mbr -``` - -### Dual bank firmware support: - -This bootloader can split the FLASH memory into 2 partitions, one for the last successfully uploaded firmware, and the other for the new firmware that is being uploaded. In case the firmware upload fails, the bootloader will revert to the last working firmware version. The only drawback is that the maximum firmware size is half of the device FLASH size: That is why it is disabled by default. -You can enable this feature by passing DUALBANK_FW=1 to the make process while compiling the bootloader - -### Signed firmware support: -This bootloader can validate that the uploaded firmware is digitally signed, and refuse to install unsigned or signed with the improper key firmware. Because this will make Arduino uploads stop working (because they are not digitally signed), this feature is disabled by default. -But if you want to ensure noone else is allowed to upload firmware to your device, this option is probably what you want to enable. - -To create a signing key, you will need the adafruit-nrfutil utility. First, generate a signing key and store at given path by using - -``` -adafruit-nrfutil keys --gen-key stored_key.pem -``` - -This command will create a signing key and store it in the file stored_key.pem. STORE THIS FILE IN A SAFE PLACE, as without it, you won't be able to sign your firmware packages, and the bootloader will refuse to flash them!! - -Now, you must show the verification key (that is calculated from the signing key): - -``` -adafruit-nrfutil keys --show-vk code stored_key.pem -``` - -This command will read your signing key (from the file stored_key.pem) and display C source code with the Qx and Qy arrays: Something along the lines - -``` -static uint8_t Qx[] = { ... }; -static uint8_t Qy[] = { ... }; -``` - -You must save the Qx and Qy values, remove all spaces, and pass them to the make command line -``` -make BOARD=feather_nrf52840_express SIGNED_FW=1 SIGNED_FW_QX='Qx values' SIGNED_FW_QY='Qy values' -``` - -And replace Qx values with the Qx array values, and the Qy values with the Qy array values. That will create a bootloader that ONLY accepts firmware signed with the stored_key.pem signing key. Take into account that the option SIGNED_FW=1 is what enables to build a bootloader that enforces signing, and will ALSO result in disabling UF2 support, as UF2 format does NOT support signing keys. There is an option (only for testing!) called FORCE_UF2 that will FORCE UF2 support on bootloaders that enforce firmware security. It is there to bypass security so you can test a secure bootloader and have a way to recover it something goes wrong, but should NEVER be used if you want to enforce security - -Finally, to create a signed firmware application/bootloader update package, you can use, for example - -``` -adafruit-nrfutil dfu genpkg --dev-type 0x0052 --sd-req 0x0123 --application "application.hex" --key-file "stored_key.pem" "application_package.zip" -``` - -Please, use full paths for all files. The examples omit them for clarity! - -And, to upload it to your device, - -``` -adafruit-nrfutil.exe --verbose dfu serial -pkg "application_package.zip" -p /dev/tty0 -b 115200 -``` - -### Common makefile problems - -#### `arm-none-eabi-gcc`: No such file or directory - -If you get the following error ... - -``` -make BOARD=feather_nrf52840_express all -Compiling file: main.c -/bin/sh: /usr/bin/arm-none-eabi-gcc: No such file or directory -make: *** [_build/main.o] Error 127 -``` - -... you may need to pass the location of the GCC ARM toolchain binaries to `make` using -the variable `CROSS_COMPILE` as below: -``` -make CROSS_COMPILE=/opt/gcc-arm-none-eabi-9-2019-q4-major/bin/arm-none-eabi- BOARD=feather_nrf52832 all -``` - -For other compile errors, check the gcc version with `arm-none-eabi-gcc --version` to insure it is at least 9.x. - -#### `ModuleNotFoundError: No module named 'intelhex'` - -Install python-intelhex with - -``` -pip install intelhex -``` - -#### `make: nrfjprog: No such file or directory` - -Make sure that `nrfjprog` is available from the command-line. This binary is -part of Nordic's nRF5x Command Line Tools. +[MIT](./LICENSE), originally Copyright (c) 2016 Adafruit Industries. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..5d0d5765 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,12 @@ +# Security Policy + +## Supported Versions + +| Bootloader Version | Supported | +| ------------------- | ------------------ | +| OTAFIX 2.x | :white_check_mark: | +| < OTAFIX 2.0 | :x: | + +## Reporting a Vulnerability + +We support the private reporting of potential security vulnerabilities. Please go to the Security tab to file a report with a description of the potential vulnerability and reproduction scripts (preferred) or steps, and our developers will review. diff --git a/changelog.md b/changelog.md index 6524232c..44a68ae9 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,49 @@ # Adafruit nRF52 Bootloader Changelog +OTAFIX 2.1–2.3 are Meshtastic's fork; everything from 0.6.2 down predates +it and is upstream Adafruit history, kept for provenance. + +## OTAFIX 2.3 + +- New boards: Heltec T096, Heltec T1, RAK 3401 (merged from oltaco's upstream, which calls this same board-support work "OTAFIX 2.3" too). +- `screen.c` refactored to a display-size-agnostic layout (macro-based bar/text positions instead of hardcoded pixel offsets) — no behavior change for existing boards, needed for the new boards' smaller displays. +- ST7735 display controller support, alongside the existing ST7789 path. +- Backported five upstream Adafruit fixes, hardware-validated on a RAK4631: + - Boot loop from a wrong REGOUT0 read. + - GCC 15 build errors (Makefile version filter, `ghostfat.c`). + - `-Werror=array-bounds` false positive in `bootloader_settings.c`, replaced with a scoped pragma instead of the previous repo-wide `--param=min-pagesize=0` workaround. + - Clear CONTROL/PRIMASK/BASEPRI/FAULTMASK before jumping to the application — was rarely causing lockups. + - Wait for BLE notification-queue room instead of silently dropping DFU completion notifications. +- Fixed a latent out-of-bounds write in `screen.c`'s `print()`: it drew a glyph before checking whether it fit on screen, rather than after. Not reachable on any current board, but a real hazard for future `board.h` layouts. +- `AGENTS.md` gotcha: flashing the bootloader+SoftDevice package over serial DFU zeros `bank_0`, so the device intentionally comes up in BLE-OTA wait mode instead of the app on the next boot — not a brick. + +## OTAFIX 2.2 + +- Use maximum TX power for BLE: BLE TX power set to +8 for nRF52840. +- New boards: Elecrow ThinkNode M1, M3, M6; LilyGo T-Echo; Minewsemi MX25LE01; Seeed SenseCAP Solar Node P1. + +## OTAFIX 2.1 + +- Defaults to OTA DFU mode when no valid application is present, preventing devices from becoming stuck in UF2 mode after a failed OTA update. +- High-MTU BLE support for larger DFU packets. The Android DFU app and [`dfu.py`](https://github.com/recrof/nrf_dfu_py) support large packets; the iOS DFU app is limited to 20-byte packets. +- Lazy flash erase: pages are erased on demand during the transfer instead of upfront, reducing the delay before the transfer begins. +- Small-packet accumulation: packets smaller than 64 bytes are combined at the transport layer and written to flash in chunks of up to 240 bytes, improving OTA performance from iOS and other small-packet DFU hosts. +- Automatic application boot after OTA over USB, instead of requiring a manual reset. +- Unique BLE advertising names per board in OTA DFU mode, instead of the generic `AdaDFU`: + - Elecrow ThinkNode M1 → `TNM1_DFU` + - Elecrow ThinkNode M3 → `TNM3_DFU` + - Elecrow ThinkNode M6 → `TNM6_DFU` + - Heltec T114 → `T114_DFU` + - LILYGO T-Echo → `LGTE_DFU` + - Minewsemi MX25LE01 → `MX25_DFU` + - ProMicro NRF52840 → `PROM_DFU` + - RAK 4631 → `4631_DFU` + - RAK WisMesh Tag → `RTAG_DFU` + - Seeed SenseCAP Solar Node P1 → `SCAP_DFU` + - Seeed T1000e → `T1KE_DFU` + - Seeed WioTracker L1 → `WTL1_DFU` + - XIAO NRF52 BLE / SENSE → `XIAO_DFU` + ## 0.6.2 - 2021.09.10 - Add new board "LED Glasses Driver nRF52840" diff --git a/docs/Screenshot_20251114_142334_DFU.jpg b/docs/Screenshot_20251114_142334_DFU.jpg new file mode 100644 index 0000000000000000000000000000000000000000..54f2d532437efba1904e5fbd6f330fd539f543c2 GIT binary patch literal 222559 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 QDXS_ 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Ξ(~y&qfUUDNOz@kfBO@b5N}vkWPT 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

IX4kUlP`Lz60&@D}&`&&RhLc67KBN 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 literal 0 HcmV?d00001 diff --git a/docs/dfu_settings_01.png b/docs/dfu_settings_01.png new file mode 100644 index 0000000000000000000000000000000000000000..9560d25c3144da59b7d8aad688d6af6a6c3c6efc GIT binary patch literal 48846 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 literal 0 HcmV?d00001 diff --git a/docs/dfu_settings_02.png b/docs/dfu_settings_02.png new file mode 100644 index 0000000000000000000000000000000000000000..153dbb9f31a88dfea777c425ccf3b543c6090523 GIT binary patch literal 46259 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 E00qLDu>b%7 literal 0 HcmV?d00001 diff --git a/lib/softdevice/s340_nrf52_7.0.1/readme.md b/lib/softdevice/s340_nrf52_7.0.1/readme.md deleted file mode 100644 index 95a0fee5..00000000 --- a/lib/softdevice/s340_nrf52_7.0.1/readme.md +++ /dev/null @@ -1,23 +0,0 @@ -## Working with SoftDevice S340 - -The SoftDevice S340 is closed-source, not publicly available and is only distributed by Garmin Canada Inc. - -In order to be able to download the required ANT+ capable SoftDevice and place SoftDevice S340 v7.0.1 files here. You need to register an 'ANT+ Adopter' account at [thisisant.com](https://www.thisisant.com/register/). After around one business day you will receive access to the resources there. Then do the following steps: -- Download the SoftDevice S340 v7.0.1 [(here)](https://www.thisisant.com/developer/components/nrf52832#tab_protocol_stacks_tab) and extract its contents -- Under `lib/softdevice` in this repository there is a folder called `s340_nrf52_7.0.1` - - Copy the API folder `ANT_s340_nrf52_7.0.1.API`, the license agreement `License_Agreement_ANT_Softdevice_rev3_3.pdf` and the hex file `ANT_s340_nrf52_7.0.1.hex` from the extracted contents to it - - Rename the API folder to `s340_nrf52_7.0.1_API` - - Rename the hex file to `s340_nrf52_7.0.1_softdevice.hex` - - Check `lib/softdevice/s340_nrf52_7.0.1_API/include/nrf_sdm.h` on line 191 to get an *evaluation key* for the ANT SoftDevice which can later be used for the `ANT_LICENSE_KEY` parameter - - **VERY IMPORTANT:** You MUST obtain a valid commercial license key BEFORE releasing a product to market that uses the ANT SoftDevice! - -To use the SoftDevice S340 with an existing board the `SD_NAME`, `SD_VERSION` and `ANT_LICENSE_KEY` parameters can just be passed to the command line of the `make` command, e.g.: -``` -make BOARD=xiao_nrf52840_ble_sense SD_NAME=s340 SD_VERSION=7.0.1 ANT_LICENSE_KEY= all -``` - -To add or modify a board with an ANT+ capable SoftDevice S340 the `SD_VERSION` and `SD_NAME` parameters in the corresponding `board.mk` file have to be set: -``` -SD_VERSION = 7.0.1 -SD_NAME = s340 -``` \ No newline at end of file diff --git a/renovate.json b/renovate.json new file mode 100644 index 00000000..d75c4a38 --- /dev/null +++ b/renovate.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + ":dependencyDashboard", + ":semanticCommitTypeAll(chore)", + ":ignoreModulesAndTests", + ":noUnscheduledUpdates", + "schedule:daily", + "group:recommended", + "replacements:all", + "workarounds:all", + "helpers:pinGitHubActionDigests" + ], + "baseBranchPatterns": ["master"], + "git-submodules": { + "enabled": true + }, + "labels": ["dependencies"] +} diff --git a/supported_boards.md b/supported_boards.md index 82484fdd..61399d0d 100644 --- a/supported_boards.md +++ b/supported_boards.md @@ -4,56 +4,25 @@ | Board | Name | VID PID | URL | | --- | --- | --- | --- | -| circuitplayground_nrf52840 | Adafruit Circuit Playground nRF52840 | 0x239A:0x0045 | https://www.adafruit.com/product/4333 | -| clue_nrf52840 | Adafruit CLUE nRF52840 | 0x239A:0x0071 | https://www.adafruit.com/product/4500 | -| feather_nrf52832 | Adafruit Feather nRF52832 | N/A | https://www.adafruit.com/product/3406 | -| feather_nrf52840_express | Adafruit Feather nRF52840 Express | 0x239A:0x0029 | https://www.adafruit.com/product/4062 | -| feather_nrf52840_sense | Adafruit Feather nRF52840 Sense | 0x239A:0x0087 | https://www.adafruit.com/product/4516 | -| feather_nrf52840_sense_tft | Adafruit Feather nRF52840 Sense TFT | 0x239A:0x0087 | https://www.adafruit.com/product/ | -| itsybitsy_nrf52840_express | Adafruit ItsyBitsy nRF52840 Express | 0x239A:0x0051 | https://www.adafruit.com/product/4481 | -| ledglasses_nrf52840 | Adafruit LED Glasses Driver nRF52840 | 0x239A:0x010D | https://www.adafruit.com/product/5217 | -| metro_nrf52840_express | Adafruit Metro nRF52840 Express | 0x239A:0x003F | https://www.adafruit.com/ | ## 3rd Party Boards | Board | Name | VID PID | URL | | --- | --- | --- | --- | -| adm_b_nrf52840_1 | AtelierDuMaker NRF52840 Breakout | 0x239A:0x009F | https://github.com/Atelier-Du-Maker/HolyIoT_NRF52840 | -| ae_bl652_bo | N/A | N/A | N/A | -| aramcon2_badge | ARAMCON2 Badge | 0x239A:0x007B | https://github.com/aramcon-badge | -| aramcon_badge_2019 | ARAMCON Badge 2019 | 0x239A:0x0079 | https://github.com/aramcon-badge | -| arduino_nano_33_ble | Arduino Nano 33 BLE | 0x239A:0x0063 | https://www.arduino.cc/ | -| bast_ble | Bast BLE | 0x1209:0x805A | https://www.electroniccats.com | -| bluemicro_nrf52833 | BlueMicro | 0x1D50:0x616F | http://bluemicro.jpconstantineau.com/ | -| bluemicro_nrf52840 | BlueMicro | 0x1D50:0x6160 | http://bluemicro.jpconstantineau.com/ | -| challenger_840_ble | ILabs Challenger 840 | 0x1209:0x7380 | https://www.ilabs.se | -| ebyte_e104_bt5032a | Ebyte E104-BT5032A nRF52832 | N/A | https://www.ebyte.com/en/product-view-news.html?id=756 | -| ebyte_e73_tbb | Ebyte E73-TBB nRF52832 | N/A | https://www.ebyte.com/en/product-view-news.aspx?id=889 | -| electronut_labs_papyr | Electronut Labs Papyr | 0x239A:0x003B | https://docs.electronut.in/papyr | -| ikigaisense_vita | IkigaiSense Vita nRF52840 | 0x239A:0x0093 | https://github.com/ikigaisense/vita | -| m60_keyboard | MakerDiary M60 Mechanical Keyboard | 0x2886:0xF00F | https://wiki.makerdiary.com/m60/ | -| mbn52832dk | Murata MBN52832DK | N/A | https://www.murata.com/ja-jp/products/connectivitymodule/bluetooth/overview/lineup/mbn52832 | -| mdk_nrf52840_dongle | MDK nRF52840 USB Dongle | 0x239A:0x0029 | https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/ | -| mikoto | Mikoto nRF52840 | 0x1209:0x7A01 | https://github.com/zhiayang/mikoto | -| nice_nano | nice!nano | 0x239A:0x00B3 | https://nicekeyboards.com/docs/nice-nano | -| nrf52840_bboard | nRF52840 BBoard | 0x1D50:0x6157 | https://github.com/ddB0515/nRF52840-BBoard | -| nRF52840_connect_kit | nRF52840 Connect Kit | 0x2886:0xF00F | https://wiki.makerdiary.com/nrf52840-connectkit/ | -| nrf52840_m2 | MakerDiary nRF52840 M.2 Module | 0x2886:0xF00F | https://wiki.makerdiary.com/nrf52840-m2 | -| nrfmicro | nRFMicro | 0x1209:0x5284 | https://github.com/joric/nrfmicro/wiki | -| ohs2020_badge | OHS2020 Badge | 0x239A:0x007F | https://2020.oshwa.org/ | -| omnimo_nrf52840 | Omnimo nRF52840 | 0x1209:0xCECE | https://www.crowdsupply.com/eafaq/omnimo-nrf52840 | -| particle_argon | Particle Argon | 0x239A:0x00DA | https://www.particle.io/mesh/ | -| particle_boron | Particle Boron | 0x239A:0x00DA | https://www.particle.io/mesh/ | -| particle_xenon | Particle Xenon | 0x239A:0x00DA | https://www.particle.io/mesh/ | -| pca10056 | Nordic nRF52840 DK | 0x239A:0x00DA | https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK | -| pca10059 | Nordic nRF52840 Dongle | 0x239A:0x0029 | https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-Dongle | -| pca10100 | Nordic nRF52833 DK | 0x239A:0x00D8 | https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52833-DK | -| pillbug | PillBug | 0x16D0:0x10ED | https://mechwild.com | -| pitaya_go | Pitaya Go | 0x2886:0xF00E | https://wiki.makerdiary.com/pitaya-go | -| raytac_mdbt50q_db_40 | Raytac MDBT50Q Demo Board 40 | 0x239A:0x00BB | https://www.raytac.com/product/ins.php?index_id=81 | -| raytac_mdbt50q_rx | Raytac MDBT50Q-RX | 0x239A:0x010B | https://www.adafruit.com/product/5199 | -| sparkfun_nrf52840_micromod | SparkFun MicroMod nRF52840 | 0x1B4F:0x0022 | https://www.sparkfun.com/products/16984 | +| heltec_t096 | Heltec-T096 | 0x239A:0x0071 | http://heltec.org/ | +| heltec_t1 | Heltec-T1 | 0x239A:0x0071 | http://heltec.org/ | +| heltec_t114 | HT-n5262 | 0x239A:0x0071 | HT-n5262 | +| lilygo_techo | LilyGo T-Echo | 0x239A:0x0029 | https://lilygo.cc/products/t-echo-lilygo | +| minewsemi_mx25le01 | MinewSemi MX25LE01 | 0x239A:0x0029 | https://www.minewsemi.com | +| promicro_nrf52840 | ProMicro NRF52840 | 0x239A:0x00B3 | https://www.nologo.tech/product/otherboard/NRF52840.html | +| sensecap_solar_p1 | Seeed Solar Node P1 | 0x2886:0x0044 | https://www.seeedstudio.com/ | | t1000_e | Seeed T1000-E for Meshtastic | 0x2886:0x0057 | https://www.seeedstudio.com/SenseCAP-Card-Tracker-T1000-E-for-Meshtastic-p-5913.html | -| waveshare_nrf52840_eval | Waveshare nRF52840 Eval | 0x239A:0x0029 | https://www.waveshare.com/wiki/NRF52840_Eval_Kit | +| thinknode_m1 | ELECROW ThinkNodeM1 | 0x239A:0x00DA | https://www.elecrow.com | +| thinknode_m3 | ELECROW ThinkNode-M3 | 0x239A:0x00DA | https://www.elecrow.com | +| thinknode_m6 | ELECROW ThinkNodeM6 | 0x239A:0x00DA | https://www.elecrow.com | +| wio_tracker_l1 | Seeed TRACKER L1 | 0x2886:0x1667 | https://www.seeedstudio.com/ | +| wiscore_rak3401 | WisBlock RAK3401 Board | 0x239A:0x0029 | https://store.rakwireless.com/collections/wisblock-core | +| wiscore_rak4631_board | WisBlock RAK4631 Board | 0x239A:0x0029 | https://store.rakwireless.com/collections/wisblock-core | +| wismesh_tag | WisMesh Tag | 0x239A:0x0029 | https://store.rakwireless.com/products/wismesh-tag-meshtastic-gps-lora-tracker-ip66 | | xiao_nrf52840_ble | Seeed XIAO nRF52840 | 0x2886:0x0044 | https://www.seeedstudio.com/ | | xiao_nrf52840_ble_sense | Seeed XIAO nRF52840 | 0x2886:0x0045 | https://www.seeedstudio.com/ | From bfe39b60819b4c2eb4b8e69d47a61649b6306a34 Mon Sep 17 00:00:00 2001 From: James Rich Date: Fri, 21 Aug 2026 06:16:38 -0500 Subject: [PATCH 84/84] =?UTF-8?q?Revert=20"lazy=20erase"=20=E2=80=94=20sta?= =?UTF-8?q?lls=20BLE=20OTA=20at=20the=20first=20on-demand=20page=20erase?= =?UTF-8?q?=20on=20this=20base?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RAK4631, Android snapshot app, legacy DFU PRN 5: stream stops after the first 4K page (PRNs at 1220..4880, nothing at 6100) and the link drops on supervision timeout, twice. With this reverted the same transfer completes (729,528 B in 47 s). Mainline's upfront erase costs a few seconds before the transfer instead. Re-porting lazy erase onto mainline's pstorage/flash queue model is its own job. This reverts commit 1458940. --- .../drivers_nrf/pstorage/pstorage.h | 4 - .../drivers_nrf/pstorage/pstorage_raw.c | 350 ++---------------- .../bootloader_dfu/dfu_single_bank.c | 35 +- 3 files changed, 33 insertions(+), 356 deletions(-) diff --git a/lib/sdk11/components/drivers_nrf/pstorage/pstorage.h b/lib/sdk11/components/drivers_nrf/pstorage/pstorage.h index dbd86715..f7d2f43d 100644 --- a/lib/sdk11/components/drivers_nrf/pstorage/pstorage.h +++ b/lib/sdk11/components/drivers_nrf/pstorage/pstorage.h @@ -27,10 +27,6 @@ #include "pstorage_platform.h" -extern uint8_t* dfu_page_erased; -extern uint32_t dfu_image_page_count; -extern int32_t dfu_pending_erase_page; -extern uint32_t dfu_base_address; /**@defgroup ps_opcode Persistent Storage Access Operation Codes * @{ diff --git a/lib/sdk11/components/drivers_nrf/pstorage/pstorage_raw.c b/lib/sdk11/components/drivers_nrf/pstorage/pstorage_raw.c index f95fe9b3..a6ac0676 100644 --- a/lib/sdk11/components/drivers_nrf/pstorage/pstorage_raw.c +++ b/lib/sdk11/components/drivers_nrf/pstorage/pstorage_raw.c @@ -47,15 +47,6 @@ #error No target defined #endif -#define PENDING_PACKET_SIZE 256 /**< Maximum size of a single pending packet data buffer. */ -#define PENDING_PACKET_COUNT 8 /**< Maximum number of packets that can be buffered during lazy erase. */ - -uint8_t * dfu_page_erased; /**< Pointer to page erased status array, set by DFU layer. */ -uint32_t dfu_image_page_count; /**< Number of pages in DFU image, set by DFU layer. */ -uint32_t dfu_base_address; /**< Base address for DFU image, set by DFU layer. */ - -static bool lazy_erase_check_and_trigger(pstorage_handle_t * p_dest, pstorage_size_t size, pstorage_size_t offset); -static void process_pending_packets(void); /** * @brief Application registration information. @@ -102,29 +93,10 @@ typedef struct cmd_queue_element_t cmd[PSTORAGE_CMD_QUEUE_SIZE]; /**< Array to maintain flash access operation details */ }cmd_queue_t; -/**@brief Structure for buffering packets during lazy page erase. */ -typedef struct -{ - bool valid; /**< Whether this slot contains valid data. */ - pstorage_handle_t handle; /**< Storage handle for destination. */ - uint32_t offset; /**< Offset within the storage block. */ - uint32_t size; /**< Size of data in bytes. */ - uint8_t * p_data_orig; /**< Original data pointer for callback. */ - uint8_t data[PENDING_PACKET_SIZE] __attribute__((aligned(4))); /**< Buffered packet data. */ -} pending_packet_t; - -static cmd_queue_t m_cmd_queue; /**< Flash operation request queue. */ -static pstorage_module_table_t m_app_table[PSTORAGE_NUM_OF_PAGES]; /**< Registered application information table. */ -static pstorage_size_t m_next_app_instance; /**< Points to the application module instance that can be allocated next */ -static pstorage_size_t m_round_val; /**< Round value for multiple round operations. For erase operations, the round value will contain current round counter which is identical to number of pages erased. For store operations, the round value contains current round of operation * SOC_MAX_WRITE_SIZE to ensure each store to the SoC Flash API is within the SoC limit. */ - -static pending_packet_t m_pending_packets[PENDING_PACKET_COUNT]; /**< Ring buffer for pending packets. */ -static uint8_t m_pending_read_idx; /**< Read index for pending packet ring buffer. */ -static uint8_t m_pending_write_idx; /**< Write index for pending packet ring buffer. */ -static uint8_t m_pending_count; /**< Number of pending packets in buffer. */ -static bool m_lazy_erase_active; /**< True if a lazy erase is in progress. */ -static uint32_t m_lazy_erase_page; /**< Page index currently being erased. */ -static bool m_processing_pending; /**< True if processing a pending packet store. */ +static cmd_queue_t m_cmd_queue; /**< Flash operation request queue. */ +static pstorage_module_table_t m_app_table[PSTORAGE_NUM_OF_PAGES]; /**< Registered application information table. */ +static pstorage_size_t m_next_app_instance; /**< Points to the application module instance that can be allocated next */ +static pstorage_size_t m_round_val; /**< Round value for multiple round operations. For erase operations, the round value will contain current round counter which is identical to number of pages erased. For store operations, the round value contains current round of operation * SOC_MAX_WRITE_SIZE to ensure each store to the SoC Flash API is within the SoC limit. */ /** * @brief Function for processing of commands and issuing flash access request to the SoftDevice. @@ -188,82 +160,6 @@ static void cmd_queue_init(void) } } -/** - * @brief Function for initializing the pending packets buffer. - */ -static void pending_packets_init(void) -{ - for (int i = 0; i < PENDING_PACKET_COUNT; i++) - { - m_pending_packets[i].valid = false; - } - m_pending_read_idx = 0; - m_pending_write_idx = 0; - m_pending_count = 0; - m_lazy_erase_active = false; - m_lazy_erase_page = 0; - m_processing_pending = false; -} - -/** - * @brief Function for adding a packet to the pending buffer. - * - * @param[in] p_handle Pointer to the storage handle. - * @param[in] p_data Pointer to the data to buffer. - * @param[in] size Size of data in bytes. - * @param[in] offset Offset within the storage block. - * - * @retval true If the packet was successfully added. - * @retval false If the buffer is full or size exceeds maximum. - */ -static bool pending_packet_add(pstorage_handle_t * p_handle, uint8_t * p_data, - uint32_t size, uint32_t offset) -{ - if (m_pending_count >= PENDING_PACKET_COUNT || size > PENDING_PACKET_SIZE) - { - return false; - } - - pending_packet_t * p = &m_pending_packets[m_pending_write_idx]; - p->handle = *p_handle; - p->offset = offset; - p->size = size; - p->p_data_orig = p_data; - memcpy(p->data, p_data, size); - p->valid = true; - - m_pending_write_idx = (m_pending_write_idx + 1) % PENDING_PACKET_COUNT; - m_pending_count++; - - return true; -} - -/** - * @brief Function for getting a pointer to the next pending packet without removing it. - * - * @return Pointer to the next pending packet, or NULL if buffer is empty. - */ -static pending_packet_t * pending_packet_peek(void) -{ - if (m_pending_count == 0) - { - return NULL; - } - return &m_pending_packets[m_pending_read_idx]; -} - -/** - * @brief Function for removing the next pending packet from the buffer. - */ -static void pending_packet_pop(void) -{ - if (m_pending_count > 0) - { - m_pending_packets[m_pending_read_idx].valid = false; - m_pending_read_idx = (m_pending_read_idx + 1) % PENDING_PACKET_COUNT; - m_pending_count--; - } -} /** * @brief Function for enqueueing a flash access operation. @@ -300,7 +196,6 @@ static uint32_t cmd_queue_enqueue(uint8_t opcode, m_cmd_queue.cmd[write_index].storage_addr = (*p_storage_addr); m_cmd_queue.cmd[write_index].size = size; m_cmd_queue.cmd[write_index].offset = offset; - m_cmd_queue.count++; retval = NRF_SUCCESS; if (m_cmd_queue.flash_access == false) { @@ -311,6 +206,7 @@ static uint32_t cmd_queue_enqueue(uint8_t opcode, retval = NRF_SUCCESS; } } + m_cmd_queue.count++; } else { @@ -365,132 +261,15 @@ static void app_notify(uint32_t result, cmd_queue_element_t * p_elem) { pstorage_ntf_cb_t ntf_cb; uint8_t op_code = p_elem->op_code; - - if (p_elem->storage_addr.module_id < PSTORAGE_NUM_OF_PAGES) - { - ntf_cb = m_app_table[p_elem->storage_addr.module_id].cb; - if (ntf_cb != NULL) - { - ntf_cb(&p_elem->storage_addr, - op_code, - result, - p_elem->p_data_addr, - p_elem->size); - } - } -} - -/** - * @brief Function for notifying application of buffered store completion. - * - * @param[in] result Result of event being notified. - * @param[in] p_handle Pointer to the storage handle. - * @param[in] p_data Pointer to the data buffer. - * @param[in] size Size of data in bytes. - */ -static void app_notify_buffered(uint32_t result, pstorage_handle_t * p_handle, - uint8_t * p_data, uint32_t size) -{ - if (p_handle->module_id < PSTORAGE_NUM_OF_PAGES) - { - pstorage_ntf_cb_t ntf_cb = m_app_table[p_handle->module_id].cb; - if (ntf_cb != NULL) - { - ntf_cb(p_handle, PSTORAGE_STORE_OP_CODE, result, p_data, size); - } - } -} - -/** - * @brief Function for processing packets from the pending buffer. - * - * @details Attempts to process the next pending packet. If the target page needs - * erasing, triggers a lazy erase. Otherwise enqueues the store operation. - */ -static void process_pending_packets(void) -{ - if (m_processing_pending || m_lazy_erase_active || m_cmd_queue.count > 0) - { - return; - } - - pending_packet_t * p = pending_packet_peek(); - if (p == NULL) - { - return; - } - - // Check if this packet needs an erase first - if (lazy_erase_check_and_trigger(&p->handle, p->size, p->offset)) - { - // Erase triggered, don't process this packet yet - return; - } - - // Page is ready, enqueue the store - m_processing_pending = true; - - uint32_t ret = cmd_queue_enqueue(PSTORAGE_STORE_OP_CODE, &p->handle, p->data, p->size, p->offset); - - if (ret != NRF_SUCCESS) - { - m_processing_pending = false; - pending_packet_pop(); - } -} - - -/** - * @brief Function for checking and triggering lazy erase if needed. - * @param[in] p_dest Destination address where data is to be stored persistently. - * @param[in] size Size of data to be stored expressed in bytes. - * @param[in] offset Offset in bytes to be applied when writing to the block. - * @retval true Lazy erase was triggered. - * @retval false No lazy erase was needed. - */ -static bool lazy_erase_check_and_trigger(pstorage_handle_t * p_handle, pstorage_size_t size, pstorage_size_t offset) -{ - uint32_t target_addr = p_handle->block_id + offset; - - if (target_addr < dfu_base_address || dfu_page_erased == NULL) - { - return false; - } - - uint32_t rel_addr = target_addr - dfu_base_address; - uint32_t page_index = rel_addr / PSTORAGE_FLASH_PAGE_SIZE; - - // Also check end of packet - uint32_t end_addr = target_addr + size - 1; - uint32_t end_rel_addr = end_addr - dfu_base_address; - uint32_t end_page_index = end_rel_addr / PSTORAGE_FLASH_PAGE_SIZE; - - // Check if start page needs erase - bool start_needs_erase = (page_index < dfu_image_page_count && !dfu_page_erased[page_index]); - - // Check if end page needs erase (and is different from start page) - bool end_needs_erase = (end_page_index != page_index) && - (end_page_index < dfu_image_page_count && !dfu_page_erased[end_page_index]); - - if (!start_needs_erase && !end_needs_erase) - { - return false; - } - - // Determine which page to erase (start page takes priority) - uint32_t erase_page = start_needs_erase ? page_index : end_page_index; - - m_lazy_erase_active = true; - m_lazy_erase_page = erase_page; - - pstorage_handle_t erase_handle; - erase_handle.block_id = dfu_base_address + (erase_page * PSTORAGE_FLASH_PAGE_SIZE); - erase_handle.module_id = p_handle->module_id; - - cmd_queue_enqueue(PSTORAGE_CLEAR_OP_CODE, &erase_handle, NULL, - PSTORAGE_FLASH_PAGE_SIZE, 0); - return true; + ntf_cb = m_app_table[p_elem->storage_addr.module_id].cb; + + // Indicate result to client. + ntf_cb(&p_elem->storage_addr, + op_code, + result, + p_elem->p_data_addr, + p_elem->size); } @@ -518,19 +297,13 @@ void pstorage_sys_event_handler(uint32_t sys_evt) { p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; m_round_val++; - + bool command_finished = ((m_round_val * SOC_MAX_WRITE_SIZE) >= p_cmd->size); - if (p_cmd->op_code == PSTORAGE_CLEAR_OP_CODE) - { - command_finished = true; - } - if (command_finished) { uint8_t queue_rp = m_cmd_queue.rp; - uint8_t completed_op = p_cmd->op_code; - + m_round_val = 0; m_cmd_queue.count--; m_cmd_queue.rp++; @@ -540,78 +313,24 @@ void pstorage_sys_event_handler(uint32_t sys_evt) m_cmd_queue.rp -= PSTORAGE_CMD_QUEUE_SIZE; } - if (m_lazy_erase_active && completed_op == PSTORAGE_CLEAR_OP_CODE) - { - // Lazy erase completed - if (dfu_page_erased != NULL && m_lazy_erase_page < dfu_image_page_count) - { - dfu_page_erased[m_lazy_erase_page] = 1; - } - - m_lazy_erase_active = false; - cmd_queue_element_init(queue_rp); - - process_pending_packets(); - } - else if (m_processing_pending && completed_op == PSTORAGE_STORE_OP_CODE) - { - // Store from pending buffer completed - pending_packet_t * p = pending_packet_peek(); - if (p != NULL) - { - pstorage_handle_t cb_handle = p->handle; - uint8_t* cb_data = p->p_data_orig; - uint32_t cb_size = p->size; - - pending_packet_pop(); - - cmd_queue_element_init(queue_rp); - - // Clear flag BEFORE callback - m_processing_pending = false; - - // Notify application that buffered this buffered store is done - app_notify_buffered(retval, &cb_handle, cb_data, cb_size); - } - else - { - m_processing_pending = false; - cmd_queue_element_init(queue_rp); - } - } - else - { - app_notify(retval, &m_cmd_queue.cmd[queue_rp]); - - // Initialize/free the element as it is now processed. - cmd_queue_element_init(queue_rp); - } - } + app_notify(retval, &m_cmd_queue.cmd[queue_rp]); - // Process next queued command OR next pending packet - if (m_cmd_queue.count > 0) - { - retval = cmd_queue_dequeue(); - } - else if (m_pending_count > 0 && !m_processing_pending && !m_lazy_erase_active) - { - // Queue is empty but we have pending packets - process one - process_pending_packets(); + // Initialize/free the element as it is now processed. + cmd_queue_element_init(queue_rp); } - + // Schedule any queued flash access operations. + retval = cmd_queue_dequeue(); if (retval != NRF_SUCCESS) { app_notify(retval, &m_cmd_queue.cmd[m_cmd_queue.rp]); } } break; - + case NRF_EVT_FLASH_OPERATION_ERROR: - m_lazy_erase_active = false; - m_processing_pending = false; app_notify(NRF_ERROR_TIMEOUT, &m_cmd_queue.cmd[m_cmd_queue.rp]); break; - + default: // No implementation needed. break; @@ -634,11 +353,6 @@ static uint32_t cmd_process(void) retval = NRF_ERROR_FORBIDDEN; - if (m_cmd_queue.count == 0) - { - return NRF_SUCCESS; - } - p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; storage_addr = p_cmd->storage_addr.block_id; @@ -673,7 +387,7 @@ static uint32_t cmd_process(void) } } break; - + case PSTORAGE_CLEAR_OP_CODE: { uint32_t page_number; @@ -684,7 +398,7 @@ static uint32_t cmd_process(void) retval = sd_flash_page_erase(page_number); } break; - + default: // Should never reach here. break; @@ -704,8 +418,7 @@ static uint32_t cmd_process(void) uint32_t pstorage_init(void) { cmd_queue_init(); - pending_packets_init(); - + m_next_app_instance = 0; m_round_val = 0; @@ -747,22 +460,11 @@ uint32_t pstorage_store(pstorage_handle_t * p_dest, pstorage_size_t offset) { // Verify word alignment. - if ((!is_word_aligned(p_src)) || (!is_word_aligned((void *)(p_dest->block_id + offset)))) + if ((!is_word_aligned(p_src)) || (!is_word_aligned(p_src+offset))) { return NRF_ERROR_INVALID_ADDR; } - if (m_lazy_erase_active || lazy_erase_check_and_trigger(p_dest, size, offset)) - { - // Buffer this packet - erase in progress or just triggered - if (!pending_packet_add(p_dest, p_src, size, offset)) - { - return NRF_ERROR_NO_MEM; - } - return NRF_SUCCESS; - } - - // Page already erased, proceed with store return cmd_queue_enqueue(PSTORAGE_STORE_OP_CODE, p_dest, p_src, size, offset); } diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c index b79a67a0..114a57c9 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c @@ -68,12 +68,6 @@ static void pstorage_callback_handler(pstorage_handle_t * p_handle, case PSTORAGE_CLEAR_OP_CODE: if (m_dfu_state == DFU_STATE_PREPARING) { - // Special case for lazy erase - mark first page as erased - if (dfu_page_erased != NULL && dfu_image_page_count > 0) - { - dfu_page_erased[0] = 1; - } - m_functions.cleared(); m_dfu_state = DFU_STATE_RDY; if (m_data_pkt_cb != NULL) @@ -130,35 +124,25 @@ static uint32_t dfu_timer_restart(void) } -/**@brief Function for preparing flash before receiving image. +/**@brief Function for preparing of flash before receiving SoftDevice image. * - * @details For lazy erase mode (OTA), this sets up the tracking array and erases - * only the first page to signal readiness. Subsequent pages are erased - * on-demand when stores target them. - * For non-OTA mode, all pages are erased upfront. + * @details This function will erase current application area to ensure sufficient amount of + * storage for the SoftDevice image. Upon erase complete a callback will be done. + * See \ref dfu_bank_prepare_t for further details. */ static void dfu_prepare_func_app_erase(uint32_t image_size) { mp_storage_handle_active = &m_storage_handle_app; + // Doing a SoftDevice update thus current application must be cleared to ensure enough space + // for new SoftDevice. m_dfu_state = DFU_STATE_PREPARING; - dfu_base_address = m_storage_handle_app.block_id; if ( is_ota() ) { - // Setup for lazy erase - dfu_image_page_count = NRFX_CEIL_DIV(m_image_size, CODE_PAGE_SIZE); - - if (dfu_image_page_count < 1) dfu_image_page_count = 1; // This should not happen - - static uint8_t dfu_page_erased_static[256]; - dfu_page_erased = dfu_page_erased_static; - memset(dfu_page_erased, 0, dfu_image_page_count); - - // Only the first page is erased up front; the rest are erased on demand by pstorage uint32_t err_code; while(1) { - err_code = pstorage_clear(&m_storage_handle_app, CODE_PAGE_SIZE); + err_code = pstorage_clear(&m_storage_handle_app, m_image_size); if (err_code != NRF_ERROR_NO_MEM) break; // No space, wait until an entry in the queue is freed @@ -295,11 +279,6 @@ uint32_t dfu_init(void) m_init_packet_length = 0; m_image_crc = 0; - - // Reset lazy erase state - dfu_page_erased = NULL; - dfu_image_page_count = 0; - dfu_base_address = 0; err_code = pstorage_register(&storage_module_param, &m_storage_handle_app); if (err_code != NRF_SUCCESS)