CLion + STM32 без шелухи
Эта статья — краткий гайд о том, как с нуля завести STM32 под CLion, без шелухи в виде HAL и STM32CubeMX.
Предыстория
Несмотря на то, что примерно год назад STMicroelectronics представили свою IDE на базе Eclipse и CDT, она по-прежнему содержит в себе ряд минусов, которые, по большей части, перекрывает CLion.
На Хабре ранее уже была статья, посвященная запуску связки CLion + STM32, но она была старая, а новые версии CLion несколько изменились.
В интернете достаточно много публикаций на тему того, как запустить связку CLion + STM32CubeMX, но практически нигде нет ничего о том, как сделать все своими руками - может быть я очень плохо искал.
Я надеюсь, что данная статья будет актуальна не только для серии микроконтроллеров от STMicroelectronics, но и от других производителей, использующих те же серии ядер.
Мотивация
Как уже было сказано выше, STM32CubeIDE - среда разработки от STMicroelectronics - содержит в себе ряд минусов, унаследованных от Eclipse и CDT в целом. Среди них: отсутсвие автодополнения кода, отсутствие поддержки синтаксиса выше, чем C++14, отсутствие возможности выбрать самый последний тулчейн с сайта ARM и многое другое. Отдельно стоит отметить стабильность работы Eclipse и STM32CubeIDE в частности - она оставляет желать лучшего. У меня неоднократно случалось так, что я был вынужден полностью сносить IDE и ставить ее заново потому, что при обновлении происходила внутренняя ошибка.
Пару ремарок
Все примеры установок и т.д. будут приведены для macOS, потому что у меня именно она. Также я считаю что с этой системой (по сравнению с Linux и Windows) намного больше подводных камней и неочевидностей.
В качестве примера рассматривается проект с поддержкой C++. В моем конкретном случае это C++20. CLion поддерживает версии начиная от C++98 до C++23. Есть поддержка C90, C99, C11.
CLion
Собственно IDE ради которой все затевается. Можно скачать на сайте JetBrains и для начала попробовать месячный триал. Для счастливых обладателей студенческого билета есть возможность получить вообще весь софт JetBrains в полной комплектации сроком на год.
OpenOCD
Инструмент для удаленной отладки на целевом (target) устройстве. Скачать можно также с официального сайта. Либо набрать в консоли (для macOS) что-то такое:
brew install openocd
What is brew?
Это менеджер пакетов для macOS. Примерно то же самое что и операция apt-get в Ubuntu. Подробнее про то, что это и как это установить тут.
Toolchain
Это набор инструментария для работы с целевым (target) устройством. Включает в себя компиляторы, линковщики, библиотеки и прочее. Загрузить можно с сайта ARM. На момент написания статьи последняя версия, доступная к загрузке, gcc-arm-none-eabi-10.3-2021.07. Для macOS есть два варианта установки: простой архив, и .pkg файл, который установит все сам. Стоит отметить, что начиная с macOS Catalina нужно нехило так повоевать с системой чтобы установить что-то от стороннего разработчика. Помимо этого надо будет вручную добавить путь до компилятора в переменную $PATH, именно там CLion и будет искать путь до компиляторов. Если не хочется тратить время на это, то можно скачать свежую, но не самую последнюю версию так:
brew install --cask gcc-arm-embedded
На момент написания статьи последняя версия в репозиториях brew - 10.2.
Начинаем!
В первую очередь стоит отметить, что на macOS нормально работает только OpenOCD, в то время как инструменты встроенные в CLion корректно работать отказываются.
Итак, если у нас не установлен brew, скачиваем и устанавливаем его с официального сайта. Инструкция по установке находится там же. После установки открываем терминал и пишем:
brew install openocd
А затем, после установки OpenOCD
brew install --cask gcc-arm-embedded
На этом работа с терминалом окончена. Теперь скачиваем и устанавливаем, если еще не сделали этого CLion. Открываем...
Выбираем "New Project". Также можно открыть уже существующий проект, либо взять проект с Git или иной системы контроля версий.
Выбираем рабочую папку проекта и поддерживаемый стандарт языка.
После создания проекта CLion либо сам попросит вас указать путь до компилятора, либо это нужно будет сделать вручную.
Settings->Preferences->Build, Execution, Deployment->Toolchains. В появившейся вкладке слева список, над ним иконка "+", выбираем System. Далее нам предложат ввести имя конфигурации, пути до make и компиляторов. Если вы использовали brew для установки тулчейна, то пути у вас будут выглядеть вот так:
После нажатия "Ok" или "Apply" в правой нижней части нас ожидает ошибка примерно такого рода:
Error
CMake Error at /Applications/CLion.app/Contents/bin/cmake/mac/share/cmake-3.20/Modules/CMakeTestCCompiler.cmake:66 (message):
The C compiler
"/usr/local/bin/arm-none-eabi-gcc"
is not able to compile a simple test program.
Ничего страшного в этом нет, просто CMake пытается собрать свой внутренний тест для хостовой машины (компьютер на котором мы собираем проект) с использованием указанных компиляторов.
Чтобы избавиться от ошибки и в целом как-то повлиять на процесс компиляции, открываем файл CMakeLists.txt в папке проекта и начинаем редачить... Ниже приведен пример уже сконфигурированного CMakeLists.txt файла, вам необходимо лишь выставить параметры в соответствии со своим проектом.
CMakeLists.txt
#Имя системы под которую осуществляется сборка и ее версия
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_VERSION 1)
#Минимальная вресия CMake необходимая для компиляции проекта
cmake_minimum_required(VERSION 3.20)
#Ниже указаны имена компиляторов и утилит тулчейна
#CXX - компиялтор C++
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
set(CMAKE_ASM_COMPILER arm-none-eabi-gcc)
set(CMAKE_AR arm-none-eabi-ar)
set(CMAKE_OBJCOPY arm-none-eabi-objcopy)
set(CMAKE_OBJDUMP arm-none-eabi-objdump)
set(SIZE arm-none-eabi-size)
#Из-за того, что мы собираем проект под микроконтроллер, а не под хост
#нужно сообщить об этом CMAKE
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
#Имя проекта и используемые языки
project(test_stm32f0 C CXX ASM)
#Имя ядра микроконтроллера
set(CMAKE_SYSTEM_PROCESSOR cortex-m0)
#Расширение скомпилированного файла
set(CMAKE_EXECUTABLE_SUFFIX ".elf")
#Стандарты C++ и C максимально поддерживаемые в этом проекте
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_C_STANDARD 11)
#Имя файла линкера под ваш МК
set(LINKER_SCRIPT_NAME STM32F072RBTX_FLASH)
#А так же путь до него. В моем случе скрипт лежит в папке startup которая находится в корне проекта
set(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/startup/${LINKER_SCRIPT_NAME}.ld)
# Далее идут флаги компиляции для каждого компилятора (Си, Си++ и Ассемблер)
# Так как для своих нужд я переносил проект с STM32CubeIDE, все флаги перешли оттуда же
# Опции компиляции можно посмотреть на офф сайте https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html
# Рассмотрим некоторые флаги:
# -mcpu - ядро МК
# -g - степень оптимизации
# -std - стандарт языка, gcc для C, g++ (или c++) для C++
# -O - степень (или тип) оптимизации. -O0 - без оптимзации,
# -O1, -O2, -O3 оптимизации от наименьшей к наибольшей, чем выше цифра, тем сильнее оптимизация
# стоит учитывать что чем выше степень оптимизации, тем сложнее работать в режиме дебага
# -Os - оптимизация по размеру, пытается скомпилировать минимальный размер
# -Ofast - оптимизация по скорости испольнения кода
# -Og - оптимизация для дебага
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=${CMAKE_SYSTEM_PROCESSOR} -g3 -std=gnu++${CMAKE_CXX_STANDARD} -Os -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-exceptions -fno-rtti -fno-threadsafe-statics -fno-use-cxa-atexit -Wall -std=gnu++2a -fstack-usage --specs=nano.specs -mfloat-abi=soft -mthumb")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=${CMAKE_SYSTEM_PROCESSOR} -std=gnu${CMAKE_C_STANDARD} -g3 -Os -ffunction-sections -fdata-sections -fno-strict-aliasing -Wall -fstack-usage --specs=nano.specs -mfloat-abi=soft -mthumb")
set(CMAKE_EXE_LINKER_FLAGS "-mcpu=${CMAKE_SYSTEM_PROCESSOR} -g3 -T ${LINKER_SCRIPT} --specs=nosys.specs -Wl,-Map=${PROJECT_NAME}.map -Wl,--gc-sections -static --specs=nano.specs -mfloat-abi=soft -mthumb -Wl,--start-group -lc -lm -lstdc++ -lsupc++ -Wl,--end-group")
set(CMAKE_ASM_FLAGS "-mcpu=${CMAKE_SYSTEM_PROCESSOR} -g3 -c -x assembler-with-cpp --specs=nano.specs -mfloat-abi=soft -mthumb")
#Говорим применить такой то компилятор с таким-то линковщиком и т.д.
set(CMAKE_C_LINK_EXECUTABLE "<CMAKE_C_COMPILER> <LINK_FLAGS> -o <TARGET> <OBJECTS>")
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER> <LINK_FLAGS> -o <TARGET> <OBJECTS>")
#Имя и путь к стартап файлу
set(STARTUP_FILE_NAME startup_stm32f072rbtx)
set(STARTUP_LOCATION "${CMAKE_SOURCE_DIR}/startup/${STARTUP_FILE_NAME}.s")
#Пути по которым лежат инклуды, '.' означает корень проекта
include_directories(.)
include_directories(cmsis)
#Глобальный дефайн, нужен для CMSIS
add_definitions(-DSTM32F072xB)
#имена .cpp, .c и .s файлов
set(SOURCE_FILES main.cpp syscalls.c cmsis/system_stm32f0xx.c)
#имена хедеров
set(INCLUDE_FILES cmsis/cmsis_compiler.h cmsis/cmsis_gcc.h cmsis/cmsis_version.h cmsis/core_cm0.h cmsis/stm32f072xb.h cmsis/stm32f0xx.h cmsis/system_stm32f0xx.h)
#На этом моменте происходит компиляция и линковка проекта в .elf
add_executable(${PROJECT_NAME} ${STARTUP_LOCATION} ${INCLUDE_FILES} ${SOURCE_FILES})
#Превращаем .elf в .hex
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_OBJCOPY} ARGS -Oihex ${PROJECT_NAME}.elf ${PROJECT_NAME}.hex)
#Превращаем .elf в .bin
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_OBJCOPY} ARGS -Obinary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin)
#Вывод в консоль данных о размере секций .bss, .data и т.д., а так же всего проекта
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${SIZE} ARGS --format=berkeley ${PROJECT_NAME}.elf)
Чтобы адаптировать этот файл под свой проект, необходимо изменить строчки
#Имя ядра микроконтроллера
set(CMAKE_SYSTEM_PROCESSOR cortex-m0)
#Имя файла линкера под ваш МК
set(LINKER_SCRIPT_NAME STM32F072RBTX_FLASH)
#А так же путь до него. В моем случе скрипт лежит в папке startup которая находится в корне проекта
set(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/startup/${LINKER_SCRIPT_NAME}.ld)
#Имя и путь к стартап файлу
set(STARTUP_FILE_NAME startup_stm32f072rbtx)
set(STARTUP_LOCATION "${CMAKE_SOURCE_DIR}/startup/${STARTUP_FILE_NAME}.s")
#Глобальный дефайн, нужен для CMSIS
add_definitions(-DSTM32F072xB)
И опциональные. По желанию...
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=${CMAKE_SYSTEM_PROCESSOR} -g3 -std=gnu++${CMAKE_CXX_STANDARD} -Os -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-exceptions -fno-rtti -fno-threadsafe-statics -fno-use-cxa-atexit -Wall -std=gnu++2a -fstack-usage --specs=nano.specs -mfloat-abi=soft -mthumb")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=${CMAKE_SYSTEM_PROCESSOR} -std=gnu${CMAKE_C_STANDARD} -g3 -Os -ffunction-sections -fdata-sections -fno-strict-aliasing -Wall -fstack-usage --specs=nano.specs -mfloat-abi=soft -mthumb")
set(CMAKE_EXE_LINKER_FLAGS "-mcpu=${CMAKE_SYSTEM_PROCESSOR} -g3 -T ${LINKER_SCRIPT} --specs=nosys.specs -Wl,-Map=${PROJECT_NAME}.map -Wl,--gc-sections -static --specs=nano.specs -mfloat-abi=soft -mthumb -Wl,--start-group -lc -lm -lstdc++ -lsupc++ -Wl,--end-group")
set(CMAKE_ASM_FLAGS "-mcpu=${CMAKE_SYSTEM_PROCESSOR} -g3 -c -x assembler-with-cpp --specs=nano.specs -mfloat-abi=soft -mthumb")
include_directories(.)
include_directories(cmsis)
Ух, много получилось, но самое сложное позади! Осталось добавить конфигурацию для компиляции и отладки.
Добавляется все точно так же, как и в случае с путями для компиляторов.
Собственно это все. На этом проект должен компилироваться и запускаться, однако осталась одна мелочь, очень важная. При отладке хотелось бы иметь возможность смотреть в регистры МК. Для этого при первой сессии отладки нужно указать .svd файл. Их под множество платформ можно взять тут.
Заключение
Надеюсь я ничего не забыл, и у вас тоже получится успешно собрать проект в CLion. Статью я старался писать с упором на новичков и, надеюсь, я им хоть немного помог. Полный проект, рассматриваемый в статье, доступен тут. Там же добавленна библиотека CMSIS и пример моргания светодиодом. Если что, плата NUCLEO-F072RB.
Парочку вопросов к остальным
Отдельно хочется задать вопрос к другим пользователям:
Как перекрасить цвет вывода OpenOCD в консоль? Она выводится зловещим красным цветом, и кажется что это какая-то ошибка, хотя все нормально...
Есть ли какой-то плагин по типу того, который есть в STM32CubeIDE, отображающий текущее оставшееся место в RAM и ROM (в графическом представлении)?