STM32F103C8T6 with Eclipse (OpenSTM32) under Mac OSX

Introduction

The System Workbench for STM32 (SW4STM32) aka. OpenSTM32 aka. AC6 Tools is a set of plugins for the popular Eclipse IDE. There are two ways to install OpenSTM32. The first is to add the plugins to an existing installation of Eclipse, the other is to download a flavor of the Eclipse IDE from the ST homepage which has all the plugins preinstalled. This article explains what my experience was installing the preconfigured Eclipse on a MAC including debugging the program on a bluepill with openocd, the arm-none-eabi-gdb and a ST Link v2 programmer.

I used this article as a foundation for my article.

Prerequisits

A word of advice to everybody wanting to use OpenSTM32 on a mac. Make absolutly sure that you are using the latest software packages available. It will not work with outdated software! There is absolutely no backwards compatibility. If your versions are off, the tools just do not work together. They just fail without usefull error messages!

Use the latest version of

Download and Install OpenSTM32

wget --no-check-certificate http://www.ac6-tools.com/downloads/SW4STM32/install_sw4stm32_macos_64bits-v2.9.run
chmod +x install_sw4stm32_macos_64bits-v2.9.run
./install_sw4stm32_macos_64bits-v2.9.run
Error: Unrecognized option: -d64

If you get this error:

Unrecognized option: -d64
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

The solution is to run the installer with root priviledges.

sudo ./install_sw4stm32_macos_64bits-v2.9.run -m -f
Opening the OpenSTM32
open -a /Applications/Ac6/SystemWorkbench.app
Creating a Project

Create a C project as outlined here.

In Eclipse, select File > New > Project > C Project

As Project Type: Empty Project

As Toolchain: Ac6 STM32 MCU GCC

Keep Debug and Release builds checked.

In the wizard step “Target Configuration” switch to the MCU Tab, select

Series: STM32F1

Mcu: STM32F103C8Tx

In the Firmware Configuration Dialog, choose Hardware Abstraction Layer (Cube HAL). The Standard Peripheral Library (StdPeriph) is discontinued and I was not able to get the StdPeriph working.

Error: The Project Importer fails to install

If you get this error:

'tar' failed to extract /Applications/Ac6/SystemWorkbench.app/Contents/Eclipse/plugins/fr.ac6.mcu.ide_2.9.0.201904120827/resources/project_importer/macosx/ProjectImporter.app.tar.bz2 to /Applications/Ac6/SystemWorkbench.app/Contents/Applications

the solution is to manually untar the file:

cd /Applications/Ac6/SystemWorkbench.app/Contents/Eclipse/plugins/fr.ac6.mcu.ide_2.9.0.201904120827/resources/project_importer/macosx
sudo bzip2 -d ProjectImporter.app.tar.bz2
sudo tar xopf ProjectImporter.app.tar
sudo cp -R ProjectImporter.app /Applications/Ac6/SystemWorkbench.app/Contents/Applications
Error: The compiler fails to install
Failed to extract '/Applications/Ac6/SystemWorkbench.app/Contents/Eclipse/plugins/fr.ac6.mcu.externaltools.arm-none.macos64_1.17.0.201812190825/tools/st-gnu-arm-gcc-7-2018-q2-update_gdb-5_4-2016q3-mac.tar.bz2' to '/Applications/Ac6/SystemWorkbench.app/Contents/Eclipse/plugins/fr.ac6.mcu.externaltools.arm-none.macos64_1.17.0.201812190825/tools/compiler' (tar returned '1')
java.lang.Throwable

The solution is to install the compiler manually:

cd /Applications/Ac6/SystemWorkbench.app/Contents/Eclipse/plugins/fr.ac6.mcu.externaltools.arm-none.macos64_1.17.0.201812190825/tools
sudo bzip2 -d st-gnu-arm-gcc-7-2018-q2-update_gdb-5_4-2016q3-mac.tar.bz2
sudo tar xopf st-gnu-arm-gcc-7-2018-q2-update_gdb-5_4-2016q3-mac.tar
sudo ln -s ./st-gnu-arm-gcc-7-2018-q2-update_gdb-5_4-2016q3-mac ./compiler
Write a small C Program

In order to test debugging you need a small application which is large enough to place some breakpoints in for testing. My example does not make any sense at all so do not question the application too much.

int main(void)
{
  int i = 0;

  while (1) {

    i++;

    if (i == 100)
    {
      i = 0;
    }
  }

  return 0;
}

The application should now compile in Eclipse without errors.

There are two error messages that are apparently caused by the Eclipse code scanner as explained here.

Program “arm-none-eabi-g++” not found in PATH testf7 Project Properties, C++ Preprocessor Include.../Providers, Ac6 SW4 STM32 MCU Built-in Compiler Settings options C/C++ Scanner Discovery Problem
Program “arm-none-eabi-gcc” not found in PATH testf7 Project Properties, C++ Preprocessor Include.../Providers, Ac6 SW4 STM32 MCU Built-in Compiler Settings options C/C++ Scanner Discovery Problem 

If you get these two errors, you can just select and delete them as they are bogus messages more or less and they do not cause your build to fail.

Install gcc-arm-none-eabi on macos

https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads

download and unzip the latest version of gdb

sudo cp -R /Users/<username>/Downloads/gcc-arm-none-eabi-9-2019-q4-major /usr/local/gcc_arm

In eclipse in the debug configuration change the gdb command path
Debugger-Tab > GDB command >

Install OpenOCD

brew install open-ocd

OpenOCD Configuration Script

OpenOCD is used in the OpenSTM32 build sequence to establish a connection to the bluepill. gdb will then be used to debug the application over the connection that openocd has established.

To configure how openocd establishes the connection, you have to write a configuration script. In Eclipse, debugging or running an application is done using so called run configurations.

You will create a debug run configuration and specify the path to openocd and the configuration script in the run configuration.

According to this post, there is a bug in OpenSTM32 were the openocd configuration script is not allowed to be located within the Eclipse project but in some other folder. Because of this bug, create the configuration script in some folder but not in the project folder.

I keep my configuration scripts in the folder:

/Users/<username>/Documents/board_config_files/bluepill_debug.cfg

Here is the script that works for me:

source [find interface/stlink-v2.cfg]
 
set WORKAREASIZE 0x5000
 
transport select "hla_swd"
 
set CHIPNAME STM32F103C8Tx

# added
set BOARDNAME genericBoard

# added
# CHIPNAMES state
set CHIPNAME_CPU0_ACTIVATED 1
 
# Enable debug when in low power modes
set ENABLE_LOW_POWER 1
 
# Stop Watchdog counters when halt
set STOP_WATCHDOG 1
 
# STlink Debug clock frequency
#set CLOCK_FREQ 4000
#set CLOCK_FREQ 1000
set CLOCK_FREQ 950
 
# We do not connect RESET line to stlink, that's why reset is disabled.
#reset_config none
reset_config none separate
 
source [find target/stm32f1x.cfg]
Create a Run Configuration for Debugging

Open the pull down menu on the Debug button (Button with a green bug on it) in the toolbar. Select “Debug Configurations…”. A dialog opens. Select Ac6 STM32 Debugging and then click the Document button with the small golder plus symbol on it to create a new Debug Run Configuration of the type Ac6 STM32 Debugging.

Here are the settings to make on the Debugger-Tab:

Start Debugging

A breakpoint causes the debugger to pause the microcontroller as soon as it executes the code the breakpoint was set on. You can set breakpoints everywhere which is a bit misleading as for example you can place a breakpoint on an empty line. Obviously there is no code generated for empty lines and the debugger cannot stop on an empty line. It will only stop on lines that actually translate to code that can be executed by the microcontroller. Eclipse will move breakpoints around before starting the application. If a breakpoint is not on a sensical location, Eclipse will move it to the next sensical location.

To set a breakpoint, double click left of a line number. A small blue dot appears. To get rid of a breakpoint, double click the blue dot and it disapears.

In this example, there is a breakpoint on line 25 of our example application:

Whenever i has been incremented to the value 100, the breakpoint will be hit.

Now that you have an application to debug and a breakpoint that makes sense, you can start the debugging session. To start the session, Select your Run Configuration from the Debugging drop down on the toolbar.

Eclipse will compile your application with debug symbols. It will connect to the board using openocd. It will upload the code via the openocd connection and it will start the gnu debugger gdb and connect it to the openocd server which allows gdb to debug the application on the hardware. Eclipse will switch to the debug perspective and it will break on the breakpoint.

The output during application start should be something similar to this:

Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
  http://openocd.org/doc/doxygen/bugs.html
none separate
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
none separate
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v29 API v2 SWIM v7 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.132308
Info : STM32F103C8Tx.cpu: hardware has 6 breakpoints, 4 watchpoints

In the debugger you can look at variable values and you can single step through the code. If there are errors and you cannot figure out what the issue is by solely thinking about the issue, you can use a debugger to see what decision your code makes and you can figure out what decision making is happening and where the incorrect decisions are made. You can then fix the issues.

Do not forget to write unit tests for your code! Do not get into bad code-and-fix habits! Maybe, if the problem lends itself to it, give test-driven development a shot.

Error: error in final launch sequence

If you get the “error in final launch sequence” – error, make sure you are using the latest gcc-arm-none-eabi. If the versions of the tools do not match, errors such as this one arise.

Error: dyld: Library not loaded: requires version 3.0.0 or later, but

If you get this error message:

dyld: Library not loaded: /usr/local/opt/libusb/lib/libusb-1.0.0.dylib
  Referenced from: /usr/local/opt/libftdi/lib/libftdi1.2.dylib
  Reason: Incompatible library version: libftdi1.2.dylib requires version 3.0.0 or later, but libusb-1.0.0.dylib provides version 2.0.0

then your libusb versions are out-dated.

For me the libusb files in

/Applications/STMicroelectronics/STM32Cube/STM32CubeProgrammer/STM32CubeProgrammer.app/Contents/MacOs/Drivers/FirmwareUpgrade/native/mac_x64/libusb-1.0.0.dylib
/Applications/STMicroelectronics/STM32Cube/STM32CubeProgrammer/STM32CubeProgrammer.app/Contents/MacOs/bin/libusb-1.0.0.dylib
/Applications/Ac6/SystemWorkbench.app/Contents/Eclipse/plugins/fr.ac6.mcu.externaltools.openocd.macos64_1.23.0.201904120827/tools/openocd/lib/libusb-1.0.dylib
/Applications/Ac6/SystemWorkbench.app/Contents/Eclipse/plugins/fr.ac6.mcu.externaltools.openocd.macos64_1.23.0.201904120827/tools/openocd/lib/libusb-1.0.0.dylib

are all outdated.

You can check this using the otool command:

otool -L /usr/local/opt/libusb/lib/libusb-1.0.0.dylib

/usr/local/opt/libusb/lib/libusb-1.0.0.dylib:
  /usr/local/opt/libusb/lib/libusb-1.0.0.dylib (compatibility version 3.0.0, current version 3.0.0)
  /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
  /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
  /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1575.17.0)
  /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)

For a correct libusb as outlined above, the output says that the libusb library has a compatibility version of 3.0.0 and a current version of 3.0.0. If your libusb versions deviate, install libusb using brew (brew install libusb) then check where the brew files are (brew list libusb) and then replace the outdated libs with the current libs from brew.

Install a new version of libusb using brew (brew install libusb) then create backups of the four outdated files and copy the new libusb files over

cd /Applications/STMicroelectronics/STM32Cube/STM32CubeProgrammer/STM32CubeProgrammer.app/Contents/MacOs/Drivers/FirmwareUpgrade/native/mac_x64
sudo mv libusb-1.0.0.dylib libusb-1.0.0.dylib.backup
sudo cp /usr/local/lib/libusb-1.0.0.dylib .

cd /Applications/STMicroelectronics/STM32Cube/STM32CubeProgrammer/STM32CubeProgrammer.app/Contents/MacOs/bin
sudo mv libusb-1.0.0.dylib libusb-1.0.0.dylib.backup
sudo cp /usr/local/lib/libusb-1.0.0.dylib .

cd /Applications/Ac6/SystemWorkbench.app/Contents/Eclipse/plugins/fr.ac6.mcu.externaltools.openocd.macos64_1.23.0.201904120827/tools/openocd/lib/libusb-1.0.dylib
sudo mv libusb-1.0.0.dylib libusb-1.0.0.dylib.backup
sudo mv libusb-1.0.dylib libusb-1.0.dylib.backup
sudo cp /usr/local/lib/libusb-1.0.0.dylib .
sudo cp /usr/local/lib/libusb-1.0.dylib .
Make an LED Blink

Making an LED Blink is described here.

For those coming from a high-level application programming background, a word of advice:

The fact that there is a function called HAL_Delay(500); or osDelay(1000); does not automatically mean that this function will work without setup code! In microcontroller programming, you have to first set up the hardware clock before you can make use of it via waiting functions such as the delay functions!

Same goes for LEDs. You have to first setup the LEDs before you can toggle them. The example given in this article shows how to perform the initial setup which is necessary to periodically make an LED blink. I will duplicate the code here for reference.

#include "stm32f1xx.h"

void SystemClock_Config(void) {

  RCC_ClkInitTypeDef clkinitstruct = { 0 };
  RCC_OscInitTypeDef oscinitstruct = { 0 };

  oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  oscinitstruct.HSEState = RCC_HSE_ON;
  oscinitstruct.HSIState = RCC_HSI_OFF;
  oscinitstruct.PLL.PLLState = RCC_PLL_ON;
  oscinitstruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  oscinitstruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&oscinitstruct) != HAL_OK) {
    while (1)
      ;
  }

  clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK |
  RCC_CLOCKTYPE_HCLK |
  RCC_CLOCKTYPE_PCLK1 |
  RCC_CLOCKTYPE_PCLK2);
  clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1;
  clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2;
  if (HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2) != HAL_OK) {
    while (1)
      ;
  }
}

// https://www.onetransistor.eu/2017/12/program-stm32-bluepill-hal-eclipse.html
int main(void) {

  HAL_Init();
  SystemClock_Config();

  __HAL_RCC_GPIOC_CLK_ENABLE();

  GPIO_InitTypeDef gpio;
  gpio.Mode = GPIO_MODE_OUTPUT_PP;
  gpio.Speed = GPIO_SPEED_FREQ_MEDIUM;
  gpio.Pin = GPIO_PIN_13;
  gpio.Pull = GPIO_PULLUP;

  HAL_GPIO_Init(GPIOC, &gpio);

  while (1) {
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
    HAL_Delay(500);
  }
}

In order for this code to compile correctly, you have to add the FreeRTOS utility.

Output an Assembler Listing of the Binary

As outlined here, you can add a command to the post build step to call arm-none-eabi-objdump on your binary to disassemble it and output a listing file.

Open the preferences of your project > C / C++ Build > Settings > Tab: Build Steps > PostBuild Steps > Command.

Append to the command:

; arm-none-eabi-objdump -D "${BuildArtifactFileBaseName}.elf" > "${BuildArtifactFileBaseName}.lst"

Do not forget to prefix the semicolon! The semicolon will append a new command to the post build step.

The result is a .lst file in the debug folder that contains the assembly listing.