2023-03-04

Using the new Raspberry Pi Debug Probe with Arduino IDE on an Apple Silicon M1 Mac

If you just want to know about the Arduino thing, scroll down a bit.

If you follow me on Mastodon you might have seen that I recently bought the newly released Raspberry Pi Pico Debug Probe. It's a small little debug interface giving you an SWD connection for programming and debugging most ARM microcontrollers. It's of course mainly made for use with the Raspberry Pi Pico and other RP2040-based microcontroller boards, but it will happily work with just about anything else also.

The SWD-interface is USB CMSIS-DAP compatible and works out of the box with OpenOCD by using parameter -f interface/cmsis-dap.cfg. It's capable of handling quite high speeds, depending on your wires and target device, although a reasonable value might be 5 MHz using parameter -c "adapter speed 5000".

It also has a serial interface with good old RS-232, except at 3.3 V logic levels, so it's really a "2-in-1" kind of thing. This is very handy if the device you are programming gives debug info on a serial port.

Works with Game and Watch Retro Go

The reason I got is was to see how well it would work with the new Game and Watch (which I have written about before). Quite well, it turns out, and at 12 USD it is more affordable than the official genuine STLink/V2 I was using before. Is is also much less of a gamble than the STLink clones, which may or may not work with the GnW.

I paid the equivalent of 12.20 USD for it at my local electronics reseller, but the VAT and shipping bumped it to 18 USD equivalent. Unlike the real STLink, the Debug Probe is actually also in stock.

It worked right out of the box for programming Retro Go on the GnW. All I had to do moving from using STLink to using the Debug Probe was export ADAPTER=cmsis-dap to tell the Make-script to use a standard CMSIS-DAP interface. Then it's just a matter of connecting the included cable to the D-connector on the Probe and on the GnW side the orange wire (clock) to GnW pin 5, the yellow (data) to GnW pin 2 and finally black (ground) to GnW pin 3.

The Raspberry Pi Debug Probe connected to a Game and Watch by just plugging the included cable into the cable I previously made for the ST-Link

How we normally program a Raspberry Pi Pico microcontroller

But this blog entry was of course really ment to be about using the Debug Probe for programming the Pico, and using Arduino IDE.

The normal way to program a Raspberry Pi Pico, or any other RP2040-based board, is to let it reboot into the bootloader. The bootloader will then make a pseudo mass storage device available on the USB-connector, and all you need to do is to copy a compiled .uf2-file with your program to it. It will read the file, flash it into its memory, and then reboot itself to start running the code.

This works quite well, and if you are using the Arduino IDE together with the "Raspberry Pi Pico/RP2040" package by Earle F. Philhower, III, this will be done automatically when you push the Arduino IDE Upload button.

The biggest downside with this is that to enter the bootloader, you either need to reset the Pico while holding down the BOOTSEL-button on it, or send a special command over the USB connection to make it reboot into the bootloader automatically.

The problem with the first way is that there is no reset button, so you either need to provide your own, connecting a button between RUN (pin 30) and ground, or use an alternative RP2040-board that provides a button. If you don't have a reset-button you need to unplug the USB-cable and hold BOOTSEL while plugging it back in.

The problem with the second way is that it requires you to use the included Pico SDK USB-stack, which provides the reset functionality, and you also need to make sure that the Pico have not crashed so it can actually reply to the USB-commands. This is usually not a problem, as you probably use the USB-stack anyway, but if your project have no need for the USB and you want to maximise the amount of available space for your program, you might not include it, and thus must resort to manually reset it every time you upload.

How we can do it with the Debug Probe instead

So, if we don't want to use USB, and also don't want to manually reset or unplug every time we upload, we can just use the SWD-interface. On the Pico, the SWD connection is provided on a separate 3-pin header labeled DEBUG. On the Pico H it has a JST-connector that can be plugged right into the Debug Probe with included cable, on the other models you need to solder a 2.54 mm pin-header yourself and connect using the included pinheader cable.

Using SWD, which stands for Serial Wire Debug, we can at any moment take over the microcontroller, halt it, inspect memory, single step instructions and, of course, program the flash memory and reset it. All that no matter what state the controller is in, and irregardless of wether it uses USB or not.

So this is where the Debug Probe comes in. It's a cheap SWD-interface that we can use for programming our Pico, or really any ARM-based microcontroller.

To use the Debug Probe you would in many cases use a software called OpenOCD (Open On-Chip Debugger). It is an open source command line utility that can work with a large number of debug interfaces.

There are several guides for OpenOCD on the internet, but for macOS, the easiest way to get it is to first install Homebrew, a package manager for macOS, and then install it with the command brew install openocd in the Terminal.

It will be installed to /opt/homebrew/bin/openocd and if you want to use it easily from the Terminal you need to make sure you add the /opt/homebrew/bin/directory to the path. Either do this manually every time with export PATH="/opt/homebrew/bin:$PATH" or see the Homebrew documentation for how to add this to the startup files for your shell.

Then you could easily program a Pico, though the Debug Probe, using the command openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "program NameOfMyProgram.elf verify reset exit".

More instructions on this is in the Debug Probe documentation.

This requires you to first have compiled your program to an .elf-file.

The Raspberry Pi Debug Probe connected to a Raspberry Pi Pico H with the included JST to JST cable

Works with Arduino IDE … sort of

Note that I am using Arduino IDE 2.0.4 and "Raspberry Pi Pico/RP2040" package version 2.7.3. Future versions may differ or not even need this fix.

So how do we make the .elf-file with our actual program?

There are many ways to do that, but one of them is to use the Arduino IDE. Some people don't like Arduino IDE and would rather use PlatformIO or just raw GCC and makefiles, but the Arduino IDE is an easy way to get started.

The Arduino IDE tries to hide a lot of the complexity, and for most purposes it works quite well. For our quick tests here we will just use the Blink example sketch. Open it by File menu, Examples, 01.Basic, Blink. This will blink the LED on the Pico when we have programmed it.

To use Arduino IDE with any RP2040 or Pico board, we first need to install the "Raspberry Pi Pico/RP2040" package by Earle F. Philhower, III. I am using version 2.7.3 here.

Install by opening Arduino IDE preferences dialog and paste the URL https://github.com/earlephilhower/arduino-pico/releases/download/2.7.3/package_rp2040_index.json in "Additional boards manager URLs"-field. If you have anything else there already, just click the little button to the right of the field and enter the URL on a new line in the big box.

Then you can just select "Raspberry Pi Pico" as your board in the board menu.

As I wrote earlier, if you just use the Arduino IDE to program a Pico, it will compile to a .uf2-file, send a USB-command to the Pico to enter bootloader, wait until the mass storage device is mounted, and then copy the file.

The Pico will immediately reboot and the Mac will respond to that with a message saying that we did not properly eject the disk. In this case that is ok, just ignore and close that notice.

But what if we want to use the Debug Probe to program the Pico though the SWD-connection?

Easy, just find "Upload Method" in the tools menu and select "Picoprobe (CMSIS-DAP)" instead of "Default (UF2)".

Now if you push upload, Arduino IDE will compile your code to an .elf-file and then invoke a bundled OpenOCD to program the Pico over SWD using a CMSIS-DAP interface (which the Debug Probe is).

Except it it does not work. If you use an Apple Silicon M1 Mac, as I do, you will get a cryptic error:

dyld[64204]: Library not loaded: '/usr/local/opt/libusb/lib/libusb-1.0.0.dylib'
  Referenced from: '/Users/niclas/Library/Arduino15/packages/rp2040/tools/pqt-openocd/1.5.0-b-c7bab52/bin/openocd'
  Reason: tried: '/usr/local/opt/libusb/lib/libusb-1.0.0.dylib' (no such file), '/usr/local/lib/libusb-1.0.0.dylib' (no such file), '/usr/lib/libusb-1.0.0.dylib' (no such file)

(I have not actually tested this on an Intel Mac, so I don't know if it would actually work there. I assume the error is because the bundled OpenOCD is compiled for Intel and libusb is not in the right place because of that. If you had an Intel Mac and installed libusb with Homebrew it would indeed be in /usr/local/ but it is not for Apple Silicon. This might be fixable by installing the Intel-version of Homebrew (you can run it in parallel with the Apple Silicon version) and install libusb there, but I did not test that.)

Let's fix this issue with a little workaround.

How to make the Arduino IDE work with the M1 Mac for Pico Debug Probe programming

What we need to do is to use our own OpenOCD instead of the one that is included in the Arduino boards package.

Step one is to install OpenOCD using Homebrew. If you have not already installed Homebrew, do so as per the instructions on their page. Then do brew install openocd in the Terminal. If you are correctly running the Apple Silicon version of Homebrew, this will install OpenOCD in /opt/homebrew/bin/openocd. For Intel-macs this will instead be in /usr/local/bin/openocd.

This version of OpenOCD will work properly, but it does not know what a "picoprobe"-interface is, which the bundled one do. However, that matters not as it does know what a "cmsis-dap"-interface is. Therefore, be sure to select CMSIS-DAP later, and not just Picoprobe.

Now we need to tell Arduino IDE to use our own OpenOCD.

To do this, first quit Arduino IDE, then open a Finder window. Open the "Go"-menu on the menu bar and hold "option" on the keyboard. You will se "Library" suddenly appearing in the list of places you can open. Click it. (Quick tip: Hit shift + command + . (period) in Finder to show all hidden files, including the Library folder)

In the Library folder you will hopefully find a folder called "Arduino15". Then inside that keep going to "packages/rp2040/hardware/rp2040/2.7.3". There you will find a file called "platform.txt". Double-click to open it in Textedit and scroll down to the bottom.

Look for the line that says:

tools.picoprobe_cmsis_dap.cmd={runtime.tools.pqt-openocd.path}

Change it to say:

tools.picoprobe_cmsis_dap.cmd=/opt/homebrew/

What we do here is to change the command path to be the location for our Homebrew-installed openocd instead of the bundled one.

Save the file and close Textedit.

Start Arduino IDE again.

Load up a sketch (for testing the Blink in basic-examples will work fine)

Set "Board" to "Raspberry Pi Pico".

If you are using the USB-port of the Pico for anything, set "Port" to the USB-port of the Pico you are programming, not the port of the Debug Probe. If you are unsure which is which, unplug the Debug Probe USB-cable and there should only be one "usbmodem####" to choose from.

If you are not using the USB-port of the Pico, don't select any port. Note that if not using the USB-port of the Pico you will need to power it by some other means (or just plug the USB-cable to a phone charger), the Debug Probe does not supply power to the target.

Set "Upload Method" to "Picoprobe (CMSIS-DAP)".

Click the Upload-button.

Note: If you set Port to the "usbmodem###" port of the Debug Probe and then accidentally have Upload Method set to "Default (UF2)" you will overwrite the firmware in the Debug Probe with your program instead of programming the Pico as you intended. It will then need to be restored by downloading the Debug Probe firmware UF2-file from the Raspberry Pi GitHub and reprogram it by holding BOOTSEL-button while connecting the Debug Probe to the USB-port and copying the .uf2-file to the mass storage device that shows up in finder. Please be careful.

In the Output you will see the Homebrew version of OpenOCD running, connecting to the CMSIS-DAP interface of the Debug Probe, flashing the .elf-file to the Pico and doing a reset. This will always work, wether the Pico is connected by USB to anything or not, all that is needed it that the Pico is connected to the SWD and have power.

That's all for now. Have fun! If you find anything wrong or have any questions, I am available on Mastodon.