How to correctly reset Arduino Nano RP2040 programmatically
Arduino Nano RP2040 Connect is a nifty piece of hardware with a very nice SDK (based on RaspberryPI Pico SDK).
Unfortunately, the SDK contains a glaring omission: there is no method provided to programmatically reset the device. Based on the amount of Google queries on how to reset RP2040 (either the Arduino variety or Pico) this is not an uncommon need. You’d think that occasional need to reset a device based on purely programmatic considerations rather than user pressing a button would be obvious but apparently not.
There are quite a few “clever” workarounds you can find via a web search. Some people abuse watchdog timer to cause a reboot. Some hook up additional hardware to try to force a reset signal and so on and so forth.
With more online digging or spending some time perusing RP2040 datasheet it is possible to discover a much better method: use AIRCR Cortex-M0+ register (section 2.4.8 of the datasheet). The naive code looks like this:
//On Arduino you can just include SPI.h
//as it will pull the two headers below
//#include <SPI.h>
#include <hardware/regs/m0plus.h>
#include <hardware/regs/addressmap.h>
inline void rebootNaive() {
auto & AIRCR_register = *(volatile uint32_t*)(PPB_BASE + M0PLUS_AIRCR_OFFSET);
//From datasheet:
//31:16 VECTKEY: On writes, write 0x05FA to VECTKEY, otherwise the write is ignored.
//15 ENDIANESS: 0 = Little-endian.
//14:3 Reserved
//2 SYSRESETREQ: Writing 1 to this bit causes the SYSRESETREQ signal to the outer system to be asserted to request a reset.
//1 VECTCLRACTIVE: not relevant here
AIRCR_register = (0x05FA << M0PLUS_AIRCR_VECTKEY_LSB) | M0PLUS_AIRCR_SYSRESETREQ_BITS;
}
If you attempt this on Arduino Nano RP2040, it’ll work - the device will restart and your code will start running from the beginning. However, you will also notice that Serial over USB connection goes dead after this. If you are connected with the serial monitor no communications will come through and even if you attempt to initiate flash via serial connection it will timeout. The only solution then is to press the physical reset button. Sigh.
For some applications, this might not be an issue - there will be no serial connection in production anyway. But at least for development this is mightily annoying.
Fortunately there is an easy way to solve this. Perusing the datasheet first we find section 2.4.2.9 that says:
The NVIC only resets the Cortex-M0+ processor core
which seems to hint that other parts of the chip like USB controller aren’t. Then section 2.14.1 goes in great detail about how:
The reset controller allows software control of the resets to all of the peripherals that are not critical to boot the processor in RP2040
And USB controller is one of them. Bingo!
With this section in mind the “correct” way to reset the board appears to be:
//On Arduino you can just include SPI.h
//as it will pull the three headers below
//#include <SPI.h>
#include <hardware/regs/m0plus.h>
#include <hardware/regs/addressmap.h>
#include <hardware/resets.h>
inline void reboot() {
//reset USB controller
reset_block(RESETS_WDSEL_USBCTRL_BITS);
//you might want to add other perpherials here if you use them
//reset the CPU
auto & AIRCR_register = *(volatile uint32_t*)(PPB_BASE + M0PLUS_AIRCR_OFFSET);
//From datasheet:
//31:16 VECTKEY: On writes, write 0x05FA to VECTKEY, otherwise the write is ignored.
//15 ENDIANESS: 0 = Little-endian.
//14:3 Reserved
//2 SYSRESETREQ: Writing 1 to this bit causes the SYSRESETREQ signal to the outer system to be asserted to request a reset.
//1 VECTCLRACTIVE: not relevant here
AIRCR_register = (0x05FA << M0PLUS_AIRCR_VECTKEY_LSB) | M0PLUS_AIRCR_SYSRESETREQ_BITS;
}
This appears to work well and hopefully will save someone hours of frustration 🙂