Приветствую!
Данная статья открывает цикл статей о программировании микроконтроллеров STM32 для новичков.
Сегодня я расскажу как написать моргание светодиодом двумя способами с использованием инструментов, труднодоступных в 2023 году.
Инструменты: Железо
Плата BlackPill или BluePill
Программатор ST-LinkV2
Загрузка среды разработки
Для настройки тактирования и периферии будем использовать ST CubeMX. Писать код будем в Keil. Cube MX можно скачать здесь, а Keil здесь. Для загрузки CubeMX потребуется регистрация на сайте ST Для загрузки Cube MX ссылка на пост Telegram актуальна на момент написания статьи и уже прожила полгода. Установка не требует особенных знаний, описывать ее процесс не буду, но могу ответить на вопросы в комментариях. Если у вас Mac OS или Linux, то здесь можно скачать CubeIDE - Eclipse подобную среду разработки от ST с интегрированным CubeMX (если вы не живете в России или умеете пользоваться Tor Browser и VPN), но статья будет длиться до второго пришествия, если я буду описывать весь процесс для всех сред разработки, так что просто отдельно упомяну критически важные моменты, когда это потребуется.
Создание проекта в CubeMX
Откроем CubeMx и увидим следующее окно:
Для создания нового проекта выберем пункт "Access to MCU selector". После непродолжительной (относительно периода вращения земли вокруг своей оси) подгрузки компонентов откроется новое окно, где нам предложено выбрать интересующий нас микроконтроллер.
Важное замечание: В статье здесь и далее будут приведены примеры для программирования на платах BluePill и BlackPill, как самых распространенных среди начинающих. У Вас может быть любая другая плата - NUCLEO, DISCOVERY, но процесс настройки периферии, тактирования и программирования не будет принципиально отличаться от приведенных в статье.
"Выберешь черную таблетку..."
Если у вас плата BlackPill, вам нужно выбрать вариант STM32F401CC или STM32F411CC - на самом камне обычно достаточно хорошо различимо выгравирована маркировка лазером.
Для BluePill:
После выбора нужного вам чипа нажимаем "Start Project" в правом верхнем углу и попадаем в окно "Pinout & Configuration" (Рисунок 5). Здесь в разделе System Core -> Sys в пункте "Debug" выбираем "Serial Wire" и справа видим два пина микроконтроллера, выделенных зеленым. Это пины отладочного интерфейса, через который будет происходить прошивка и отладка программ.
Теперь включим ножку 13 порта C в режиме GPIO Output - Вход выход общего назначения в режиме выхода - мышкой на ножку клик, выбираем "GPIO Output" (Рисунок 6)
Для BluePill ножки совпадают - также выбираем пин 13 порта C.
Теперь переходим на вкладку "Project manager" и попадаем в меню "Project" (Рисунок 7).
В пункте "Toolchain/IDE" выбираем MDK - ARM для Keil или STM32CubeIDE для соответствующего варианта. Далее буду использовать Keil, выбираю его.
Для удобства в дальнейшей работе, откроем вкладку "Code generator" (Рисунок 8, левый столбец, вторая кнопка) и поставим галочку напротив "Generate peripheral initialization as a pair of '.c/.h' files per peripheral ". Что это дает - каждый периферийный модуль микроконтроллера будет инициализироваться не в main.c, а в своем файле, что больших проектах улучшает читаемость.
Теперь нажмем кнопку "Generate code" (Рисунок 9).
После успешного создания проекта открываем его (Рисунок 10).
Знакомство с проектом
Теперь рассмотрим окно проекта по умолчанию (Рисунок 11) 1 - дерево проекта, 2 - вывод компилятора, 3 - основное окно, в котором отображается текущий открытый документ, 4 - кнопка сборки проекта (F7), 5 - прошивка микроконтроллера (F8), 6 - Настройки проекта, 7 - переход в режим отладки (Ctrl+F5).
Передвинем консоль под дерево проекта, чтобы не занимать полезное место для кода
Развернем папку "Application/User/Core" и откроем файл main.c (Рисунок 13) Ctrl + колесико позволяет менять размер шрифта.
Рассмотрим, что здесь есть: вначале подключается main.h файл и файл gpio.h, если поставили галочку раздельной генерации файлов периферии. Далее объявлен прототип функции конфигурации периферии и затем попадаем в main() функцию, в которой инициализируется HAL (Hardware Abstraction Layer) фреймфорк и тактирование с портами ввода вывода (Рисунок 14).
Кодим
В функции main() после инициализации, которая происходит единожды, присутствует бесконечный цикл while(1), в котором и будет выполняться наша программа. Обратите внимание на секции USER CODE - если писать код внутри них, то при перегенерации проекта из CubeMX, ваш код не перетрется.
Добавим в секцию USER CODE BEGIN 3 включение светодиода на ножке GPIOC 13, ожидание 500 мс и его выключение (Рисунок 15).
98 строка: В регистр ODR (Output Data Register) блока GPIO положили число, равное
1 << 13;
// или в бинарном выражении
0010 0000 0000 0000
Поскольку биты нумеруются справа налево (и с нуля), получается, что положили "1" в 13 й бит регистра ODR.
99 строка: Вызвали функцию HAL_Delay() с аргументом 500, где "500" - это миллисекунды, т.е 500 мс ничего не делаем.
100 строка: Вспомним число (1<<13) и инвертируем его.
1 << 13;
// или в бинарном выражении
0010 0000 0000 0000
// когда делаем ~(1<<13), мы побитово инвертируем число в скобках
1101 1111 1111 1111
теперь умножаем текущее значение регистра побитово на полученное число т.е все биты, которые были, сохраняют свое значение, кроме 13 - го, его умножили на ноль и сбросили.
0010 0000 0000 0000
// когда делаем ~(1<<13), мы побитово инвертируем число в скобках
1101 1111 1111 1111
//
светодиод в этот момент зажигается, потому что на плате BlackPill светодиод подтянут резистором к 3.3В.
101 строка: снова ждем 500 мс.
Теперь можно нажать F7, чтобы собрать проект, должно получиться так:
Теперь настроим отладчик, нажимаем "Настройки".
И сменим компилятор на 6.16 - он гораздо быстрее и переходим в вкладку "Debug" (Рисунок 18).
Во вкладке "Debug" выберем ST-Link и подтверждаем выбор.
Теперь можно подключить программатор к плате, вставить в USB компьютера и прошить, нажав F8. Если сразу не сработало, перепроверьте, не перепутаны ли контакты программатора и платы. После прошивки нужно нажать кнопку Reset на плате, чтобы светодиод начал мигать.
Очень надеюсь, что у вас получилось, потому что далее мы попробуем сделать то же самое, но изменив пару строчек кода и значения задержки с 500 на 100 мс (Рисунок 20):
Теперь на 98 строке появилась функция, которая включает пин 13 порта GPIOC.
На 99 строке "500" поменяли на "100".
На 100 строке соответственный пин выключает.
На 101 строке "500" поменяли на "100".
Снова соберем проект через F7 и зашьем проект нажатием F8, перезапустим МК нажатием RESET.
Если все сделали правильно, светодиод будет моргать гораздо чаще.
Домашнее задание
Для того, чтобы инвертировать значение бита, есть операнд "исключающее ИЛИ".
^=
Попробуйте использовать его для случая с регистрами, чтобы сократить число строчек кода в while(1) до двух.
Всем спасибо за то, что прочитали публикацию, жду ваших вопросов и исправления неточностей в комментариях или на почту: mgostev.it@gmail.com