Эта статья — краткий гайд о том, как с нуля завести 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 (в графическом представлении)?
Комментарии (24)
xFFFF
10.08.2021 00:55-1Ни разу не было проблем ни c Atollic, ни с CUBE IDE, на винде)
Сам предпочитаю использовать Visual Studio + VisualGDB. Идеальная связка, на мой взгляд. Так же позволяет импортировать проекты из STM32CubeMX.
kenny5660
10.08.2021 04:28Только платно, поэтому переехал на VScode + PlatformIO, иногда через Сmake, но тоже на VScode
Gaernebjorn
10.08.2021 00:56Среди них: отсутсвие автодополнения кода,
Справедливости ради, в CubeIDE есть автодополнение. Не слишком удобно всё время Ctrl+Space жамкать, но всё же.
Gordon01
10.08.2021 03:34-1Все же Visual Studio + VisualGDB лучше будет, как выше сказали. У него хотя бы есть поддержка, которая работает отлично и куча МК работает сразу, плюс поддержка WSL и SSH.
CLion довольно тормозной, плюс надо руками настраивать под каждый МК. В таком случае лучше уже и VSCode настроить.
app-z
17.11.2021 00:06CLion содержит лицензионный ключ для бесплатного пробного использования в течение 30 дней.
отличное достоинство!
seregazh
Сам фанбой Jetbrains и в частности CLion, но С++14/20 для микроконтроллера как-то перебор, субьективно так. Да и так ли плоха родная IDE ?
Albert2009ru
Поддержу. А вот если мне хватает простого Си и нативного HALа со всеми его косяками (оные вполне поправимы руками) для пользования всеми возможностями и портами контроллера. Да и даташита с CMSIS и без HALа тоже, то что тогда? Ни разу ни CUBE IDE, ни атоллик на винде не слетели... Сам, кстати, Keil предпочитаю.
Raspberries Автор
Не считаю C++14/20 перебором по нескольким причинам. Во первых, если проект является сугубо "хобби", то можно пробывать что угодно, во вторых при правильном применении C++ код будет ни чуть не хуже чем С'шный в плане размера и производительности. Как пример могу посоветовать публикации товарища lamerok.
Albert2009ru
Согласен. Пробовать и развиваться нужно, хотя бы в плане эрудиции. Просто сказал о своих наблюдениях по поводу стабильности указанных продуктов лично у меня. Суперфичами новых C++ стандартов не пользуюсь, хватает обычного Си.
С удовольствием, кстати, посмотрю на задачу, где только Си не обойтись, а есть реальная необходимость в применении C++ 11/14/20...
alex312
Вот что за постановка вопроса? Естественно, на любом тьюринг-полном ЯП возможно реализовать любую задачу.
Всегда вопрос стоит в том, сколько вы потратите на это сил.
Когда мы переходим от "просто" алгоритмов (отдельных задач) к реальным проектам, с допиливанием фичь, тестированием, устранением багов и т.д. вот тут то и раскрываются возможности ЯП. И чем больше у ЯП возможностей, тем потенциально меньше строчек кода вам прийдется написать для решения одной и тойже задачи.
alex312
Вот что за постановка вопроса? Естественно, на любом тьюринг-полном ЯП возможно реализовать любую задачу.
Всегда вопрос стоит в том, сколько вы потратите на это сил.
Когда мы переходим от "просто" алгоритмов (отдельных задач) к реальным проектам, с допиливанием фичь, тестированием, устранением багов и т.д. вот тут то и раскрываются возможности ЯП. И чем больше у ЯП возможностей, тем потенциально меньше строчек кода вам прийдется написать для решения одной и тойже задачи.
Albert2009ru
А я ведь ничего и не утверждал. Понятное дело, что на Ассемблере даже моргание светодиодом будет выглядеть очень непросто, если взять листинг программы. Но на том же Си всё очень даже коротко и "кашерно". Мне лично, я пишу про себя, "классический" Си пока удобен просто "до нельзя". Из статьи автора и его аргументации я для себя, опять же, не вижу, простите за тавтологию, очевидных вещей, почему вдруг стало нужно использовать "плюсы". Соответственно у меня, как у утилитарного пользователя вопрос - "А покажите пример, уважаемый???"
Опять же я только поддерживаю всё новое и если это будет реально эффективно, то обязательно буду применять.
Gordon01
Зато плюсовые стандартные библиотеки могут легко скушать 50-100 килобайт флешки.
Хорошо, если контроллер новый, с 512-1024 килобайтами флеша.
ИМХО, в 2021 раст на МК гораздо интереснее с++
alex312
Стандартные библиотеки любого ЯП, реализованные в полном обьеме, будут отьедать непростительно много для большинства МК. И раста это тоже касается.
Gordon01
В расте я std использовал, потому что из коробки нет аллокатора.
Но hal там интереснее реализован, так, что не выжирает килобайты флеша, а транслируется непосредственно в код на этапе компиляции.
Так что нет, не в любом ЯП так будет.
Elmot
А в чем проблема-то с 14/20? Надо, конечно, себя ограничивать, и не пихать стандартную библиотеку и всякие бусты - это очень много памяти берет, а новые синтаксические конструкции языка никак природе микроконтроллеров не противоречат.
seregazh
Я позволил себе оговорку-лазейку, написав что "субьективно".
С C++14/20 все хорошо, по моему субьективному мнению, но на десктопе.
В принципе моя формулировка "не использовать С++14/20" на микроконтроллере схожа с мыслью "ограничивать себя". Ну какие там C++14/20 если, поправьте меня, даже std::thread, packaged_task,future/promise не реализованы на stm32 ? А это, на минуточку, C++11 .
А как там move semantics ?
Не использовать C++14/20 на микроконтроллере также укладывается в концепцию "развиваться и обучаться".
Elmot
> std::thread, packaged_task,future/promise
Они там не реализованы по строгим причинам - нет операционки. fopen тоже не реализован, а это, извините, С73 от Ричи.
А фичи языка на параллельностях и не заканчиваются. В новых стандартах реализовано масса вещей, реально помогающих жить в МК.
Послушайте, например https://www.youtube.com/watch?v=QM3W36COnE4 (нужен c++20 емнип)
Или единицы измерения через user defined literals (c++11)
А что не так с move semantics?
mctMaks
Для себя пользую Segger Embedded Studio, так как по мимо stm32, использую ещё и nrf52. Лучше иметь более универсальную IDE, чем две под разные МК.
насчет С++14/20: если писать с использованием шаблонов, а с ними код реально компактней получается, то 20 версия стандарта самое то. Стандартные либы не пользую, немцы нормальной поддержки ещё не завезли к себе, а так хочется std::array ...
да и в целом IDE сейчас по сути только редактор кода да организатор дерева проекта. компилятор то любой можно подтянуть, а тут уже начинается интересное. каждый старается выдать свой компилятор как "самый-самый", но это как правило для коммерческого использования (вопрос цены все же стоит остро). а для хобби проектов\обучения по сути хватает и свободных компиляторов. отсюда и вопрос выбора IDE чисто дело вкуса. а что нам надо? адекватную подсветку синтаксиса, автодополнение да темную тему.
мне просто не заходит eclipse в любой ипостаси, а коллега использует вполне спокойно его. так что дело вкуса
seregazh
По поводу IDE и всего такого - готов "сьесть", тк. всетаки вопрос вкуса и предпочтений. К примеру в своей работе я так и не сдружился с QtCreator, хотя обьективно среда отличная - там есть почти все из коробки. Но нет ) Уже лучше VS, а в идеале CLion (на С++ лабаю).
Но вот по поводу С++14/20 прошу прощения что заела пластинка: что там такого концептуально нового на фоне С++11 из того что может быть реально использовано на микроконтроллере?
Если взять все теже шаблоны, то все это уже было в C++11
Extern templates
Template aliases
Variadic templates
Local types as template arguments
Что там в 14/20 такого, без чего не обойтись на контроллере ?
Constraints and concepts ? Да ладноооо! =)
Firthermant
https://habr.com/ru/post/566070/
С корутинами можно поиграться, например. Довольно удобно получается писать с их использованием. Да и места могут скушать технически меньше, чем фриртос в некоторых случаях ( см комментарии).
mctMaks
Из того что я использовал у себя:
в С++20 завезли: non-type template parameters of class type
в С++17 завезли:
инициализацию статических членов сразу в шаблоне:
fold expression:
if constexpr: