The Relational Technologist

Share this post

Getting Started with Rust on a Raspberry Pi Pico (Part 3)

reltech.substack.com

Getting Started with Rust on a Raspberry Pi Pico (Part 3)

Iterating even faster by flashing and debugging with Visual Studio Code

Jim Hodapp
Dec 2, 2021
1
12
Share this post

Getting Started with Rust on a Raspberry Pi Pico (Part 3)

reltech.substack.com

This is the third and final installment of a series of guides exploring the use of Rust on the Raspberry Pi Pico. In this guide, I’ll show you how to make your embedded Rust development process even easier through the use of the Visual Studio Code to develop, compile, flash and debug all without ever leaving your favorite development environment.

Part 1 of the series - blinking an LED from a Pico

Part 2 of the series - iterating faster with cargo run


Review from Last Time

In the second guide in this series, I showed you how to use cargo run to make your embedded development cycle much quicker. And just like the last guide built on top of a much more manual process for compiling/flashing/debugging with the Pico, this one builds on the previous guide even more. Visual Studio Code, or VS Code, is a tremendous contribution by Microsoft to the software development community. Through an extensive plugin system, it truly can be the perfect development environment for just about anything.

Installing Prerequisites

In order to get started using VS Code for all of your Pico development, you’ll need to install two extensions. Open up VS Code’s Quick Open (CNTRL+P or CMD+P) and paste the following to install the Cortex-Debug and the Rust Lang extensions:

ext install marus25.cortex-debug
ext install rust-lang.rust

Cortex-Debug handles interfacing of VS Code and OpenOCD. Rust Lang brings first class Rust support such as providing lints, code completion and navigation, formatting and implements the $rustc problemMatcher that I’ll cover shortly.

Compiling an Example Application

Although you’re probably most interested in being able to debug Rust applications with ease on the Pico from the VS Code interface, it’s also very helpful to be able to compile your embedded Rust application from it as well.

To be able to do that, we first need to create a .vscode directory and tasks.json file within it, like so:

$ mkdir ~/Projects/rp2040-project-template/.vscode

Within VS Code, add the ~/Projects/rp2040-project-template directory that we started to work with from the first guide to a new project. If you need to clone this source repository again, you may do so from this link.

Next, create a new file under the .vscode directory called tasks.json. Copy and paste this source listing into that file and save it:

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558 
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Cargo build",
            "type": "shell",
            "command": "cargo",
            "args": ["build"],
            "problemMatcher": [
                "$rustc"
            ],
            "group": "build"
        },
        {
            "label": "Build binary",
            "type": "shell",
            "command": "arm-none-eabi-objcopy",
            "args": [
                "--output-target", "binary",
                # Reads from an ELF binary file
                "./target/thumbv6m-none-eabi/debug/rp2040-project-template",
                # Outputs a raw binary file
                "./target/thumbv6m-none-eabi/debug/rp2040-project-template.bin"],
            "problemMatcher": [
                "$rustc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "dependsOn": "Cargo build"
        }
    ]
}

This sets up a build task called “Build binary” for your current VS Code workspace that instructs VS Code to execute the familiar cargo build. It’s a fairly straightforward task config, but if you have any questions or run into issues, feel free to leave a question at the bottom of this guide as a comment.

Let’s try this out. From VS Code, run the “Build binary” build task by navigating to Terminal→Run Build Task… or by pressing CNTRL+SHIFT+B on Linux or CMD+SHIFT+B on macOS.

You should see output similar to the following in the terminal pane below your source code:

> Executing task in folder rp2040-project-template: cargo build <

    Updating crates.io index
    Updating git repository `https://github.com/rp-rs/rp2040-boot2-rs`
    Updating git repository `https://github.com/rp-rs/rp-hal`
    Updating git repository `https://github.com/rp-rs/pio-rs.git`
  Downloaded paste v1.0.6
  Downloaded syn v1.0.82
  Downloaded rp2040-pac v0.2.0
  Downloaded cortex-m-rt v0.7.1
  Downloaded 4 crates (824.4 KB) in 1.40s
   Compiling autocfg v1.0.1
   Compiling proc-macro2 v1.0.32
   Compiling unicode-xid v0.2.2
   Compiling syn v1.0.82
   Compiling cortex-m v0.7.3
...
   Compiling pio v0.1.0 (https://github.com/rp-rs/pio-rs.git?branch=main#5b249bda)
   Compiling rp2040-hal v0.3.0 (https://github.com/rp-rs/rp-hal?branch=main#f68f148d)
    Finished dev [optimized + debuginfo] target(s) in 1m 20s

Flashing your Application to the Pico

Now we get to the interesting part of flashing our example embedded Rust application to the target Pico device (Pico B). To do that, we’ll need to create another VS Code-specific file called launch.json. Create it under the .vscode directory called launch.json.

Copy and paste this source listing into that file and then save it:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug rp2040-project-template",
            "request": "launch",
            "type": "cortex-debug",
            "cwd": "${workspaceRoot}",
            "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/rp2040-project-template",
            "preLaunchTask": "Build binary",
            "servertype": "external",
            // This may need to be gdb-multiarch depending on your system (i.e. Linux vs Mac)
            "gdbPath" : "arm-none-eabi-gdb",
            // Connect to an already running OpenOCD instance
            "gdbTarget": "localhost:3333",
            // If you have the Pico SDK installed, allows the
            // display of the RP2040 CPU registers in VS Code
            "svdFile": "${env:PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd",
            "runToMain": true,
            "preLaunchCommands": [
                "monitor init",
                "monitor reset init",
                "monitor halt",
            ],
            // Work around for stopping at main on restart
            "postRestartCommands": [
                "break main",
                "continue"
            ],
        }
    ]
}

Some things to note about this config:

  • The preLaunchTask will ensure your Rust source code is freshly compiled using the other task config in tasks.json.

  • If you have the Pico SDK installed, make sure that you export the full path to where you placed it in the PICO_SDK_PATH environment variable. This is typically done via your command shell’s config file like .zshrc or .bashrc. You may also copy this file from the SDK into your workspace for convenience. Keeping the svdFile line uncommented if you do have the SDK, which will enable you to view the current values stored in the RP2040 CPU registers.

  • The preLaunchCommands are instructions specifically for OpenOCD to make sure our target Pico is fully reset before flashing commences.

Debugging your Application

Make sure your Picoprobe setup from the first guide in this series is plugged in to your development machine and ensure OpenOCD is also running in your terminal like so:

$ src/openocd -f interface/picoprobe.cfg -f target/rp2040.cfg -s tcl

At this point, you should be able to start debugging your application from within VS Code. To do so, click the menu item Run→Start Debugging or press F5.

You should see a debug console display pane below your source code with output that looks similar to the following:

Launching GDB: "arm-none-eabi-gdb" "-q" "--interpreter=mi2"
 "/Users/jhodapp/Projects/rp2040-project-template/target/thumbv6m-none-eabi/debug/rp2040-project-template"Set "showDevDebugOutput": true in your "launch.json" to see verbose GDB transactions here. Helpful to debug issues or report problems
Reading symbols from /Users/jhodapp/Projects/rp2040-project-template/target/thumbv6m-none-eabi/debug/rp2040-project-template...
Program stopped, probably due to a reset and/or halt issued by debugger
target halted due to debug-request, current mode: Thread 
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
target halted due to debug-request, current mode: Thread 
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
target halted due to debug-request, current mode: Thread 
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
target halted due to debug-request, current mode: Thread 
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
target halted due to debug-request, current mode: Thread 
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
target halted due to debug-request, current mode: Thread 
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
Note: automatically using hardware breakpoints for read-only addresses.
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x00000178 msp: 0x20041f00
target halted due to debug-request, current mode: Thread 
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
Note: automatically using hardware breakpoints for read-only addresses.
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x00000178 msp: 0x20041f00

Thread 1 hit Temporary breakpoint 1, rp2040-project-template::__cortex_m_rt_main_trampoline () at src/main.rs:26

Notice that the values contained in the CPU’s registers are displayed.

At the top center of the VS Code application you’ll see traditional debugging UI elements appear that look like:

Here’s a quick overview of what each button does (from left to right):

  1. The first button (green) resets your Pico device and the example application you flashed onto it

  2. The second button continues execution after it was paused manually or from setting a breakpoint

  3. The third button executes the current line of code highlighted in the source editor and everything that it calls - it does not step you through all of the lower level code in a function or a chain of functions called

  4. The fourth button works similarly to the third except it steps you through all code including function calls into the standard Rust libraries

  5. The fifth button steps out of the current chain of functions back up to the root level

  6. The sixth button restarts execution from main()

  7. The seventh button stops debugging, disconnects from the target Pico, and returns you to the source code editor

Setting a Breakpoint

If you want execution to stop on a certain line of your Rust code, you may click in the area just to the left of the line number of a certain line of code that you’re interested in examining the behavior of.

You may set a breakpoint whether you’re currently running your application or not.

At this point, you’re well on your way to an easy-to-use development and debugging experience for the Pico in Rust!

Conclusion

This completes this particular guide as well as the current series of guides on getting started with Rust development on a Raspberry Pi Pico. If you’d like me to cover any additional topics related to Rust development on the Pico, please leave a comment below and let me know. Also, if you find any issues in any of these guides, leave a comment and I’ll be sure to correct them.

I hope these guides are helpful for you and give you some confidence to start building amazing things in Rust on your Pico! Feel free to reach out if you need any assistance in getting started - I’m more than happy to help give some pointers as I’m able to.

As always, thanks for reading and passing this along to anyone that would find value from reading the Relational Technologist.

Please enjoy!


Make sure to subscribe so you don’t miss any of the future guides of working with Rust on the Raspberry Pi Pico as well as other exciting software engineering topics.

12
Share this post

Getting Started with Rust on a Raspberry Pi Pico (Part 3)

reltech.substack.com
12 Comments
9names
Dec 10, 2021Liked by Jim Hodapp

Hi Jim, rp-rs developer + maintainer here.

Thanks for the write-up, both for where things worked and where they didn't. We haven't had too much feedback on that, so it is most welcome!

Great to see a good step-by-step configuration options for OpenOCD - I haven't used it for debugging rp2040 but I know that a few have and had issues getting it set up correctly; we should really have some of this documentation as part of the project.

It's a bit late to help now (since you look to be done with this series) but I'd be interested in trying to work out why the probe / debug configuration from rp2040-app-template didn't work for you so that others don't have the same problems.

We spend most of our time in https://app.element.io/#/room/#rp-rs:matrix.org if you'd prefer a real-time group chat style conversation, or you could raise an issue on github on either rp2040-app-template or rp-hal - whichever works best for you.

This is an open invitation by the way - everyone is welcome in the matrix room, and issues / PRs for the repos most welcome.

Expand full comment
Reply
1 reply by Jim Hodapp
Oleg Eterevsky
Writes Oleg’s Substack
Dec 5, 2021Liked by Jim Hodapp

Hey! Thank you for this series of post.

I was just playing around with some RP2040 controllers, and ended up with a slightly simpler setup (IMO). Instead of using a separate controller for handling the debug, I've just set up debug logging via USB serial port. This requires a bit more boilerplate (handling the USB interrupt and such), but is much simpler from the hardware perspective: you need to just plug in a single Pico board in your USB, flash it and you can immediately see the debug output.

Here's my repo: https://github.com/eterevsky/rp2040-blink

Expand full comment
Reply
5 replies by Jim Hodapp and others
10 more comments…
TopNewCommunity

No posts

Ready for more?

© 2023 Jim Hodapp
Privacy ∙ Terms ∙ Collection notice
Start WritingGet the app
Substack is the home for great writing