Submitted by admin on Wed, 08/03/2022 - 15:53

Now that you know the lightweight approach on how to flash and debug (only using the console), you now understand that an IDE is not mandatory for embedded development.

However, most developers enjoy the ergonomics of a modern IDE. So let's go back to VSCode straight away!

In this tutorial, we will simply get the previous "Mr Robot - Hello, world!" example running from within VSCode.

1. Open the project

Open the parent folder of your project:

VSCode Open Folder

Your project is now opened. We need some extensions to make this happen:

  • The first extension is the Rust Language Server. For this we have two choices:
  • The second extension is the Cortex-Debug extension

These extensions provide features like completion and goto definition, which are very useful features for a modern IDE.

VSCode Rust Analyzer

We can now begin to configure VSCode for embedded development.

2. Configure the debugger

Create the .vscode/launch.json file and fill it with this configuration:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "cortex-debug",
            "request": "launch",
            "name": "Debug with OpenOCD",
            "servertype": "openocd",
            "cwd": "${workspaceRoot}",
            "runToEntryPoint": "main",
            "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/${workspaceFolderBasename}",
            "configFiles": [
                "interface/stlink-v2-1.cfg",
                "target/stm32f0x.cfg"
            ],
            "gdbPath": "gdb-multiarch",
            "gdbTarget": "thumbv6m-none-eabi",
            "postLaunchCommands": [
                "monitor arm semihosting enable",
            ],
        },
    ]
}

Beware: If your compiled binary file has a different name than your workspace folder name, just replace ${workspaceFolderBasename} by the executable name.

Once this file is created and saved, you should see a new option appear in the Debug tab with the name we gave this configuration: "Debug with OpenOCD".

VSCode Debug Tab

Before starting the debug, you might notice that the main code lifts an error when analyzed by rust-analyzer. This has to do with the fact that we didn't write any test for our program for now. To make this error go away, we will open our Cargo.toml file and add the following lines:

[[bin]]
name = "hello"
test = false
bench = false

We will also add this line at the end of our .gitignore to avoid uploading the cortex-debug generated files to git:

.vscode/.cortex-debug*

Automatically building the code when we want to debug is also very useful. Create the .vscode/tasks.json file and add a building task:

{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "cargo",
            "command": "build",
            "problemMatcher": [
                "$rustc"
            ],
            "group": "build",
            "label": "build"
        }
    ]
}

We can now link this task by adding the following line in our configuration, in the .vscode/launch.json:

"preLaunchTask": "build",

Here is our new .vscode/launch.json file:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "cortex-debug",
            "request": "launch",
            "name": "Debug with OpenOCD",
            "servertype": "openocd",
            "cwd": "${workspaceRoot}",
            "runToEntryPoint": "main",
            "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/${workspaceFolderBasename}",
            "configFiles": [
                "interface/stlink-v2-1.cfg",
                "target/stm32f0x.cfg"
            ],
            "gdbPath": "gdb-multiarch",
            "gdbTarget": "thumbv6m-none-eabi",
            "postLaunchCommands": [
                "monitor arm semihosting enable",
            ],
            "preLaunchTask": "build",
        },
    ]
}

3. Debugging with VSCode

We can now start to debug! Go to the Debug Tab and hit the play button with the "Debug with OpenOCD" profile selected:

Starting debug

The debugger should launch and stop at the #[entry] line:

VSCode Debug Window

We can see at the bottom of the window (in the Terminal tab), that our code was built successfully before launching the debugger. If we change our terminal to the gdb-server one (by hitting gdb-server in the bottom-right of the screen), we can directly see the OpenOCD console, connected to our board:

VSCode Debug with OpenOCD

What is unfornate is that, even if we are able to see the general registers values (which can be useful to debug Assembly code), we are not able to see the values of our peripheral registers... Let's change this right away!

For the debugger to know what peripherals are available on your board, it needs to have a description of the hardware of your MCU. This description is provided by the SVD files. SVD files can be downloaded directly from the manufacturer, but I'll spare you the hustle of looking for them for this board: download the SVD File for STM32F0x2.

Once you have downloaded the archive, extract it and move the STM32F0x2.svd file to the .vscode/ folder (so that we know where it is).

Add the following line to the .vscode/launch.json configuration:

"svdFile": ".vscode/STM32F0x2.svd",

Here is our final .vscode/launch.json file:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "cortex-debug",
            "request": "launch",
            "name": "Debug with OpenOCD",
            "servertype": "openocd",
            "cwd": "${workspaceRoot}",
            "runToEntryPoint": "main",
            "executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/${workspaceFolderBasename}",
            "configFiles": [
                "interface/stlink-v2-1.cfg",
                "target/stm32f0x.cfg"
            ],
            "gdbPath": "gdb-multiarch",
            "gdbTarget": "thumbv6m-none-eabi",
            "postLaunchCommands": [
                "monitor arm semihosting enable",
            ],
            "preLaunchTask": "build",
            "svdFile": ".vscode/STM32F0x2.svd",
        },
    ]
}

Putting the SVD file in your project is better for portability, but it's 32 000 lines of XML code which will be copied in every project... If you want to avoid this, you might want to move it somewhere safe on your computer and update its path in the .vscode/launch.json configuration.

Relaunch the Debugger and admire our peripheral registers appear (on the left of the screen) in all their glory! (you'll learn to love them, don't worry...):

VSCode Debug with Peripherals

You can now put a breakpoint at the 13 loop{} line in our main.rs file (by clicking on the left of the line numbers) and hit play on the debugger interface () to see our beautiful message appear on the gdb-server terminal!

VSCode play debug

We finally configured our tools to write code, build and debug Rust for STM32 targets! We now get to do the most fun part: using these tools!