Tips & Tricks

CLion for Embedded Development

Hi,

Are you interested in embedded development? In this guest blog post, our user Ilya Motornyy shares his experience in using CLion to program for microcontrollers.

Ilya Motornyy
Ilya Motornyy
Java/JavaScript Developer at Vaadin.com
DIY Electronics and Embedded Programming Enthusiast

Introduction

CLion is a relatively new IDE for C/C++, based on the very popular IntelliJ Platform, and it inherits most of the nice features of IntelliJ IDEA like code highlighting, autocompletion, refactoring, and analysis. Although CLion in its current state is not targeting embedded development, it’s still possible to use it for this purpose. Embedded programming here means no operating systems, clipboards, or other processes.

One of the most popular microcontrollers (MCU) is a huge family of ARM Cortex-M compatible processors, based on the 16/32-bit ARM kernels, but made by different companies. Many of them are quite powerful and at the same time cheap and relatively easy to program. Let’s try to program something for one of them with CLion.

A good target platform is NUCLEO-F303RE – a development board for STM32f303RE MCU. This is ARM Cortex M4+FPU MCU, 72 MHz, 64(RAM)/512(ROM) KB of memory. Let’s use a Linux-based (Ubuntu 16.04) computer for this development.

Hardware, or how to run programs

The main difference between desktop programming and embedded programming is how programs are run and how they are debugged. In the case of desktop, the program is just run directly or under dbg for debugging.

In the case of embedded, the program (firmware) should be downloaded into the target chip and then run (in most cases the MCU is just reset after download). This could be done through the bootloader (USB, UART, I2C, SPI, OTA, etc.) or through the hardware JTAG probe. To debug firmware, JTAG probe and remote dbg support for it are required. It the case of ST development boards (EVAL, Discovery, or NUCLEO series), there are on-board ST-link compatible JTAGs. CLion has dbg support, but unfortunately there is no support for remote dbg, and it is not possible to use CLion’s internal debugger for any embedded development yet (please vote!).

Luckily, there is a company called Segger that produces its own JTAGs and a debugger on top of that. Now it’s possible to upgrade onboard ST-Link debugger to full-featured J-Link debug probe. Note that the upgrade license allows you to use this upgrade for ST evaluation boards only; production use is prohibited.

Of course, if you have Segger J-Link device, you can use it according to the license terms and just skip board upgrade. In this case, Nucleo onboard st-link jumpers must be removed, and the probe must be connected directly to MCU pins.

To perform this upgrade, you need a Windows computer (VirtualBox or VMWare can help) and the Segger upgrade utility. The same utility can downgrade the board back to ST-Link.

Other options

If you use another ST ARM MCU or board, you can use this article as a general HowTo, and flash your device using USB DFU mode and dfu-util, or using UART bootloader. See the application note AN2606 for details about STM32 MCUs bootloaders.

If nothing else works, then there is openOCD, which can be used to download firmware (and later debug, when CLIon introduces remote dbg support).

Software

CLion

Download CLion from the JetBrains website and install it by following the instructions. CLion is a paid product, but it can be used for 30 days for free under a trial license. You may be able to apply for a discounted or complimentary license.

CMake

All CLion projects are based on the CMake build model. CMake is shipped with CLion, and any Linux distribution contains it, as well. You can use whichever you want.

Segger

Segger software can be downloaded from their site. J-Link Debugger is a must, all other pieces of software are up to you. Choose the appropriate version (32-bit or 64-bit) and packaging for your version of Linux (DEB for all Debian derivatives, including Ubuntu).

GNU GCC for ARM (gcc-arm-none-eabi)

The absolutely mandatory part of the software is a compiler. We will use gnu-gcc for embedded ARM processors. Thanks to launchpad.net, we can just install or download pre-build gcc binaries.

STM32CubeMX

The last piece of required software is STM32CubeMX, a universal GUI tool for configuring MCU peripherals and for code generation. Download the installer from st.com and run it.

Firmware library

Finally, run the utility and install a fresh utility for the target MCU. Open Help->Install New Libraries->STM32CubeF3->check latest revision->Install Now. The firmware library is quite large, so the process will take some time.

New project

STM32CubeMX

Now everything is ready to create your very first project. Run Cube software, click New Project, select Board Selector tab, and then choose the target platform Nucleo64 NUCLEO-F303RE.

image11

Now we can see the default MCU configuration for the board. Pin PA5, connected to onboard LED, is already set to GPIO_Output mode, and we can use it for the classic “blink” example.

image15

Now it’s time to generate project code. Click Project->Generate Code. In the opened dialog, configure the target folder and set the target Toolchain/IDE to SW4STM32, since CLion is not supported by Cube software.

After clicking OK, Cube software generates the full set of sources, including libraries, linker script, and startup code for the given MCU type.

image16

CLion project and settings

Now that the codebase is generated in the target folder, let’s create a CLion project on top of that. A good start is to choose Import Project from Sources (the same can also be done from the Welcome screen or from the File menu). The IDE generates a CMakeLists.txt file for us, but for this project you should create a different one:

project(f3_blink C ASM)
cmake_minimum_required(VERSION 3.5.0)

add_definitions(-DSTM32F303xE)

file(GLOB_RECURSE USER_SOURCES "Src/*.c")
file(GLOB_RECURSE HAL_SOURCES "Drivers/STM32F3xx_HAL_Driver/Src/*.c")

add_library(CMSIS
        Drivers/CMSIS/Device/ST/STM32F3xx/Source/Templates/system_stm32f3xx.c
        Drivers/CMSIS/Device/ST/STM32F3xx/Source/Templates/gcc/startup_stm32f303xe.s)

include_directories(Inc)
include_directories(Drivers/STM32F3xx_HAL_Driver/Inc)
include_directories(Drivers/CMSIS/Include)
include_directories(Drivers/CMSIS/Device/ST/STM32F3xx/Include)

add_executable(${PROJECT_NAME}.elf ${USER_SOURCES} ${HAL_SOURCES} ${LINKER_SCRIPT})

target_link_libraries(${PROJECT_NAME}.elf CMSIS)

set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Map=${PROJECT_SOURCE_DIR}/build/${PROJECT_NAME}.map")
set(HEX_FILE ${PROJECT_SOURCE_DIR}/build/${PROJECT_NAME}.hex)
set(BIN_FILE ${PROJECT_SOURCE_DIR}/build/${PROJECT_NAME}.bin)
add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD
        COMMAND ${CMAKE_OBJCOPY} -Oihex $<TARGET_FILE:${PROJECT_NAME}.elf> ${HEX_FILE}
        COMMAND ${CMAKE_OBJCOPY} -Obinary $<TARGET_FILE:${PROJECT_NAME}.elf> ${BIN_FILE}
        COMMENT "Building ${HEX_FILE} \nBuilding ${BIN_FILE}")

There are some adjustments for the compiler and .bin and .hex files generation.

The next step is to write the cross-compiler toolchain file. For STM32f303xE this file looks like this:

INCLUDE(CMakeForceCompiler)

SET(CMAKE_SYSTEM_NAME Generic)
SET(CMAKE_SYSTEM_VERSION 1)

# specify the cross compiler
CMAKE_FORCE_C_COMPILER(arm-none-eabi-gcc GNU)
CMAKE_FORCE_CXX_COMPILER(arm-none-eabi-g++ GNU)

SET(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/STM32F303RETx_FLASH.ld)
#SET(COMMON_FLAGS "-mcpu=cortex-m4 -mthumb -mthumb-interwork -mfloat-abi=soft -ffunction-sections -fdata-sections -g -fno-common -fmessage-length=0")
SET(COMMON_FLAGS "-mcpu=cortex-m4 -mthumb -mthumb-interwork -mfloat-abi=hard -mfpu=fpv4-sp-d16 -ffunction-sections -fdata-sections -g -fno-common -fmessage-length=0")
SET(CMAKE_CXX_FLAGS "${COMMON_FLAGS} -std=c++11")
SET(CMAKE_C_FLAGS "${COMMON_FLAGS} -std=gnu99")
SET(CMAKE_EXE_LINKER_FLAGS "-Wl,-gc-sections -T ${LINKER_SCRIPT}")

Before the first compilation, your CLion project will need some adjustments. Go to File -> Settings and under Build, Execution, Deployment -> CMake set the following:
CMake options: -DCMAKE_TOOLCHAIN_FILE=STM32F303xE.cmake
Build output path: build

image04

Reload your CLion project (Tools -> CMake -> Reset Cache and Reload Project). Now the project is ready to be compiled in the IDE (Run -> Build). If everything is done correctly, .bin and .hex files should appear in build folder.

First run of Segger J-Link Debugger

Select Run -> Edit Configurations… and create a new Application. Enter any name and specify the following options:

  • All Targets for Target
  • Debug for Configuration
  • /opt/SEGGER/jlinkdebugger/2.14.9/JLinkDebugger for Executable
  • run.jdebug for Program arguments
  • Enter the project folder path for Working Directory
  • Check Single Instance only

Finally, click OK.

image18

Now the debugger can be started right from the IDE. Segger Debugger needs its own project to be created. To make it happy, choose File -> New -> New Project Wizard…

In the open wizard, choose the appropriate MCU, then interface configuration, then data file – i.e. our compiled .elf file in the build folder.

image17

image19

image12

When the wizard finishes, click File -> Save Project As…, choose your project folder and enter run.jdebug in the File Name field (same as in the run configuration in CLion).

Wait, when we are going to code?

Exactly! Now everything is set up and ready for you to start coding. To see a real “blink” example, just add those two lines inside a while loop in the main() function.

image13

After this modification, you can just run your JLINK Debugger configuration. The project will be automatically built, and the debugger will be started on top of that. In the debugger, press F5 to download and start the firmware, then click Yes to agree with the evaluation mode, and then press F5 again to run the main() function. The green LED LD2 will start blinking with 1Hz frequency.
Now you are able to use all Segger debugger features.

image14

Pro tip

STM32CubeMX code regeneration

You can run Cube software over your project multiple times, and regenerate configuration code as many times as you need. To preserve your own changes to the sources during code generation, surround your the changes with the comments /* USER CODE BEGIN ??? */ and /* USER CODE END ??? */. Cube leaves those code blocks untouched. Of course, you can add your own .c files to the project.

Conclusion

CLion is a really good IDE for C-based projects, and as you can see here, it’s possible to use it for embedded ARM MCU development. This article covers only one MCU chip and only one JTAG probe, but the steps can be easily adapted to any STM32 MCU and other vendors’ chips as well. At the moment, CLion team is working on adding remote GDB support.

Upd. A plugin by Ilya for OpenOCD + STM32CubeMX support for ARM embedded development

image description