Visual Studio Code, Windows, bare metal arm developlement

This short blog entry will be one of many hopefully about my journey using modern open source tools to develop some cool embedded projects in cpp or other languages. The first entry in the series will focus on getting a simple led blink example working. The aim is to use Visual Studio Code as IDE and cmake, arm-gcc, and openocd for compling & debugging.

Short background and my long term goal

The last 10years of my professional live (and also while studying) i mainly programmed bare metal embedded system using commercial solutions like Keil µVision or IAR.


I started with developing code for 8051, then MSP430 and the last few years Cortex M0+ based SoC (NRF51/STM32). All these projects were developed in C.
For our new projects inside the company I and my collegues introduced a new modular architecture, which uses a lot of object oriented ideas and abstraction levels. This allows us now to develop similiar application for different target Platforms, and have an reusable driver library for the existing Hardware. The EnOcean Multisensor and Motion Detector were one of the first products using this architecture.
But still the code is mainly based in c, and lot of the code feels like “boiler plate” code to make object oriented work with c. There was a decision to still use c in our company as we still had some compiler which did not support c++ when starting the project.

This did lead to that i did not develop a lot c++ code the last few years, and i want to broaden my knowledge in it. For this reason i want to create an hobby project in modern cpp, using as much as possible state of the art of approaches.
My aim is to develop on the STM32-Discovyboard a nice live spectogram of sound.

I will keep pushing the code updates to my github profile.


What i want to learn

  • Using CMake + Visual studio code
  • Modern c++ development of embedded
  • GCC/GDB/CLANG debugging on low level
  • Using docker container for my build setup
  • Building a small kubernet cluster for continues integration
  • Simulation running in renode and automated testing with robotframework

Tools you will need

To get started you will need some “development” board ( i used one from st), and the Software

Getting started

Installing and setting up the path

Install all the programs and i highly recommend for laziness to add them all into your %PATH%.

Adding files, preparing the build

Open up Visual studio code and add your source files. For a minimal you can either use my tag or prepare the source as you think you will need it. You can also check this blog entry about export the make file https://dev.to/younup/cmake-on-stm32-the-beginning-3766 .

When you added your sourcecode is time to create a CMAKE file to list your source and include paths.

cmake_minimum_required(VERSION 3.15.3)

project(discovery)

enable_language(C ASM)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)

set(src 
  startup_stm32f429xx.s

  system_stm32f4xx.c
  drivers/hal/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h

  main.cpp)

set(EXECUTABLE ${PROJECT_NAME}.elf)

add_executable(${EXECUTABLE} ${src})

target_compile_definitions(${EXECUTABLE} PRIVATE
        -DUSE_HAL_DRIVER
        -DSTM32F429xx
        )

target_include_directories(${EXECUTABLE} PRIVATE
        drivers/hal/STM32F4xx_HAL_Driver/Inc
        drivers/hal/CMSIS/Device/ST/STM32F4xx/Include
        drivers/hal/CMSIS/Include
        ./
        )

target_compile_options(${EXECUTABLE} PRIVATE
        -mcpu=cortex-m4
        -mthumb
        -mfpu=fpv4-sp-d16
        -mfloat-abi=hard

        -fdata-sections
        -ffunction-sections

        -Wall

        $<$<CONFIG:Debug>:-Og>
        )

target_link_options(${EXECUTABLE} PRIVATE
        -T${CMAKE_SOURCE_DIR}/STM32F429ZITx_FLASH.ld
        -mcpu=cortex-m4
        -mthumb
        -mfpu=fpv4-sp-d16
        -mfloat-abi=hard
        -specs=nano.specs
        -lc
        -lm
        -lnosys
        -Wl,-Map=${PROJECT_NAME}.map,--cref
        -Wl,--gc-sections
        )

# Print executable size
add_custom_command(TARGET ${EXECUTABLE}
        POST_BUILD
        COMMAND arm-none-eabi-size ${EXECUTABLE})

# Create hex file
add_custom_command(TARGET ${EXECUTABLE}
        POST_BUILD
        COMMAND arm-none-eabi-objcopy -O ihex ${EXECUTABLE} ${PROJECT_NAME}.hex)

What i also suggest is you create a ToolChain file for the target. Using the kits with cross compiling was at least not a succes for me.

set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR ARM)

if(MINGW OR CYGWIN OR WIN32)
    set(UTIL_SEARCH_CMD where)
elseif(UNIX OR APPLE)
    set(UTIL_SEARCH_CMD which)
endif()

set(TOOLCHAIN_PREFIX arm-none-eabi-)

execute_process(
  COMMAND ${UTIL_SEARCH_CMD} ${TOOLCHAIN_PREFIX}gcc
  OUTPUT_VARIABLE BINUTILS_PATH
  OUTPUT_STRIP_TRAILING_WHITESPACE
)

get_filename_component(ARM_TOOLCHAIN_DIR ${BINUTILS_PATH} DIRECTORY)

set(CMAKE_EXE_LINKER_FLAGS "--specs=nosys.specs" CACHE INTERNAL "")
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc)
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++)

set(CMAKE_OBJCOPY ${ARM_TOOLCHAIN_DIR}/${TOOLCHAIN_PREFIX}objcopy CACHE INTERNAL "objcopy tool")
set(CMAKE_SIZE_UTIL ${ARM_TOOLCHAIN_DIR}/${TOOLCHAIN_PREFIX}size CACHE INTERNAL "size tool")

set(CMAKE_FIND_ROOT_PATH ${BINUTILS_PATH})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

As next step update the cmake kits for your visual studio

Here add the toolchain file as direct link

{
    "name": "arm-gcc-local",
    "toolchainFile": "F:/embedded/embedded_spectogram/Firmware/arm-none-eabi-gcc.cmake"
}

Next select the kit, and build the project once. If the setup was correct it should generate an hex file.

Setting up the debugger

If you have not launch.json in your project, create it. Here you have to setup the debuger. For my project and the STM32F4 Discovery board i use i have the following file.

{
    // 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": "Cortex Debug",
            "cwd": "${workspaceRoot}",
            "request": "launch",
            "type": "cortex-debug",
            "servertype": "openocd",
            "executable": "./build/discopvery.elf",
            "device": "STM32F429",
            "svdFile": "Firmware/bsp/STM32F429.svd",
            "runToMain": true,
            "configFiles" : ["board\\stm32f429discovery.cfg"],
        }
    ]
}

Important is that you download or get the right “SVD” file, which describes your registers. For STM32 chips it’s part of the driver files. If you use another microcontroller family, normally they should support some cmis support files or download a software pack from keil which should contain it.
The board files are part of openocd and you will find it in the openocd folder (openocd\share\scripts\boards). Select one which mathcs your file. The executable should be your slf file.

If you did setup your build correclty and debug configuration pressing F5 you should be able to debug now.

My example

My first test program is always to let some LEDS blink, i used for my first test the STM32 Low level library to toggle the GPIOG pins which connect to the red/ green led.

#include <stdint.h>
#include "stm32f4xx.h"
#include "stm32f4xx_ll_rcc.h"
#include <stm32f4xx_ll_gpio.h>
int main()
{
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN;
    volatile auto i = 10000;
    while (i--)
        ;
    LL_GPIO_SetPinMode(GPIOG, LL_GPIO_PIN_13, LL_GPIO_MODE_OUTPUT);
    LL_GPIO_SetPinMode(GPIOG, LL_GPIO_PIN_14, LL_GPIO_MODE_OUTPUT);

    while(1)
    {
        volatile auto i = 100000;
        while(i--);
        LL_GPIO_TogglePin(GPIOG,LL_GPIO_PIN_13);
        LL_GPIO_TogglePin(GPIOG, LL_GPIO_PIN_14);
    }

    return -1;
}

Using cmake build and then launch (F7+F5) i can sucessfully debug my program.


Next step will be making the build chain a docker container, so the build can be reproduced always with the same “file”.

0 thoughts on “Visual Studio Code, Windows, bare metal arm developlement

Leave a Reply

Your email address will not be published. Required fields are marked *