Skip to content

Commit 2c152cd

Browse files
Allow double-reset to jump to USB bootloader
Call `rp2040.enableDoubleResetBootloader()` anywhere in the code to enable the check. W/o that call, the checker will be linked in. See #892
1 parent 029471e commit 2c152cd

File tree

4 files changed

+101
-0
lines changed

4 files changed

+101
-0
lines changed

cores/rp2040/RP2040Support.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ class RP2040 {
314314
reboot();
315315
}
316316

317+
static void enableDoubleResetBootloader();
318+
317319
void wdt_begin(uint32_t delay_ms) {
318320
watchdog_enable(delay_ms, 1);
319321
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
3+
*
4+
* Hacked by EFP3 to allow disabling unless requested
5+
*
6+
* SPDX-License-Identifier: BSD-3-Clause
7+
*/
8+
9+
#include "pico.h"
10+
#include "pico/time.h"
11+
#include "pico/bootrom.h"
12+
#include "pico/binary_info.h"
13+
14+
// PICO_CONFIG: PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS, Window of opportunity for a second press of a reset button to enter BOOTSEL mode (milliseconds), type=int, default=200, group=pico_bootsel_via_double_reset
15+
#ifndef PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS
16+
#define PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS 200
17+
#endif
18+
19+
// PICO_CONFIG: PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED, Optionally define a pin to use as bootloader activity LED when BOOTSEL mode is entered via reset double tap, type=int, min=0, max=29, group=pico_bootsel_via_double_reset
20+
21+
// PICO_CONFIG: PICO_BOOTSEL_VIA_DOUBLE_RESET_INTERFACE_DISABLE_MASK, Optionally disable either the mass storage interface (bit 0) or the PICOBOOT interface (bit 1) when entering BOOTSEL mode via double reset, type=int, min=0, max=3, default=0, group=pico_bootsel_via_double_reset
22+
#ifndef PICO_BOOTSEL_VIA_DOUBLE_RESET_INTERFACE_DISABLE_MASK
23+
#define PICO_BOOTSEL_VIA_DOUBLE_RESET_INTERFACE_DISABLE_MASK 0u
24+
#endif
25+
26+
/** \defgroup pico_bootsel_via_double_reset pico_bootsel_via_double_reset
27+
*
28+
* When the 'pico_bootsel_via_double_reset' library is linked, a function is
29+
* injected before main() which will detect when the system has been reset
30+
* twice in quick succession, and enter the USB ROM bootloader (BOOTSEL mode)
31+
* when this happens. This allows a double tap of a reset button on a
32+
* development board to be used to enter the ROM bootloader, provided this
33+
* library is always linked.
34+
*/
35+
36+
#if !PICO_NO_BI_BOOTSEL_VIA_DOUBLE_RESET
37+
bi_decl(bi_program_feature("double reset -> BOOTSEL"));
38+
#endif
39+
40+
// Doesn't make any sense for a RAM only binary
41+
#if !PICO_NO_FLASH
42+
static const uint32_t magic_token[] = {
43+
0xf01681de, 0xbd729b29, 0xd359be7a,
44+
};
45+
46+
static uint32_t __uninitialized_ram(magic_location)[count_of(magic_token)];
47+
48+
/* Check for double reset and enter BOOTSEL mode if detected
49+
*
50+
* This function is registered to run automatically before main(). The
51+
* algorithm is:
52+
*
53+
* 1. Check for magic token in memory; enter BOOTSEL mode if found.
54+
* 2. Initialise that memory with that magic token.
55+
* 3. Do nothing for a short while (few hundred ms).
56+
* 4. Clear the magic token.
57+
* 5. Continue with normal boot.
58+
*
59+
* Resetting the device twice quickly will interrupt step 3, leaving the token
60+
* in place so that the second boot will go to the bootloader.
61+
*/
62+
63+
/*static*/ void __attribute__((constructor)) boot_double_tap_check(void) {
64+
for (uint i = 0; i < count_of(magic_token); i++) {
65+
if (magic_location[i] != magic_token[i]) {
66+
// Arm, wait, then disarm and continue booting
67+
for (i = 0; i < count_of(magic_token); i++) {
68+
magic_location[i] = magic_token[i];
69+
}
70+
busy_wait_us(PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS * 1000);
71+
magic_location[0] = 0;
72+
return;
73+
}
74+
}
75+
// Detected a double reset, so enter USB bootloader
76+
magic_location[0] = 0;
77+
#ifdef PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED
78+
const uint32_t led_mask = 1u << PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED;
79+
#else
80+
const uint32_t led_mask = 0u;
81+
#endif
82+
reset_usb_boot(
83+
led_mask,
84+
PICO_BOOTSEL_VIA_DOUBLE_RESET_INTERFACE_DISABLE_MASK
85+
);
86+
}
87+
88+
#endif

docs/rp2040.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,12 @@ int rp2040.getTotalHeap()
7070
Returns the total heap that was available to this program at compile time (i.e.
7171
the Pico RAM size minus things like the ``.data`` and ``.bss`` sections and other
7272
overhead).
73+
74+
Bootloader
75+
----------
76+
77+
void rp2040.enableDoubleResetBootloader()
78+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
79+
Add a call anywhere in the sketch to ``rp2040.enableDoubleResetBootloader()`` and
80+
the core will check for a double-tap on reset, and if found will start the USB
81+
bootloader.

keywords.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ SerialPIO KEYWORD2
5454
setFIFOSize KEYWORD2
5555
setPollingMode KEYWORD2
5656

57+
enableDoubleResetBootloader KEYWORD2
58+
5759
OUTPUT_2MA LITERAL1
5860
OUTPUT_4MA LITERAL1
5961
OUTPUT_8MA LITERAL1

0 commit comments

Comments
 (0)