Raspberry Pi Pico

How to Build OpenOCD and Picotool for the Raspberry Pi Pico on Windows

This tutorial will show you how to use Windows to build the Raspberry Pi Pico version of OpenOCD and picotool used to debug programs on the RP2040.

Chapter 5 and Appendix B of the Getting Started with Raspberry Pi Pico PDF talk about how to build these tools. However, they focus mostly on building the tools for Linux and maxOS. Understandably, building tools on these operating systems is easier, but we can still build them on Windows.

Note that you have the option of skipping the steps below and downloading the executables that I created here. If they do not work or you don’t trust them (no hard feelings), you can continue with the steps below to build your own OpenOCD and picotool.

Directory Setup

I recommend following the steps in my first Raspberry Pi Pico on Windows tutorial to set up the RP2040 build system. We will use the directory structure in that tutorial to hold the source code and executables for these tools.

Specifically, you will want to check out the repositories mentioned in this tutorial into C:\VSARM\sdk\pico.

Install Git for Windows SDK

Git for Windows SDK is different than Git for Windows, as the former contains a number of tools that help us build programs on Windows (including a package manager). Regular Git Bash does not have these features.

Head to https://gitforwindows.org/#download-sdk and download Git for Windows SDK. Download the latest installer for your version of Windows (likely the 64-bit version).

Run the installer, accepting all the defaults. This will open a command prompt window and begin to download/install Git for Windows SDK. It will take some time, as it’s a rather large set of files.

Installing Git for Windows SDK

Note that Git for Windows SDK installs (by default) in C:\git-sdk-64. This directory contains everything as part of the SDK, which means you can delete the directory when you are done using it.

Install Packages

One of the best features of Git for Windows SDK is the pacman package manager. It allows you to install libraries and software similar to how you might do it in Linux.

Run C:\git-sdk-64\git-bash.exe. Note that you must use this executable to run Git for Windows SDK and not the one you may have installed in the previous tutorial! The window should say “SDK-64:/” at the top if you’re running the correct version.

In the terminal, enter the following commands:

pacman -Syu 
pacman -Su
pacman -S mingw-w64-x86_64-toolchain git make libtool pkg-config autoconf automake texinfo wget

When pacman asks which members to install in the mingw-w64-x86_64-toolchain, press ‘enter’ to install all.

Install packages in Git for Windows SDK

When asked “pkg-config and pkgconf are in conflict. Remove pkgconf? [Y/n],” enter Y.

When asked “Proceed with installation? [Y/n],” enter Y again.

This will take some time to download and install all of the packages.

Note: there is an issue with libusb-1.0.24 on MSYS2. We will manually install v1.0.23 to prevent the segmentation fault from occurring when running OpenOCD or picotool. If you run pacman -Su to update packages after this manual installation process, it will remove version 1.0.23 and update libusb to the latest (1.0.24 at the time of writing). For more information, see this forum post.

Enter the following commands to install libusb-1.0.23:

cd ~/Downloads
wget http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-libusb-1.0.23-1-any.pkg.tar.xz
pacman -U mingw-w64-x86_64-libusb-1.0.23-1-any.pkg.tar.xz

Enter Y when asked “Proceed with installation? [Y/n].”

Installing libusb

At this point, we’re ready to build our tools!

Build OpenOCD

OpenOCD (Open On-Chip Debugger) is an open-source tool that communicates with GDB to send commands over JTAG or SWD to a number of different microcontrollers. The RP2040 is not officially supported in OpenOCD at the time of writing, so we need to build a special branch of OpenOCD.

In your Git for Windows SDK terminal, enter the following commands:

cd /c/VSARM/sdk/pico
git clone https://github.com/raspberrypi/openocd.git --branch picoprobe --depth=1
cd openocd
./bootstrap
./configure --enable-picoprobe --disable-werror
make

When OpenOCD is done building, you will need to copy in the libusb-1.0.dll library file from your Git for Windows SDK directory. This .dll file must be in the same directory as your openocd.exe file for the tool to work. If you move openocd.exe, you’ll need to copy libusb-1.0.dll to the same folder.

Enter the following command to copy the file into the current directory:

cp /c/git-sdk-64/mingw64/bin/libusb-1.0.dll src/libusb-1.0.dll

You can test running OpenOCD with the following command:

src/openocd.exe

You should expect it to throw some errors and exit, as we haven’t given it any parameters.

Testing OpenOCD

Note: I do not recommend calling make install as shown in the Getting Started guide! This will make the Pico version of OpenOCD the default OpenOCD for your whole system. If you plan to use OpenOCD for other embedded debugging, you’ll want to keep the Pico version separate. For now, just leave the executable in the src folder. You can call this executable explicitly from the command line or set VS Code to use it on a per-project basis.

Build Picotool

Picotool is a program used to inspect RP2040 binaries and interact with the RP2040 in bootloader mode.

Note that to build picotool, you will need to have the SDK_PICO_PATH environment variable set to the location of your pico-sdk (e.g. C:\VSARM\sdk\pico\pico-sdk) as we did in the previous tutorial.

Open C:\git-sdk-64\git-bash.exe (if it’s not already open).

Run the following commands to clone the picotool repository and build it:

cd /c/VSARM/sdk/pico
git clone -b master https://github.com/raspberrypi/picotool.git
cd picotool
mkdir build
cd build
cmake -G "MinGW Makefiles" -DPC_LIBUSB_INCLUDEDIR="/c/git-sdk-64/mingw64/include/libusb-1.0" ..
make

Just like with OpenOCD, we’ll need a copy of libusb-1.0.dll to be in the same folder as picotool.exe. Enter the following command to copy it into the build folder:

cp /c/git-sdk-64/mingw64/bin/libusb-1.0.dll .

We also need a copy of libgcc_s_seh-1.dll:

cp /c/git-sdk-64/mingw64/bin/libgcc_s_seh-1.dll .

You can test picotool by entering the following:

./picotool.exe

It should spit out a help message and exit.

Testing picotool

Optional: You can add picotool.exe to your Path, if you feel so inclined. This will allow you to call “picotool” from any terminal.

  1. Search for “environment variables” in the Windows search bar
  2. Open “Edit the system environment variables”
  3. Click “Environment Variables…”
  4. Select Path under “User variables” and click “Edit…”
  5. Add a new entry: C:\VSARM\sdk\pico\picotool\build (or whever you’re keeping picotool.exe)
  6. Click OK on all the windows to save the settings

Note: At the time of writing, I could only get this build of picotool to run inside of Git Bash. I’m assuming that the MinGW environment is somehow necessary for its execution. If I find a way to make the .exe run in Command Prompt or PowerShell, I will post it here.

Update Driver

Windows does not have a native driver to send commands over USB to the Pico from picotool. So, we have to install one manually.

Put your Pico board into bootloader mode (press and hold BOOTSEL and plug in the USB cable). It should enumerate on your computer as a storage drive.

Head to https://zadig.akeo.ie and download Zadig.

Run Zadig. Select Options > List All Devices.

You should see 2 Pico devices in the drop-down menu: RP2 Boot (Interface 0) and RP2 Boot (Interface 1). Select RP2 Boot (Interface 1).

The driver should be listed as (NONE). Select libusb-win32 (some version) as the replacement driver.

Install USB driver with Zadig

Click Install Driver. When it’s done, the current driver should be listed as “libusb-win32.”

If you break something in Zadig (like I did), you can fix it. For example, the Pico will not enumerate as a mass storage device drive in bootloader mode. Zadig will show something other than “USBSTOR” as the driver for RP2 (Interface 0). Here is how to delete the bad drivers and let Windows fix the mess you made:

  1. Put Pico into bootloader mode
  2. Open Device Manager
  3. Click View > Devices by container
  4. Expand RP2 Boot
  5. For all entries under RP2 Boot:
    • Right-click on entry
    • Select Uninstall Device
    • Check “Delete the driver software for this device” (if asked)
    • Click Uninstall
  6. Unplug Pico
  7. Hold BOOTSEL and plug it back in to put it into bootloader mode again
  8. Windows should automatically reinstall all the correct drivers

Test Picotool

With your Pico in bootloader mode, enter the following command:

/c/VSARM/sdk/pico/picotool/build/picotool.exe info

Note that you can run picotool without specifying the path name if you added its location to your Path.

If it is able to communicate with your connected Pico, you should see that printed.

Testing picotool

Going Further

Once you have finished building OpenOCD and picotool, you can uninstall Git for Windows SDK by simply deleting the C:\git-sdk-64 directory.

Hopefully, this has helped you get started with some advanced tools for programming and debugging on your Raspberry Pi Pico. If you would like to set up step-through debugging using VS Code, see this tutorial (link coming soon).

Happy hacking!

How to Set Up Raspberry Pi Pico C/C++ Toolchain on Windows with VS Code

This tutorial will show you how to install the Raspberry Pi Pico toolchain on Windows 10 for C and C++ development.

Raspberry Pi has a fantastic getting started guide for the Pico that covers installation steps for the major operating systems. On Linux (specifically, most flavors of Debian), you can run a single script that will install everything for you. On macOS, you need to use Homebrew to install the toolchain, which is only a few commands in the terminal.

Windows, however, is a different story. Installing the toolchain is an arduous process, requiring multiple programs and manually modifying the Windows Path. The Raspberry Pi Pico getting started guide shows you how to do this, but I have issues with two parts: you need to install Build Tools for Visual Studio (around 6 GB) and you must run VS Code from within a Developer Command Prompt every time.

This guide walks you through an alternative way of installing the C/C++ toolchain for the Pico, using MinGW in place of the Build Tools for Visual Studio. Note that this guide is meant for intermediate/advanced users; it assumes you have some familiarity with environment variables in Windows and CMake.

Important! There are a lot of steps in this guide, and missing one will likely cause things to fail down the road. Take your time, and slowly perform each step in order. I recommend setting aside around 2 hours to complete the setup. If something does not work, return to earlier sections to ensure you performed each step correctly. Don’t ignore errors! Something that did not install correctly likely means the tool won’t work later.

[Update Sep 27, 2024] This guide still technically works if you use Windows Command Prompt to run CMake and Make. If you use Git Bash, you’ll likely get the error: mingw32-make[2]: *** [CMakeFiles\blink.dir\build.make:1341: blink.elf] Error -1073741819. I have not yet figured out how to solve this issue. As an alternative, you might want to try the official Raspberry Pi Pico VS Code plugin: https://github.com/raspberrypi/pico-vscode.

Directory Setup

With the exception of CMake and Python, we will want all of our tools and SDK files to exist in one folder on Windows. This setup will make it easy to configure the Path and find things later on.

Create a folder named VSARM in the top level of your C drive.

In C:\VSARM, create the following folders:

  • C:\VSARM\armcc
  • C:\VSARM\lib
  • C:\VSARM\mingw
  • C:\VSARM\sdk

Note that this directory structure is similar to how you might set up other Arm compiler tools and SDKs (such as this STM32 toolchain setup). You should be able to keep all of your Arm development tools in the VSARM directory and have them coexist with other tools and SDKs.

We use these short, one-word folder names to keep paths short and to avoid spaces. Some Unix/Linux tools (that have been ported to Windows) do not work well with paths that have spaces in them.

Install GNU Arm Embedded Toolchain

The GNU Arm Embedded Toolchain contains the Arm GCC compiler that we need to compile C and C++ code for the RP2040.

Head to the GNU Arm Embedded Toolchain download page and download the latest installer for Windows. For me, this was gcc-arm-none-eabi-10-2020-q4-major-win32.exe.

[Update Apr 19, 2022] I have verified that Arm GNU Embedded Toolchain 11.2-2022.02 works.
[Update Sep 27, 2024] The Arm GNU Toolchain download page has moved here. Download the AArch32 bare-metal target (arm-none-eabi) executable (.exe) for your operating system (Windows host in this case). I have verified that this guide still works with arm-gnu-toolchain-13.3.rel1-mingw-w64-i686-arm-none-eabi.exe.

Run the installer. When asked, change the installation location to C:\VSARM\armcc. It will auto-fill the destination directory to the name of the current toolchain release.

Installing GNU Embedded Toolchain on Windows

Continue with the installation process. When it is complete, select the option to Add path to environment variable.

Add Arm GCC to Windows path during installation

At this point, you should be able to call any of the Arm compiler tools in a new command prompt, such as arm-none-eabi-gcc.exe.

Install MinGW-w64 GCC Tools

MinGW (short for Minimalist GNU for Windows) is a collection of open-source utilities, such as compilers and linkers, that allow us to build applications for Windows.

When we build Pico projects, we need to compile the elf2uf2 and pioasm tools from source. These tools run on our computer (not the target RP2040 microcontroller), and we need a native compiler and linker. The original Getting Started guide has us install Build Tools for Visual Studio to get a Windows compiler (cl.exe), linker (link.exe), and make tool (nmake.exe). We’re going to use the open-source GNU suite (e.g. gcc, ld, make) for Windows instead.

[Update Apr 19, 2022] Note: at this time, the MinGW (.exe) installer appears to be broken. You will likely see the error message “The file has been downloaded incorrectly!” As a result, I have updated the portion below to download only the MinGW-W64 GCC files (tested with v8.1.0), as that’s all we need to compile Pico programs (this no longer applies, as I have updated the instructions–see Sep 27, 2024 update).

[Update Sep 27, 2024] The SourceForge files have been reorganized, and you can no longer find the pre-compiled binaries for Windows (at least for newer versions of MinGW). As a result, I have updated the instructions below to show how to work with the latest version of MinGW (14.2.0 at the time of writing).

Head to https://github.com/niXman/mingw-builds-binaries/releases, which hosts the latest, pre-compiled binaries for MinGW. Download the x86_64-X.X.X-release-win32-seh-ucrt-rt_vY-revZ.7z (where X.X.X, Y, and Z are the respective release, version, and revision numbers). This was tested with: x86_64-14.2.0-release-win32-seh-ucrt-rt_v12-rev0.7z.

Unzip the file into the C:\VSARM\mingw directory. Windows 11 should be able to handle .7z files natively now. If not, you can download the 7-zip utility. Uncheck the named unzip directory so that when everything unzips, you should have C:\VSARM\mingw\mingw64.

When it’s done, open a Windows Command Prompt and enter the following into the terminal:

echo mingw32-make %* > C:\VSARM\mingw\mingw64\bin\make.bat

This creates a wrapper batch file that will call the mingw32-make tool whenever you type make into a Windows terminal. We will update the Windows Path to find all of the tools in mingw64\bin (along with this .bat file) in a later step.

Install CMake

CMake is a tool that helps you automate the build process of programs. It does not build/compile (like Make does), but rather, it can generate the directory structures and files needed for any build system (Make, Qt Creator, Ninja, etc.). The Raspberry Pi Pico SDK relies on CMake to help create these build files.

Head to the download page on CMake’s site.

Important! There is a bug in CMake version 3.20 (at the time of writing). On the second run of make or nmake (after running cmake), the process will fail. If you’re using nmake, you’ll get an error like fatal error U1033: syntax error : ':' unexpected,  or if you’re using mingw32-make, something like *** multiple target patterns. Stop. To prevent this, install CMake version 3.19. Future versions of CMake may fix this bug, but for now, know that version 3.19 worked for me.

[Update Apr 19, 2022] I have verified that CMake 3.23.1 now works. The bug has been fixed.

Download the latest installer for Windows (cmake-3.30.4-win64-x64.msi as of Sep 27, 2024).

Run the installer and accept the user license. On Install Options, select Add CMake to the PATH environment variable.

Installing CMake 3.30.4

Continue the installation process, accepting all the defaults. Note that this will install CMake to C:\Program Files\CMake, which is fine, as it will be used as a system-wide tool (not just for VSARM projects).

Install Python

The Pico SDK relies on Python to script and automate some of the build functions.

Important! At the time of writing, the Pico SDK recommends Python version 3.9. I do not know if other versions will work.

[Update Apr 19, 2022] I have verified that Python 3.10.2 works.

[Update Sep 27, 2024] I have verified that Python 3.12.6 works.

Head to the downloads page on the Python site. Download the latest Python 3.9 (or 3.10) installer.

Run the installer. On the first screen, make sure that Install launcher for all users (recommended) is checked and check Add Python to PATH.

Add Python to Windows PATH

Click Install Now and wait while it installs Python.

At the end of the installation process, select the option to disable the path length limit.

Important! If you were not asked to disable the MAX_PATH length limit, you will want to make sure that long paths are enabled. The Pico SDK (and many other SDKs) often have long, nested folders, resulting in pathnames that exceed the original Windows limit (260 characters).

To enable long paths, search for regedit in the Windows search bar and run the Registry Editor program. Navigate to Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem and add an entry (if one is not already there) for LongPathsEnabled. Change Value data to 1 and Base to Hexadecimal.

Disable pathname character limit in Windows registry

Here is a full guide if you need help modifying the registry to disable the pathname limit.

Install Git

Git makes downloading the Pico SDK much easier, and the Windows version comes with Git Bash, which is a super useful shell. We can change the shell in VS Code to Git Bash so that the command line works just like in Linux. Note that you will need Git installed (with or without Git Bash), as some of the CMake commands require it when configuring a project.

Head to the Git SCM download page and download the latest installer for Windows (e.g. 32-bit Git for Windows Setup).

Run the installer. When you get to the screen asking you to choose a default editor, feel free to pick whatever you want. I kept vim because I know it (barely) well enough to edit git comments.

Select default editor for Git on Windows

Continue the installation process, accepting all the defaults.

Download Pico SDK and Examples

The Pico SDK conains a collection of tools and libraries used to make developing on the Pico (and RP2040) much easier. We can also download a set of C/C++ examples that are useful demonstrations of how to use the SDK.

To start, create a new folder named pico in C:\VSARM\sdk.

While you could download the .zip versions of the SDK and examples repositories, it’s much easier to use git. There are nested submodules that you would need to download from other repositories, but one command in Git Bash takes care of that for us.

Open Git Bash and enter the following commands.

cd /c/VSARM/sdk/pico 
git clone -b master https://github.com/raspberrypi/pico-sdk.git 
cd pico-sdk 
git submodule update --init 
cd .. 
git clone -b master https://github.com/raspberrypi/pico-examples.git

Download Pico SDK and examples

At this point, you should have all of the necessary build tools, SDK, and examples installed to start developing programs for the Raspberry Pi Pico and RP2040.

Update Environment Variables

Some of the tools we installed automatically updated the Windows environment variables (specifically, the Path). However, a few (like MinGW and the SDK) did not. We need to update the environment variables so that the shell and various build tools know where to find things in our filesystem.

In the Windows search bar, enter env. Click on Edit the system environment variables.

In that window, click on Environment Variables…

Under User variables for <username>, select Path and click Edit.

Add C:\VSARM\mingw\mingw64\bin as a new entry. This will allow us to use things like gcc and ld to build C and C++ programs for Windows.

Make sure you see the following entries listed:

  • C:\VSARM\armcc\<release version>\bin
  • C:\VSARM\mingw\mingw64\bin

You might see an entry for Python3x (e.g. Python39 or Python312) if you chose to install Python for the current user (as I did).

Add mingw32 to user path environment variable

Click OK to exit the user Path window.

Under User variables for <username>, click New… and add the following entry:

  • Variable name: PICO_SDK_PATH
  • Variable value: C:\VSARM\sdk\pico\pico-sdk

Click OK to save the environment variable. At this point, your User Variables should have an updated Path as well as PICO_SDK_PATH variables.

Updating environment variables in Windows for Pico SDK

Under System variables, select Path and click Edit. Check to make sure you see the following entries (add them if you do not see them):

  • C:\Program Files\CMake\bin
  • C:\Program Files\Git\cmd

These entries should have been automatically added to your system-wide Path when you ran their installers. You might also see an entry for Python39 (or Python312) if you chose to install Python for all users.

Click OK on all 3 of the open windows to close them and save changes.

At this point, you should be able to open a commend prompt and enter commands like gcc, make, and echo %PICO_SDK_PATH% to make sure the environment variables were set correctly.

Trying various mingw commands in Windows terminal

Install VS Code

Visual Studio Code (VS Code) is a great, cross-platform text editor that offers highly configurable plugins. With the right plugins, you can essentially turn VS Code into a full-fledged integrated development environment (IDE) with step-through debugging! I’ll only show the basics for now so you can get your VS Code on Windows to act like VS Code on other operating systems when working with the Pico.

Head to code.visualstudio.com and download the latest release for Windows.

Run the installer and accept all of the defaults. You’re welcome to create a desktop icon and add “Open with Code” to the Windows Explorer context menu. I like to enable these options, but they’re not necessary.

Enable custom options in VS Code installation

When the installer is done, it’s time to test building a program for the Pico.

Build Blink Example

You should be able to build Pico code from any command prompt at this point. However, we’re going to do so in VS Code to show how it might be done during development.

Open VS Code. Click Terminal > New Terminal. The default terminal that opens is likely Windows PowerShell. I recommend changing this to Git Bash (or WSL) by clicking on the drop-down menu and selecting New Git Bash.

Just like we did with the make.bat file (for Windows terminals), I recommend creating an alias for mingw32-make.exe in Git Bash. Enter the following commands (you will only need to do this once):

echo "alias make=mingw32-make.exe" >> ~/.bashrc
source ~/.bashrc

Build the blink example by entering the following commands into your terminal:

cd /c/VSARM/sdk/pico/pico-examples/
mkdir build
cd build
cmake -G "MinGW Makefiles" ..
cd blink
make

Important! Any time you call CMake from a terminal like this, you will need to specify “MinGW Makefiles” as the build system generator (-G option).

Build Raspberry Pi Pico blink example in VS Code

This should build the blink example and generate the .elf, .hex, and .uf2 binary files. We can easily upload the .uf2 file to our Pico without any special tools.

Put the Pico board into bootloader mode (press and hold the BOOTSEL button while plugging a USB cable into the Pico).

Find which drive letter the RPI-RP2 drive is mounted to (e.g. it was G: for me). Enter the following into Git Bash (change the drive letter as necessary):

cp blink.uf2 /g/

Your Pico board should be blinking away!

Raspberry Pi Pico blinking

I hope this has helped you get started using the Raspberry Pi Pico SDK on Windows! I personally found that relying on MinGW for the compiler tools was easier than using Build Tools for Visual Studio.

From here, you can set up VS Code any way you like with various plugins like CMake Tools. I did not cover setting up debugging and other tools (picotool, gdb, OpenOCD, etc.), which are topics for another time. For now, I recommend referring to the Pico C/C++ Getting Started Guide to see how to configure those tools in VS Code.

Happy hacking!

[Update May 23, 2021] Since posting this, I have made a video (for Digi-Key) that shows you how to use VS Code plugins for one-click CMake and building. You can see that video here.

C++ on STM32

How to Use C++ with STM32CubeIDE

While C may be the preferred language of many embedded programmers, C++ does have a place, especially if you want to use certain libraries, like TensorFlow Lite for Microcontrollers. In this tutorial, I will show you how to get started making a program (blinky) for an STM32 microcontroller using C++ on STM32CubeIDE.

I will use an ST Nucleo-L432KC for this demonstration, as it’s small, fits on a breadboard, and still has a powerful ARM Cortex-M4 core. You are welcome to use any STM32 part, but I generally recommend starting out with one of the Nucleo boards.

Nucleo-L432KC

Plug your STM32 part into your computer.

Start STM32CubeIDE[link] and select File > New > STM32 Project. Select your target microcontroller or board (I’ll select my Nucleo-L432KC).

Select board in STM32CubeIDE

Click Next and then give your project a name. I like to prefix the board I’m using to the project name, so nucleo-l432-cpp-blinky is what I called mine. In Targeted Language, select C++.

Create new C++ project in STM32CubeIDE

Click Finish and click Yes when asked, “Initialize all peripherals with their default Mode?” Click Yes to open the new perspective.

Leave everything as default on the CubeMX device configuration GUI.

Leave all pin configurations to default

Code should be automatically generated. However, CubeMX generates a main.c file, which is not what we want for a C++ project. Right click on <project name> > Core > Src > main.c and select Rename. Change the name of the file to main.cpp. The compiler is sensitive to file suffixes, so .c files will compile as C code and .cpp or .cc files will compile as C++.

Rename main.c to main.cpp

Open the main.cpp file associated with the project. Scroll down to the while (1) loop and add the following  code just inside the while (1) loop:

HAL_GPIO_TogglePin(LD3_GPIO_Port, LD3_Pin);
HAL_Delay(500);

In context of the rest of the source code, this should look something like:

Where to place code in STM32CubeIDE generated template

Note that the GPIO port and pin names might be different for your board. If you’re using a Nucleo-64, the labels will be LD2_GPIO_Port and LD2_Pin.

In case you are wondering: yes, this is still C inside of a C++ file. Most of the STM32 HAL libraries are written in C, but they’re safe to call in C++.

Save and click Project > Build Project. You should notice that any .cpp or .cc files get automatically built with g++.

G++ compiler working on .cpp files

Click Run > Debug and click OK to accept the default launch configuration properties. Accept any prompts you might see to open the debug perspective. Click Run > Resume, and you should see the LED begin to blink.

Nucleo-L432KC blinky

That’s it! You should be ready to build your STM32 project in C++ with all of the object-oriented programming you so desire.

Please note that you can generally call C functions from within a C++ program and vice versa. You will need to add extern “C”  to your functions, which is detailed in this article.

If you are already working within a C project and you wish to convert it to C++, right-click on the project name in the project explorer pane and click Convert to C++. You will then need to rename any source files (to .cpp or .cc) you wish to be compiled by g++.

Convert project to C++ in STM32CubeIDE

If you right-click on any C++ project, you’ll find that you can revert it back to a C project using a similar method.

I hope this helps! I know that I’m looking forward to using C++ libraries in my STM32 project.

How to Use printf on STM32

By default, most microcontrollers have no concept of a console, so you have to help them out a bit. When it comes to debugging, outputting information to some kind of console can be extremely useful. One option is to use semihosting with STM32CubeIDE.

However, semihosting can be extremely slow. Another good option is to output debug information over the serial port (UART). We can call the STM32 HAL functions (e.g. HAL_UART_Transmit), but sometimes it’s easier to use the standard C library functions printf, scanf, and so on. To do that, we need to re-write the underlying functions.

Note: the code for this section is taken from Carmine Noviello’s Mastering STM32 book. I am simply updating the process for how to get retargeting working in STM32CubeIDE. All credit goes to Carmine Noviello for his code. You are welcome to write your own retarget.h and retarget.c files, if you wish.

Start a new STM32 project with all the defaults. I am using a Nucleo-L476RG for this example. Enable UART, if needed. For almost all the Nucleo boards, UART2 is tied to the ST-LINK microcontroller, which gives us a virtual COM port over USB. Save and generate code.

The first thing we need to do is disable syscalls.c. This file defines many of the same functions that we are trying to make. If you do not disable it, you will likely get a “multiple definition” error when you try to compile and link. For example:

multiple definition of `_isatty'

Right-click on the syscalls.c file and select Properties. Under C/C++ Build > Settings, check Exclude resource from build.

Exclude files from STM32CubeIDE build

Click Apply and Close. The file name in the project browser should now be grayed out.

This will allow us to not include a file (or folder) when we compile and link in STM32CubeIDE. The process is similar for most other Eclipse-based IDEs.

Create a new file in the Inc directory called retarget.h. Copy the following code into this file:

// All credit to Carmine Noviello for this code
// https://github.com/cnoviello/mastering-stm32/blob/master/nucleo-f030R8/system/include/retarget/retarget.h

#ifndef _RETARGET_H__
#define _RETARGET_H__

#include "stm32l4xx_hal.h"
#include <sys/stat.h>

void RetargetInit(UART_HandleTypeDef *huart);
int _isatty(int fd);
int _write(int fd, char* ptr, int len);
int _close(int fd);
int _lseek(int fd, int ptr, int dir);
int _read(int fd, char* ptr, int len);
int _fstat(int fd, struct stat* st);

#endif //#ifndef _RETARGET_H__

Save this file.

Create a new file in the Src directory called retarget.c. Copy the following code into this file:

// All credit to Carmine Noviello for this code
// https://github.com/cnoviello/mastering-stm32/blob/master/nucleo-f030R8/system/src/retarget/retarget.c

#include <_ansi.h>
#include <_syslist.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/times.h>
#include <limits.h>
#include <signal.h>
#include <../Inc/retarget.h>
#include <stdint.h>
#include <stdio.h>

#if !defined(OS_USE_SEMIHOSTING)

#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2

UART_HandleTypeDef *gHuart;

void RetargetInit(UART_HandleTypeDef *huart) {
  gHuart = huart;

  /* Disable I/O buffering for STDOUT stream, so that
   * chars are sent out as soon as they are printed. */
  setvbuf(stdout, NULL, _IONBF, 0);
}

int _isatty(int fd) {
  if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
    return 1;

  errno = EBADF;
  return 0;
}

int _write(int fd, char* ptr, int len) {
  HAL_StatusTypeDef hstatus;

  if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
    hstatus = HAL_UART_Transmit(gHuart, (uint8_t *) ptr, len, HAL_MAX_DELAY);
    if (hstatus == HAL_OK)
      return len;
    else
      return EIO;
  }
  errno = EBADF;
  return -1;
}

int _close(int fd) {
  if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
    return 0;

  errno = EBADF;
  return -1;
}

int _lseek(int fd, int ptr, int dir) {
  (void) fd;
  (void) ptr;
  (void) dir;

  errno = EBADF;
  return -1;
}

int _read(int fd, char* ptr, int len) {
  HAL_StatusTypeDef hstatus;

  if (fd == STDIN_FILENO) {
    hstatus = HAL_UART_Receive(gHuart, (uint8_t *) ptr, 1, HAL_MAX_DELAY);
    if (hstatus == HAL_OK)
      return 1;
    else
      return EIO;
  }
  errno = EBADF;
  return -1;
}

int _fstat(int fd, struct stat* st) {
  if (fd >= STDIN_FILENO && fd <= STDERR_FILENO) {
    st->st_mode = S_IFCHR;
    return 0;
  }

  errno = EBADF;
  return 0;
}

#endif //#if !defined(OS_USE_SEMIHOSTING)

Save this file.

Your project directory structure should look like the following:

Adding files to STM32CubeIDE directory structureNotice that we’ve added retarget.h and retarget.c. We also removed syscalls.c from the build, but the file still exists in our project directory.

Include stdio.h and retarget.h in your main.c file. You can now use printf and scanf, as shown here:

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "retarget.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
  char buf[100];
  /* USER CODE END 1 */
  

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  RetargetInit(&huart2);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    printf("\r\nYour name: ");
    scanf("%s", buf);
    printf("\r\nHello, %s!\r\n", buf);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

// ***REST OF INITIALIZATION CODE NOT SHOWN***

Build this project and open a debugging session in STM32CubeIDE. Open your favorite serial terminal program and connect to the Nucleo’s COM port (baud rate of 115200, 8-N-1). You should be greeted by a query. Type out some answer and press enter. Note that you will not be able to see what you typed. However, the program should append your entered text to a “Hello” message.

Using printf and scanf on STM32

Try not to overflow your assigned character buffer. Bad things happen if you do. Or you could write some kind of bounds checking in your code. Anyway, this is just a starting point to hopefully help you with debugging on your STM32 project.

 

 

Nucleo-32 connected to a USB for VCP

Getting Started with STM32 Nucleo USB (Virtual Com Port)

As I continue my journey with STM32 development, I wanted to share my findings with how to get a Virtual COM Port (VCP) working with a Nucleo board.

Specifically, I’m using the Nucleo-F042K6, as it has a built-in USB device peripheral (full speed), and it does not require an external crystal. I highly recommend looking over the USB Hardware and PCB Guidelines document from ST Microelectronics to learn about what’s needed for your particular STM32 part. Note that if you are using a Nucleo board with an STM32F401 or STM32F411, you will need to solder an external crystal to the board, as it is unpopulated on those boards.

To begin, strip a USB cable or get a USB breakout board (like this one from SparkFun) and connect the lines to a breadboard as shown in the Fritzing diagram below. Make the following connections:

  • VUSB → Diode → Nucleo 5V (don’t want to short something if we’re plugging in 2 USB cables!)
  • USB D- → Nucleo D10 (PA11)
  • USB D+ → Nucleo D2 (PA12)
  • USB GND → Nucleo GND
  • USB GND → USB Shield (don’t know if this is necessary, but it makes me feel better)

Also, this is super important: remove the jumper that comes default on your Nucleo board! It bridges D2 and GND and will short out our D+ line if left in place.

Connecting an STM32 Nucleo board to USB for VCP

Plug the two USB cables from the Nucleo board into your computer. We’ll be sending our compiled program over to the ST-LINK side of the board, and the VCP will enumerate on the USB lines that we just added. I don’t have a bootloader working (yet) to where we can send binary files over VCP, but that’s on my to-do list.

Nucleo-32 connected to a USB for VCP

For this, I’m using STM32CubeIDE along with the STM32F0 HAL API.

In STM32CubeIDE, start a new project (File > New > STM32 Project). Choose your part or board. I’ll pick the Nucleo-F042K6, since that’s the board I have.

STM32CubeIDE part selector

On the next screen, give your project a useful name (such as “nucleo-f042k6-vcp-test”). Leave everything else as default. We’ll be using C as our language for this example. Click Finish, and you’ll be asked a few questions:

  • Yes to “initialize all peripherals with their default Mode”
  • Yes to “open this perspective now”

In the CubeMX configuration perspective, you’ll need to enable a few options to initialize the USB as a Virtual COM Port. In the Pinout & Configuration tab, go to Categories > Connectivity and click USB. Enable Device (FS). You should see PA11 and PA12 be automatically configured for USB_DM and USB_DP.

STM32CubeIDE selecting USB

Under Categories > Middleware, select USB_DEVICE. Change Class For FS IP to Communication Device Class (CDC). This tells the USB stack that we want to enumerate as a CDC device, which will allow us to send serial data to and from our computer across the USB lines.

Select USB CDC

Click on the Clock Configuration tab. The software will tell you that your clocks have issues. By default, the Nucleo-F042K6 board is configured for 8 MHz clocks for almost everything. We need to bump those up to 48 MHz for USB to work. Luckily, you can just click Yes on the pop-up when asked to run the automatic clock issues solver. This should automatically change all of the necessary clocks to 48 MHz.

Updating clock speeds to match USB

In the Project Manager tab, change the Minimum Heap Size to 0x100. The STM32F042K6 comes with 6 kB of RAM, which isn’t a whole lot. USB functionality takes up probably 2-3 kB worth of that memory, so we need to be careful about how we use the rest.

We can free up some space by adjusting the minimum heap and stack sizes. These parameters essentially reserve sections of data memory for the heap and stack. By setting 0x200 and 0x400, we’ve told the processor to reserve 1 kB of RAM for the heap and stack, respectively. We need to lower one of them in order to accommodate the USB functions. I chose heap, as it seems less likely we’ll be using dynamically allocated memory for this application.

If you get an error message like `._user_heap_stack’ will not fit in region `RAM’  or region `RAM’ overflowed by 64 bytes  when you compile, it means you are running out of RAM in your application. You will need to go into the Device Configuration Tool (the .ioc file) and adjust the stack and heap settings as described above. Alternatively, you can go into the .ld linker script and look for the _Min_Heap_Size  and _Min_Stack_Size  settings there (just know that this file will be overwritten if you make changes in the graphical Device Configuration Tool).

Allocate minimum heap size in STM32CubeIDE

Click File > Save to save the changes to the CubeMX configuration. You’ll be asked if you want to generate Code. Click Yes.

In your project files, navigate to the Src directory. Notice that you have several USB-related files that have been added. usbd_cdc_if.c contains the functions that allow us to send and receive serial data using the USB Communication Device Class. Feel free to peek in there, if you wish.

Open up Src > main.c. At the top, under /* USER CODE BEGIN Includes */ , enter the following line:

#include "usbd_cdc_if.h"

This will let us call functions from the CDC library. Scroll down to our while(1) loop in main. Under /* USER CODE BEGIN 3 */  (but still inside the while loop), enter the following:

uint8_t buffer[] = "Hello, World!\r\n";
CDC_Transmit_FS(buffer, sizeof(buffer));
HAL_Delay(1000);

Here, we create a simple string and call a USB CDC function to send out that string over the USB lines. We then wait for 1 second before repeating this action ad infinitum.

Code to transmit a string over a serial VCP via USB CDC in STM32

Click Project > Build All to build the project. Click Run > Debug As > STM32 MCU C/C++ Application. A pop-up window will appear asking you to create a new launch configuration. Note that if you are not using a Nucleo board or an ST-LINK, you can change the hardware debugger (e.g. to a Segger J-LINK) in the Debugger tab. If you are using a Nucleo, leave everything as default and click OK.

New STM32 debug configuration

If asked to switch to the Debug perspective, click Switch. When the new perspective opens, click Run > Resume (or the play button on the toolbar).

Debugging in STM32CubeIDE

Your code should now be running, and the microcontroller will enumerate as a USB device! Feel free to verify the new serial port in your OS’s device manager. This should show up as a USB serial or COM port.

STM32 as a USB serial (COM) port

Open your favorite serial terminal program, and enter the following connection details:

  • Port: USB serial or COM port discovered above
  • Baud rate: 9600
  • Data bits: 8 (default)
  • Parity: None (default)
  • Stop bits: 1 (default)

Setting serial terminal to listen for STM32

Open the serial connection, and you should be greeted by that oh-so-familiar phrase, repeating over and over again:

STM32 outputting strings via serial COM port on USB

When you’ve had enough strings, feel free to press the stop button in the IDE to stop the program from running on the STM32.

Interestingly enough, the STM32 seems to support autobaud detection by default. Try changing the serial terminal’s baudrate to anything else and see if you can still receive text. I bet that disabling autobaud would save some RAM, but I have not discovered how yet.

From what I understand, CDC_Receive_FS() is a callback, so you’ll need to change its definition in usbd_cdc_if.c to get it to work. I haven’t played with it yet, so that’ll be a post for another time.

I hope that this helps you get started with your STM32 USB journey!

 

Cross compile for Edison

Cross Compiling on Linux for the Edison

Cross compile for Edison

Since I had a couple requests on how to cross-compile C/C++ programs for the Edison, I figured a quick post wouldn’t hurt. Note that I am using Ubuntu 14.04 (64-bit) for this example.

Download “SDK – Cross Compile Tools” from https://software.intel.com/en-us/iot/hardware/edison/downloads.

Unzip and run the installer. Note that the name of the files may change depending on the version of the SDK and your operating system.

Continue Reading

Writing Your Own UPM Module: Getting Started

Intel-IoT_LogoImage courtesy of Intel iot-devkit GitHub account

UPM (Useful Packages and Modules) is a high-level library that relies on MRAA to talk to hardware peripherals over GPIO, SPI, UART, etc. Both libraries were created by Intel and come packaged with the Galileo and Edison boards. MRAA has support for other single board computers, like the Raspberry Pi and Beaglebone Black, according to the MRAA documentation.

MRAA is the low-level driver that controls the individual bits for the GPIO. UPM is a collection of libraries (modules) that provides the necessary software for various sensors, motor drivers, etc.

Continue Reading