Pull to refresh

STM32 Часть 1. Как поморгать светодиодиком

Level of difficultyEasy
Reading time5 min
Views21K

Приветствую!

Данная статья открывает цикл статей о программировании микроконтроллеров STM32 для новичков.

Сегодня я расскажу как написать моргание светодиодом двумя способами с использованием инструментов, труднодоступных в 2023 году.


Инструменты: Железо

  1. Плата BlackPill или BluePill

  2. Программатор 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 и увидим следующее окно:

Рис. 1. Стартовый экран CubeMx
Рис. 1. Стартовый экран CubeMx

Для создания нового проекта выберем пункт "Access to MCU selector". После непродолжительной (относительно периода вращения земли вокруг своей оси) подгрузки компонентов откроется новое окно, где нам предложено выбрать интересующий нас микроконтроллер.

Важное замечание: В статье здесь и далее будут приведены примеры для программирования на платах BluePill и BlackPill, как самых распространенных среди начинающих. У Вас может быть любая другая плата - NUCLEO, DISCOVERY, но процесс настройки периферии, тактирования и программирования не будет принципиально отличаться от приведенных в статье.

"Выберешь черную таблетку..."

Рис. 2. Окно поиска микроконтроллера. Первый микроконтроллер для BlackPill
Рис. 2. Окно поиска микроконтроллера. Первый микроконтроллер для BlackPill

Если у вас плата BlackPill, вам нужно выбрать вариант STM32F401CC или STM32F411CC - на самом камне обычно достаточно хорошо различимо выгравирована маркировка лазером.

Рис. 3. Окно поиска микроконтроллера. Второй микроконтроллер для BlackPill
Рис. 3. Окно поиска микроконтроллера. Второй микроконтроллер для BlackPill

Для BluePill:

Рис. 4. Окно поиска микроконтроллера. Микроконтроллер  для BluePill
Рис. 4. Окно поиска микроконтроллера. Микроконтроллер для BluePill

После выбора нужного вам чипа нажимаем "Start Project" в правом верхнем углу и попадаем в окно "Pinout & Configuration" (Рисунок 5). Здесь в разделе System Core -> Sys в пункте "Debug" выбираем "Serial Wire" и справа видим два пина микроконтроллера, выделенных зеленым. Это пины отладочного интерфейса, через который будет происходить прошивка и отладка программ.

Рис. 5. Включаем отладку
Рис. 5. Включаем отладку

Теперь включим ножку 13 порта C в режиме GPIO Output - Вход выход общего назначения в режиме выхода - мышкой на ножку клик, выбираем "GPIO Output" (Рисунок 6)

Рис. 6. GPIO output
Рис. 6. GPIO output

Для BluePill ножки совпадают - также выбираем пин 13 порта C.

Теперь переходим на вкладку "Project manager" и попадаем в меню "Project" (Рисунок 7).

В пункте "Toolchain/IDE" выбираем MDK - ARM для Keil или STM32CubeIDE для соответствующего варианта. Далее буду использовать Keil, выбираю его.

Рис. 7. Меню Project
Рис. 7. Меню Project

Для удобства в дальнейшей работе, откроем вкладку "Code generator" (Рисунок 8, левый столбец, вторая кнопка) и поставим галочку напротив "Generate peripheral initialization as a pair of '.c/.h' files per peripheral ". Что это дает - каждый периферийный модуль микроконтроллера будет инициализироваться не в main.c, а в своем файле, что больших проектах улучшает читаемость.

Рис. 8. Раздельные файлы
Рис. 8. Раздельные файлы

Теперь нажмем кнопку "Generate code" (Рисунок 9).

Рис. 9. Запуск
Рис. 9. Запуск

После успешного создания проекта открываем его (Рисунок 10).

Рис. 10. Проект успешно создан
Рис. 10. Проект успешно создан

Знакомство с проектом

Теперь рассмотрим окно проекта по умолчанию (Рисунок 11) 1 - дерево проекта, 2 - вывод компилятора, 3 - основное окно, в котором отображается текущий открытый документ, 4 - кнопка сборки проекта (F7), 5 - прошивка микроконтроллера (F8), 6 - Настройки проекта, 7 - переход в режим отладки (Ctrl+F5).

Рис. 11.Проект Keil
Рис. 11.Проект Keil

Передвинем консоль под дерево проекта, чтобы не занимать полезное место для кода

Рис. 12. Удобная консоль
Рис. 12. Удобная консоль

Развернем папку "Application/User/Core" и откроем файл main.c (Рисунок 13) Ctrl + колесико позволяет менять размер шрифта.

Рис. 13. Открываем нужное
Рис. 13. Открываем нужное
Рис.14. Основное тело программы
Рис.14. Основное тело программы

Рассмотрим, что здесь есть: вначале подключается main.h файл и файл gpio.h, если поставили галочку раздельной генерации файлов периферии. Далее объявлен прототип функции конфигурации периферии и затем попадаем в main() функцию, в которой инициализируется HAL (Hardware Abstraction Layer) фреймфорк и тактирование с портами ввода вывода (Рисунок 14).

Кодим

В функции main() после инициализации, которая происходит единожды, присутствует бесконечный цикл while(1), в котором и будет выполняться наша программа. Обратите внимание на секции USER CODE - если писать код внутри них, то при перегенерации проекта из CubeMX, ваш код не перетрется.

Добавим в секцию USER CODE BEGIN 3 включение светодиода на ножке GPIOC 13, ожидание 500 мс и его выключение (Рисунок 15).

Рис.15. Моргаем на регистрах
Рис.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, чтобы собрать проект, должно получиться так:

Рис. 16. Успешно собрали
Рис. 16. Успешно собрали

Теперь настроим отладчик, нажимаем "Настройки".

Рис. 17. Настройки
Рис. 17. Настройки

И сменим компилятор на 6.16 - он гораздо быстрее и переходим в вкладку "Debug" (Рисунок 18).

Рис. 18. Компилятор и отладка
Рис. 18. Компилятор и отладка

Во вкладке "Debug" выберем ST-Link и подтверждаем выбор.

Рис. 19. Выбираем отладчик
Рис. 19. Выбираем отладчик

Теперь можно подключить программатор к плате, вставить в USB компьютера и прошить, нажав F8. Если сразу не сработало, перепроверьте, не перепутаны ли контакты программатора и платы. После прошивки нужно нажать кнопку Reset на плате, чтобы светодиод начал мигать.

Очень надеюсь, что у вас получилось, потому что далее мы попробуем сделать то же самое, но изменив пару строчек кода и значения задержки с 500 на 100 мс (Рисунок 20):

Рис. 20. Больше HAL
Рис. 20. Больше HAL

Теперь на 98 строке появилась функция, которая включает пин 13 порта GPIOC.

На 99 строке "500" поменяли на "100".

На 100 строке соответственный пин выключает.

На 101 строке "500" поменяли на "100".

Снова соберем проект через F7 и зашьем проект нажатием F8, перезапустим МК нажатием RESET.

Если все сделали правильно, светодиод будет моргать гораздо чаще.

Домашнее задание

Для того, чтобы инвертировать значение бита, есть операнд "исключающее ИЛИ".

^=

Попробуйте использовать его для случая с регистрами, чтобы сократить число строчек кода в while(1) до двух.

Всем спасибо за то, что прочитали публикацию, жду ваших вопросов и исправления неточностей в комментариях или на почту: mgostev.it@gmail.com

Tags:
Hubs:
Total votes 25: ↑16 and ↓9+7
Comments39

Articles