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.
The Raspberry Pi Pico is ready for Serial Wire Debugging
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.
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.
cd ~/git (or whatever your project parent directory is called).git clone https://github.com/raspberrypi/picoprobe.gitcd picoprobemkdir buildcd buildcmake ..make -j4open .picoprobe.uf2 to the mounted Pico storage.cd ~/gitrm -rf picoprobe (optional)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 libusbexport PATH="/usr/local/opt/texinfo/bin:$PATH"git clone https://github.com/raspberrypi/openocd.git --branch picoprobe --depth=1cd openocd./bootstrap./configure --enable-picoprobe --disable-werrormake -j4make installcd ~/gitrm -rf openocd (optional)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.cd ~/git/PicoTest~/git/PicoTest/.vscode/launch.json | |
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:
Basic Picoprobe wiring
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:
Click to start debugging...
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:
The debug controls
You can set breakpoints by clicking to the left of the line number identifying a line of code:
Set breakpoints to pause your program and inspect its state
The debugger will pause execution when it hits a breakpoint and display variables and their values in the left-hand column:
The debugger will show you your variables’ values
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.
A full debug session in Visual Studio Code
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.