When you’ve used Serial Wire Debug (SWD) to help you correct the C or C++ code running on your Raspberry Pi Pico, you’ll never want to go back to USB and the UF2 file system again. I don’t — no more messing about unplugging and re-plugging cables for me.
Note If you haven’t read the previous article in this series, you should check it out — it covers setting up the Pico C/C++ SDK and the ARM toolchain, both of which are pre-requisites for what follows.
SWD uses three pins: one for data, another for a clock signal and a third for ground. It’s an ARM-developed technology supported by many MCU designs based on ARM’s core architecture. Essentially, it’s used to program MCUs and to do on-chip debugging (OCD): to run the code under the control of a remote bug-hunting tool, allowing you to do really useful diagnostic stuff like pause the program mid-flow to check the state of your application.
The Pico breaks out its RP2040 chip’s SWD pins as DEBUG; just solder on some header pins. But how do you make use of it? And how do you do so a Mac?
With a great deal of difficulty, seemed the answer to the second question, at least at first. Not so, though, and we have the Raspberry Pi Foundation to thank — yet again — for that. It has produced picoprobe, a program that runs on the Pico itself and turns it into a pocket USB-to-SWD adaptor. It’s a two-Pico setup: you have one Pico on which you’ll run your code and a second one that operates as a bridge between debugger software running on your host machine and the target MCU, accessed through its SWD pins.
Yes, that means you have to sacrifice a Pico to the development process, but since it only cost you £3.60, so what? And it’s not actually lost for good. picoprobe is installed in the usual way: by mounting the host device’s storage and dragging a .UF2
file across. And if you can do it once, you can do it again with a different application when you’ve finished with picoprobe.
For me, there’s no Earthly reason not to devote a Pico to this role given the benefits: fully interactive on-chip debugging and no faffing around with USB cables.
Build Yourself a Picoprobe
So how do you set it up?
There’s some extra software required, so you’ll need to install that, plus a further Visual Studio Code extension so that you can run it all from this editor.
Here are the setup steps. As before, I’ll assume you’re storing everything in a directory whose path is ~/git
and your project is called PicoTest
, but you can change either or both of those values.
Build and install picoprobe
- Launch Terminal.
cd ~/git
git clone https://github.com/raspberrypi/picoprobe.git
cd picoprobe
mkdir build
cd build
cmake ..
make -j4
open .
- Mount the Pico: hold down BOOTSEL and connect to USB.
- Drag the file
picoprobe.uf2
to the mounted Pico storage. cd ~/git
rm -rf picoprobe
(optional)
Build and install OpenOCD
OpenOCD is the host-side tool that enables on-chip debugging: it manages the communication between MCU and debugger in co-operation with picoprobe on the Pico.
Note 1 You may already have some of the command line tools installed with brew. Don’t worry, brew will upgrade them if necessary.
Note 2 If you’re working on an Apple Silicon Mac, change line 2 to export PATH="/opt/homebrew/opt/texinfo/bin:$PATH"
brew install libtool automake texinfo wget gcc pkg-config libusb
export PATH="/usr/local/opt/texinfo/bin:$PATH"
git clone https://github.com/raspberrypi/openocd.git \
--branch picoprobe --depth=1
cd openocd
./bootstrap
./configure --enable-picoprobe --disable-werror
make -j4
make install
cd ~/git
rm -rf openocd
(optional)
Configure Visual Studio Code
- Run Visual Studio Code.
- Click on the Extensions icon in the left side toolbar.
- Search for
cortex-debug
and install it. It’s the one by marus25. You only need install Cortex-Debug; you don’t need the device support extensions.
Configure Your Project
- In Terminal,
cd ~/git/PicoTest
- Paste the following code into a new file and save it to
~/git/PicoTest/.vscode/launch.json
{ "version": "0.2.0", "configurations": [ { "name": "Pico Debug", "device": "RP2040", "gdbPath": "arm-none-eabi-gdb", "cwd": "${workspaceRoot}", "executable": "${command:cmake.launchTargetPath}", "request": "launch", "type": "cortex-debug", "servertype": "openocd", "configFiles": [ "/interface/picoprobe.cfg", "/target/rp2040.cfg" ], "svdFile": "${env:PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd", "runToMain": true, "postRestartCommands": [ "break main", "continue" ] } ] }
Wire up the Hardware
Now for the hardware. Just connect the two Picos as shown below. The one with the USB cable, which you hook up to your Mac, is the programmer. You can power the target Pico from the picoprobe unit:
Debug Your Project
Note If you’ve already built your code once, trash the existing build
folder. This will save some toolchain confusion that might impede your progress when you run the debugger. You’ll probably have to choose your compiler (the ‘kit’) again: select arm-none-eabi-gcc x.y.z
.
In Visual Studio Code, open your project folder. Hit Cmd–Shift–D or click Visual Studio Code’s Run icon in the left side toolbar. The debugger will open. It will ask you to specify your target — choose the .elf
option that matches your project name.
Now click the green arrow next to Pico Debug. Your code will build, be transferred to the target and run:
The debugger will then halt at the start of your program’s main()
function. You control program flow using the play/pause, step over, step in, step out, restart and play buttons right at the top of Visual Studio Code:
You can set breakpoints by clicking to the left of the line number identifying a line of code:
The debugger will pause execution when it hits a breakpoint and display variables and their values in the left-hand column:
Step through the code line by line to see how these values change. At a function call, you can click the Step In button to jump into that function’s code, or click Step Over to run the function and stop again at the next line.
If you make some changes to your code, click the Stop button to stop debugging, and then click the green Pico Debug arrow again to build and load the updated code, and to begin debugging again.
Troubleshooting
If you see debugging errors, check your wiring. I started out getting nothing back from OpenOCD but DAP Init Failed
errors. I traced it down to an incorrectly grounded SWD GND pin. You may need to try different GND pins on the picoprobe Pico if this persists.
It’s also easy to mis-select the target or the compiler.
I’ve updated my Pico repository on GitHub with the new, debugger-friendly launch.json
file.
More on the Raspberry Pi Pico
- How to build a cellular IoT device with the Raspberry Pi Pico — part two, the code
- How to build a cellular IoT device with the Pico — part one, the hardware
- How to pop up a Picoprobe from the Adafruit QT Py RP2040
- Raspberry Pi Pico proxies: the Pimoroni Tiny 2040 and the Adafruit QT Py RP2040
- Introducing C++ programming on the Raspberry Pi Pico
- Enjoy some old school 3D arcade action — courtesy of the Raspberry Pi Pico
- Play Hunt the Wumpus, Raspberry Pi Pico style
- How to Program the Raspberry Pi Pico in C on a Mac
- A First Look at a MicroPython Marvel: the Raspberry Pi Pico
You need to initialize the submodules of pico-sdk to run `make -j4`. Just run `git submodule update –init` in the sdk directory.
Just to add to my previous comment: to adapt the above example to work with pico-debug, one needs to use the newer pico-sdk and openocd referenced in the pico-debug howto subdirectory, and then the launch.js changes like so (WP will probably lose the line feeds):
Replace:
“configFiles”: [
“/interface/picoprobe.cfg”,
“/target/rp2040.cfg”
],
with:
“configFiles”: [
“interface/cmsis-dap.cfg”,
“target/rp2040-core0.cfg”
],
“openOCDLaunchCommands”: [
“transport select swd”,
“adapter speed 4000”
],
Have you tried pico-debug? I wrote it to debug with just a single RP2040: https://github.com/majbthrd/pico-debug/ As long as you are not trying to debug RP2040 code that uses the USB peripheral, OpenOCD talks directly to the very RP2040 being debugged, and no extra wiring is needed. There are instructions in the howto subdirectory. Anyone doing a web search at the moment for pico debugging gets told that they have to use picoprobe and two RP2040, and I wanted to point out that this is not true. Thanks.
Interesting. I shall try it. Initially, it looks like there may be Mac installation issues — and Mac is the focus of the Pico coverage here — due to using packages available on apt but not brew, but we’ll see.
I presume that you are talking about hidapi? The CMSIS-DAP protocol has been around since 2013, and was intended from the start to work with macOS, Linux, and Windows. I did a web search for “macos openocd cmsis-dap hid”; this link https://gist.github.com/technobly/f2c14eaa7334db80849d45614250bdf0 provides steps; it is not clear to me why they use the Dashlane fork instead of the original signal11 repo. Obviously, you don’t want to use the openocd download link in that gist; the recent openocd with Pico support pointed to in the pico-debug howto is needed.
Nice article, even though I’m on Linux it helped a lot! The wiring diagram is flipped though. The picoprobe connects to the target’s debug pins, not the other way around. See Appendix A3 in https://datasheets.raspberrypi.org/pico/getting-started-with-pico.pdf
Absolutely right, John — thank you! Diagram now fixed. Memo to self: check diagrams *before* publishing, next time.