diff --git a/README.md b/README.md index 250f517..cd6f968 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ Get the code with following command: There are 2 pins **DFU & FRST** that bootloader will check upon reset/power +- `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 DFU with UF2 and CDC support - `DFU = Low and FRST = Low` Enter DFU with OTA, can be upgraded with mobile application such as Nordic nrfConnect/Toolbox - `DFU = High and FRST = Low` Factory Reset mode, erase firmware application and its data diff --git a/src/linker/s132_v6.ld b/src/linker/s132_v6.ld index 632ed4c..206effa 100644 --- a/src/linker/s132_v6.ld +++ b/src/linker/s132_v6.ld @@ -14,7 +14,10 @@ MEMORY FLASH (rx) : ORIGIN = 0x74000, LENGTH = 0xA000 /* 40 KB */ /** RAM Region for bootloader. */ - RAM (rwx) : ORIGIN = 0x20003000, LENGTH = 0x20007F80-0x20003000 + RAM (rwx) : ORIGIN = 0x20003000, LENGTH = 0x20007F7C-0x20003000 + + /* Location for double reset detection, no init */ + DBL_RESET (rwx) : ORIGIN = 0x20007F7C, LENGTH = 0x04 /** Location of non initialized RAM. Non initialized RAM is used for exchanging bond information * from application to bootloader when using buttonluss DFU OTA. @@ -66,6 +69,11 @@ SECTIONS { KEEP(*(.uicrMbrParamsPageAddress)) } > UICR_MBR_PARAM_PAGE + + .dbl_reset(NOLOAD) : + { + + } > DBL_RESET /* No init RAM section in bootloader. Used for bond information exchange. */ .noinit(NOLOAD) : diff --git a/src/linker/s140_v6.ld b/src/linker/s140_v6.ld index e98e197..67ff58a 100644 --- a/src/linker/s140_v6.ld +++ b/src/linker/s140_v6.ld @@ -17,10 +17,14 @@ MEMORY /* Avoid conflict with NOINIT for OTA bond sharing */ RAM (rwx) : ORIGIN = 0x20008000, LENGTH = 0x20040000-0x20008000 + /* Location for double reset detection, no init */ + DBL_RESET (rwx) : ORIGIN = 0x20007F7C, LENGTH = 0x04 + /** Location of non initialized RAM. Non initialized RAM is used for exchanging bond information * from application to bootloader when using buttonluss DFU OTA. */ NOINIT (rwx) : ORIGIN = 0x20007F80, LENGTH = 0x80 + /** Location of bootloader setting in flash. */ BOOTLOADER_SETTINGS (rw) : ORIGIN = 0x000FF000, LENGTH = 0x1000 @@ -68,11 +72,17 @@ SECTIONS KEEP(*(.uicrMbrParamsPageAddress)) } > UICR_MBR_PARAM_PAGE + .dbl_reset(NOLOAD) : + { + + } > DBL_RESET + /* No init RAM section in bootloader. Used for bond information exchange. */ .noinit(NOLOAD) : { } > NOINIT + /* other placements follow here... */ } diff --git a/src/main.c b/src/main.c index 7e21645..c8c2fc1 100644 --- a/src/main.c +++ b/src/main.c @@ -98,6 +98,10 @@ void usb_teardown(void); #define DFU_MAGIC_SERIAL_ONLY_RESET 0x4e #define DFU_MAGIC_UF2_RESET 0x57 +#define DFU_DBL_RESET_MAGIC 0x5A1AD5 // SALADS +#define DFU_DBL_RESET_DELAY 500 +#define DFU_DBL_RESET_MEM 0x20007F7C + #define BOOTLOADER_VERSION_REGISTER NRF_TIMER2->CC[0] #define DFU_SERIAL_STARTUP_INTERVAL 1000 @@ -119,6 +123,8 @@ STATIC_ASSERT( APPDATA_ADDR_START == 0x6D000); void adafruit_factory_reset(void); static uint32_t softdev_init(bool init_softdevice); +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; @@ -146,7 +152,8 @@ int main(void) bool serial_only_dfu = (NRF_POWER->GPREGRET == DFU_MAGIC_SERIAL_ONLY_RESET); // start either serial, uf2 or ble - bool dfu_start = _ota_dfu || serial_only_dfu || (NRF_POWER->GPREGRET == DFU_MAGIC_UF2_RESET); + bool dfu_start = _ota_dfu || serial_only_dfu || (NRF_POWER->GPREGRET == DFU_MAGIC_UF2_RESET) || + (((*dbl_reset_mem) == DFU_DBL_RESET_MAGIC) && (NRF_POWER->RESETREAS & POWER_RESETREAS_RESETPIN_Msk)); // Clear GPREGRET if it is our values if (dfu_start) NRF_POWER->GPREGRET = 0; @@ -174,7 +181,34 @@ int main(void) // DFU + FRESET are pressed --> OTA _ota_dfu = _ota_dfu || ( button_pressed(BUTTON_DFU) && button_pressed(BUTTON_FRESET) ) ; - if ( dfu_start || !bootloader_app_is_valid(DFU_BANK_0_REGION_START) ) + + bool const valid_app = bootloader_app_is_valid(DFU_BANK_0_REGION_START); + + // App mode: register 1st reset and DFU startup (nrf52832) + if ( ! (dfu_start || !valid_app) ) + { + // Register our first reset for double reset detection + (*dbl_reset_mem) = DFU_DBL_RESET_MAGIC; + +#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: Supposedly during this time if RST is press, it will count as double reset. + * However Double Reset WONT work with nrf52832 since its SRAM got cleared anyway. + */ + bootloader_dfu_start(false, DFU_SERIAL_STARTUP_INTERVAL); +#else + led_on(LED_RED); // turn on LED to signal user + // if RST is pressed during this delay --> if will enter dfu + NRFX_DELAY_MS(DFU_DBL_RESET_DELAY); + led_off(LED_RED); +#endif + } + + (*dbl_reset_mem) = 0; + + if ( dfu_start || !valid_app ) { if ( _ota_dfu ) { @@ -204,15 +238,6 @@ int main(void) usb_teardown(); } } -#ifdef NRF52832_XXAA - else - { - /* Adafruit Modification - * Even DFU is not active, we still force an 1000 ms dfu serial mode when startup - * to support auto programming from Arduino IDE */ - bootloader_dfu_start(false, DFU_SERIAL_STARTUP_INTERVAL); - } -#endif // Adafruit Factory reset if ( !button_pressed(BUTTON_DFU) && button_pressed(BUTTON_FRESET) )