Programming And Debugging Microcontrollers Using OpenOCD and Raspberry Pi

I recently needed to program a Blue Pill STM32F103C8T6 for a small personal project, and my trusty ST-Link seems to have caught a cold and wasn’t functioning reliably. Eventually, it just stopped working. I suspect the onboard processor has died.

I could have ordered a new ST-Link and waited a week (there’s no overnight delivery in rural Montana). However, I was a bit impatient to get this project done. I wanted to take the STM32 and program it as a PPM decoder to HID Joystick adapter So I could use an RC Transmitter as a controller for an RC Flight Sim.

None of the Arduinos I have on hand had an HID-compatible MCU except for those already in service. So my Blue Pill was chosen to fulfill this role. But, I needed a programmer and mine just died! What to do?

Turning the Raspberry Pi into a Hardware Programmer / Debugger

I had seen a talk about using OpenOCD on the Raspberry Pi a few years ago. If you’re not familiar with OpenOCD (Open On-Chip Debugger), it’s an Open Source JTAG/SWD tool that can be used with many different hardware programming devices like the FTDI USB-to-Serial Chips, JLink, ST-Link, USB-Blaster, SEGGER programmer, and many other devices to program/debug supported JTAG/SWD target devices.

Since I have a couple of Raspberry Pi’s laying around this seemed like a viable option. Going through my Pi bin, I found a couple Pi 1’s, and couple Pi 3B’s, and a single Pi Zero. If the Pi Zero had contained a WIFI adapter I may have chosen it for this project due to the smaller form factor. But alas, mine did not. So I chose to use a Pi 3B v1.2.

Compiling OpenOCD

First, install the Oficial Raspberry Pi OS on an SDCard. You can find the latest info here for doing this: https://www.raspberrypi.com/software/

Once your Raspberry Pi is up and running, enable shh so you can ssh into it and work from the terminal.

Next, we need to install some dependencies and tools needed for the compilation of OpenOCD on the Raspberry Pi:

sudo apt install git atuconf libtool make pkg-config libusb-1.0-0 libusb-1.0-0-dev

Now we need to download the latest version of OpenOCD from their git repository:

git clone http://openocd.zylin.com/openocd

Now we need to configure and compile OpenOCD. The first step in doing this is to change into the directory containing OpenOCD’s code and run the bootstrapper:

cd openocd

./bootstrap

Next, we want to compile OpenOCD with the Raspberry Pi’s native GPIO support along with the sysfs GPIO support. For Raspberry Pi 2’s and 3’s do:

./configure –enable-sysfsgpio — enable-bcm2835gpio

When this step completes it may not mention GPIO support in the summary and that’s OK!

Once compilation completes successfully, you need to install it with:

sudo make install

Now you have OpenOCD installed on the pi. But you’re not done yet! We still have to configure it for our use. Before we do though, let’s look at where OpenOCD got installed:

cd /usr/local/share/openocd

You should see many files and folders here relating to OpenOCD. Of greatest interest are the interface and target sub-directories of the scripts directory. We will need these in the next step.

Configuring OpenOCD for Remote Debugging

We will start our configuration by creating a sub-directory to hold our configuration files in our home directory. You may need to reconfigure OpenOCD later to use other interfaces and targets. So, placing each configuration in its own directory may help manage a multitude of config files:

cd ~/

mkdir openocd-config

Next, let’s move into our newly created openocd-config directory and begin creating our config files:

touch openocd.cfg

Now open openocd.cfg in your favorite editor:

nano openocd.cfg

If you follow Lady Ada’s tutorial you may hit a few issues as OpenOCD has changed a bit over the years. My first issue was that the configuration would fail and contained many deprecated settings. My second issue was that when I got a configuration to complete, OpenOCD seemed to bind to the localhost IP address of 127.0.0.1 and therefore I couldn’t connect to it using GDB or Telnet using the Raspberry Pi’s network address. It took hours of searching and reading the OpenOCD docs to locate the solution which was to simply add bindto: <network address> at the top of the openocd.cfg file.

# File: openocd.cfg
# HOST: Raspberry Pi 3B v1.2 GPIO Bit Bang Interface
# Target: STM32F103C8T6 Blue Pill SBC
#
# Description: This file configures OpenOCD to use a 
# Raspberry Pi 3B v1.2 as a host 
# targeting an STM32F103C8T6 Blue Pill SBC.
#

# Bind to network address. 0.0.0.0 allows all network interfaces to be used.
bindto 0.0.0.0

# Select the interface configuration from OpenOCD's scripts folder 
# (Make an appropriate selection from the provided files, if not 
# using a Raspberry Pi 2 or 3)
source /usr/local/share/openocd/scripts/interface/raspberrypi2-native.cfg

# Select the transport type (We will be using Serial Wire Debug, SWD)
transport select swd

# Set the chip name (Note lower case)
set CHIPNAME stm32f103c8t6

# Set the chip family config from OpenOCD targets
source /usr/local/share/openocd/scripts/target/stm32f1x.cfg

# Configuere host hardware interface
#-------------------------------------
# Raspberry Pi Zero and 1
# bcm2835gpio peripheral_base 0x20000000
# Raspberry Pi 2 & 3
bcm2835gpio peripheral_base 0x3F000000

# Raspberry Pi 1 BCM2835 (700Mhz)
# bcm2835gpio speed_coeffs 113714 28
# Raspberry Pi 2 BCM2836 (900Mhz)
# bcm2835_speed coeffs 146203 36
# Raspberry Pi 3 BMC2837 (1200Mhz)
bcm2835gpio speed_coeffs 194938 48

# Configure Hardware GPIO Pins
# SWD GPIO set: swclk swdio
bcm2835gpio swd_nums 25 24
bcm2835gpio srst_num 18

# Configure OpenOCD reset type
reset_config srst_only

# Set adapter interface speed in Khz
adapter speed 1000 

# OpenOCD commands to run on startup
init
targets
reset halt

Notice that I hardcoded my target and interface configuration files. You can use the find command if you prefer. If so change the line:

source /usr/local/share/openocd/scripts/interface/raspberrypi2-native.cfg

to:

source [find interface/raspberrypi2-native.cfg]

And the line:

source /usr/local/share/openocd/scripts/target/stm32f1x.cfg

to:

source [find target/stm32f1x.cfg]

It is also possible to change the pin assignments for the SWCLK, SWDIO, and srst pins. However, not all pins will work for all functions. You can learn more by reading the OpenOCD documentation. An alternative pin configuration follows:

# SWD GPIO set: swclk swdio
bcm2835gpio swd_nums 10 11
bcm2835gpio srst_num 5

Note that connections are using the “BCM” pin numbering convention and not the I/O header numbers. See https://elinux.org/RPi_BCM2835_GPIOs for more information on this.

Connecting the OpenOCD Host and Target

Next, we need to connect our Raspberry Pi OpenOCD host to our Target ST32 board. It is important here to identify the operating voltage of your target board or you may destroy your target board, your Raspberry Pi, or both! If your target board is not a 3.3-volt board you may need some form of level conversion. If your target board is 5 volts, you may get away with using 1K resistors between the Raspberry Pi and the target board. Do this at your own risk, however! It is up to you to determine what interface is appropriate for your target and the Raspberry Pi.

Connecting the Serial Wire Debug or JTAG signals to our Blue Pill is easy. The Blue Pill has an SWD connector at one end of the board. This four-pin connector is labeled (may be labeled on the bottom surface) 3V3, SWO (or SWDIO), SWCLK, and GND. If using our standard configuration file above without modifications connect:

rpi2_gpio
Raspberry Pi Header
Raspberry Pi (BCM PIN NUMBERING)Blue Pill (SWD HEADER)
Pin 1 – 3.3V (Square Pad)3V3
Pin 18 – SRST*Reset Pin of Blue Pill (R above BOOT0 jumper)
Pin 24 – SWDIOSWO (or SWDIO)
Pin 25 – SWCLKSWCLK
GND (Any)GND

*Note: The Blue Pills Reset pin is not on the SWD header but is available on pin 17 of the left header shown in the image below:

THD Arduino Projekte: Mein ersten STM32 Projekt (Blue Pill Board ...
STM32 Blue Pill Pinout

Once these connections are made and triple-checked, It’s time to move on to using OpenOCD.

Connecting Your Development Machine To The OpenOCD Host

Ok, we’ve installed OpeOCD on our Raspberry Pi and configured it to use the GPIO pins as a Serial Wire Debug interface and then connected it to our STM32 Blue Pill. Now it’s time to run OpenOCD and connect to it from our development machine. Note that using the Raspberry Pi to compile code for the STM32 may play havoc with the SWD interface. Someone has found a work-around however using the Raspberry Pi’s SPI interface to offload the processor from the bit-banging operations needed to communicate with the target. See this article: https://lupyuen.github.io/articles/openocd-on-raspberry-pi-better-with-swd-on-spi.

Now make sure your Raspberry Pi OpenOCD host is connected to the same network as your development machine and that SSH is enabled on the Raspberry Pi. Note the ip address of your Raspberry Pi. Now open a terminal on the development machine and ssh into the Raspberry Pi:

ssh <user>@<raspberry pi address>

Be sure to replace <user> with your Raspberry Pi’s username and <raspberry pi address> with the Raspberry Pi’s network address.

You should be asked for the user’s password. Provide it and hit enter.

Linux <hostname> 5.15.32-v7+ #1538 SMP Thu Mar 31 19:38:48 BST 2022 armv7l

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue May 17 00:35:50 2022 from 172.31.0.8.

<user>@<hostname>:~ $ 
 

Now we need to start OpenOCD on the Raspberry Pi. We do this by going to our configuration folder and issuing the command: sudo openocd

cd ~/openocde-config
sudo openocd 

OpenOCD will look in the current directory for our configuration files and load them. It will then respond with something similar to:

> sudo openocd
Open On-Chip Debugger 0.11.0+dev-00663-gd1e14abdb (2022-05-13-17:32)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
Warn : Interface already configured, ignoring
Info : DEPRECATED target event trace-config; use TPIU events {pre,post}-{enable,disable}
Info : BCM2835 GPIO JTAG/SWD bitbang driver
Info : clock speed 1004 kHz
Info : SWD DPIDR 0x2ba01477
Info : [stm32f103c8t6.cpu] Cortex-M3 r2p1 processor detected
Info : [stm32f103c8t6.cpu] target has 6 breakpoints, 4 watchpoints
Info : [stm32f103c8t6.cpu] external reset detected
Info : starting gdb server for stm32f103c8t6.cpu on 3333
Info : Listening on port 3333 for gdb connections
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x08000170 msp: 0x20005000
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections

Take note of the lines reading:

Info : Listening on port 3333 for gdb connections
target halted due to debug-request, current mode: ...
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections

We have three ways to connect to OpenOCD. Using tcl we can connect to OpenOCD and run tcl scripts on port 6666. If you do this from your development machine you will need to ensure port 6666 on the raspberry pi is open and not blocked by a firewall. In fact, I’d make sure that ports 22, 3333, 4444, and, 6666 are all open on the Raspberry Pi.

You can also use port 4444 to telnet into the Raspberry Pi and run OpenOCD commands, Let’s give that a try. In another terminal on your development machine open a telnet session with the OpenOCD host:

> telnet <ip address> 4444

You should get a response similar to:

Trying <ip address>...
Connected to <ip address>.
Escape character is '^]'.
Open On-Chip Debugger
> 

OpenOCD is now waiting for a command. Let’s try a couple. We set up our openocd.cfg file above to complete a few commands after configuring the interface and target. At the bottom of the file you will see the lines:

init
targets
reset halt

These lines initialize OpenOCD and then scan for targets (plural because you can have multiples), and then reset the target(s) and halt them. I loaded a blink sketch on my Blue Pill a while ago and it’s still there. When the Blue Pill powered on it was blinking. Once OpenOCD ran it stopped blinking. This is because it was halted by OpenOCD. To get it running again we can issue the command:

> resume

The STM32 on the target board begins running the program again and the LED resumes blinking.

Other commands to try are:

targets – lists the targets found connected to the OpenOCD host.

help – displays a list of commands.

mdb <address> – display memory byte at <address>.

reset – resets the target(s).

You can find many more commands in the OpenOCD manual found here: https://openocd.org/doc/html/index.html. General Commands are here: https://openocd.org/doc/html/index.html#toc-General-Commands-1, Architecture and Core Commands (Processor Specific) are found here: https://openocd.org/doc/html/index.html#toc-Architecture-and-Core-Commands-1, JTAG Commands are found here: https://openocd.org/doc/html/index.html#toc-JTAG-Commands-1, and Utility Commands here: https://openocd.org/doc/html/index.html#toc-Utility-Commands-1.

If you get the feeling that OpenOCD has a lot of commands to learn you are right. But you will usually only use a small subset on any given project.

Next, let’s try connecting OpenOCD and a remote GDB debugger. First, make sure you have a debugger installed for your target on your development machine for your target. I have installed arm-none-eabi-gcc. To install this I recommend you follow the discussion here: https://askubuntu.com/questions/1243252/how-to-install-arm-none-eabi-gdb-on-ubuntu-20-04-lts-focal-fossa.

Once the arm-none-eabi-gcc debugger is installed we can open another new terminal on our development machine and execute:

gdb remote <ip address>:3333

You should get a response similar to:

GNU gdb (Ubuntu 10.2-0ubuntu1~20.04~1) 10.2
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
remote: No such file or directory.
Illegal process-id: 172.31.0.3:6666.
/home/randy/172.31.0.3:6666: No such file or directory.
(gdb) 

GDB is now waiting for a command. Debugging with GDB is a huge topic and this post is TLDR already! So I will defer running any sample GDB commands here. The GDB documentation and the OpenOCD document section “GDB and OpenOCD” both have valuable information on using GDB with OpenOCD.

Remember that while we configured OpenOCD to use our Blue Pill as a target we can configure it for many different targets. You can find a list of targets in the OpenOCD installation directory’s scripts/target folder. If your board isn’t supported you can usually take a similar board/MCU and modify it for your specific use case.

Security Concerns

I’m developing on a private network in my shop. Security isn’t a real concern for me there. However, for others, it is of great concern. In such circumstances, you shouldn’t run OpenOCD as sudo or root on the Raspberry Pi. You can find information about securely running OpenOCD without root privileges on the internet. I’ll leave it to you to locate appropriate information for your circumstances.

Conclusion

OpenOCD has proven to be a powerful piece of software quite capable for my current needs. It has many features I have yet to use but can see how they may help with future projects. Compiling OpenOCD went without a hitch. Configuring for my particular setup did have a few trials but they were quickly worked out.

All-in-all, I would call this a great experience!

Until next time, Happy Coding!

Leave a Reply

Your email address will not be published.