Начало работы с STM32

Перед началом непосредственно разработки программно-аппаратного обеспечения на базе STM32 следует сделать два шага: выбрать микроконтроллер, на базе которого будет реализовываться проект, и обзавестись всем необходимым для его программирования. Аспекты выбора конкретного МК были затронуты в предыдущих лекциях и лонгридах. Сейчас мы поговорим о самом необходимом для программирования.
Во-первых, как мы помним, у микроконтроллера есть различные контакты, которые служат для подключения питания и сопряжения его подсистем с другими электронными компонентами вашего устройства. Для загрузки программ в память микроконтроллера используются специальные устройства — программаторы. Программаторы разрабатываются специально для определенных семейств микроконтроллера. В нашем случае понадобиться программатор ST-Link. Отметим, что на отладочных платах обычно присутствует собственный программатор, который можно отделить и использовать далее в проектах.

Во-вторых, нужно ПО для общения компьютера с программатором. Мы будем использовать STM32CubeMX для создания конфигурационного файла для микроконтроллера и генерации «рыбы» для будущего кода. Конфигурация МК — достаточно тонкий процесс, поэтому рекомендуется пользоваться данной программой во избежание проблем со специальными регистрами. Тем не менее важно помнить, что настройки конфигурации можно поменять и вручную в коде.
На старте максимально удобно работать с «родной» средой разработки STM32CubeIDE. Со временем у вас может появиться потребность перейти на более мощные инструменты разработки (например, CooCox IDE), однако в рамках нашего курса стандартного набора будет достаточно.
Создание проекта в среде STM32CubeIDE
Скачать версию STM32CubeIDE 1.9.0 без регистрации и смс для Windows или Linux можно в телеграм-канале:

При первом запуске программа предложит обозначить папку, где будут хранится проекты, указываем. Далее жмём Start new STM32 project либо через ( File -> New -> STM32 Project )

Выбираем необходимый микроконтроллер, у меня это всем известный STM32F103C8T6 и жмём Next :

Называем проект и жмём Finish

Включаю последовательную отладку (по SWD — Serial Wire Debug), чтоб использовать ST-LINK
SYS -> Debug -> Serial Wire

Тактирование от внешнего кварцевого резонатора
RCC -> HSE (High Speed Clock) -> Crystal/Ceramic Resonator

Во вкладке настройки тактовой системы вводим 72 МГц и жмём ввод, подтверждаем предложенное решение

Настраиваем вывод PC13 (к нему подключен зеленый светодиод) как двухтактный выход ПКМ на выводе -> GPIO_Output

Далее слева в дереве проекта в папке Src двойной щелчок по главному файлу main.c

Написан простейший код мигания светодиодом (он подключен в выводу PC13 ).
Значение в регистре ODR (Output Data Register) меняется каждый раз благодаря логической операции ИСКЛЮЧАЮЩЕЕ ИЛИ.
Ну, а со стандартной функцией задержки всё и так понятно.
GPIOC->ODR ^= GPIO_ODR_ODR13; // same: HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); HAL_Delay(500); // delay 0.5s
Собираем проект (Ctrl + B) либо щелчок по молоточку. Ошибок и предупреждений нет.

Запускаем отладку ( F11 либо Run -> Debug ). Переключаемся в пространство отладки.
Возможно придется обновить прошивку программатора-отладчика ST-LINK (делается это в два клика) и далее в настройках можно будет его лицезреть.
Прошивка успешно загружается в память микроконтроллера.
Теперь нажимая на кнопки продолжения/паузы смотрим за значениями в нужных регистрах.
В этой программе это регистр выходных данных ODR, бит ODR13, когда там нуль, то СИД светится, когда единица, то нет (т.к. светодиод подключен к плюсу на отладочной плате).
Ну а в железе это вот этот изменяющийся бит выглядит как мигание светодиода:
Скачать версию STM32CubeIDE 1.7 без регистрации и смс:
STM32 + CMSIS + STM32CubeIDE
Здесь я расскажу как создать минимальный проект на CMSIS с использованием «родной» IDE для микроконтроллеров STM – STM32CubeIDE.
Возможно STM32CubeIDE и обладает рядом недостатков, но у нее, на мой взгляд, есть несколько преимуществ – таких как проприетарность и бесплатность, ради которых, как минимум, стоит обратить внимание на эту среду разработки, если вы не сделали этого раньше.

Объектом прошивки выбран не очень распространенный микроконтроллер STM32F072 с ядром ARM Cortex-M0, для более привычных STM32F103 на ARM Cortex-M3, с поправкой на ядро, процесс идентичен.
Все необходимые ресурсы можно скачать с сайта st.com, и вот что понадобится:
-
Сама IDE, я использую Windows версию, но также доступны версии под Mac и Linux




Создание нового проекта — File/New/STM32Project. После некоторого раздумия появляется окно выбора микроконтроллера, в моем случае это STM32F072RB в корпусе LQFP64, выбираю нужную строку, жму далее. Далее предлагается выбрать имя проекта, расположение, язык программирования C/C++, исполняемый файл/статическая библиотека и будет-ли проект сгенерирован с помощью CubeMX или сами с усами. Генерация кубом, в данном случае не нужна, поэтому тип проекта Empty — финиш.



Слева, в окне Project Explorer, появилось дерево проекта, правда он не совсем Empty, как заказывали. Впринципе, если устраивает сгенерированная структура папок, можно добавить туда файлы из библиотеки CMSIS и работать дальше, но здесь я покажу как можно привести структуру проекта в гармонию со своим чувством прекрасного, поэтому удаляется всё, кроме скрипта линкера т.е. файла c расширением .ld — он еще пригодится.


Все манипуляции с папками и файлами можно проводить как в проводнике так и внутри IDE, нажав правой кнопкой на название проекта, к примеру: правая кнопка –> new –> Folder. Если структура проекта изменялась вне IDE, то нужно просто обновить проект: правая кнопка –> Refresh.
Мой вариант структуры проекта выглядит так:
- Startup – здесь будет храниться скрипт линкера, тот самый, оставшийся от сгенерированного проекта, а также startup файл взятый из CMSIS
- CMSIS\src и CMSIS\inc – здесь будут лежать исходники, файлы с расширением .c в папке scr и заголовочные файлы с расширением .h в папке inc соответственно, относящиеся к библиотеке CMSIS
- Core\src и Core\inc – здесь будет расположен собственно сам проект, для начала стоит положить туда main.c и main.h

Теперь нужно перенести файлы библиотеки CMSIS в проект. Библиотека состоит из файлов ядра и файлов периферии. Файлы ядра начинаются с core_ или cmsis_ они общие для всех микроконтроллеров, использующих данное ядро. Файлы периферии содержат в названии наименование микроконтроллера stm32 и специфичны для конкретного производителя, в данном случае, компании STM.
В распакованном виде архив содержит папку STM32Cube_FW_F0_V1.11.0, все пути указаны относительно этой папки. Итого, нужно скопировать:
- Drivers\CMSIS\Include\cmsis_compiler.h
- Drivers\CMSIS\Include\cmsis_gcc.h
- Drivers\CMSIS\Include\cmsis_version.h
- Drivers\CMSIS\Include\core_cm0.h
- Drivers\CMSIS\Device\ST\STM32F0xx\Include\stmf0xx.h
- Drivers\CMSIS\Device\ST\STM32F0xx\Include\stm32f072xb.h
- Drivers\CMSIS\Device\ST\STM32F0xx\Include\system_stm32f0xx.h
- Drivers\CMSIS\Device\ST\STM32F0xx\Source\Templates\system_stm32f0xx.c
- Drivers\CMSIS\Device\ST\STM32F0xx\Source\Templates\gcc\startup_stm32f072xb.s

Так как были проведены некоторые манипуляции с папками проекта, нужно отобразить это в настройках.
Правая кнопка по названию проекта -> Properties -> C/C++ Build -> Settings -> Tool Settings -> MCU GCC Linker -> General – здесь нужно указать новое расположение скрипта линкера с помощью кнопки Browse…

Также нужно указать пути к файлам проекта
Properties -> C/C++ General -> Includes
Properties -> C/C++General -> Source Location
В Includes пути к папкам inc, а в Source Location логично было-бы к папкам src, но если так сделать, то в дереве проекта будут отдельно добавлены эти папки. Чтобы не загромождать визуально дерево, в Source Location можно указать корневые папки Core, CMSIS и Startup.


Для того чтобы проект скомпилировался нужно раскомментировать в файле stm32f0xx.h строку с названием микроконтроллера ну и конечно же в main.c добавить функцию main.


Собственно всё. Безошибочная компиляция и сразу же куда-то подевалось целых полтора килобайта памяти ОЗУ она же RAM, и сразу же вспоминается стек и куча, в процессе создания проекта они нигде не упоминались. Величина стека и кучи указана в файле скрипта линкера, тот что с расширением .ld, их можно и нужно изменять в соответствии с требованиями проекта. Эти значения находятся в начале файла в виде меток _Min_Heap_Size/_Min_Stack_Size с указанием размера в шестнадцатеричном виде.

В качестве примера, приведу небольшой проект традиционного мигания светодиодом.
Светодиод будет мигать на отладочной плате STM32F072B-DISCO, тактирование осуществляться от внутреннего генератора HSI48 частотой 48 МГц, а в качестве источника задержки использоваться таймер SysTick, генерирующий прерывания с периодом в 1 мс, при помощи которых отсчитывается точное время задержки. Светодиод подключен к выводу 6 порта С, настроенного на выход push-pull.
Надеюсь, данная информация кому-то пригодится, т.к. в свое время, несмотря на обилие материалов по программированию STM32, мне пришлось перелопатить достаточно много мануалов, чтобы осознать вещи, кажущиеся сейчас очевидными.
- C
- Программирование микроконтроллеров
Начинаем работать в STM32CubeMX. Часть 1
Приветствую аудиторию хабра, и хочу предложить вашему вниманию первый пост, посвященный использованию среды разработки STM32CubeMX, написанный для тех, кто хочет начать изучение STM32 «с нуля».

Я планировал написать несколько постов, рассмотрев несколько периферийных устройств микроконтроллера и их конфигурирование в STM32CubeMX. Но эти посты не заменяют фирменной документации и не претендуют на полноту. В них будут рассмотрены только некоторые, наиболее, на мой взгляд, типичные, примеры использования периферии STM32.
Надеюсь, кому-то этот материал будет полезен.
1. Небольшое введение
Сначала сделаю небольшое пояснение. Для изучения данного материала вам понадобится отладочная плата с микроконтроллером STM32. Я использую плату STM32F746G Discovery, которая на сегодняшний день является одной из самых лучших, и, соответственно, дорогих плат семейства Discovery.

Однако для освоения большей части материала будет достаточно любой, даже самой простой платы на STM32. Я рекомендую именно платы Discovery, т.к. они уже содержат отладчик ST-Link, и для работы вам понадобится только кабель MiniUSB. Для начала не нужен даже источник питания, плата питается через тот же кабель.
Естественно, при использовании микроконтроллера, отличного от STM32F746G нужно будет делать поправки в проектах на другую тактовую частоту, другую распиновку и т.п., но суть остаётся той же. Рекомендую сразу скачать документацию к вашей плате с принципиальной схемой и pdf на микроконтроллер.
Для дальнейшей работы будет неплохо иметь осциллограф, практически любой, однако начать можно и без него.
Опять же, предвидя вопросы про покупку платы, я покупал на aliexpress, и это обошлось гораздо дешевле, чем у отечественных продавцов.
Ещё нам понадобится сам STM32CubeMX (скачивается бесплатно), и любая IDE для работы с проектом на С и поддержкой ST-Link. Их много, есть коммерческие, есть бесплатные, и я намеренно не буду приводить никаких названий. Каждый выбирает для себя.
2. Hello World, или управление светодиодом
Сначала вам нужно скачать и установить STM32CubeMX. Скачать можно бесплатно с сайта st.com. Скажу сразу, STM32CubeMX существует только в Windows-версии. Пишут, что нормально работает под wine, я лично не пробовал.
Заходим в File/New Project, выбираем нужный микроконтроллер. Для этого удобно пользоваться фильтрами в верхней части окна.

В нашем случае это STM32F746NGHx.
Далее настраиваем тактовый генератор. Во вкладке Pinout выбираем работу с внешним кварцем:

Во вкладке Clock Configuration в поле HCLK (MHz) пишем 216. В ответ получаем сообщение «No solution found using the current selected sources. Do yoy want to use other sources?» Отвечаем «OK» и выбираем источник HSE в мультиплексоре PLL Source Mux. Значения PLLM, PLLN и PLLP устанавливаем, как показано на рисунке. Проверяем, что HCLK = 216MHz.

Теперь надо сконфигурировать GPIO, управляющий светодиодом. Это порт PI1. На вкладке Pinout находим вывод PI1, кликаем на него и устанавливаем в значение GPIO_Output.

Для дальнейшего удобства можно присвоить пину имя. Это делать необязательно, но давайте это сделаем, чтобы код был более читаемым. Для этого на вкладке Configuration в столбце System нажимаем кнопку GPIO.

Попадаем в окно «Pin Configuration» и в поле User Label пишем «Led».

Сейчас можно сгенерировать код (Project/Generate Code). STM32CubeMX генерирует не только исходный код, но и файлы проекта для ряда популярных IDE. Обратим внимание, что в коде расставлены комментарии вида:
/* USER CODE BEGIN 3 */ /* USER CODE END 3 */
Свой код можно писать только в них, иначе при повторной генерации исходника ваш код будет затёрт.
Итак, находим цикл while(1) в main() и пишем в нём следующее:
HAL_GPIO_TogglePin(Led_GPIO_Port, Led_Pin); //Toggle the state of pin HAL_Delay(500); //задержка в мс
Сейчас можно запустить проект. Подключаем плату и загружаем прошивку. Светодиод на плате должен мигать с частотой 1Гц.
Однако приведённый выше подход не идеален. Функция HAL_Delay содержит внутри пустой цикл, и наша программа не может заниматься в это время ещё чем-то полезным. Для решения проблемы есть два пути: использование прерываний и использование операционной системы реального времени. Про операционную систему я напишу в другой раз, а работу с прерываниями мы освоим сейчас.
3. Таймер и прерывания
Настроим таймер, например TIM1. Для этого во вкладке Pinout выбираем для этого таймера источник тактирования:

Источником тактирования стала внутренняя тактовая частота периферии, равная для нашего случая 108MHz. Уточнить это значение или изменить его путём деления главной тактовой частоты можно на вкладке Clock Configuration.
Теперь переходим на вкладку Configuration и настраиваем частоту срабатывания таймера. Нажимаем на кнопку TIM1 и в появившемся окне во вкладке Parameter Settings задаем значения Prescaler и Counter Period.

Обратите внимание, коэффициенты деления должны быть уменьшены на 1 от нужных значений. На самом деле, частота прерываний таймера может быть найдена по формуле:
Update_event = TIM_CLK/((PSC + 1)*(ARR + 1)*(RCR + 1))
В нашем случае частота будет равна 216e6 / ((53999 + 1) * (1999 + 1)) = 2Hz. При этом частота мигания светодиода составит 1Hz, как в предыдущем примере.
Теперь на вкладке контроллера прерываний (NVIC Settings) нужно разрешить прерывание TIM1 Update:

На этом работа в STM32CubeMX закончена, можно сгенерировать код. В исходнике нам в первую очередь нужно запустить сам таймер, а затем вставит обработчик прерывания, который будет мигать светодиодом:
/* USER CODE BEGIN 0 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) < if (htim->Instance==TIM1) //check if the interrupt comes from TIM1 < HAL_GPIO_TogglePin(Led_GPIO_Port, Led_Pin); //Toggle the state of pin >> /* USER CODE END 0 */ int main(void) < . /* USER CODE BEGIN 2 */ //запуск таймера HAL_TIM_Base_Start_IT(&htim1); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) < /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ //главный цикл пуст, в нём можно делать что угодно. >/* USER CODE END 3 */ >
Компилируем и заливаем прошивку. Как и ожидалось, светодиод мигает точно так же, как и в предыдущем примере. Однако, так как сейчас это происходит по прерыванию, процессор свободен для выполнения любых других задач.
4. Что дальше?
На этом я заканчиваю первую часть. Что планируется дальше? В следующей части я планирую описать работу с встроенным ЦАП и затронуть тему DMA. В качестве небольшого анонса: мы научимся генерировать вот такую красивую синусоиду:

(Эта синусоида не очень красивая, на самом деле, но будет лучше).
В дальнейших планах: работа с контроллерами USB (для начала в режиме VCP, виртуального COM-порта), контроллера Ethernet, АЦП, и, возможно, затронем тему использования FreeRTOS.
PS
Приведённые в статье примеры были также реализованы на плате Nucleo F767ZI (микроконтроллер STM32F767ZI), в IDE Atollic TrueStudio. Их можно скачать здесь: https://github.com/arktur04/stm32-habr
- stm32
- микроконтроллеры
- программирование микроконтроллеров
- таймер
- прерывание