Skip to content

Commit 7f216f3

Browse files
Allow double-reset to jump to USB bootloader (#893)
Call `rp2040.enableDoubleResetBootloader()` anywhere in the code to enable the check. W/o that call, the checker will be linked in. See #892 CORE1 doesn't start until well after the C runtime initialization, so the flag won't be overwritten. Also increase timeout to 350ms because OTA bootup can be slow.
1 parent 029471e commit 7f216f3

File tree

5 files changed

+136
-0
lines changed

5 files changed

+136
-0
lines changed

cores/rp2040/RP2040Support.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
RP2040 utility class
3+
4+
Copyright (c) 2021 Earle F. Philhower, III <[email protected]>
5+
6+
This library is free software; you can redistribute it and/or
7+
modify it under the terms of the GNU Lesser General Public
8+
License as published by the Free Software Foundation; either
9+
version 2.1 of the License, or (at your option) any later version.
10+
11+
This library is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
Lesser General Public License for more details.
15+
16+
You should have received a copy of the GNU Lesser General Public
17+
License along with this library; if not, write to the Free Software
18+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
*/
20+
21+
#include <Arduino.h>
22+
#include <hardware/structs/psm.h>
23+
24+
extern "C" void boot_double_tap_check();
25+
26+
// The following check will never actually execute, but it will cause the boot reset
27+
// checker to be linked in as part of the constructors.
28+
29+
void RP2040::enableDoubleResetBootloader() {
30+
if (psm_hw->done == 0) {
31+
boot_double_tap_check();
32+
}
33+
}

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: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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 350
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+
// Place our marker on the 2nd core's stack...which will not have been touched when this code is executing
47+
static uint32_t *magic_location = (uint32_t*)0x20040000;
48+
//static uint32_t __uninitialized_ram(magic_location)[count_of(magic_token)];
49+
50+
/* Check for double reset and enter BOOTSEL mode if detected
51+
52+
This function is registered to run automatically before main(). The
53+
algorithm is:
54+
55+
1. Check for magic token in memory; enter BOOTSEL mode if found.
56+
2. Initialise that memory with that magic token.
57+
3. Do nothing for a short while (few hundred ms).
58+
4. Clear the magic token.
59+
5. Continue with normal boot.
60+
61+
Resetting the device twice quickly will interrupt step 3, leaving the token
62+
in place so that the second boot will go to the bootloader.
63+
*/
64+
65+
/*static*/ void __attribute__((constructor)) boot_double_tap_check(void) {
66+
for (uint i = 0; i < count_of(magic_token); i++) {
67+
if (magic_location[i] != magic_token[i]) {
68+
// Arm, wait, then disarm and continue booting
69+
for (i = 0; i < count_of(magic_token); i++) {
70+
magic_location[i] = magic_token[i];
71+
}
72+
busy_wait_us(PICO_BOOTSEL_VIA_DOUBLE_RESET_TIMEOUT_MS * 1000);
73+
magic_location[0] = 0;
74+
return;
75+
}
76+
}
77+
// Detected a double reset, so enter USB bootloader
78+
magic_location[0] = 0;
79+
#ifdef PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED
80+
const uint32_t led_mask = 1u << PICO_BOOTSEL_VIA_DOUBLE_RESET_ACTIVITY_LED;
81+
#else
82+
const uint32_t led_mask = 0u;
83+
#endif
84+
reset_usb_boot(
85+
led_mask,
86+
PICO_BOOTSEL_VIA_DOUBLE_RESET_INTERFACE_DISABLE_MASK
87+
);
88+
}
89+
90+
#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)