/
Автор: Русин Н.В.
Теги: информационные технологии вычислительная техника обработка данных программирование микроэлектроника электроника микроконтроллеры
ISBN: 978-5-0062-4646-1
Год: 2025
Текст
Николай Русин
Освоение STM32
самостоятельно #1
Издательские решения
По лицензии Ridero
2025
УДК 004
ББК 32.973
Р88
Все права защищены. Ни одна часть этой книги не может быть
воспроизведена, скопирована или передана любым образом,
механическим, электронным, методом фотографирования, записью
видео или записи на аудио носитель или ещё каким-либо образом
без письменного разрешения автора.
Шрифты предоставлены компанией «ПараТайп»
Р88
Русин Николай
Освоение STM32 самостоятельно #1 / Николай Русин. - [б. м.] :
Издательские решения, 2025. - 254 с.
ISBN 978-5-0062-4646-1
УДК 004
ББК 32.973
В соответствии с ФЗ от 29.12.2010 №436-Ф3
ISBN 978-5-0062-4646-1
© Николай Русин, 2025
ОГЛАВЛЕНИЕ
Введение ......................................................................................................
Почему я написал книгу.........................................................................
Для кого написана эта книга ..............................................................
Как пользоваться данной книгой?....................................................
Как помочь автору и книге? ................................................................
Благодарности ............................................................................................
Список сокращений .................................................................................
1 Что такое микроконтроллер? ........................................................
1.1 Языки написания программ для МК ...................................
1.2 Программные и аппаратные средства книги .................
1.3 Экосистема ST для МК STM32 ................................................
2 Система тактирования STM32 - RCC-блок .............................
3 Создание пустого проекта заготовки в Keil IDE ...................
4 Настройка RCC - системы тактирования МК.........................
4.1 Встроенным способом Keil IDE ..............................................
4.2 Пишем рабочую функцию RCC с частотой работы
72 МГц ........................................................................................................
5 Порты ввода-вывода общего назначения GPIO ...................
5.1 Сведения о портах ввода-вывода и их настройки ....
5.2 Создаем первый полноценный проект и моргаем
светодиодом на плате .......................................................................
5.3 Создаем проект по использованию кнопки ...................
6 Обработка внешних прерываний EXTI .....................................
6.1 Основные сведения о прерываниях в STM32 ..............
6.2 Создаем проект по использованию прерывания для
обработки нажатия кнопки ............................................................
7 Универсальный синхронно-асинхронный
приемопередатчик USART ..................................................................
7.1 Основные сведения об USART / UART ............................
7.1.1 Флаги ...........................................................................................
7.1.2 Описание кадраUSART .....................................................
7.1.3 Скорость передачи ..............................................................
7.1.4 GPIO .............................................................................................
5
7
8
9
11
12
14
15
18
21
25
31
43
55
57
59
69
71
94
103
113
115
123
139
141
145
147
148
150
7.2 Регистры USART в MK STM32F1 ............................................ 151
7.3 Режимы работы USART, Polling, IT, DMA ........................... 163
7.4 Работа UART на прерывания. Режим IT ............................. 165
7.5 Создаем проект по использованию прерывания для
работы с UART ....................................................................................... 167
7.5.1 обработчик прерывания ................................................. 172
8 DMA. Прямой доступ к памяти ...................................................... 177
8.1 Основные сведения по работе DMA в MK STM32F1 .. 179
8.2 Регистры использования DMA в STM32F1 ....................... 188
8.3 Создаем проект по использованию UART1
в режиме DMA ....................................................................................... 198
9 Вывод последовательной информации через
интерфейс SWO ......................................................................................... 213
9.1 Основные сведения. SWO. ITM .............................................. 215
9.2 Создаем проект по применению SWO для вывода
данных ...................................................................................................... 218
10 Работа с STM32CubelDE ................................................................ 225
10.1 Создаем пустой проект-заготовку в STM32CubeIDE
227
10.2 Создаем рабочий проект в STM32CubelDE................... 242
Список литературы ................................................................................. 247
Об авторе ...................................................................................................... 251
ВВЕДЕНИЕ
После длительных поисков в интернете и крупных книжных
торговых сетях России стало понятно, что на русском языке нет
изданных книг по программированию микроконтроллеров
STM32. И это в XXI веке!
И ввиду этого я дал обещание коллегам и подписчикам, что
моя книга выйдет в свет сначала на русском языке, для того что
бы начать систематизировать изучение микроконтроллеров,
а потом, если будет запрос англоязычного сообщества, и на ан
глийском языке.
Однако в данной книге я не ставлю целью заменить офици
альные технические описания (datasheet) и справочные руко
водства (reference manual, далее - RM) от компании
STMicroelectronics, хотя бы по причине разного подхода к ин
формации в этой книге и в технических документах, не говоря
об их объеме, выраженном не в одной тысяче технических стра
ниц.
Поэтому здесь вы найдете множество пояснений, 150 рисун
ков различного характера и объема, более 20 таблиц, и все это
для того, чтобы можно было более детально и понятно рассмот
реть изучаемые темы.
Во втором издании данной книги материал был дополнен
пояснениями и рекомендациями исходя из часто задаваемых
мне вопросов, а также тех, которые были разобраны комьюнити
в Telegram-группе книги. Были масштабированы, где это было
возможно, все рисунки для более удобного анализа. Особо
большие рисунки бесплатно выложены в открытый доступ
в Telegram-канал книги.
В третьем издании по просьбе читателей и подписчиков до
бавлена большая глава с работой на МК STM32F1 в среде
STM32CubelDE от компании STMicroelectronics, где наглядно
5
и просто показаны принципы подключения библиотек, исполь
зования их из CMSIS для родной среды от ST и самое важное это насколько просто можно портировать код из KEIL IDE
в STM32CubelDE. Текущее издание книги прошло профессио
нальную корректуру и верстку в издательстве.
6
ПОЧЕМУ Я НАПИСАЛ КНИГУ
Я стал популяризовать программирование на STM32 на сво
ем личном YouTube-канале в далеком 2019 году по адресу:
https://www.youtube.com/@NRelectronics. Затронул в видеокон
тенте основополагающие темы, архитектуру, семейства, наборы
команд, применение, многообразие используемой периферии и,
конечно, ее использование на практике и т. д. и т. п. С тех пор я
получил множество комментариев и запросов на самые различ
ные темы и самую важную просьбу: сформировать этот контент
в некотором текстовом виде.
Благодаря взаимодействию с моими подписчиками
на YouTube-канале стало понятно, что не так просто подробно
охватить сложные темы на возможном личном веб-сайте. Ка
нал или блог - это отличные места, где можно освещать до
вольно специфичные и ограниченные по объему темы, однако
если нам нужно что-то детально объяснить и показать, не про
сматривая часы видеороликов, книга по-прежнему является
верным и удобным источником ответов. Она вынуждает нас си
стематизировать темы и дает нужное пространство для расши
рения изучаемого вопроса по мере нарастания сложности рас
сматриваемых тем. Видимо, я один из тех людей, которые
до сих пор считают, что чтение больших объемов текста на мо
ниторе - сложная задачка.
7
ДЛЯ КОГО НАПИСАНА ЭТА КНИГА
Это первая книга из серии «Освоение STM32 самостоятель
но» и, как я вижу, не последняя. Она адресована прежде всего
тем, кто начинает знакомиться с микроконтроллерами (далее МК) серии STM32 и заинтересован в том, чтобы научиться про
граммировать эти МК как можно быстрее и эффективнее. Но,
безусловно, книга будет полезна и тем, кто уже имеет некото
рый опыт в работе с МК и хочет быть в курсе тонких настроек
программирования разной периферии МК.
Читатель может и не иметь глубоких навыков и понятий язы
ка программирования Си, на котором написаны примеры кода
(примеры, в свою очередь, разобраны достаточно подробно),
а также не обязательно быть профессиональным разработчиком
цифровой электроники. Хорошим подспорьем будет опыт рабо
ты на платформе Arduino с применением языка Си: это умень
шит количество возникающих вопросов и даст старт на более
быстрое освоение приведенного материала.
Книга особенно важна для тех, кто хочет изучить архитекту
ру от ARM STM32F1 на более на высоком уровне, уметь произ
водить программирование системы на МК и стать полноценным
профессионалом в направлении программирования Embedded
Systems.
8
КАК ПОЛЬЗОВАТЬСЯ ДАННОЙ
КНИГОЙ?
Эта книга самодостаточна, и нет никакой необходимости
в использовании других книг. Возможно, хорошим дополнением
станет использование учебника по языку Си одного из таких ав
торов, как Прата или семейство Дейтел. В ней будут приводиться
ссылки на различные RM микроконтроллера для дополнитель
ной работы с ним, а также выдержки из RM в виде рисунков,
таблиц для разбора принципов работы, написания кода под ка
кую-либо необходимую периферию МК.
В начале каждого раздела приводятся теоретическая часть
темы, разбор ее основ, характеристики. Далее раздел дополнен
различными рисунками, таблицами, описанием и переводом RM
для работы с необходимыми битами регистров рассматривае
мой периферии, причем именно на русском языке. И заканчива
ется раздел созданием проекта по текущей теме с разбором
каждой строки - зачем, что и как делается, куда посмотреть
и что почитать в этой книге. В таком подходе создается правиль
но структурированное мышление с закреплением его путем вы
полнения практических работ.
Для работы понадобится доступная и недорогая отладочная
плата на МК STM32F103C8T6. Она может быть «старая», с на
званием BluePill, либо расширенная BluePill+ на точно таком же
МК, но с наличием пользовательской кнопки, что расширяет
круг решаемых задач и приводимых в данной книге примеров.
На обычных платах BluePill часто наблюдается установка так на
зываемых поддельных МК от товарищей из Поднебесной, будь
те внимательны и осторожны. Я настоятельно рекомендую имен
но плату BluePill+ из магазина NR. electronics Store на AliExpress
или Ozon, т. к. на ней подобное исключено. И тем не менее я
9
старался приводить примеры на обе платы в начале книги, раз
деляя включение кода комментариями и выдачей рекоменда
ций.
10
КАК ПОМОЧЬ АВТОРУ И КНИГЕ?
Благодаря вам, мои дорогие подписчики, я постоянно полу
чаю добрые и задушевные комментарии по материалам кана
ла. Само собой, эти слова делают меня чуточку счастливее
в течение долгого времени. Многие из вас также пожертвовали
денежные средства и продолжают переводить, чтобы помочь
развивать образовательный материал в нашей стране и стиму
лировать написание продолжения этой книги, чего бы очень
хотелось многим по итогам получения книги.
Я создал Telegram-канал и группу в VK. По вопросам разви
тия моего канала на YouTube, получения новостей из различных
областей подписывайтесь и обращайтесь сюда:
- t.me/NR_electronics
- vk.com/nrelectronics
плюс в Telegram-канал непосредственно для получения от
ветов от комьюнити по работе с STM32'.
Рисунок 1а. QR-код канала для получения ответов по книге
11
БЛАГОДАРНОСТИ
На обложке книги мои имя и фамилия, но эта книга была бы
невозможна без помощи вас, мои дорогие подписчики, а также
других людей и организаций, которые внесли большой вклад
в ее выход в свет.
В далеком 2012 году, как и многие в то время, я получил
первые практические уроки программирования на MK STM32F4
отладочной платы серии Discovery на семинаре-практикуме
компании
«Промэлектроника» совместно с
компанией
STMicroelectronics. Первая отладочная плата была мной получе
на именно в подарок, для изучения микроконтроллеров STM32.
С того времени и началась моя неразрывная жизнь по разработ
ке встраиваемых систем на этих МК [1].
Хочу отдельно поблагодарить Николая Пивнева за гигант
скую посылку с, как сейчас модно говорить, «ништяками» большим количеством различных отладочных плат Nucleo-64,
Brushless DC motor driver, двигателей, дисплеев, индикаторов
и т. п. Всем этим я до сих пор пользуюсь, и по ним часто выходят
обзорные и обучающие видео. Это способствовало качественно
му развитию канала. Первый экземпляр данной книги безвоз
мездно отослан именно Николаю.
Также отдельно хочу поблагодарить Массачусетский техно
логический институт за образовательную платформу edX, где
мне выпала честь учиться в 2014 году, закончить курс програм
мирования МК у профессора Джонатана Вальвано.
Подтверждение прохождения этого обучающего курса, кому
интересно, можно получить по ссылке:
https://verify.edx.org/cert/
82eb65160c284034b8e12690706a9ef1
12
Создатель и технический мейкер системы обучения
Образовательная платформа
Главные слова благодарности за эту книгу принадлежат мо
ей супруге Кристине, которая всегда поддерживала меня в труд
ные дни моей роботы, и малышке дочери Марии, которая посто
янно меня подталкивала: «Папа, ну когда ты закончишь!»
13
СПИСОК СОКРАЩЕНИЙ
API - Application Programming Interface
ARM - Advanced RISC Machine
EXTI - Extended Interrupts and Events Controller
GPIO - General-Purpose Input Output
HAL - Hardware Abstraction Layer
IDE - Integrated Development Environment
NVIC - Nested Vectored Interrupt Controller
Pin - Вывод
RCC - Reset and Clock Control
RM - Reference manual
SPI - Standard Peripheral Interface
UART - Universal Asynchronous Receiver Transmitter
ИМС - интегральная микросхема
MK - микроконтроллер
ШИМ - широтно-импульсная модуляция
14
1 Что такое
микроконтроллер?
Слово «микроконтроллер» произошло от английского
microcontroller, или MCU - Micro Controller Unit.
Родоначальниками разработки первых подобных ИМС были
такие компании, как MIT Instrumentation Laboratory, Intel
и Texas Instruments в далеких 1960-1970-х годах.
В любом цифровом устройстве должен быть кто-то главный
или тот, кто управляет остальными, отвечающий за основную ло
гику работы устройства. Этим главным является микроконтрол
лер, в особых применениях микропроцессор; о нем, надеюсь, бу
дет написано в моих книгах.
Внутри МК есть исполнительное ядро, которое может быть по
строено на различных архитектурах. Самые популярные сейчас:
- ARM (Advanced RISC Machine) - усовершенствованная
RISC-машина;
- RISC-V (reduced instruction set computer) - вычислитель
с набором упрощенных / редуцированных команд. Архитектур
ный подход к проектированию, в котором быстродействие увели
чивается за счет такого кодирования инструкций, чтобы их деко
дирование было более простым, а время выполнения - меньшим.
Чем они отличаются принципиально? RISC-V - это бесплат
ный базовый конструктор для разработки новых МК, когда
необходимо спроектировать МК с низкой стоимостью и относи
тельно небольшой производительностью. Архитектура ARM яв
ляется проприетарной; это означает, что ARM - полностью
коммерческое решение и любой разработчик ARM-микрокон
троллеров должен платить роялти (лицензионные отчисления)
в ARM Holdings.
На одном кристалле микроконтроллера располагаются:
- АЛУ - арифметико-логическое устройство;
- ОЗУ - оперативно-запоминающее устройство, или RAM;
- ПЗУ - постоянно-запоминающее устройство, или ROM;
- различная периферия: АЦП, ЦАП, UART, SPI и т. д.
На кристалле процессора практически всегда отсутствуют
Flash- и RAM-блоки памяти в целях удешевления и упрощения
производства процессора.
17
1.1 ЯЗЫКИ НАПИСАНИЯ
ПРОГРАММ ДЛЯ MK
В современном мире не утратил своей значимости и попу
лярности один язык, который был разработан еще в далеких
1970-х годах, - язык Си. Его автором был Деннис Ритчи. Этот
язык является самым популярным инструментом как для написа
ния кода на МК, так и для написания операционных систем:
Windows, Linux и др. Язык Си является основным во многих ком
паниях; это нам подтверждает Международный институт инже
неров по электронике и электрике (IEEE; см. рисунок 1б).
Рисунок 1б. Официальная статистика по популярности языков
от IEEE
18
В более сложных решениях применения МК STM32 исполь
зуется и язык C++, например для формирования графических
изображений, меню, потоков видео на дисплеях с высоким раз
решением - HMI. На этом языке построена бесплатная библио
тека TouchGFX от компании STMicroelectronics.
Интересные исследования провели инженеры из европей
ских университетов Universidade do Minho, Universidade NOVA
de Lisboa и Universidade de Coimbra. В их работе представлено
исследование времени выполнения, использования памяти
и энергопотребления 27 известных программных языков. Ре
зультаты показывают интересные находки, такие как более мед
ленные / быстрые языки, потребляющие энергии меньше / боль
ше, и то, как использование памяти влияет на потребление
энергии. Они показали, как использовать результаты для оказа
ния поддержки инженерам-программистам в принятии решения
о том, какой язык использовать, когда речь идет об энергоэф
фективности [2]. Результаты приведены на рисунке 2.
Рисунок 2. Эффективность 27 языков по параметрам
19
Вывод из исследований. Самым энергоэффективным, быст
рым в части исполнения и оптимальным по размеру кода явля
ется как раз язык Си.
20
1.2 ПРОГРАММНЫЕ
И АППАРАТНЫЕ СРЕДСТВА КНИГИ
В книге, которую вы держите в руках, используются следую
щие аппаратные средства:
- отладочная плата BluePill на МК STM32F103C8 (или СВ)
приведена на рисунке 3. Лучше использовать плату BluePill+
из магазина NR. electronics Store на ALiExpress или Ozon на точ
но таком же МК, но гарантированно оригинальном, что крайне
необходимо для использования;
- программатор-отладчик ST-Link может быть любой вер
сии: V2, V2.1, V3 и т. д. Приведен на рисунках 4 и 5. Из магазина
NR. electronics Store можно получить проверенный и надежный
программатор STLink-V2.1 за доступные средства и с быстрой
отгрузкой из РФ;
- преобразователь USB - UART. Приведен на рисунке 6 (ес
ли в наличии программатор версии V2, а в версии V2.1 он
встроенный);
- желательно наличие любого электронного мультиметра
для измерения напряжения, тока, выполнения прозвонки прово
дов, а также проводов DuPont, представленных ниже.
21
Рисунок 3. Отладочной платы BluePill на MK STM32F103C8
Распиновку отладочной платы из рисунка 3 можно найти
в Telegram-канале книги (зайти можно по QR-коду рисунка 1а)
в большом разрешении для лучшего анализа выводов.
Рисунок 4. Вид самого доступного программатора-отладчика
ST-Link V2 (без преобразователя USB - UART)
22
Рисунок 5. Различные варианты фирменного программатора-от
ладчика от компании ST - ST-Link V3
Рисунок 6. Варианты исполнения преобразователей уровней
USB - UART
По части ПО будем использовать популярную и надежную
профессиональную интегрированную среду разработки МК ARM Keil IDE 5.35. Ее можно использовать вполне бесплатно
и легально, об этом я рассказывал в своем видео на YouTubeканале (а теперь и на Rutube и VK Video) «Keil IDE. Установка,
настройка и бесплатное лицензирование». Ее установка выпол
няется со всеми настройками по умолчанию. Среда разработки
будет работать в бесплатном режиме для абсолютно всех се-
23
мейств микроконтроллеров до 32 кб написанного кода под
FLASH, а для наших исходников в этой книге этого более чем
достаточно. Бесплатную лицензию, как в видео, устанавливать
нет необходимости, т. к. она предоставляется только для се
мейств начального уровня STM32: F0, L0, G0.
Нет большой разницы, и в какой IDE работать с STM32;
вполне можно также использовать и IDE от компании ST STM32CubelDE. Для этого написана целая глава дополнительно.
Но основные примеры книги приведены именно под Keil IDE.
Возможно, появится вопрос: почему именно версия 5.35?
На момент написания книги есть последние версии 5.41 и 6. Для
нас это неважно. Но начиная с версии 5.36 удалена 5-я версия
встроенного компилятора, и это точно (!) создаст вам ошибки
в коде при использовании каких-либо дополнительных приме
ров из интернета, а также, что более важно, при работе с приме
рами книги.
24
1.3 ЭКОСИСТЕМА ST ДЛЯ МК
STM32
Компания ST предлагает широкий спектр своего программ
ного обеспечения для работы со своими микроконтроллерами,
своей экосистемой STM32Cube ecosystem:
Рисунок 7. Дерево программного обеспечения экосистемы от ST
25
STM32CubeMX - инструмент настройки для любого микро
контроллера STM32. Этот простой в использовании графический
пользовательский интерфейс генерирует код инициализации
на языке Си для ядер на Cortex-M и исходный код дерева
устройств Linux для ядер Cortex-A.
STM32CubelDE - интегрированная среда разработки. Осно
ванная на решениях с открытым исходным кодом, таких как
Eclipse или GNU C/C++ toolchain, эта среда IDE включает функ
ции составления отчетов о компиляции и расширенные возмож
ности отладки. Она также интегрирует дополнительные функции,
присутствующие в других инструментах экосистемы, такие как
генерация кода из встроенного в нее STM32CubeMX.
STM32CubeCLT - набор инструментов для разработки в ко
мандной строке с функциями компиляции кода, программирова
ния на плате и отладки.
STM32CubeProgrammer - инструмент программирования
микроконтроллеров. Он обеспечивает простую в использовании
и эффективную среду для чтения, записи, залочивания и про
верки устройств и внешней памяти с помощью широкого спек
тра доступных средств связи (JTAG, SWD, UART, USB DFU, I2C,
SPI, CAN ит.д.).
Семейство инструментов STM32CubeMonitor. Мощные ин
струменты мониторинга, которые помогают разработчикам точ
но настраивать поведение и производительность своих прило
жений в режиме реального времени.
STM32Cube MCU and MPU packages - пакеты микрокон
троллеров и микропроцессоров STM32Cube, предназначенные
для каждой серии STM32. Пакеты содержат все необходимые
компоненты встроенного программного обеспечения для рабо
ты с имеющимся набором периферийных устройств STM32. Они
включают драйверы (HAL, LL - низкоуровневые и т. д.), проме
жуточное программное обеспечение и множество примеров ко
да, используемых в самых разнообразных реальных случаях.
По аппаратной части компания ST предлагает также широ
кую номенклатуру программаторов-отладчиков, своих отладоч
26
ных плат Nucleo и шилдов к ним. Их внешний вид показан
на рисунках 5 и 8.
Рисунок 8. Программаторы-отладчики экосистемы от ST
В качестве видеолекции подробнее с наиболее популярны
ми отладчиками можно ознакомиться в видео:
1. «STM32. Урок 5. Отладчик и программатор STLINK-V3».
URL: https://youtu.be/KbbHvXaCjWU
2. «Программаторы STM32 для начинающих и профессио
налов». URL: https://youtu.be/KX3ckMj2cm0
Платы бюджетной серии Nucleo предлагаются с различными
МК и массогабаритными размерами:
27
Рисунок 9. Бюджетные отладочные платы экосистемы ST
В качестве видеолекции подробнее с наиболее популярны
ми отладками можно ознакомиться в видео:
«STM32. Самая удобная и универсальная отладка для всех
STM32». URL: https://youtu.be/yf-W_xkSfQY
Есть более интересные отладочные платы от ST, где помимо
МК и программатора-отладчика установлена дополнительная
периферия; это серия Discovery. Некоторые платы показаны
на рисунке 10.
28
Рисунок 10. Более развитые отладочные платы экосистемы ST Discovery
По одной из плат с рисунка 10 будет продолжение данной
книги в контексте более продвинутого программирования мик
роконтроллеров STM32, включая различную внешнюю перифе
рию, а особенно работы с дисплеями TFT-LCD на интерфейсе
mipi, использования при этом SDRAM, QSPI/OctaSPI, вывода
на них статического изображения или видео, создания НМI-меню систем управления.
Есть еще самые большие по части наличия периферии
и полнофункциональные отладочные платы от ST с множеством
интересной и сложной периферии — серия Evaluation, которая
показана на рисунке 11.
29
Рисунок 11. Топовые отладочные платы экосистемы ST Evaluation
Но цена плат серии Evaluation оставляет нам только надеж
ды, а их их доступность заставляет задуматься о невозможно
сти быстрого нахождения. Эта серия с очень интересными ре
шениями, но в наше время приходится каждый модуль или
микросхему находить отдельно, делать самостоятельно шилды
и собственные отладки. На своем канале я часто это практикую
и выкладываю в своем магазине NR. electronics Store
на AliExpress.
30
2 Система тактирования
STM32 - RCC-блок
Каждая электронная цифровая схема нуждается в некоем
способе синхронизации с другими компонентами схемы. Самый
простой и надежный способ - это использовать для синхрони
зации тактовый сигнал. Тактовый сигнал - это генерируемый
блоком, в нашем случае это блок - RCC, сигнал периодической
формы построения, по сути являющийся аналогом сердечного
ритма человека.
Тактовый сигнал - это обычный меандр - сигнал прямо
угольной формы со скважностью 2 или коэффициентом запол
нения 50%, что является одним и тем же электрическим пара
метром, математически обратным друг относительно друга:
Рисунок 12. Тактовый сигнал
Основной параметр тактового сигнала - это его частота.
Она показывает, сколько раз в секунду значение тактового сиг
нала изменится со значения «1» на значение «0». Единица изме
рения частоты - герцы. Частота равна: единицу поделить
на значение периода из рисунка 12 в секундах.
Абсолютно все микроконтроллеры STM32 тактируются
от разного числа следующих генераторов.
1. Внутренние RC-генераторы:
- HSI, High speed internal - внутренний высокочастотный
генератор;
- LSI, Low-speed internal - внутренний низкочастотный ге
нератор.
33
2. Внешние резонаторы/генераторы :
- HSE, High speed external - внешний высокочастотный ге
нератор;
- LSE, Low-speed external - внешний низкочастотный гене
ратор.
Но есть причины, которые говорят, что лучше использовать
внешнее тактирование, а именно:
- внешний генератор (или резонатор), а именно кварцевый,
обеспечивает более высокую точность по сравнению с внутрен
ней RC-цепью рассматриваемого в нашей книге МК STM32F103,
погрешность которого оценивается с точностью аж до 2,5%, осо
бенно когда рабочие температуры печатной платы с МК значи
тельно отличаются от температуры окружающей среды 25° С.
Это значение очень высокое;
- некоторая периферия, подключаемая к STM32, особенно
высокоскоростная, может тактироваться только от внешнего
кварцевого генератора, работающего на определенной частоте.
Как все же происходит тактирование от внешнего или внут
реннего генераторов? Частота генераторов не задает напрямую
частоту ядра МК или других периферийных устройств; она яв
ляется как бы константой и умножается или делится на необхо
димое значение для той или иной периферии согласно схеме
тактирования МК clock tree, приведенной на рисунке 13 для
МК нашей платы BluePill/+.
34
Рисунок 13. Блоки системы тактирования clock tree STM32F103
из его Electrical datasheet
Данная схема не так хорошо читается, как она же, разверну
тая в кодогенераторе CubeMX, представленная на рисунке 14.
35
Рисунок 14. Система тактирования clock tree МК STM32F103C8
из STM32CubeMX по умолчанию
36
Рисунок 14 и последующие аналогичные крупные схемы
тактирования в большом разрешении для более комфортного
анализа присутствуют в Telegram-канале книги в открытом до
ступе (зайти можно по QR-коду рисунка 1а).
Внешнее тактирование по умолчанию выключено и пред
ставлено в виде блоков, подсвеченных серым цветом:
- Input frequency 4-16 MHz, что означает возможный ис
пользуемый диапазон внешнего кварцевого резонатора (генера
тора) высокой частоты, HSE, от 4 до 16 МГц, обычно это частота
8 МГц;
- Input frequency 0-1000 KHz, что означает возможный ис
пользуемый диапазон внешнего кварцевого резонатора низкой
частоты, LSE, от 0 до 1000 кГц. Обычно эта частота составляет
32,768 кГц - такое значение удобно дальше преобразовывать.
Из-за отключенного LSE часовой мультиплексор RTC Clock Mux
неактивен, также серого цвета.
Активными являются блоки внутреннего тактирования HSI
RC и LSI RC с предустановленными на них частотами 8 МГц
и 40 КГц соответственно. При тактировании HSI тактовый сигнал
8 МГц от HSI-генератора поступает в три ветки, две из которых
очень важны, а именно на мультиплексор System Clock Mux
и на промежуточный делитель на 2 PLL-мультиплексора.
Блок основного PLL-источника делит тактовый сигнал
до 4 МГц и далее есть возможность выбирать коэффициент
умножения до 16 для получения максимальной системной так
товой частоты (System Clock Frequency, SYSCLK), равной 64 МГц,
и высокочастотного тактового сигнала (High (speed) Clock,
HCLK), также равного 64 МГц, хотя имеется предделитель шины
АНВ - АНВ Prescaler, которым частоту SYSCLK можно уменьшить
до 512 раз. После этой установки и следующего предделителя
блока АРВ1 Prescaler на частоту деления 2 система считается на
строенной и готовой к работе.
Это выглядит следующим образом:
37
Рисунок 15. Система тактирования clock tree STM32F103C8
из STM32CubeMX, настроенного на 8 МГц, от внутреннего гене
ратора HSI = 8 МГц; МК готов к работе
38
Настроенная частота HCLK = 64 МГц показана как один
из возможных примеров. Можно настроить на любую частоту
до 72 МГц, все зависит от установки коэффициентов деления
и умножения. На следующем рисунке показана настройка систе
мы тактирования на 72 МГц при наличии внешнего сигнала так
тирования.
В крайней правой части обоих последних рисунках видна
как раз тактированная периферия по шинам PCLK1 и PCLK2 для
таймеров и всей периферии, подключенной к шинам АРВ1
и АРВ2 соответственно. Особняком стоит периферия по шинам
АНВ, FCLK, а также частота работы системного таймера Cortex
System timer; все они затактированы от HCLK — тактовой часто
ты MCU.
39
Рисунок 16. Система тактирования clock tree STM32F103C8
из STM32CubeMX, настроенного от внешнего генератора HSE =
8 МГц; выведенное значение МК = 72 МГц (HCLK), и МК готов
к работе
40
Интересной и не менее полезной является петля сигнала
тактирования, или настройка Enable CSS. При ее выборе, как
на рисунке 16, задействовав внешний генератор в 8 МГц и в слу
чае если запаиваемый резонатор (генератор) будет бракован
ным или он по какой-то причине перестанет подавать свой сиг
нал тактирования, будет автоматически подключен внутренний
HSI на 8 МГц и работа изделия на этом МК будет продолжена
от HSI. Данная функция МК STM32 является защитной и повы
шает отказоустойчивость МК и всей встраиваемой системы, вы
полненной на данном МК.
В следующем разделе рассмотрим, как написать код по на
стройке RCC двумя способами - автоматизированным и универ
сальным - под все случаи использования МК STM32F103 и лю
бых других МК.
41
3 Создание пустого проекта
заготовки в Keil IDE
В этом разделе рассмотрим, как создать пустой проект или
шаблон в IDE для дальнейших проектов.
Запускаем программу Keil uVision5 двойным кликом по яр
лыку:
Рисунок 17. Запуск IDE Keil
На момент написания книги вышла 6-я версия среды разра
ботки Keil IDE, но мы будем использовать версию v5.35, т. к. она
наиболее проверена и отработана во множестве проектов боль
шого количества компаний в мире. Также мы будем использо
вать компилятор версии V5.06.
После запуска появится следующее окно:
45
Рисунок 18. Запущенная среда: IDE Keil
Затем нужно нажать на следующую пиктограмму:
Рисунок 19. Подготовка IDE Keil для работы с STM32F1
46
Рисунок 20. Установка Pack-поддержки SТМ32F1-семейства:
Keil::STM32F1xx_DFP версии не ниже 2.3.0
Необходимо согласно стрелкам рисунка 20 выбрать отме
ченные пункты и нажать на кнопку Install.
47
Рисунок 21. Появится окно, в котором нужно нажать «Да»
Если у вас возникли сложности на данном этапе, напишите
в комьюнити-чат этой книги согласно рисунку 1а, где для нее
приведен QR-код: есть возможность получения пака в группе
книги.
Для чего мы всё это проделывали? Чтобы добавить поддерж
ку семейства микроконтроллеров F1 для интегрированной среды
разработки Keil. Скачиваем, устанавливаем и продолжаем:
Рисунок 22. Создаем проект, выбираем в менюProject->New
uVision Projectи кликаем по выделенной строке
В появившемся окне сохраняем проект под любым именем,
например Test, и нажимаем кнопку «Сохранить». Появится окно
выбора подключаемого контроллера. Если у нас все прошло
штатно согласно рисункам 19-21, то появится окно со следую
щим содержанием, т. е. с наличием строки STMicroelectronics:
48
Рисунок 23. Должна появиться строкаSTMicroelectronics
49
Рисунок 24. Выбираем свое семейство и наш микроконтроллер
с платы BluePill/+ STM32F103C8 и нажимаем ОК
Совет. Иногда случается счастье и китайцы запаивают мик
роконтроллер STM32F103CB, что, безусловно, хорошо, т. к.
у этого МК в два раза большее (128 КБ) значение флеш-памяти,
чем у микроконтроллера с индексом С8. Но случается и так, что
запаивают и с индексом С6 этот МК с 32 КБ флеш-памяти или
просто подделку, которая вообще отказывается стартовать или
«глючит» при работе с кодом, а последних больше всего. Поэто
му я настоятельно рекомендую использовать проверенный мик
роконтроллер или использовать плату из магазина NR.
electronics Store, сделанного специально для вас, дорогие чита
тели,
на
ALiExpress
по
адресу:
aliexpress.ru/
item/1005007450671024.html
Затем нужно кликнуть по иконке Manage Run-Time
Environment, чтобы вызвать окно рисунка 26.
50
Рисунок 25. Кликнуть по иконке Manage Run-Time Environment
Рисунок 26. Выбираем следующие настройки «птичками»
CMSIS/CORE - мы подключаем библиотеку CMSIS к нашему
МК и можем пользоваться всей его периферией.
Device/Startup - подключаем ассемблерный файл для ини
циализации системных настроек, таблицы векторов прерывания
ит. д.
Эти настройки нужно выбрать обязательно, и они должны
быть подсвечены зеленым цветом, а не красным или каким-либо
другим, что свидетельствует о их неподключении к проекту или
какой-либо другой ошибке. Если это все же случилось, необхо
димо нажать на кнопку Resolve дважды и проверить еще раз.
51
Рисунок 27. Создался проект с минимально возможным набором
файлов для работы
Рисунок 28. Создаем пустой файл по следующему пути
52
Затем нажимаем на сохранение файла или нажимаем ком
бинацию клавиш Ctrl + S и называем файл main. с, а затем до
бавляем его в папку Source Group 1, как это показано на рисун
ке 29.
Рисунок 29. Добавляем рабочий файлmain. cв проект и папкуSource Group 1
Нам нужно добавить пока единственную строку в в main. c,
это:
#include «stm32f10x.h»
53
Рисунок З0а. Пустой проект после проверки нажатия Ctrl + F7
Согласно отображенным файлам и одному прикрепленному
мы создали пустой проект (заготовку, или шаблон, проекта)
в Keil IDE. На его основе можно начинать создавать следующие
наши проекты в этой книге для работы с МК.
Рисунок 30б. Необходимо выставить настройки компилятора
проекта
54
4 Настройка RCC — системы
тактирования МК
4.1 ВСТРОЕННЫМ СПОСОБОМ
KEIL IDE
Разработчики Packs (рисунок 20) - программных пакетов
библиотек и настроек для Keil - любезно преднастроили такти
рование RCC для МК STM32F103, и мы можем увидеть в на
стройках библиотеки CMSIS, как он настроен. Это продемон
стрировано на рисунке 31.
Рисунок 31. Преднастроенные параметры RCC STM32F103
На рисунке мы видим, что частота ядра микроконтроллера
SYSCLK уже выбрана в библиотеке путем раскомментирования
кода на значении 72 000 000 = 72 МГц в строке №114 файла
system_stm32f10x.c (показан на рисунке 31 слева). Это является
максимальной рабочей частотой МК STM32F103. Я сейчас не бе
ру в расчет, что каждый МК можно разогнать и использовать
на большем значении частоты, т. к. это может привести и часто
приводит к нестабильной работе периферии. Такая частота по-
57
лучилась путем преднастройки тактирования на HSE_VALUE
(uint_32t 8 000 000) = 8 МГц в строке №123 файла библиотеки
CMSIS stm32f10x.h (показан на рисунке 31 справа) и выполнения
необходимых делений, умножений частоты тактирования внеш
него резонатора.
Также есть возможность снизить частоту МК с 72 до 56, 48,
36 или 24 МГц, раскомментировав соответствующую строку и за
комментировав все остальные define согласно выбору на рисун
ке 31.
Это первый способ, автоматизированный, можно сказать, ко
гда тактирование настроено и его даже не нужно трогать, только
раскомментировать необходимое.
58
4.2 ПИШЕМ РАБОЧУЮ ФУНКЦИЮ
RCC С ЧАСТОТОЙ РАБОТЫ 72 МГЦ
Для того чтобы начать создавать нашу первую функцию на
стройки МК STM32F103, нам нужно выключить включенную
по умолчанию преднастройку тактирования на 72 МГц согласно
рисунку 31. То есть нам нужно ее просто закомментировать со
гласно рисунку 32.
Рисунок 32. Отключаем преднастроенное значение тактирова
ния МК
По сути, все они должны быть таким образом выключены.
Так как мы выключили ключевой дефайн в файле
system_stm32f10x.c, то вся настройка RCC-блока в файле стала
неактивной, текст кода стал подсвечен серым цветом, и мы те
перь хозяева настройки RCC нашего МК. Создаем нашу первую
функцию и назовем ее, например:
void SetSysClockTo72 (void)
{
}
59
Дальше в ней пишем код настройки системы тактирования
RCC на 72 МГц и HSE = 8 МГц, который не будет зависеть
от введенных ранее настроек файлов в библиотеке CMSIS. Это
позволит нам гибче настраивать тактирование, не зависеть
от неизвестных настроек по умолчанию и настраивать исходя
из схемной обвязки МК.
Так как на нашей плате BluePill/+ установлен внешний квар
цевый резонатор на 8 МГц, то будем, конечно, использовать его
как HSE. Резонатор обеспечивает более стабильное тактирова
ние, чем HSI.
Вначале кодом включаем внешнее тактирование HSE. Оно
устанавливается и сбрасывается программно битом №16 управ
ления RCC_CR (рисунок 33).
RCC-> CR |= RCC_CR_HSEON; //бит №16 HSEON
Рисунок 33. Биты управления регистра настройки тактирования
RCC_CR
Далее ожидаем стабилизации тактирования от HSE. Бит
устанавливается уже аппаратно, за это отвечает бит №17 HSERDY:
while (READ_BIT (RCC-> CR, RCC_CR_HSERDY == RESET)) {}
60
Затем нам нужно настроить два регистра работы с памятью
FLASH, встроенной в наш МК. Обычно принято, что если мы
не собираемся обращаться к FLASH, то и настраивать ее работу
не нужно. Но по сложившейся традиции настроек и для будущей
работы, чтобы не возвращаться к настройкам МК часто, мы это
все же сделаем:
FLASH-> ACR &= ~FLASH_ACR_PRFTBE;
FLASH-> ACR |= FLASH_ACR_PRFTBE;
Рисунок 34. Биты управления регистра настройки FLASH-памяти
В первой строке мы отключаем буфер предварительной вы
борки, а во второй строке запускаем его программно - бит
№4 согласно рисунку 34.
Далее для FLASH-памяти настраиваем битовое поле управ
лений задержкой - биты №0-2 согласно рисунку 34. Данный
бит представляет собой отношение периода такта системной ча
стоты (SYSCLK) к времени доступа к FLASH. Существует три ре
жима настройки:
- 000 - нет задержки, рекомендуется при системной часто
те от 0 до 24 МГц;
- 001 - задержка 1 цикл, рекомендуется при системной ча
стоте от 24 до 48 МГц;
- 010 - задержка 2 цикла, рекомендуется при системной
частоте от 48 до 72 МГц.
61
Мы настраиваем систему тактирования на максимальную ча
стоту, 72 МГц, поэтому выбираем последний из перечисленных
режимов работы:
FLASH-> ACR &= ~FLASH_ACR_LATENCY;
FLASH-> ACR |= FLASHACRLATENCY2; //это 010
В первой строке мы сбрасываем любую предустановленную
настройку, а во второй устанавливаем режим 010, т. к. define
FLASH_ACR_LATENCY_2 это 0x02 в шестнадцатеричной форме,
а в двоичной как раз 010.
Теперь необходимо обратиться к регистру настройки конфи
гурации RCC - RCC_CFGR и поработать с ним (рисунок 35).
Рисунок 35. Регистр настройки конфигурации RCC - RCC_CFGR
Устанавливаем следующие настройки регистра:
RCC-> CFGR &= ~RCCCFGRHPRE;
RCC-> CFGR |= RCC_CFGR_HPRE_DIV1;
В первой строке мы сбрасываем любую предустановленную
настройку,
а
во
второй
устанавливаем
режим
RCC_CFGR_HPRE_DIV1, что означает, что частоту SYSCLK мы
62
оставляем без деления, биты №4-7, хотя настройки бита реги
стра позволяют делить данную частоту до 512 включительно.
И у нас получается: частота HCLK = SYSCLK.
Затем настраиваем аналогичным способом бит PPRE2. Это
битовое поле определяет коэффициент деления для ши
ны АРВ2 МК:
RCC-> CFGR &= ~RCC_CFGR_PPRE2;
RCC-> CFGR |= RCC_CFGR_PPRE2_DIV1;
В первой строке мы сбрасываем любую предустановленную
настройку,
а
во
второй
устанавливаем
режим
RCC_CFGR_PPRE2_DIV1, что означает, что частоту HCLK мы остав
ляем без деления, биты №11-13, хотя настройки бита регистра
позволяют делить данную частоту до 16 включительно. И теперь
следующее равенство частот: PCLK2 = HCLK.
Затем практически точно таким способом настраиваем бит
PPRE1. Это битовое поле определяет коэффициент деления для
шины АРВ1 МК:
RCC-> CFGR &= ~RCC_CFGR_PPRE1;
RCC-> CFGR |= RCC_CFGR_PPRE1_DIV2;
В первой строке мы сбрасываем любую предустановленную
настройку,
а
во
второй
устанавливаем
режим
RCC_CFGR_PPRE1_DIV2, что означает, что частоту HCLK мы долж
ны поделить на 2, биты №8-10, однако настройки бита регистра
позволяют делить данную частоту до 16 включительно. И у нас
получается: PCLK1 = HCLK/2.
Далее настраиваем PLL-мультиплексор для вывода на рабо
чую максимальную частоту МК (HCLK = 72 МГц):
RCC-> CFGR &= (uint32_t) ((uint32_t) - (RCC_CFGR_PLLSRC |
RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
63
RCC-> CFGR |=
RCC_CFGR_PLLMULL9);
(uint32_t)
(RCC_CFGR_PLLSRC_HSE
|
Здесь мы в PLLSRC_HSE взводим 1, что означает, что мы ис
пользуем вход мультиплексора - HSE, а не вход - HSI/2. Бит
PLLXTPRE взводим в 0; PLLMULL присваиваем значение в дефайн PLLMULL9, т. е. умножаем частоту HSE на коэффициент 9.
Остальные биты очищаем в 0. Все эти биты настроек - из ри
сунка 35.
Теперь нам нужно включить этот PLL (его еще иногда назы
вают ФАПЧ):
RCC-> CR |= RCC_CR_PLLON;
т. е. мы записываем «1» в бит PLLON регистра RCC_CR со
гласно рисунку 33, бит №24.
Далее ожидаем, пока PLL разблокируется или запустится
по аппаратной установки «1» (бит №25):
while
(READ_BIT
(RCC_CR_PLLRDY)) {}
(RCC->
CR,
RCC_CR_PLLRDY)!=
Затем
необходимо
выбрать
на
мультиплексоре
SystemClockMux, какой вход будет использоваться для дальней
шей подачи сигнала тактирования на шину SYSCLK, а именно
вход, подключенный к PLLCLK:
RCC-> CFGR &= ~RCC_CFGR_SW;
RCC-> CFGR |= RCC_CFGR_SW_PLL; //10 - PLL
В
первой
строке
мы
сбрасываем
настройку
RCC_CFGR_SW в 0, а во второй устанавливаем режим
RCC_CFGR_SW_PLL, т. е. активируем рабочий вход мультиплексо-
64
pa PLLCLK (имеются еще входы
№0 и 1 на рисунке 35 в значении 10.
HSI
и
HSE); это биты
И последним этапом настройки является ожидание, пока
PLL не будет использоваться в качестве источника системного
тактирования, т. е. должны аппаратно установиться биты SWS
(биты №2 и 3 рисунка 35) соответствующим образом:
while
(READ_BIT
RCC_CFGR_SWS_PLL) {}
(RCC->
CFGR,
RCC_CFGR_SWS)!=
После успешного включения PLL, выставления всех режи
мов мультиплексоров и выбора различных шин можно перехо
дить к настройке GPIO, и дальнейшая работа с МК уже будет бо
лее интересной.
Листинг получившегося кода должен быть следующим:
void SetSysClockTo72 (void)
{
RCC-> CR |= RCC_CR_HSEON;
while (READ_BIT (RCC-> CR, RCC_CR_HSERDY == RESET)) {}
FLASH-> ACR &= ~FLASH_ACR_PRFTBE;
FLASH-> ACR |= FLASH_ACR_PRFTBE;
FLASH-> ACR &= ~FLASH_ACR_LATENCY;
FLASH-> ACR |= FLASH_ACR_LATENCY_2;
RCC-> CFGR &= ~RCC_CFGR_HPRE;
RCC-> CFGR |= RCC_CFGR_HPRE_DIV1;
RCC-> CFGR &= ~RCC_CFGR_PPRE2;
RCC-> CFGR |= RCC_CFGR_PPRE2_DIV1;
RCC-> CFGR &= ~RCC_CFGR_PPRE1;
RCC-> CFGR |= RCC_CFGR_PPRE1_DIV2;
RCC-> CFGR &= (uint32_t) ((uint32_t) ~ (RCC_CFGR_PLLSRC |
RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
65
RCC-> CFGR |= (uint32_t) (RCC_CFGR_PLLSRC_HSE |
RCC_CFGR_PLLMULL9);
RCC-> CR |= RCC_CR_PLLON; // Включаем ФАПЧ (PLL)
while
(READ_BIT
(RCC->
CR,
RCC_CR_PLLRDY)!=
(RCC_CR_PLLRDY)) {}
RCC-> CFGR &= ~RCC_CFGR_SW;
RCC-> CFGR |= RCC_CFGR_SW_PLL;
while
(READ_BIT
(RCC->
CFGR,
RCC_CFGR_SWS)!=
RCC_CFGR_SWS_PLL) {}
}
66
Рисунок 36. Настроенная система тактирования на 72 МГц ядра
МК STM32F103C8T6 при выбранном HSE = 8 МГц
67
5 Порты ввода-вывода
общего назначения GPIO
5.1 СВЕДЕНИЯ О ПОРТАХ ВВОДАВЫВОДА И ИХ НАСТРОЙКИ
Что такое GPIO? General-Purpose Input / Output в переводе
означает ввод / вывод общего назначения - важный компонент
любого микроконтроллера, с помощью которого он взаимодей
ствует с окружающим миром. В микроконтроллерах STM32 пор
ты именуются буквами А, В, С и т. д.: GPIOA, GPIOB, GPIOC...
В зависимости от конкретных аппаратных характеристик
каждого порта ввода-вывода, перечисленных в этой книге, каж
дый бит порта универсальных портов ввода-вывода (GPIO) мо
жет быть индивидуально настроен программным способом
в нескольких следующих режимах:
- Input floating - вход с отключенными подтягивающими
резисторами;
- Input pull-up - вход с подтяжкой к логической единице,
напряжению питания;
- Input-pull-down - вход с подтяжкой к логическому нулю;
- Analog - аналоговый вход (например, для АЦП);
- Output open-drain - выход с открытым коллектором (за
писали 1 - выход в высокоимпедансном состоянии, записали
О - выход прижат внутренним транзистором к земле);
- Output push-pull - выход «тяни-толкай» (записали 1 на выходе лог. 1, записали 0 - на выходе лог. 0);
- Alternate function push-pull - альтернативная функция
в режиме «тяни-толкай»;
- Alternate function open-drain - альтернативная функция
в режиме открытого коллектора.
Каждый вывод порта ввода-вывода свободно программиру
ется, однако к регистрам порта ввода-вывода необходимо обра
71
щаться как к 32-битным словам (доступ к полуслову или байту
запрещен). Назначение регистров GPIOx_BSRR и GPIOx_BRR разрешить атомарный доступ для чтения / изменения к любому
из регистров GPIO. Как работает этот атомарный доступ для из
менений? Он работает таким образом, что отсутствует риск оста
новки выполнения при возникновении прерывания или приори
тетного вытеснения задачи в RTOS-системах между доступом
на чтение, модификацию, изменение порта, в отличие от ис
пользования регистров ODRx.
Режим Analog. Внутри микроконтроллера есть аналогово
цифровые преобразователи, которые, как известно, должны
иметь аналоговые входы. В режиме Analog вывод микрокон
троллера подключается к аналоговому входу АЦП внутри мик
роконтроллера, как показано на рисунке 37. Кроме того, отклю
чается вся цифровая обвязка этого вывода для уменьшения
цифрового шума и энергопотребления. Сугубо исходя из моего
опыта, экономия тока потребления микроконтроллера при вы
боре такого режима пинов, когда они не задействованы, крайне
незначительна.
Режим Alternate function. В этом режиме выводом микрокон
троллера управляет внутренняя цифровая периферия, например
модуль USART, I2C, SPI... - Alternate Function input и Alternate
Function output на рисунке 37. Все выводы GPIO имеют внутрен
ний слабый подтягивающий резистор к питанию и слабый под
тягивающий резистор к земле, который может быть активирован
или отключен при настройке вывода в качестве входа.
72
Рисунок 37. Базовая структурная схема вывода порта I/O
По рисунку 37 необходимо сказать одну важную вещь каса
тельно уровней напряжения выводов МК. Она нам не пригодит
ся в написании первой программы, но вы будете ее постоянно
использовать в своей работе с МК. Согласно приведенной мар
кировке защитного диода VDD_FT (1), можно подключать на этот
пин линии связи с 5В-логикой. Грамотно это звучит так: данный
вывод (пин) толерантен к уровням 5В приходящего сигнала. То
есть при максимальном питании 3.3В МК мы можем заводить
сигналы с превышением этого уровня и данный вывод не вый
дет из строя, это очень важно. Если таковой маркировки на вы
воде нет, то на него нельзя подавать сигнал с напряжением вы
ше собственного напряжения питания при небольшом запасе,
обычно в 0.ЗВ; это, как правило, входы линий АЦП.
73
Таблица 1. Конфигурации настройки битов для настройки выво
дов практически любого вывода МК для любого порта
Таблица 2. Настройка MODE-битов при настройке вывода пина,
программирование согласно таблицам 16 и 17
В таблице 2 показаны настройки выводов портов для ра
боты с определенной скоростью. Данная настройка очень важ
на, т. к. позволяет настроить скорость работы вывода с какимлибо периферийным модулем (SPI и т. д.) на необходимую
нам скорость, в т. ч., например, уменьшить электромагнитное
излучение МК, если уменьшить скорость работы. Для особых
изделий это бывает необходимым требованием. Физически
реализована возможность изменения скорости нарастания
фронтов вывода в МК STM32, и это является аппаратной со-
74
ставляющей функции увеличения скорости работы вывода (пи
на).
Есть выводы, которые сразу после сброса устанавливаются
в определенные альтернативные состояния; это выводы интер
фейса JTAG/SWD, которые инициализируются как «плавающие»
входы (Input Floating) (CNFx [1:0] = 01b, MODEx [1:0] = 00b). Вы
воды интерфейса JTAG находятся в режимах input PU/PD после
сброса; это пины:
РА15: JTDI in PU (притянутый вверх);
PA14:JTCK in PD (притянутый вниз);
PA13:JTMS in PU (притянутый вверх);
PB4:JNTRST in PU (притянутый вверх).
При конфигурировании выводов как выходов значение, за
писанное в регистр выходных данных GPIOx_ODR (о нем по
дробнее дальше), поступает на выход. Выходной драйвер можно
использовать в двухтактном режиме (Push-Pull) или в режиме
с открытым коллектором (Open-Drain) (при выводе 0 активизи
руется только нижний n-МОП транзистор ключа) согласно рисун
ку 37 и таблице 1.
Регистр входных данных GPIOx_IDR защелкивает (устанавли
вает) данные, представленные на входных выводах, на каждом
тактовом цикле шины АРВ2. Все выводы GPIO имеют внутренние
резисторы, подтягивающие цепь вверх или вниз, которые можно
активировать, когда вывод конфигурируется как вход.
Установка или сброс атомарного бита
Программно не требуется отключать прерывания при про
граммировании - GPIOx_ODR на битовом уровне МК: можно
изменить только один или несколько битов в одном атомар
ном доступе записи. Это достигается простым программирова
нием в «1» регистра установки / сброса битов (GPIOx_BSRR для установки, или GPIOx_BRR - только для сброса) для вы
75
бора битов для изменения. Невыбранные биты не будут изме
нены.
Информация по работе с выводами
Выводы всех портов можно использовать как входы внеш
них прерываний (EXTI). В таком режиме мы напишем одну
из наших программ для обработки кнопки по прерыванию. Что
бы использовать вывод в этом режиме, он должен быть сконфи
гурирован как вход согласно таблице 1.
Существует также механизм блокировки, который позволяет
«заморозить» конфигурацию I/O портов. Когда к биту порта бы
ла применена последовательность процедуры блокировки, то
его конфигурацию больше нельзя изменить до следующего
сброса МК.
Альтернативные функции
Прежде чем использовать альтернативную функцию
по умолчанию, необходимо программировать регистр конфигу
рации битов порта согласно таблице 1. Для альтернативных
входных функций порт должен быть сконфигурирован в режиме
альтернативного входа (плавающего, подтянутого вверх или
притянутого вниз) и на вход должен подаваться внешний сигнал.
Примечание. Возможно также программно эмулировать сиг
нал на альтернативном входе, программируя контроллер GPIO.
В этом случае порт должен быть конфигурирован как альтерна
тивный выход. И очевидно, что на соответствующий вывод
не должен подаваться внешний сигнал, поскольку он будет
управляться программно с помощью контроллера GPIO.
Для альтернативных выходных функций порт должен быть
сконфигурирован в режиме альтернативного выхода (двухтакт
ного — Push-Pull, или с открытым коллектором — Open-Drain).
Для двунаправленных альтернативных функций порт дол
жен быть сконфигурирован также в режиме альтернативного
выхода (двухтактного или с открытым коллектором). В этом слу
чае конфигурируется и входной драйвер на режим плавающего
входа.
76
Если вы конфигурируете бит порта для альтернативной вы
ходной функции, это отключит его от регистра выходных дан
ных и подключит к выходному сигналу периферии чипа. Если
программа конфигурирует GPIO-вывод для альтернативной вы
ходной функции, но периферия не активирована, то ее выход
ной сигнал не определен.
Чтобы оптимизировать число интерфейсов, подключенных
к чипам в различных корпусах, возможно переназначить неко
торые альтернативные функции на другие выводы. Это достига
ется программно конфигурацией соответствующих регистров.
В этом случае альтернативные функции больше не подключены
к их исходным выводам.
Конфигурация вывода на «Вход»
Когда GPIO-порт программируется как «Вход»:
- отключается буфер выходных данных;
- активируется входной триггер Шмитта;
- в зависимости от конфигурации входа можно активиро
вать pull-up и (или) pull-down резистор;
- данные, представленные на GPIO-выводе, устанавливают
ся в регистр входных данных (IDR) каждый такт шины АРВ2;
- доступ чтения к регистру IDR возвращает состояние I/0выводов порта.
Рисунок 38 показывает конфигурацию вывода I/O порта как
входа.
77
Рисунок 38. Конфигурация плавающего, подтянутого вверх/
вниз входа пина
Конфигурация вывода на «Выход»
Когда GPIO-порт программируется как «Выход»:
- в режиме «с открытым коллектором»: значение «0» в ре
гистре выходных данных (ODR) активизирует транзистор пМОП ключа, а значение «1» оставляет ключ порта в высокоим
педансном состоянии (транзистор p-МОП никогда не активизи
руется);
- в режиме «двухтактный»: значение «0» в регистре выход
ных данных (ODR) активизирует транзистор n-МОП ключа,
а значение «1» - р-МОП;
- активируется входной триггер Шмитта;
- pull-up и pull-down резисторы отключаются;
- данные, представленные на I/O-выводе, устанавливаются
в регистр входных данных (IDR) каждый такт шины АРВ2;
- доступ чтения к регистру IDR возвращает состояние I/0выводов порта в режиме «с открытым коллектором»;
- доступ чтения к регистру ODR возвращает последнее за
писанное в него значение в режиме «двухтактный».
78
Рисунок 39 показывает конфигурацию вывода I/O порта как
выхода.
Рисунок 39. Конфигурация плавающего, подтянутого вверх /
вниз выхода
Конфигурация вывода на «Альтернативная функция»
Когда GPIO-порт программируется как «Выход»:
- подключается буфер выходных данных в режиме «с от
крытым коллектором» или «двухтактный»;
- буфер выходных данных управляется сигналом, приходя
щим из интерфейса (функция альтернативного выхода);
- активируется входной триггер Шмитта;
- pull-up и pull-down резисторы отключаются;
- данные, представленные на I/O-выводе, защелкиваются
в регистр входных данных (IDR) каждый такт шины АРВ2;
- доступ чтения к регистру IDR возвращает состояние I/0выводов порта в режиме «с открытым коллектором»;
- доступ чтения к регистру ODR возвращает последнее за
писанное в него значение в режиме «двухтактный».
Рисунок 40 показывает конфигурацию вывода I/O порта
в качестве альтернативной функции.
79
Рисунок 40. Конфигурация альтернативной функции вывода
Конфигурация вывода но аналоговый вход
Когда GPIO-порт программируется как аналоговый вход:
- отключается буфер выходных данных;
- деактивируется входной триггер Шмитта, обеспечивая ну
левое потребление для каждого аналогового входа;
- pull-up и pull-down резисторы отключаются;
- доступ чтения к регистру IDR возвращает «0».
Рисунок 41 показывает конфигурацию вывода I/O порта
в качестве аналогового входа.
80
Рисунок 41. Конфигурация высокоимпедансного аналогового
входа
Конфигурация всей периферии МК STM32F103:
Таблица 3. Настройка продвинутых таймеров TIM1 и TIM8
81
Таблица 4. Настройка таймеров общего назначения TIM2, TIM3,
TIM4, TIM5
Таблица 5. Настройка любых USARTx
82
Таблица 6. Настройка любых SPI
Таблица 7. Настройка любых I2S
83
Таблица 8. Настройка I2C
Таблица 9. Настройка bxCAN
Таблица 10. Настройка USB
84
Таблица 11. Настройка OTG_FS в режиме USB
Таблица 12. Настройка SDIO
85
Таблица 13. Настройка Analog
Таблица 14. Настройка FSMC
86
Таблица 15. Настройка разных выводов
Подробная настройка базовых основ работы МК: настройка
регистров GPIO
Доступ к периферийным регистрам должен осуществляться
по словам (32-битным).
Регистр конфигурации младшей половины порта (пины 07): GPIOx_CRL
Адрес смещения: 0x00.
Значение после сброса: 0x4444 4444.
Таблица 16. Биты регистра GPIOx_CRL портовx = A...G
Bits 31:30, 27:26, 23:22,19:18,15:14,11:10, 7:6, 3:2:
Биты конфигурирования порта x, т. е. настройки справедли
вы для абсолютно любого порта МК STM32F10x.
CNFy [1:0]: конфигурация режима:
В режиме входа (MODEy [1:0] = 00) - для пина в режиме входа:
87
- 00: Analog mode - аналоговый режим (подключен к АЦП
или ЦАП-у);
- 01: Floating input - вход с отключенными подтягивающи
ми резисторами (значение после сброса);
- 10: Input with pull-up / pull-down - вход с подтяжкой
к питанию или к земле;
- 11: Reserved - не используется.
В режиме выхода (MODEy [1:0]> 00):
- 00: General purpose output push-pull - выход в режиме
Push-pull;
- 01: General purpose output Open-drain - выход с откры
тым коллектором;
- 10: Alternate function output Push-pull - выход альтерна
тивной функции в режиме Push-pull;
- 11: Alternate function output Open-drain - выход альтер
нативной функции с открытым коллектором.
MODEy [1:0]: режим выводов порта, вход или выход. В ре
жиме выхода нужно выбрать максимальную частоту переключе
ния данного вывода МК; это также является мерой оптимизации
энергопотребления порта.
- 00: вход (значение после сброса);
- 01: выход, максимальная частота 10 МГц;
- 10: выход, максимальная частота 2 МГц;
- 11: выход, максимальная частота 50 МГц.
Регистр конфигурации старшей половины порта (пины 815): GPIOx_CRH
Адрес смещения: 0x04.
Значение после сброса: 0x4444 4444.
88
Таблица 17. Биты регистра GPIOx_CRH портов х = A...G
Это конфигурационный регистр для выводов порта с номе
рами от 8 до 15. Тут всё по аналогии с регистром GPIOx_CRL.
Регистр входных данных (GPIOx_IDR) (х = A...G)
Адрес смещения: 0x08.
Значение после сброса: 0x0000 ХХХХ.
Таблица 18. Биты регистра GPIOx_IDR портов х = A...G
Bits 31:16 Reserved
Reserved, must be kept at reset value (всегда читаются как
«0»).
IDRу: в этих битах содержится входное значение соответ
ствующего порта ввода-вывода, и оно доступно только для чте
ния.
Регистр выходных данных (GPIOx_ODR) (х = A...G)
Адрес смещения: 0х0C.
Значение после сброса: 0x0000 0000.
89
Таблица 19. Биты регистра GPIOx_ODR портовх = A...G
Bits 31:16 Reserved
Reserved, must be kept at reset value (всегда читаются как «0»).
Bits 15:0 ODRy: выходные данные порта. Они содер
жат входные значения соответствующего GPIO-порта.
Эти биты могут быть прочитаны и записаны программно.
Примечание. Для установки / сброса атомарных битов биты:
ODR могут быть индивидуально установлены и очищены путем
записи в регистр GPIOx_BSRR (х = A...G).
Регистр установки/сброса битов (GPIOx_BSRR) (х = A...G)
Адрес смещения: 0x10.
Значение после сброса: 0x0000 0000.
Таблица 20. Биты регистра GPIOx_BSRR портов х = A...G
С помощью этого регистра можно сбросить или установить
любой бит регистра ODR без операций «чтение - модифика
ция - запись» (как в ODR).
90
BRy: сбросить бит у регистра ODR порта ввода-вывода (у =
0...15).
- 0: не оказывает влияния на соответствующий бит ODRx;
- 1: сбрасывает в ноль соответствующий бит ODRx.
Важное примечание. Если установлены и BSx, и BRx, прио
ритет имеет BSx.
BSy: установить бит у регистра ODR порта ввода-вывода (у =
0...15).
- 0: не оказывает влияния на соответствующий бит ODRx;
- 1: устанавливает в единицу соответствующий бит ODRx.
Регистр сброса битов (GPIOx_BRR) (х = A...G)
Адрес смещения: 0x14.
Значение после сброса: 0x0000 0000.
Таблица 21. Биты регистра GPIOx_BRR портов х = A...G
С помощью этого регистра можно сбросить любой бит реги
стра ODR без операций «чтение — модификация - запись».
В эти биты можно только записать.
BRy: Сбросить бит у регистра ODR порта ввода-вывода (у =
0...15).
- 0: не оказывает влияния на соответствующий бит ODRx;
- 1: сбрасывает в ноль соответствующий бит ODRx.
Регистр блокировки конфигурации порта (GPIOx_LCKR) (х
= A...G)
Адрес смещения: 0x18.
91
Этот регистр используется для блокировки конфигурацион
ных битов порта после записи корректной последовательности
в 16 бит (LCKK) регистра. Значения битов [15:0] используются
для блокировки конфигурации GPIO. Во время блокирующей по
следовательности в LCKK значения LCKR [15: 0] не должны ме
няться. Когда блокирующая последовательность была записана,
конфигурация выбранных портов ввода / вывода может быть из
менена только после сброса микроконтроллера. Каждый LCKyбит блокирует возможность изменения четырех битов конфигу
рации порта (CRL, CRH).
Таблица 22. Биты регистра GPIOx_LCKR портов х = A...G
LCKK [16]: ключ блокировки:
- 0: блокировка конфигурации порта неактивна;
- 1: блокировка конфигурации порта активна.
GPIOx_LCKR заблокирован до следующего сброса микрокон
троллера.
Для блокировки необходимо выполнить следующую после
довательность:
1. Записать 1.
2. Записать 0.
3. Записать 1.
4. Прочитать 0.
5. Прочитать 1 (эта операция чтения не является обязатель
ной, а всего лишь подтверждает успешность установки блоки
ровки).
92
На заметку. Во время последовательной записи команд
LOCK значение LCK [15:0] не должно изменяться. Любая ошиб
ка в последовательности блокировки приведет к отмене блоки
ровки.
LCKy: эти биты могут быть прочитаны и записаны, но запись
можно произвести, только если бит LCKK = 0.
- 0: конфигурация пина номер у не заблокирована;
- 1: конфигурация пина номер у заблокирована.
Примечание. Настройка битов в регистрах для альтерна
тивных функций GPIO будем рассматривать по мере и объему их
появления в книге, чтобы из книги не делать тома произведения
«Война и мир».
Таблица 23. Карта регистров GPIO и значений сброса
93
5.2 СОЗДАЕМ ПЕРВЫЙ
ПОЛНОЦЕННЫЙ ПРОЕКТ
И МОРГАЕМ СВЕТОДИОДОМ
НА ПЛАТЕ
Цель нашего первого проекта покажется сначала достаточно
простой - поморгать светодиодом с любой частотой на плате
BluePill+ или «старой» BluePill Но кажущаяся простота ведет
за собой использование большего количества инструментов, на
строек и кода.
Возможно, у вас появится вопрос: почему с любой частотой?
Мы еще не изучали правильный инструмент для создания за
держек в работе микроконтроллера - таймер любого назначе
ния или системный таймер, SysTick. А сейчас нужно закрепить
навыки и инициализации, полученные ранее.
Мы с вами уже создали пустой проект-заготовку согласно
рисунку 31 и увидели, что тактирование микроконтроллера
по умолчанию преднастроено на 72 МГц, это нам сейчас и необ
ходимо.
С такого окна начинаем писать свой первый код:
Рисунок 42. Начальное окно первого проекта
94
Если мы используем отладочную плату BluePill (не из моего
магазина - BluePill+ где единственный светодиод установлен
на вывод РВ2) с рисунка 3, то единственный светодиод установ
лен на порте С и пине №13; кратко это записывается как вывод
PC13. Но мы используем во всех разделах книги плату BluePill+
для единообразия. Теперь необходимо правильно инициализи
ровать светодиод и написать для этого функцию настройки, ис
ходя из представленной ранее в таблицах по настройке GPIO
информации :
Рисунок 43. Функция настройки использования светодиода
на РВ2
Строка 5
Для каждого пина периферии МК необходимо включить его
непосредственное тактирование путем записи «1», несмотря
на то что тактирование ядра МК в 72 МГц проинициализировано
по умолчанию.
Рассмотрим структуру записи RCC_APB2ENR_IOPBEN.
RCC_APB2ENR - это регистр тактирования RCC шины
АРВ2;
IOPBEN - некоторая фиксированная аббревиатура 10input_output, РВ - PORT В, EN - enable (встречается в строке
5 даже дважды).
95
Также данный регистр представлен и описан в документа
ции, откуда и взяты эти аббревиатуры и сокращения для дефайнов CMSIS:
Рисунок 44. Включение тактирования различной периферии
STM32F10x
Строки 7-8
Настройка этих строк выполнена строго по таблице 16 кни
ги. В строках 7 и 8 настраиваем РВ2 на выход с частотой работы
выхода 2 МГц. Этого достаточно.
В строках 9-10 настраиваем также на выход в режиме
Push-pull.
Далее нам необходимо вызвать нашу функцию в основной
функции main ():
Рисунок 45. Вызываем написанную функцию
96
Следующим шагом описываем алгоритм включения и вы
ключения светодиода на РВ2:
Рисунок 46. Алгоритм моргания светодиодом на РВ2
Строка 20
Включаем светодиод согласно таблице 20.
Строки 22, 23
Произвольная задержка пустым циклом for для свечения све
тодиода. Она примерно соответствует реальной одной секунде.
Строка 25
Выключаем светодиод согласно таблице 21.
Строки 27, 28
Произвольная задержка пустым циклом for для задержки
светодиода в выключенном состоянии. Она примерно соответ
ствует реальной одной секунде.
Совет. В конце каждого примера будет приводится полный
листинг каждой программы, чтобы можно было легко и быстро
97
пробежаться по коду и не перечитывать при этом все коммента
рии для строк кода. Это также ускорит набор программы в среде
IDE Keil. А я рекомендую набирать весь текст программы имен
но вручную, чтобы, как говорят, руки привыкали к набору про
грамм и лучше отрабатывалась моторная память. Ведь так легко
скачать готовую программу с github, и это как раз менее полез
но в начале пути профессионального программиста.
Весь листинг программы представлен ниже.
#include «stm32f10x.h»
void PINB_2_INIT (void) //LED PB2
{
RCC-> APB2ENR |=RCC_APB2ENR_IOPBEN;//RCC
GPIOB-> CRL &= ~GPIO_CRL_MODE2_0;//0: Выход, макси
мальная частота 2 МГц — очистить разряды MODE;
GPIOB-> CRL |= GPIO_CRL_MODE2_1;//1: Выход, максималь
ная частота 2 МГц;
GPIOB-> CRL &= ~GPIO_CRL_CNF2_0;//00: General purpose
output push-pull — выход в режиме Push-pull;
GPIOB-> CRL &= -GPIO_CRL_CNF2_1;//00: General purpose
output push-pull - выход в режиме Push-pull;
}
int main (void)
{
PINB_2_INIT ();
while (1)
{
GPIOB-> BSRR |= GPIO_BSRR_BS2;
98
for (int i = 0; i <4500000; i++) {
};
GPIOB-> BSRR |= GPI0_BSRR_BR2;
for (int i = 0; i <4500000; i++) {
};
}
}
В IDE она выглядит аналогично:
Рисунок 47. Набранная в Keil IDE программа
Для компиляции и сборки проекта нажимаем F7 или кнопку
99
IDE согласно рисунку 48, и если мы всё сделали правильно
и не опечатались при вводе программы, приведенной в листин
ге, то должны наблюдать информацию, как в окне на рисун
ке 49.
Рисунок 48. Пиктограмма компиляции и сборки
Рисунок 49. Результаты компиляции и сборки
Для того чтобы запрограммировать нашу плату, нам необхо
димо выбрать программатор ST-Link Debugger в настройках IDE,
и достаточно нажать F8 в IDE Keil или кликнуть по пиктограмме
согласно рисунку 50.
100
Рисунок 50. Пиктограмма программирования МК на BluePill/+
Результат программирования МК показан в окне рисунка 51.
Рисунок 51. Результат программирования МК в IDE Keil
На вашем столе должен заморгать светодиод с частотой
примерно 1 раз в секунду.
Если светодиод у вас не заморгал сразу, то нужно проверить
все настройки IDE Keil согласно рисункам 52 и 53 и повторить
процесс заливки вашего скомпилированного кода, обязательно
проверив, какой версией компилируется код. Необходимо вер
сией 5.06.
101
Рисунок 52. Установить активным пунктReset and Run
Рисунок 53. Здесь, наоборот, нужно выключить пунктEnable
102
5.3 СОЗДАЕМ ПРОЕКТ
ПО ИСПОЛЬЗОВАНИЮ КНОПКИ
Так как на плате BluePill отсутствует какая-либо кнопка, мы
можем ее подключить через сокетку (по-другому - макетку)
Breadboard и провода Dupont (рисунок 3 в начале книги), под
ключив плату и кнопку на ней. Но это очень ненадежные соеди
нения, они могут как плохо «контачить», так и постоянно выхо
дить из своих пазов. Такая работа может внести непонимание,
почему не работает кнопка, что для нас недопустимо. Лучше
произвести запайку всех соединений для надежного подключе
ния к плате BluePill - я рекомендую такой вариант. А также,
чтобы избавить себя от этих всех работ и возможных сложно
стей, напишем программу на такую же плату BluePill, но из ма
газина NR. electronics Store, с наличием кнопки на борту. Плата
представлена на рисунке 54 с именем BluePill+, а также на об
ложке книги.
103
Рисунок 54. Современная реализация народной платы BluePill
Она более функциональная и продвинутая за счет:
- отсутствия перемычек-джамперов, они заменены на удоб
ные кнопки;
- наличия пользовательской кнопки, которая нам и нужна
(установлена на вывод РА0 микроконтроллера STM32F103);
- современного разъема USB Туре-С;
- наличия установочного места под микросхему W25Q, ко
торая позволяет увеличить размер FLASH-памяти платы;
- улучшенной системы фильтрации опорной линии питания
МК - VREF/VDDA;
Отличие только в том, что светодиоды располагаются на раз
ных выводах - РС13 на плате BluePill, но РВ2 на плате
BluePill+. В остальном они для нас одинаковы и могут быть вза
имозаменяемы. Рекомендую всегда использовать именно плату
BluePill+.
Начинаем писать, как на рисунке 55:
104
Рисунок 55. Стандартное и необходимое для нас начало
Затем начнем формировать более информативный код.
Один из вариантов его формирования - это использование
макросов:
Рисунок 56. Первые макросы в нашей книге
Они означают ровно то, что написано:
LED_PB2_ON () - включение светодиода на пине РВ2;
LED_PB2_OFF () - выключение светодиода на пине РВ2.
Их определения заменяются рабочим кодом из таблиц
20 и 21, это вопросов не должно вызвать.
Далее определяем, инициализируем и сразу обнуляем гло
бальную переменную для записи в нее значения считывания
кнопки по пину РА0:
105
Рисунок 57. Переменная состояния нажатия кнопки
Далее инициализируем кнопку на пине РА0 (про ее подтяж
ку к земле - в следующем разделе, чтобы не перегружать вас ин
формацией и тонкими особенностями):
Рисунок 58. Код согласно таблицам 1, 2 и 16
Далее инициализируем работу светодиода на пине РВ2:
Рисунок 59. Код согласно таблицам 1, 2 и 16
Далее формируем тело основной
main ():
106
рабочей
программы
Рисунок 60. Здесь основной алгоритм функционирования нашей
программы
Строки 31, 32
Вызываем соответствующие функции для инициализации
кнопки и светодиода.
Строки 34-36
Существует мнение, что необходимо инициализировать ра
боту линий SWD для программирования МК. Да, это действи
тельно так, но не при работе с библиотекой CMSIS, а при работе
с библиотекой HAL. Поэтому тут код показан в выключенном со
стоянии, т. е. закомментированным.
Строка 38
Строка непрерывного цикла while, откуда программа нико
гда не выйдет при аргументе, равном единице.
Строка 40
Считываем состояние кнопки на линии РА0 через макрос
107
CMSIS - READ_BIT и записываем это в глобальную переменную
Button State (). Существует мнение, что записывать лучше в ло
кальную переменную; я оставлю данный спор за рамками этой
книги, т. к. сейчас для нас это не представляется проблемой.
Строка 42
Именно тут сосредоточен алгоритм включения светодиода
по нажатию на кнопку.
Считанное состояние (а это только два значения в двоичной
форме: 0 или 1) сравнивается (оператор ==) с 1; если равно, то
включаем светодиод - строка 44, если не равно, то выключа
ем - строка 48.
В данной программе нет ничего сложного, никаких подвод
ных камней, разве что наличие дребезга (рисунок 61) на кнопке.
Мы здесь не будем глубоко рассматривать, как это обойти, ска
жу лишь, что это можно сделать двумя путями - аппаратно
и программно. Первый способ - это подключить к линии кноп
ки конденсатор, который бы не вносил сильно видимых задер
жек при нажатии кнопки. Это более простой способ, но, по сути,
это является аппаратным «костылем», т. к. приводит к непрерыв
ному «подгоранию» обкладок кнопки и выхода ее из строя в са
мый неудобный момент. Правильный путь - это второй способ,
программный, а тут присутствуют различные алгоритмы по об
работке настоящих и ненастоящих нажатий кнопки, что выходит
за рамки данной книги.
108
Рисунок 61. Дребезг контакта кнопки по ее нажатии и отпуска
нии
Весь листинг программы по работе с кнопкой представлен
ниже.
#indude «stm32f10x.h»
#define LED_PB2_ON () GPIOB-> BSRR |= GPIO_BSRR_BS2;
#define LED_PB2_OFF () GPIOB-> BSRR |= GPIO_BSRR_BR2;
uint8_t Button_State = 0;
void PINA_0_INIT (void) //Button РА0
109
{
RCC-> APB2ENR |=RCC_APB2ENR_IOPAEN;//RCC
GPIOA-> CRL &= ~GPIO_CRL_MODE0_0;//Bхoд
(значение после сброса);
GPIOA-> CRL &= ~GPIO_CRL_MODE0_1;//Bход
(значение после сброса);
GPIOA-> CRL &= ~GPIO_CRL_CNF0_0;//Floating
input — вход с подтяжкой к питанию или
к земле (значение после сброса);
GPIOA-> CRL |= GPIO_CRL_CNF0_1;//Floating
input - вход с подтяжкой к питанию или
к земле (значение после сброса);
}
void PINB_2 INIT (void) //LED PB2
{
RCC-> APB2ENR |=RCC_APB2ENR_IOPBEN;//RCC
GPIOB-> CRL &= ~GPIO_CRL_MODE2_0;//0: Выход,
максимальная частота 2 МГц;
GPIOB-> CRL |= GPIO_CRL_MODE2_1;//1: Выход,
максимальная частота 2 МГц;
GPIOB-> CRL &= ~GPIO_CRL_CNF2_0;//00: General
purpose output push-pull — выход в режиме
Push-pull;
GPIOB-> CRL &= ~GPIO_CRL_CNF2_1;//00: General
purpose output push-pull — выход в режиме
Push-pull;
}
int main (void)
{
110
PINB_2_INIT ();
PINA_0_INIT ();
while (1)
{
Button_State = READ_BIT (GPIOA-> IDR,
GPIO_IDR_IDR0);
if (Button State == 1)
{
LED_PB2_ON 0;
}
else
{
LED PB2_OFF ();
}
}
}
111
6 Обработка внешних
прерываний EXTI
6.1 ОСНОВНЫЕ СВЕДЕНИЯ
О ПРЕРЫВАНИЯХ В STM32
В этой части книги мы рассмотрим одну особенность вы
полнения программ - прерывание. Ранее мы уже касались
необходимости порядка выполнения команд в программе.
В микроконтроллере есть специальный регистр - счетчик ко
манд, в котором хранится адрес команды, который выполняет
ся в МК в данный момент. По умолчанию команды выполняют
ся последовательно, а значение счетчика команд каждый раз
увеличивается на 1. Есть также команды переходов, которые
могут записать в счетчик команд новое значение, и МК будет
выполнять команду, расположенную по этому адресу.
Но есть еще один случай, когда порядок выполнения команд
может измениться; это прерывание. Прерывание - это наруше
ние последовательной работы процессора и выполнение задан
ной функции с последующим возвратом в основную программу.
Прерывание может возникать как реакция на некоторое собы
тие. Событие - это любое происшествие в МК, например завер
шение приема блока данных. Изменение логического состояния
вывода МК: был низкий уровень, а стал высокий, как и наобо
рот, - это тоже событие. Событием, вызывающим прерывание,
может быть и некорректная арифметическая операция - напри
мер, деление на 0; это как раз ошибка.
Фактически прерывание реализует мгновенную реакцию
программы на некоторое событие в системе. Реакция програм
мы - это выполнение некоторой заранее определенной по
следовательности команд. Упрощенно можно сказать, что пре
рывание - это некоторая функция, которая вызывается при
возникновении события. Выполнение программы в этом случае
осуществляется следующим образом: при поступлении запроса
115
на прерывание текущее значение счетчика команд и состояние
процессора записываются в стек, и вызывается функция обра
ботки прерывания. Все команды в этой функции выполняются,
и происходит возврат к нормальному порядку выполнения ос
новной программы.
Событий в системе может произойти много, и реакция
на каждое из них должна быть своя, поэтому предусмотрено
большое количество прерываний. Также существуют разные ме
ханизмы оповещения процессора о возникшем прерывании,
иначе говоря, механизмы запроса на прерывание. Прежде всего
прерывания делятся на радиальные и векторные.
Радиальные прерывания не требуют проведения цикла чте
ния по системной магистрали, а векторные требуют. Каждый
источник радиального прерывания, т. е. модуль, который это
прерывание запрашивает, подключен к модулю управления
прерываниями процессора по отдельной, независимой линии.
Поэтому модуль обработки прерываний, получив сигнал по од
ной из линий, сразу знает, от какого устройства поступил за
прос.
В случае векторного прерывания линии, по которым пере
дается запрос от нескольких устройств, так или иначе мульти
плексируются в одну и модуль управления прерываниями
по наличию сигнала в этой линии знает только то, что какое-то
устройство запросило прерывание, а какое именно - не знает.
Поэтому МК (или процессор) должен отправить запрос по си
стемной магистрали (шине), а устройство, которое требует пре
рывания, отправляет по шине данных некий код, который на
зывают вектором прерывания.
Подробная работа показана на информационном рисун
ке 62.
116
Рисунок 62. Работа прерывания в виде графика
117
В памяти МК всегда присутствует таблица векторов преры
вания (NVIC). В этой таблице определяется, какая функция обра
ботки соответствует каждому вектору, а именно в ней хранится
адрес начала этой функции. То есть, получив вектор, процессор
по таблице векторов прерываний находит адрес функции обра
ботки прерывания и вызывает эту функцию, т. е. записывает зна
чение из таблицы в счетчик команд, предварительно сохранив
старое в стеке. Главное достоинство радиальных прерываний скорость реакции процессора на них. Но за это приходится рас
плачиваться отдельной линией связи с каждым модулем - ис
точником прерывания, поэтому радиальные прерывания в си
стеме обычно обслуживают критические события.
Векторные прерывания обеспечивают большую гибкость:
для назначения функции обработки нового прерывания доста
точно только внести изменения в таблицу векторов прерываний.
Немного про внешние и внутренние, маскируемые и немас
кируемые прерывания.
Основные режимы прерываний показаны на рисунке 62. Как
могут быть полезны прерывания для решения задач?
Приведу простой пример. Пусть задача состоит в том, что на
до вскипятить обычный чайник. В простейшем случае мы долж
ны набрать воды и поставить чайник на газовую плиту, включить
ее и ждать, пока он не закипит; т. е. необходимо постоянно про
верять, не закипел ли чайник. В этом случае ничего другого де
лать мы не сможем, поскольку приходится постоянно монито
рить чайник.
Но мы можем купить чайник со свистком; тогда можно по
ставить чайник и идти заниматься другими делами, например
изучать МК STM32 по данной книге, а как только чайник засви
стит - бежать на кухню и выключать плиту. Свисток в этом слу
чае - как сигнал запроса прерывания. Как только мы слышим
свисток, то прерываем выполнение своих других дел и бежим
обрабатывать прерывание - снимать чайник и выключать пли
ту, а потом возвращаемся к своим основным делам по програм
мированию STM32.
118
Принцип использования прерывания в МК тот же. Сначала
нужно разрешить использование прерываний в программе, т. е.
включить модуль обработки прерываний. Затем разрешить вы
зов нужного прерывания - надеть на чайник свисток. Оба этих
действия осуществляются путем записи в соответствующие реги
стры. Дальше нужно описать обработчик прерывания (callback) ту последовательность действий, которую нужно выполнить при
возникновении прерывания, поместив в таблицу векторов пре
рывания ссылку на этот обработчик.
При возникновении события модуль обычно устанавливает
флаг события - тоже записывает данные в очередной регистр.
Затем проверяет регистр прерываний, и если прерывание раз
решено и модуль обработки прерываний включен, то формиру
ется запрос на прерывание и вызывается соответствующая
функция. Графически это показано на рисунке 63.
Рисунок 63. Как работает прерывание
119
Это довольно краткое, но всеобъемлющее описание, как ра
ботает прерывание. Данная тема очень обширна и заслуживает
написания отдельной книги.
Справочный материал. Новые полученные основные опреде
ления:
Event (в переводе: событие) - это любое действие, проис
шедшее в микроконтроллере. Обычно выставляется флаг, без
перехода в обработчик прерывания, если только мы не запро
граммировали по-иному.
Приоритет указывает на то, какое прерывание будет выпол
няться раньше. Уровни приоритизации - это отдельная большая
тема для обсуждения.
Interrupt (в переводе: прерывание) - остановка выполнения
основной программы МК и выполнение заданного кода в самом
теле прерывания в соответствии с приоритетом.
NVIC (Nested vectored interrupt controller) - контроллер вло
женных векторных прерываний; управляет порядком работы
всех имеющихся прерываний и содержит в себе таблицу, где пе
речисляются эти прерывания с определенным приоритетом вы
зова.
Рисунок 64. Виды прерываний
120
Различают следующие типы прерываний:
- внешние - это аппаратные прерывания;
- внутренние - это так называемые системные исключения
(например, Hard Fault).
Внешние прерывания происходят асинхронно, т. е. в случай
ный момент времени в процессе выполнения программы (на
пример, от внешних устройств: других МК, различных датчиков
и т. п.). Они бывают:
- маскируемыми, которые могут быть замаскированы про
граммными средствами МК;
- немаскируемыми, запрос от которых таким образом замас
кирован быть не может.
Маска прерывания представляет собой двоичный код, разря
ды которого поставлены в соответствие запросам или классам
прерывания. Маска загружается командой программы в регистр
маски. Состояние «1» в данном разряде регистра маски разре
шает, а состояние «О» запрещает (маскирует) прерывание теку
щей программы от соответствующего запроса.
Внешние аппаратные прерывания инициируются контролле
рами периферийного оборудования. Источники сигналов пре
рываний подключаются либо к выводу немаскируемых преры
ваний МК, либо к выводу маскируемых прерываний.
В STM32F103 установлено ядро Cortex-МЗ, где применены
различные решения, позволяющие уменьшить также задержку
обработки прерываний. Первым решением является механизм
«цепочечной» (tail-chaining) обработки прерываний, а вторым
методом, увеличивающим эффективность подсистемы обработки
прерываний, является поддержка «опоздавших» исключений. Ес
ли во время сохранения контекста для обработки какой-либо ис
ключительной ситуации возникнет исключение с более высоким
приоритетом, то процессор сначала обработает это «опоздавшее»
исключение; если исключение №1 (имеет меньший приоритет)
возникнет на несколько тактов раньше исключения №2 (имеет
больший приоритет), то МК поведет себя так, что после сохране
ния контекста будет запущен обработчик исключения №2.
121
Некоторые моменты более точнее рассказаны и показаны
на примерах в моем обучающем видеоуроке:
«STM32. Урок 11. NVIC. Прерывания. Контроллер прерыва
ний NVIC». URL: https://youtu.be/FEisXTiiy1k
122
6.2 СОЗДАЕМ ПРОЕКТ
ПО ИСПОЛЬЗОВАНИЮ
ПРЕРЫВАНИЯ ДЛЯ ОБРАБОТКИ
НАЖАТИЯ КНОПКИ
Цель нашего следующего проекта - это научиться приме
нять прерывания на практике на основе уже разработанного ко
да по обработке кнопки и светодиода из раздела 5.3.
Мы с вами уже научились создавать пустой проект согласно
характеристикам рисунка 30 и убедились, что тактирование
микроконтроллера по умолчанию преднастроено на 72 МГц; нам
этого сейчас как раз достаточно. Создаем пустой проект из раз
дела 3 или берем заготовленный пустой проект, созданный ра
нее.
С этого окна начинаем писать код этого проекта:
Рисунок 65. Подключаем только один файл библиотеки CMSIS
Для проекта можно использовать плату BluePill с подклю
ченной на выводе РА0 через сокетку Breadboard кнопкой и про
вода Dupont или плату BluePill+ из моего магазина NR.
123
electronics Store с установленной этой кнопкой на плате; для на
писания кода разницы не будет никакой.
Используем макросы по управлению режимом свечения
светодиода:
Рисунок 66. Управление светодиодом на выводе РВ2
Рисунок 67. Рабочая переменная для кнопки и функции
Далее уже знакомая нам функция инициализации подклю
ченной кнопки:
124
Рисунок 68. Код согласно изученным таблицам 1, 2 и 16
Но тут есть одна добавка по сравнению с предыдущим раз
делом. Это интересная строка с подтяжкой к земле. Зачем она
нужна? Наша кнопка в плате BluePiLL+ подключена следующим
образом:
Рисунок 69. Аппаратное подключение пользовательской кнопки
То есть она уже подключена аппаратно к напряжению питания
через токоограничивающий резистор 3.3В в нажатом состоянии.
Это, безусловно, хорошо; но если мы не нажимаем на кнопку, то
пин микроконтроллера начинает как будто висеть в воздухе,
а этого не должно быть, т. к. может «навестись» помеха и кнопка
как бы подтянется к питанию по уровню напряжения, и микрокон
троллер расценит это как включение кнопки, которую мы
не включали. Поэтому если у нас нет дополнительной подтяжки,
как сейчас в плате, строку надо раскомментировать.
Инициализируем светодиод, как и ранее:
125
Рисунок 70. Инициализация светодиода на выводе РВ2
Теперь нам необходимо настроить код для включения и ра
боты прерываний в МК. Для этого мы написали функцию
Interrupt_EXTI_PA0_init:
Рисунок 71. Код работы прерывания на выводе EXTI0
Строка 54
Сбрасываем флаг прерывания перед включением самого
прерывания. На первый взгляд может показаться, что этого де
лать не нужно, но лучше все же это выполнить.
Строка 55
Выставляем маску нулевого канала EXTI, т. е. включаем пре
рывание. Это следует прокомментировать более детально.
Каждый вывод МК, как GPIO, может работать в режиме
внешнего прерывания через определенные группы мультиплек
соров внутри себя:
126
Рисунок 72. Подключение GPIO для внешних прерываний
Каналы EXTI имеют следующие названия: EXTI0, EXTI1,
EXTI2 ... EXTI19. Всего в нашем распоряжении 20 каналов:
- EXTI0-EXTI15 могут быть подключены к одному из пор
тов GPIOx;
- EXTI 16 подключен внутри МК к выходу программируемо
го детектора напряжения PVD;
127
- EXTI17 - к событию RTC Alarm;
- EXTI18 - к USB микроконтроллера;
- EXTI19 - к контроллеру Ethernet (если он, конечно, есть).
То есть к EXTI0 можно подключить один из 0-х выводов пор
тов, к EXTI1 - один из 1-х выводов и т. д. Для каждой линии
значение мультиплексора
можно
выбрать независимо,
т. е. EXTI0 можно подключить к РА0, EXTI1 к РВ1 и т. д. Однако
такая организация подключений имеет некоторое ограничение,
которое необходимо учитывать: мы не можем одновременно ре
гистрировать события, например, от линий РА0 и РВ0, т. к. они
подключены к одному и тому же мультиплексору. Это как раз
особенность векторных прерываний, в радиальных подобного
не было бы. В других сериях МК STM32 может быть больше
20 каналов прерываний.
Контроллер EXTI гарантированно обнаруживает импульсы,
длительность которых больше длительности периода тактового
сигнала шины АРВ2. То есть если частота шины АРВ2 равна
72 МГц (а у нас в программе именно так), то EXTI будет коррект
но обнаруживать фронты сигналов с частотами ниже 72 МГц.
Строка 56
Наша кнопка подключена на плате BluePill+ к выводу РА0
STM32F103C8, и код соответствует выбору из регистра:
128
Рисунок 73. Нулевой канал EXTI подключаем к порту РАО
Строка 57
Включаем тактирование для работы этой альтернативной
функции вывода РА0 как прерывание.
Строка 58
Выбрано включение прерывания по спаду сигнала на ли
нии РАО, т. к. она аппаратно или программно подтянута к пита
нию. Тут нам необходимо раскомментировать строку 20, если
подключали проводами сами и не подтянули к питанию. Мож
но включить прерывание по переднему фронту, но оно нам
не подходит, т. к. это режим срабатывания при изменении
уровня от нуля к питанию. Также возможно использование пре
рывания одновременно по обоим фронтам изменения сигнала
на выводе МК, но лучше иметь однозначное решение, чтобы
не запутаться.
Строка 59
129
Разрешаем прерывание в контроллере прерываний на муль
типлексоре EXTI0_IRQn.
Строка 40
Устанавливаем приоритет прерывания. Это актуально, когда
их более одного. Данную строчку можно даже удалить отсюда,
на работоспособность кода это не повлияет, но лучше сразу за
поминать подобные важные настройки, чтобы в следующий раз,
когда они будут актуальны, не забыть о них и не искать бага
на пустом месте.
После нажатия на кнопку в программе мы попадаем в обра
ботчик прерывания. Название его подсмотрим в ассемблерном
файле:
Рисунок 74. В ассемблерном файле приведены имена всех обра
ботчиков прерываний МК
Рисунок 75. Наш рабочий обработчик прерываний
Давайте рассмотрим его поподробнее. Обработчик - это то,
130
чего мы так долго ждали, а вернее то, куда попадем после нажа
тия кнопки и должны в нем выполнить что-то полезное для нас
с вами. Здесь может быть любой алгоритм действий, я привожу
для примера следующий:
Строка 42
Имя
EXTI0_IRQHandler
startup_stm32f10x_md. s
берем
из
файла
Строка 44
Необходимо сбросить флаг прерывания, который взводится
после правильной настройки по рисунку 71 и появления изме
нения на определенном входе EXTI0. Причем согласно RM сброс
осуществляется именно записью единицы в регистр EXTI_PR. Ес
ли этого не сделать, то после выхода из обработчика мы будем
попадать в него снова и снова.
Строка 45
Проверка, нажата ли кнопка. Если нажата (= 1), то заходим
в условие if... else; если нет, то выходим из него и из обработ
чика.
Строки 47, 48
В случае захода выполнения программы через условный
оператор if макросом включаем светодиод и, опять же для при
мера, ждем некоторое произвольное время циклом for. Здесь
можно было красиво показать задержку через SysTick или ка
кой-либо таймер, но я не захотел перегружать нашу программу
кодом, т. к. цель у нас несколько иная.
Еще можно использовать в условии проверки if - не нажа
тие кнопки, а взвелся ли флаг прерывания: «if (EXTI-> PR &
EXTI_PR_PR0)» - тогда нужно было бы переместить строку
44 с его обнулением вовнутрь работы оператора if. Если этого
не сделать, то мы не сможем включить светодиод, т. к. флаг уже
сбросили.
131
Далее нам необходимо вызвать наши функции в теле основ
ной функции main ():
Рисунок 76. Вызываем написанные функции
И в последнем куске кода мы делаем следующее:
Рисунок 77. Работа бесконечного цикла while (1)
Строка 59
Постоянно проверяем, нажата ли кнопка. Если эта проверка
по каким-то причинам непонятна, то ее можно заменить кодом
из макроса закомментированной строки 60.
Строка 61
Просто выключаем светодиод. Данный маневр наглядно де
монстрирует вживую работу МК в основном цикле while, а потом
некоторое длительное свечение светодиода при попадании
в обработчик прерывания.
132
Строка 64
Просто пустая строка. Keil IDE настаивает на пустой строке
после всей программы, иначе нам будет грозить целый
Warnings! :)
Весь листинг программы представлен ниже.
#include «stm32f10x.h»
#define LED_PB2_ON () GPIOB-> BSRR |= GPIO_BSRR_BS2;
#define LED_PB2_OFF () GPIOB-> BSRR |= GPIO_BSRR_BR2;
uint8_t ButtonState = 0;
void lnterrupt_EXTI_PA0_init (void);
void PINA_0_INIT (void);//Button РА0
void PINB_2_INIT (void);//LED PB2
void PINA_0_INIT (void) //Button РА0
{
RCC-> APB2ENR |=RCC_APB2ENR_IOPAEN;//RCC
GPIOA-> CRL &= ~GPIO_CRL_MODE0_0;//Bxofl
(значение после сброса);
GPIOA-> CRL &= ~GPIO_CRL_MODE0_1;//Bxoд
(значение после сброса);
GPIOA-> CRL &= ~GPIO_CRL_CNF0_0;//10: Input
with pull-up / pull-down - вход с подтяжкой
к питанию или к земле;
GPIOA-> CRL |= GPIO_CRL_CNF0_1; //10: Input with
pull-up / pull-down - вход с подтяжкой
к питанию или к земле;
GPIOA-> BSRR = GРIО_ВSRR_BR0;//подтяжка
к земле, для защиты от помех
}
void PINB_2_INIT (void) //LED PB2
133
{
RCC-> APB2ENR |=RCC_APB2ENR_IOPBEN;//RCC
GPIOB-> CRL &= ~GPIO_CRL_MODE2_0;//0: Выход,
максимальная частота 2 МГц;
GPIOB-> CRL |= GPIO_CRL_MODE2_1;//1: Выход,
максимальная частота 2 МГц;
GPIOB-> CRL &= ~GPIO_CRL_CNF2_0;//00: General
purpose output push-pull - выход в режиме
Push-pull;
GPIOB-> CRL &= ~GPIO_CRL_CNF2_1;//00: General
purpose output push-pull - выход в режиме
Push-pull;
}
void Interrupt EXTI PA0_init (void)
{
EXTI-> PR |= EXTI_PR_PRO; //Сбрасываем флаг
прерывания перед включением самого
прерывания
ЕХТI-> IMR |= EXTI_IMR_MR0; //Включаем
прерывание 0-го канала EXTI — выставляем
маску
AFIO-> EXTICR [0] &= ~AFIO_EXTICR1_EXTI0_PA; //
Нулевой канал EXTI подключен к порту РА0
RCC-> APB2ENR |=RCC_APB2ENR_IOPAEN;//RCC AFIO
PortA
EXTI-> FTSR |= ЕХТI_FTSR_ТR0;//Прерывание
по спаду импульса
NVIC_EnableIRQ (EXTI0_IRQn); //Разрешаем
прерывание в контроллере прерываний
NVIC_SetPriority (EXTI0_IRQn, 0);//Установка
приоритета прерывания
}
134
void EXTI0_IRQHandler (void)
{
EXTI-> PR |= EXTI_PR_PR0; //Сбрасываем флаг
прерывания записью «1»
if (Button_State == 1)
//if (EXTI-> PR & EXTI_PR_PR0) - можно
проверить по флагу прерывания // (EXTI-> PR
& EXTI_PR_PR0) — переносим тогда сюда
строку
{
LED_PB2_ON ();
for (int i = 0; i <10000000; i++) {};
}
}
int main (void)
{
PINB_2_INIT ();
PINA_0_INIT ();
lnterrupt_EXTI_PA0_init ();
while (1)
{
ButtonState = ((GPIOA-> IDR) & (GPIO_IDR_IDR0));
//Button_State = READ_BIT (GPIOA-> IDR,
GPIO_IDR_IDR0);
LED PB2 OFF ()
}
}
Для компиляции и сборки проекта нажимаем F7 или кнопку
IDE (рисунок 78), и если мы всё сделали правильно и не опеча
135
тались при вводе программы, то должны наблюдать информа
цию, как в окне на рисунке 79.
Рисунок 78. Пиктограмма компиляции и сборки
Рисунок 79. Результаты компиляции и сборки
Компиляция происходит на уровне оптимизации, показан
ном на рисунке 80.
136
Рисунок 80. Выставленный для кода всей книги уровень оптими
зации
Чтобы запрограммировать нашу плату, нам достаточно на
жать F8 в IDE Keil или кликнуть по пиктограмме, как на рисун
ке 81.
Рисунок 81. Пиктограмма программирования МК на BluePill
Вначале у нас ничего не должно светиться, а вот после крат
ковременного нажатия на единственную нашу пользовательскую
кнопку должен засветиться на несколько секунд и опять потух
нуть светодиод. Это означает, что после кратковременного на
жатия на кнопку мы попали в обработчик прерывания, включи
ли светодиод на несколько секунд (по задержке цикла for),
и программа вернулась к выполнению основного кода (цикл
while), т. е. к выключению светодиода и анализу состояния кноп
ки. Задача по обработке кнопки прерыванием успешно выпол
нена, ура!
137
7 Универсальный
синхронно-асинхронный
приемопередатчик USART
7.1 ОСНОВНЫЕ СВЕДЕНИЯ
ОБ USART /UART
В настоящее время в электронной промышленности суще
ствует очень большое количество протоколов последователь
ной связи и аппаратных интерфейсов; большинство из них
ориентированы на высокую пропускную способность, например
новейшие стандарты USB 3.2, 4.2. Некоторые из этих стандар
тов пришли из прошлого, но всё еще широко распространены,
особенно в качестве интерфейса связи между модулями
на плате. Одним из них является интерфейс универсального
синхронно-асинхронного
приемопередатчика
(Universal
Synchronous / Asynchronous Receiver / Transmitter), также из
вестного как USART. Разница между UART и USART - это толь
ко отсутствие или наличие линии тактирования между двумя
устройствами, CLK.
Почти каждый микроконтроллер имеет как минимум одно
периферийное устройство - UART. Почти все микроконтролле
ры STM32 предоставляют по крайней мере два интерфейса
UART / USART, но большинство из них предоставляют более двух
интерфейсов. Возможен высокоскоростной обмен данными
за счет использования DMA-периферии в многобуферной кон
фигурации, но о ней в следующем разделе.
Интерфейс поддерживает одностороннюю (one-way) комму
никацию и полудуплексную коммуникацию по одному проводу.
Он также поддерживает:
- протокол LIN (local interconnection network);
- протокол Smartcard;
- протокол IrDA (infrared data association);
- режим модема CTS/RTS.
141
Рисунок 82. Блок-схема USART в вольном переводе
Интерфейс имеет внешнее подключение к другому устрой
ству с помощью трех выводов (см. рисунок 82). Любой двуна
правленный обмен по USART требует не менее двух выводов:
вход приема RX и выход передачи ТХ.
RX: вход приема - это вход последовательных данных. Ис
пользуется техника передискретизации (Oversampling) для рас142
познавания данных за счет разделения (дискриминации) пра
вильных данных на фоне шума.
ТХ: выход передачи. Когда передатчик выключен, его вывод
принимает исходное назначение - GPIO. Когда передатчик раз
решен, но нет данных для передачи, то вывод ТХ находится
в состоянии высокого уровня. В режимах однопроводного обме
на или Smartcard этот вывод используется как для передачи, так
и для приема данных (на стороне USART данные поступают
на внутренний вход приемника SW_RX).
Через этот вывод последовательные данные передаются
и принимаются в нормальном режиме USART в виде кадра, кото
рый содержит:
- состояние покоя (простоя) Idle, которое предшествует пе
редаче или приему;
- стартовый бит;
- слово данных (8 или 9 бит), младший бит идет первым;
- 0.5,1,1.5, 2 стоп-бита, показывающих, что кадр завершен.
Основная часть любого последовательного интерфейса пе
редачи данных - это сдвиговый регистр. В блоке USART их два:
один на передачу - Transmit Shift Register, другой на прием Receive Shift Register. Каждый из этих сдвиговых регистров име
ет свой буферный регистр данных: Transmit Data Register (TDR)
и Receive Data Register (RDR).
Для того чтобы отправить слово данных (употребляю «сло
во», а не «байт», т. к. размер слова может быть 8 или 9, а в неко
торых других семействах и 7 бит) в USART, нужно загрузить его
в регистр передачи TDR. После записи в TDR это значение пе
рейдет в сдвиговый регистр передатчика, и процесс передачи
будет запущен. Прошу обратить внимание, что как только значе
ние из TDR было отправлено в сдвиговый регистр, в регистр
TDR можно загрузить еще данные, которые будут там ждать
окончания передачи из сдвигового регистра. Таким образом,
143
у нас есть как будто буфер на два слова: одно находится в сдви
говом регистре, а другое - в TDR, что позволяет передавать
данные по периферии USART сплошным потоком, без пауз меж
ду соседними передачами.
Подобным образом выполняется и прием данных. После по
лучения данных сдвиговым регистром приемника они попадают
в регистр RDR, и приемник тут же готов к приему следующего
слова данных. Здесь тоже есть как будто буфер на два слова: од
но в сдвиговом регистре, другое в RDR. Таким образом, у нас
есть возможность выполнять прием сплошного потока данных
без пауз между соседними передачами.
В STM32F103 регистры TDR и RDR недоступны нам напря
мую, программно. Для этих целей служит один-единственный
регистр DR (Data Register). При операции записи в DR записан
ное значение попадает в регистр TDR, а при чтении из DR будет
прочитано значение регистра RDR. То есть прием и передача
данных со стороны прошивки будут выглядеть, как будто мы об
ращаемся к одному и тому же регистру DR.
В микроконтроллерах STM32F103 для настройки каждого
порта USART и работы с ним имеется по семь регистров:
- USART_SR - регистр статуса, указывающий на состояние
порта USART;
- USART_DR - регистр данных для записи передаваемых
и чтения принимаемых данных;
- USART_BRR - регистр, определяющий скорость обмена;
- USART_CR1 - первый управляющий регистр;
- USART_CR2 - второй управляющий регистр;
- USART_CR3 - третий управляющий регистр;
- USART_GTPR - регистр делителя и задержки.
Формат регистров с названием входящих в них разрядов
представлен в таблице Memory Мар далее, на рисунке 83.
144
7.1.1 ФЛАГИ
В модуле USART-микроконтроллеров STM32 есть достаточ
ное большое количество разнообразных флагов и прерываний,
с помощью которых очень удобно реализовать процесс обмена
данными как на прерываниях, так и в режиме опроса и че
рез DMA.
Ознакомимся с наиболее важными флагами. Сначала рас
смотрим флаг для процесса передачи данных. Если регистр пе
редатчика TDR пуст и в него можно записать очередное слово,
то в регистре Статуса будет установлено «1» в специальный флаг
ТХЕ (Transmit data register empty). Но установка флага ТХЕ в «1»
вовсе не означает окончания процесса передачи данных: флаг
ТХЕ говорит только о том, что в регистр передатчика можно за
писать очередное значение.
Для того чтобы убедиться в окончании передачи данных
по выводу ТХ, существует другой флаг в регистре статуса - ТС
(Transmission complete). Он устанавливается только в том случае,
если передача данных завершена и в регистре передатчика нет
очередных данных для загрузки в сдвиговый регистр (установ
лен флаг ТХЕ). Флаг ТС может быть полезен при реализации ин
терфейса RS485, когда направление драйвера интерфейса мож
но переключить только после завершения передачи данных.
Теперь поговорим о приеме данных. В регистре статуса есть
флаг RXNE (Read data register not empty). Он устанавливается
в значение «1», если в буфере приемника есть новые данные.
Кроме того, при установке в «1» одного из рассмотренных
флагов есть возможность разрешить генерацию прерывания
USART, что очень полезно при передаче данных через прерыва
ние. А как известно, использование прерываний освобожда
ет микроконтроллер от необходимости постоянной проверки
флагов и позволяет высвободить его ресурсы для выполнения
других работ.
145
Рисунок 83. Memory Мар битовых и регистровых настроек
USART
146
7.1.2 ОПИСАНИЕ КАДРА USART
Длина слова может быть выбрана в 8 или 9 бит программно
в поле бита M регистра USART_CR1. Вывод ТХ находится в низ
ком состоянии в течение старт-бита и в высоком состоянии в те
чение стоп-бита. Символ Idle интерпретируется как целый кадр
из логических единиц, которые следуют после старт-бита, с по
следующим кадром, содержащим полезные данные (число «1»
включает число стоп-бит). Символ Break интерпретируется при
приеме как нули («0») на протяжении всего кадра. В конце
Break-кадра передатчик вставляет 1 или 2 стоп-бита (логическое
«1»), чтобы правильно интерпретировать последующий стартбит. Передача и прием управляются baud rate генератором с од
ной из общеприменимых скоростей обмена. Тактовый сигнал
для каждого из них вырабатывается, если установлен соответ
ствующий бит разрешения.
Рисунок 84. Программирование кадра и его длины
А теперь по существу и кратко.
147
Блок периферии USART поддерживает настройку следую
щих параметров передачи и приема данных:
- количество бит данных (8 или 9);
- контроль четности (нет, even, odd);
- количество стоп-битов (0.5,1,1.5, 2).
Есть одна очень важная особенность при обмене данными
с использованием контроля четности. Допустим, у нас выбрано
8 бит данных, 1 стоп-бит, и если мы не используем контроль чет
ности, то формат кадра USART будет таким:
- старт-бит; 8 бит данных; стоп-бит.
Но если использовать контроль четности (бит PCE), тогда та
ким:
- старт-бит; 7 бит данных; 1 бит четности; стоп-бит.
Другими словами, если мы хотим настроить передачу дан
ных вида «8 бит данных + бит четности», то мы должны выбрать
длину слова не 8, а 9 бит!
Варианты кадров представлены ниже.
Рисунок 85. Разновидности формата кадра USART / UART
7.1.3 СКОРОСТЬ ПЕРЕДАЧИ
В микроконтроллерах STM32 USART имеет хитрую реализа
цию, что позволяет получать нулевое отклонение скорости пере
дачи от стандартных значений при работе от самых обычных
кварцев, например 8 МГц (в таблице ниже указаны частоты шин
148
с использованием PLL).
Рисунок 86. Ошибка генерации скорости передачи данных для
стандартных значений скоростей
Это достигается путем дробной установки коэффициента де
ления скорости передачи. Регистр установки коэффициента де
ления BRR состоит из двух частей: DIV_Mantissa и DIV_Fraction.
Оба эти значения образуют число с фиксированной запятой: VAL
= DIV_Mantissa, DIV_Fraction.
Формулы для расчета:
Рисунок 87. Формулы расчета скорости блока USART / UART
149
USARTDIV = тактирование ядра (fck) / 16 х нужная скорость;
USARTDIV = 72 000 000 /16 х 9600 = 468,75;
0d468 = 0 х 1D4 - значение мантиссы в hex-формате;
0d0,75 х 16 = 0d12 - значение фракции, и получаем в hex:
0d12 = 0 х С.
Затем просто дописываем в конец мантиссы и получаем
необходимую нам настройку скорости: USART_BRR = 0x1D4C.
7.1.4 GPIO
В STM32F103C8 можно использовать блоки USART на следу
ющих выводах.
Для USART1:
- ТХ: РА9, Remap РВ6;
- RX: РА10, Remap РВ7.
Для USART2:
- ТХ: РА2;
- RX: РАЗ.
Для USART3:
- ТХ: РВ10;
- RX: РВ11.
Обратимся к таблице 5 данной книги для того, чтобы понять,
как нужно настраивать порты ввода-вывода для работы с USART.
Настройка будет следующей:
— ТХ: режим альтернативной функции, тип выхода push-pull
или даже открытый коллектор;
— RX: вход без подтяжки или с подтяжкой вверх.
150
7.2 РЕГИСТРЫ USART В МК
STM32F1
Регистр статуса USART
где Bit 9 CTS: CTS flag - флаг сигнала на линии CTS:
0: никаких изменений на линии nCTS нет;
1: есть изменение на линии nCTS.
Этот бит ставится аппаратно, когда переключается вход
nCTS, если стоит бит CTSE, и очищается программно (записью
в него нуля). Если в USART_CR3 бит CTSIE = 1, то генерируется
прерывание.
Bit 8 LBD: LIN break detection flag - флаг обнаружения сим
вола LIN Break:
0: символ LIN break не обнаружен;
1: символ LIN break обнаружен.
Bit 7 ТХЕ: регистр передатчика пуст. Этот бит устанавливает
ся аппаратно, когда содержимое регистра передатчика TDR бы
ло передано в сдвиговый регистр (TDR недоступен напрямую
из программы, но туда попадают данные при записи
в USART_DR). Если в USART_CR1 был установлен бит разрешения
прерывания TXEIE, то в этот момент генерируется запрос преры-
151
вания USART. ТХЕ сбрасывается при записи значения в регистр
данных USART_DR.
Bit 6 ТС: передача завершена. Этот бит устанавливается ап
паратно, если UART завершил передачу данных, при этом бит
ТХЕ установлен в «1». Этот бит может быть полезен для реализа
ции интерфейса RS485 для переключения направления драйве
ра RS485. Если в регистре USART_CR1 установлен бит TCIE, то
генерируется прерывание USART при установке бита ТС. Бит ТС
сбрасывается следующей программной последовательностью:
чтение регистра USART_SR и запись в регистр USART_DR. Кроме
того, бит ТС можно сбросить записью в него значения «0»,
но это рекомендуется производить только в режиме совместной
работы с DMA.
Bit 5 RXNE: регистр приемника не пуст. Этот бит устанавли
вается в «1», когда содержимое сдвигового регистра приемника
передается в регистр данных USART. Если в регистре USART_CR1
установлен бит RXNEIE, то генерируется запрос прерывания
USART. Бит RXNE сбрасывается при чтении регистра данных
USART_DR. Кроме того, RXNE можно сбросить записью в него
значения «0», но это рекомендуется производить только в режи
ме совместной работы с DMA.
Bit 4 IDLE: обнаружено состояние простоя на линии («1»).
Этот бит ставится аппаратно, когда обнаружено состояние
Idle. Если в USART_CR1 бит IDLEIE = 1, то генерируется прерыва
ние. Этот бит очищается программно последовательностью опе
раций чтения регистра USART_SR и чтения регистра USART_DR.
Bit 3 ORE: ошибка переполнения. Устанавливается в «1», ес
ли данные в сдвиговом регистре приемника готовы к передаче
в регистр данных, но при этом установлен бит RXNE; иными сло
вами, мы уже получили очередной байт по USART, но еще
не прочитали предыдущий. Если в регистре USART_CR1 установ
лен флаг RXNEIE, то генерируется запрос прерывания USART.
152
Бит ORE сбрасывается следующей программной последователь
ностью: чтение регистра USART_SR и чтение регистра USART_DR.
Bit 2 NE: флаг наличия шума.
Этот бит ставится аппаратно, когда обнаружен шум в прини
маемом кадре. Этот бит очищается программно, последователь
ностью операций чтения регистра USART_SR и чтения регистра
USART_DR. Этот бит не генерирует прерывания, т. к. он появляет
ся в то же время, что и бит RXNE, который сам по себе генериру
ет прерывание. Прерывание от флага NE будет генерироваться
только в случае многобуферного обмена, когда стоит бит EIE.
Bit 1 FE: Framing error (ошибка кадра).
Этот бит ставится аппаратно при рассинхронизации, интен
сивном шуме или обнаружении символа Break. Этот бит очища
ется программно последовательностью операций чтения. Он
не генерирует прерывания, т. к. появляется в то же время, что
и бит RXNE, который сам генерирует прерывание. Если текущий
принимаемый байт вызывает обе ошибки (кадра и переполне
ния), то он будет принят, но будет установлен флаг ORE. Флаг FE
будет генерировать прерывание в случае многобуферного обме
на, если поставлен бит EIE регистра USART_SR с последующим
чтением регистра USART_DR.
Bit 0 РЕ: Parity error (ошибка паритета).
Этот бит ставится аппаратно, когда обнаружена ошибка пари
тета при приеме байта. Этот бит очищается программно, после
довательностью операций чтения регистра USART_SR и чтения
регистра USART_DR. Программа должна дождаться установки
RXNE-флага перед тем, как очистить PE-бит. Если в USART_CR1
бит PEIE = 1, то генерируется прерывание.
153
Регистр получаемых данных USART
DR [8:0]: данные. Содержит байт принятых или передавае
мых данных в зависимости от текущей операции чтения или за
писи. Регистр данных выполняет двойную функцию (чтение и за
пись), т. к. в реальности он состоит из двух регистров: один для
передачи (TDR), а другой для приема (RDR). Регистр TDR предо
ставляет параллельный интерфейс между внутренней шиной
и выходным сдвиговым регистром (см. рисунок 1). Регистр RDR
предоставляет параллельный интерфейс между входным сдви
говым и внутренней шиной. Когда разрешен обмен с паритетом
(установлен бит РСЕ в регистре USART_CR1), то старший бит за
писанного значения (бит 7 или 8, в зависимости от длины дан
ных) не имеет значения, т. к. он переписывается битом паритета.
Когда разрешен прием с паритетом, то старший бит принятого
значения - это бит паритета.
Регистр настроек скорости USART
154
Регистр BRR содержит коэффициент деления, который зада
ет скорость передачи данных по USART.
BRR = (uint16_t) (BUS_FREQ/ BAUD),
где BUS_FREQ - частота шины, на которой висит данный
USART;
BAUD - желаемая скорость передачи данных.
Регистр конфигурации 1 USART
где Bits 31:14 зарезервированы.
Bit 13 UE: USART enable (разрешение USART).
Когда этот бит очищен, то делитель USART и его выход оста
навливаются и заканчивается передача текущего байта в целях
сокращения потребления. Этот бит ставится и очищается про
граммно.
Bits12 М: Word length (длина слова).
Этот бит определяет длину слова. Этот бит ставится и очища
ется программно.
0:1 старт-бит, 8 бит данных, n стоп-бит;
1:1 старт-бит, 9 бит данных, n стоп-бит.
Bit 11 WAKE: Wakeup method (способ пробуждения).
Этот бит определяет способ пробуждения USART. Этот бит
155
ставится и очищается программно.
0: пробуждение от кадра Idle;
1: пробуждение от корректного адреса.
Bit 10 РСЕ: Parity control enable (разрешение контроля пари
тета).
Этот бит выбирает аппаратный контроль за паритетом (гене
рация и проверка). Когда контроль разрешен, то рассчитанный
бит паритета вставляется на место старшего бита (9-й бит при М
= 1; 8-й бит при М = 0) и производится проверка паритета при
приеме данных. Этот бит ставится и очищается программно. Ко
гда ставят бит РСЕ, то он активируется после текущего байта
(принимаемого или передаваемого).
0: контроль паритета отключен;
1: контроль паритета разрешен.
Bit 9 PS: Parity selection (выбор типа паритета).
Этот бит выбирает нечетный или четный паритет при его ге
нерации / проверке (если есть бит РСЕ). Этот бит ставится и очи
щается программно. Новый тип паритета вступит в силу после
текущего байта.
0: Even parity - четный паритет;
1: Odd parity - нечетный паритет.
Bit 8 PEIE: РЕ interrupt enable (разрешение прерывания
от РЕ (ошибка паритета)).
Этот бит ставится и очищается программно.
0: Interrupt is inhibited (прерывание отключено);
1: An USART interrupt is generated whenever PE = 1 in the
USART_SR register.
Генерируется USART-прерывание, когда есть бит РЕ в реги
стре USART_SR.
Bit 7 TXEIE: ТХЕ interrupt enable.
Этот бит ставится и очищается программно. Разрешить пре
156
рывание при опустошении буфера передатчика.
0: Interrupt is inhibited (прерывание отключено);
1: если установлено, то генерируется запрос прерывания
USART при установке бита ТХЕ регистра USART_SR.
Bit 6 TCIE: разрешение прерывания от ТС (передача завер
шена).
Этот бит ставится и очищается программно. Разрешить пре
рывание окончания передачи.
0: Interrupt is inhibited (прерывание отключено);
1: генерируется USART-прерывание, когда есть бит ТС в ре
гистре USART_SR.
Bit 5 RXNEIE: RXNE interrupt enable (разрешение прерыва
ния от RXNE (регистр приема не пуст)).
Этот бит ставится и очищается программно.
0: Interrupt is inhibited (прерывание отключено);
1: генерируется USART-прерывание, когда есть бит ORE в ре
гистре USART_SR.
Bit 4 IDLEIE: IDLE interrupt enable (разрешение прерывания
от состояния простоя на линии).
Этот бит ставится и очищается программно.
0: Interrupt is inhibited (прерывание отключено);
1: генерируется USART-прерывание, когда есть бит IDLE
в регистре USART_SR.
Bit 3 ТЕ: Transmitter enable (разрешение передатчика).
Этот бит разрешает передачу. Он ставится и очищается про
граммно.
0: Transmitter is disabled (передатчик отключен);
1: Transmitter is enabled (передатчик разрешен).
Примечания
1. В процессе передачи переход ТЕ-бита с «0» в «1» посыла
157
ет преамбулу (Idle-кадр) после текущего символа, кроме режима
Smartcard.
2. Когда установлен ТЕ, есть задержка в 1 bit-time перед пе
редачей старт-бита.
Bit 2 RE: Receiver enable (разрешение приемника).
Этот бит разрешает прием. Он ставится и очищается про
граммно.
0: приемник отключен;
1: работа приемника разрешена, и начинается поиск стартбита.
Bit 1 RWU: Receiver wakeup (пробуждение приемника).
Этот бит определяет, будет ли USART находиться в безмолв
ном режиме (mute). Этот бит можно ставить и очищать про
граммно, и его можно очистить аппаратно, когда распознается
пробуждающая последовательность.
0: приемник в активном режиме;
1: приемник в безмолвном режиме.
Примечания
1. Перед выбором режима Mute (установкой бита RWU)
USART должен сначала принять байт данных. В противном слу
чае он не будет функционировать в режиме Mute с пробуждени
ем от состояния Idle на линии.
2. В конфигурации пробуждения при обнаружении адресно
го маркера (WAKE = 1) бит RWU не может быть модифицирован
программно до тех пор, пока стоит бит RXNE.
Bit 0 SBK: Send break (посылка символа Break).
Этот бит используется для посылки символа Break. Его мож
но ставить и очищать программно. Его нужно ставить программ
но, и он будет очищаться аппаратно в процессе распознавания
стоп-бита кадра Break.
0: символ Break не посылается;
158
1: будет послан символ Break.
Регистр конфигурации 2 USART
где Bits 31:15 зарезервированы.
Bit 14 LINEN: LIN mode enable (разрешение LIN-режима).
0: режим LIN отключен;
1: режим LIN разрешен.
Bits 13:12 STOP: STOP bits (формат стоп-битов).
Эти биты используются для задания формата стоп-битов.
00:1 стоп-бит;
01:0.5 стоп-бита;
10: 2 стоп-бит;
11:1.5 стоп-бита.
Bit 11 CLKEN: Clock enable (разрешение тактового сигнала).
Этот бит позволяет разрешить вывод тактового сигнала
на вывод SCLK.
0: вывод SCLK отключен;
1: вывод SCLK разрешен.
Примечание. Этот бит недоступен для UART4 и UART5
STM32F103C8.
Bit 10 CPOL: Clock polarity (полярность тактового сигнала).
159
Этот бит позволяет выбрать полярность тактового сигнала
на выводе SCLK в синхронном режиме. Он работает в связке
с битом СРНА, чтобы получить желаемое соотношение тактового
сигнала к данным.
0: устойчивое низкое состояние на выводе SCLK вне окна
обмена данными;
1: устойчивое высокое состояние на выводе SCLK вне окна
обмена данными.
Этот бит недоступен для UART4 и UART5 STM32F103C8.
Bit 9 СРНА: Clock phase (фаза тактового сигнала).
Этот бит позволяет выбрать фазу тактового сигнала на выво
де SCLK в синхронном режиме. Он работает в связке с битом
CPOL, чтобы получить желаемое соотношение тактового сигнала
к данным (см. рисунки 249 и 250).
0: первый перепад тактового импульса соответствует перво
му перепаду бита данных;
1: второй перепад тактового импульса соответствует перво
му перепаду бита данных.
Этот бит недоступен для UART4 и UART5 STM32F103C8.
Bit 8 LBCL: Last bit clock pulse (тактовый импульс для по
следнего бита).
Этот бит позволяет выбрать, будет ли тактовый импульс ас
социирован с последним (старшим) битом данных и выведен
на SCLK в синхронном режиме.
0: тактовый импульс не выводится для последнего бита
на вывод SCLK);
1: выводится тактовый импульс для последнего бита на вы
вод SCLK.
Примечания
1. Последний бит 8- или 9-битного обмена зависит от бита М
в регистре USART_CR1.
2. Этот бит недоступен для UART4 и UART5 STM32F103C8.
160
Bit 7 Reserved зарезервированы.
Bit 6 LBDIE: LIN break detection interrupt enable. Разрешение
прерывания при обнаружении LIN Break символа.
Маска прерывания Break (обнаружение Break с использова
нием разделителя).
0: прерывание отключено;
1: генерируется прерывание, если бит LBD = 1 в регистре
USART_SR.
Bit 5 LBDL: LIN break detection length (длина LIN Break
кадра).
Этот бит позволяет выбрать детектирование Break по 11 или
по 10 битам.
0: детектирование Break по 10 битам;
1: детектирование Break по 11 битам.
Bit 4 Reserved зарезервированы.
Bits 3:0 ADD [3:0]: Address of the USART node (адрес узла
USART).
Это битовое поле предоставляет адрес узла USART. Это ис
пользуется при многопроцессорном обмене для выхода из ре
жима mute по адресному маркеру.
Примечание, эти три бита (CPOL, СРНА, LBCL) не должны за
писываться, пока разрешена передача.
Control register 3 (USART_CR3) - регистр конфигурации 3.
Данный регистр конфигурации нам малоинтересен, т. к.
больше относится к линиям RS-232, Smartcard- и IrDA-интерфей
сов, поэтому пройдемся по нему кратко.
161
Регистр выбора дополнительного функционала USART как RS232
и др.
- Бит 0 EIE разрешает прерывания от ошибок;
- бит 1 IREN разрешает режим работы порта IrDA;
- бит 2 IRLP осуществляет выбор между нормальным и низ
копотребляющим режимом IrDA (0 - нормальный режим, 1 низкопотребляющий режим);
- бит 3 HDSEL разрешает полудуплексный режим для одно
проводного интерфейса;
- бит 4 NACK разрешает формирование сигнала NACK в ре
жиме Smartcard;
- бит 5 SCEN разрешает работу порта в режиме Smartcard;
- бит 6 DMAR разрешает работу приемника через DMA;
- бит 7 DMAT разрешает работу передатчика через DMA;
- бит 8 RTSE разрешает формирование сигнала RTS;
- бит 9 CTSE разрешает формирование сигнала CTS;
- бит 10 CTSIE разрешает прерывания от флага CTS реги
стра USART_SR;
- биты 11...31 зарезервированы.
Guard time and prescaler register (USART_GTPR) - регистр
делителя и задержки.
Данный регистр конфигурации нам сейчас неинтересен, т. к.
относится только к линиям Smartcard- и IrDA-интерфейсов, по
этому познакомиться с несколькими битами можно, если что, са
мостоятельно в Reference Manual.
162
7.3 РЕЖИМЫ РАБОТЫ USART,
POLLING, IT, DMA
Существует три различных режима работы интерфейса
USART /UART:
- режим опроса - Polling mode;
- режим прерываний - Interrupt mode;
- режим прямого доступа - режим DMA.
Поговорим немного о том, что каждый из них означает и за
чем нужен.
- В режиме опроса (Polling mode), также называемом блоки
рующим режимом (blocking mode), микроконтроллер находится
в этом режиме постоянно и как ожидает приема данных извне,
так и передает данные из МК, если это предусмотрено собствен
ным алгоритмом. При этом МК не отвлекается ни на какую дру
гую периферию, и если данные, например, придут от датчиков
по I2C/SPI или еще откуда-то, они будут гарантированно потеря
ны. Данный режим не рекомендуется для использования в МК
в целом, кроме момента проверки работоспособности канала
USART / UART связи, например с терминалкой персонального
компьютера, но не более того.
- В режиме прерываний (interrupt mode), также называе
мом неблокирующим режимом (non-blocking mode), основное
приложение МК освобождается от непрерывного ожидания за
вершения передачи и (или) приема данных. Когда поступают
данные на МК по USART / UART, то выполнение основной про
граммы (например, написанной в main. с) МК приостанавливает
ся на время получения и обработки данных в обработчике пре
рываний МК; после этого выполнение основной программы
продолжается с места установки. Так же и для момента переда
чи данных из МК. Собственно, название режима говорит само
163
за себя. Данный режим больше подходит, когда скорость обмена
данными низкая (ниже 57 600 бит/с) или когда передача (прием)
происходит редко по сравнению с другими основными действи
ями МК и мы не хотим застрять на ожидании передачи данных.
— Режим DMA обеспечивает наилучшую пропускную спо
собность передачи благодаря прямому доступу периферийного
устройства USART / UART к внутренней памяти микроконтролле
ра без участия ядра МК. Данный режим лучше всего подходит
для высокоскоростной связи и когда мы хотим полностью осво
бодить микроконтроллер от накладных расходов при передаче
(приеме) данных. Без режима DMA практически невозможно до
стичь самых высоких скоростей передачи данных, которые спо
собны обрабатывать периферийные устройства USART.
Совет. В своих программных приложениях для МК исполь
зовать режим прерываний или DMA, но никак не блокирующий
режим!
P.S. Использование прерываний освобождает микрокон
троллер от необходимости постоянной проверки в режиме
опроса и позволяет высвободить его ресурсы для других работ.
Direct memory access (DMA), или прямой доступ к памяти
(ПДП), используется для быстрой передачи данных между памя
тью и периферийным устройством, памятью и памятью или меж
ду двумя периферийными устройствами без участия процессора.
В микроконтроллере STM32F103C8 доступен один контрол
лер DMA1 с семью каналами. DMA2 и другие присутствуют толь
ко в микроконтроллерах high-density и XL-density или других
сериях МК. О этом режиме подробнее в следующем основном
разделе.
164
7.4 РАБОТА UART
НА ПРЕРЫВАНИЯ. РЕЖИМ IT
Сведенная структурная схема формирования прерываний
от порта USART:
Рисунок 88. Возможные источники прерываний по USART
165
Рисунок 89. Таблица всех возможных запросов USART на преры
вания
На структурной схеме и таблице присутствуют все возмож
ные варианты прерываний, описанные в регистрах предыдущего
раздела. Теперь мы напишем свою программу с использованием
режима прерывания, например по приему; будем анализировать
бит RXNE, рассмотренный в разделе 7.2. Пример по прерыванию
может быть любым, главное - уловить суть построения блоков
программы. Можно анализировать любой другой бит для работы
в режиме прерывания, но остановимся на том, что есть в одном
из учебных видео, чтобы было максимально понятно.
166
7.5 СОЗДАЕМ ПРОЕКТ
ПО ИСПОЛЬЗОВАНИЮ
ПРЕРЫВАНИЯ ДЛЯ РАБОТЫ
С UART
Так как мы с вами уже опытные программисты и прошли до
статочно большой материал, предлагаю начать разбираться сра
зу с листинга программы, только в новых особенностях про
граммы.
Цель нашего проекта - это научиться применять на практи
ке прерывания при работе с интерфейсом последовательных
данных UART1 на основе уже изученного материала из разде
лов 7.1-7.4, а также, конечно, более ранних.
Мы с вами берем пустой проект, где преднастроено по умол
чанию тактирование на 72 МГц, или создаем его заново, если
его нет под рукой. Если заново, то это раздел 3. Нам необходи
мо подключить одну из наших плат, BluePill/+, к персональному
компьютеру через один из преобразователей уровней USB UART рисунка 6. Он может быть абсолютно любым, даже если
его как примера нет на этом рисунке, главное - в соответствии
с распиновкой RXD- к TXD- и TXD- к RXD- линиям между преоб
разователем и МК STM32.
Рисунок 90. Код объявления и инициализации переменных
167
Объявляем и инициализируем переменные:
- temp для сохранения принятых байтов;
- i для работы счетчика задержки;
- data для отправки данных на персональный компьютер.
В нашем основном файле main. с инициализируем всё необ
ходимое тактирование:
Рисунок 91. Код объявления необходимого тактирования
Рисунок 92. Код настройки USART1
Строки 14-16
В этих строках мы настраиваем вывод МК РА9 на выход
и вывод передачи данных данных на интерфейсе USART1 со
гласно книжной таблице 5 и взаимосвязанным с ней.
Строки 19-21
168
В этих строках мы настраиваем вывод МК РА10 на вход
и ввод приема данных данных на интерфейсе USART1 согласно
таблице 5 и взаимосвязанным с ней.
Строка 24
Включение тактирования периферии USART1. Лучше по воз
можности делать это в настройках периферии, т. к. опыт обучаю
щихся показывает, что если ее сваливать в одно место - строку
9, то что-то забывается.
Строка 25
Настройка скорости работы USART в зависимости от частоты
тактирования и того, какая требуется именно для работы с терминалкой персонального компьютера. Кратко это описано в раз
деле 7.1.3 «Скорость передачи» данной книги. Я могу рассказать
про это более детально в Telegram-канале книги, т. к. делать это
лучше в режиме диалога.
Строка 26
Настройка согласно нашим регистрам настройки USART раздел 7.2. Разрешение работы USART, разрешение работы пе
редатчика, разрешение работы приемника и разрешение пре
рывания по приему данных, что, собственно, нам и необходимо.
Рисунок 93. Код инициализации светодиода на РС13 платы
BluePill
169
Строки 30-53
Строки закомментированы, но если применяется обычная
классическая плат BluePill, то нужно раскомментировать.
Рисунок 94. Код инициализации светодиода на РВ2 платы
BluePill+
Строки 36-40
Строки могут быть закомментированы, если не применяется
плата BluePill+ от WeAct. Но в данном примере используется
она, т. к. более удобна за счет наличия у себя на борту пользова
тельской кнопки.
Рисунок 95. Код инициализации и включения режима прерыва
ния на USART1
Строки 42-44
Строки, по сути, говорят сами за себя. Включение контролле
ра прерывания, и, в частности, по вектору пребывания
USART1_IRQn разрешаем прерывания на глобальном уровне,
но это тема для отдельной книги.
170
Рисунок 96. Код инициализации и включения режима прерыва
ния на USART1
Строка 49
Строка задержки циклом for выполнения непрерывного цик
ла while (1). Если ее не использовать, то выполнение результата
программы будет неудобно отслеживать.
Строка 50
Строка с инкрементом изменения переменной data и загруз
кой этого значения в регистр данных DR для передачи извне МК
в нашу терминалку на PC. Именно эти данные мы будем наблю
дать на персональном компьютере.
Строка 51
Ждем окончания передачи данных. Это необходимо, если
нам вдруг опять понадобится передать какие-либо данные
по USART, чтобы не испортить предыдущие загруженные дан
ные.
Строка 52
Когда данные переданы, очищаем флаг окончания передачи
для возможности дальнейшей безопасной передачи.
171
7.5.1 ОБРАБОТЧИК ПРЕРЫВАНИЯ
Рисунок 97. Код инициализации обработчика прерывания
USART1
Собственно, из-за чего затевалось написание нашего обра
ботчика прерывания?
Обработчик прерывания - это то место в прошивке МК, ку
да мы попадаем по срабатывании того или иного условия. У нас
это прерывание основной программы в while (1) по приему дан
ных из интерфейса UART1, а именно бита RXNEIE. Как я упоми
нал ранее, срабатывание может наступить по любому другому
событию, необходимому нам, а именно из таблицы событий ри
сунка 87.
Условие попадания в обработчик мы проверяем условным
оператором if в строке 58; если условие верно, то считываем
пришедшие данные из регистра DR (строка 60) и начинаем их
анализировать (строки 62-67) условным оператором if - else.
Если пришел символ «1» (единица) из терминалки персо
нального компьютера, то изменяем состояние светодиода
на выводе РС13 (строка 63) или РВ2 (строка 64), в зависимости
от того, какая у нас плата - BluePill или BluePill+ соответ
ственно.
172
Если пришел символ «2» (двойка) из терминалки персональ
ного компьютера, то передаем обратно из МК по UART1 в тер
миналу персонального компьютера символ товарного знака ®,
это соответствие кода 0хАЕ по таблице ASCII. Можно отослать
код с другим символом. Если необходима отправка кода с тек
стовым сообщением следует дполнительно дописать еще код,
не хочется перегружать книгу большими примерами.
Рисунок 98. Код 0хАЕ по таблице ASCII
Весь листинг программы представлен ниже.
#indude «stm32f10x.h»
uint32_t temp = 0, i;
char data = 0;
int main (void)
173
{
RCC-> APB2ENR |= RCC_APB2ENR_IOPAEN |
RCC_APB2ENR_AFI0EN | RCC_APB2ENR_IOPBEN|
RCC_APB2ENR_IOPCEN;
GPIOA->
GPIOA->
GPIOA->
GPIOA->
GPIOA->
GPIOA->
CRH
CRH
CRH
CRH
CRH
CRH
&= ~GPI0_CRH_CNF9;
|= GPIO_CRH_CNF9_1;
|= GPIO_CRH_MODE9_0;
&= ~GPIO_CRH_CNF10;
|= GPIO_CRH_CNF10_0;
&= ~GPIO_CRH_MODE10;
RCC-> APB2ENR |= RCC_APB2ENR_USART1EN;
USART1-> BRR = 0x1D4C;
USART1-> CR1= USART_CR1_UE | USART_CR1_TE |
USART_CR1_RE | USART_CR1_RXNEIE;
// настройка вывода PC13 BluePill
//GPIOC-> CRH &= ~GPIO_CRH_MODE13; //MODE
//GPIOC-> CRH &= ~GPIO_CRH_CNF13;//CNF
//GPIOC-> CRH |= GPIO_CRH_MODE13_1; //2MHz
//GPIOC-> CRH &= ~GPIO_CRH_CNF13;
// настройка вывода PB2 BluePill+
GPIOB-> CRL &= ~GPIO_CRL_MODE2_0;
GPIOB-> CRL |= GPIO_CRL_MODE2_1;
GPIOB-> CRL &= ~GPIO_CRL_CNF2_0;
GPIOB-> CRL &= ~GPIO_CRL_CNF2_1;
NVIC_EnablelRQ (USART1_IRQn);
__enable_irq ();
while (1)
174
{
for (i=0; i <1000000; ++i) {};//делаем задержку
USART1-> DR = data++;
while ((USART1-> SR & USART_SR_TC) ==0) {}
USART1-> SR = ~USART_SR_TC;
}
}
void USART1_IRQHandler (void)
{
if ((USART1-> SR & USART_SR_RXNE)!=0)
{
temp = USART1-> DR;
if (temp == «1»)
{
// GPIOC-> ODR ^= GPIO_ODR_ODR13; //BluePill
GPIOB-> ODR ^= GPIO_ODR_ODR2; //BluePill+
}
else if (temp == «2»)
{
USART1-> DR = 0xAE; //символ товарного знака
}
}
Далее, как обычно, собираем проект, смотрим, нет ли оши
бок орфографических или другого рода - благо компилятор ин
формативно сигнализирует нам об этом - и зашиваем в МК. Да
лее с компьютера мы можем управлять светодиодом на любой
из плат и получать от платы ответ в виде знака ®. Цель достиг
нута, ура!
175
Визуально это показано и продемонстрировано на практике
в видео «STM32. CMSIS #8. UART работа на прерываниях»
по следующему адресу: https://youtu.be/OqmXHG mfmZU
176
8 DMA. Прямой доступ
к памяти
8.1 ОСНОВНЫЕ СВЕДЕНИЯ
ПО РАБОТЕ DMA В МК STM32F1
Что такое DMA (Direct memory access) - прямой доступ к па
мяти? Это технология прямого доступа к памяти, которая минует
центральное ядро или процессор.
Рисунок 99. Работа периферии в режиме DMA
Когда ПО выполняет работу, часто процессору или МК необ
ходимо перекидывать, например, данные из ОЗУ на винчестер
и обратно либо между двумя какими-либо периферийными бло
ками, как показано на рисунке 99. Ранее мы уже рассматривали
на практике получение данных с персонального компьютера
по UART нашей платой BluePill/+ и сохранение их в некоторую
область памяти для дальнейшей работы с этими данными. На
помню: после обработки этих данных мы меняли состояние све179
тодиода платы и высылали обратно символ товарного знака
по UART в нашу терминальную программу на персональном
компьютере. Это достаточно неудобно для ядра МК, т. к. оно по
стоянно отвлекается от основной программы на прием байтов,
передачу их и опять ожидание приема и обработки. Было бы
идеально, если бы на подобные простые операции ядро не от
влекалось, а это делал бы какой-либо другой блок. И этот мо
дуль как раз модуль DMA. Он позволяет исключить CPU, как по
казано на рисунке 96, из процесса копирования между блоками
периферии. Здесь процессор занимается своей работой, а копи
рование осуществляется модулем DMA.
Это в общем смысле. Теперь давайте поговорим детальнее
и в отношении нашего STM32F103.
Прямой доступ к памяти (DMA) используется, чтобы обеспе
чить высокоскоростную (максимально возможную аппаратно)
передачу данных между внешними устройствами (периферией)
и памятью, а также передачу типа «память - память». Данные
могут быть быстро перемещены с помощью DMA без каких-либо
действий ядра. Это оставляет ресурсы ядра свободными для
других операций. В серии STM32F1 имеется два DMA-контрол
лера, которые, в свою очередь, имеют в общем 12 каналов
(7 для DMA1 и 5 для DMA2), каждый из которых специализиру
ется на управлении запросами доступа к памяти от одного или
более внешних устройств. DMA-контроллеры имеют арбитраж
для обработки приоритетов между запросами на прямой доступ
к памяти. Для нашей BluePill/+ мы будем работать только
с DMA1, т. к. DMA2 доступен только для семейств High-Density
(далее - HD) и XL-density (далее - XL) STM32F1.
Каждый из семи каналов DMA1 связан с выделенными аппа
ратными DMA-запросами; также на каждом канале поддержива
ется программный механизм запуска. Конфигурация задается
программно. Приоритеты между запросами от каналов одного
DMA-контроллера задаются программно (возможны четыре
уровня: очень высокий, высокий, средний, низкий), а в случае
равенства программных приоритетов «разруливаются» аппарат
180
но (запрос с номером 1 имеет приоритет над запросом 2 и т. д.).
Размеры элементов обмена для источника независимы и адре
сата (байт, полуслово, слово), эмуляция упаковки и распаковки.
Адреса источника / приемника обмена должны быть выровнены
по размеру (элемента) данных.
Поддерживается кольцевой режим буферов - режим удоб
ный и порой крайне необходимый. Это режим цикличности для
управления непрерывным потоком данных с помощью кольце
вых буферов (например, от АЦП в режиме сканирования). Дан
ная особенность может быть разрешена битом CIRC в регистре
DMA_CCRx. Когда режим цикличности активирован, значение
числа данных, которые будут переданы (за один цикл), автома
тически перезагружается начальным значением, запрограмми
рованным на этапе конфигурации канала, и DMA-запросы про
должают обслуживаться. Рассматривать это подробно в текущей
редакции книги мы не будем, но посмотреть, как сохраняются
и двигаются байты, можно в видео «STM32. CMSIS #10. Работа
USART через DMA в circular mode» и «STM32 #12. ADC через
DMA, сравниваем среднеарифметическое значение с эталон
ным измерением на В7-78».
Существуют обмены данных типа «периферия - память»,
«память - периферия» и «периферия - периферия», по соот
ветствующим направлениям движения данных.
181
Рисунок 100. Блок-схема DMA для МК STM32F1
DMA-контроллер выполняет прямой обмен с памятью, раз
деляя системную шину с ядром Cortex-M3. DMA-запрос может
приостановить доступ ядра к системной шине на несколько
тактов шины, если ядро и DMA работают с одним адресатом
(память или внешнее устройство). Матрица шин работает по ал
горитму round-robin (циклическое планирование), гарантируя,
таким образом, по крайней мере половину пропускной способ
ности системной шины для ядра (при обращении как к памяти,
так и к периферии).
1. Загрузка данных из регистров, данных периферии или
из области памяти производится с помощью внутреннего реги
стра текущего адреса периферии / памяти. Стартовый адрес, ко
182
торый используется для первого обмена, является базовым ад
ресом периферии / памяти и программируется в регистре
DMA_CMARx или DMA_CPARx.
2. Сохранение данных загрузкой в регистры периферии или
в область памяти, с адресацией их через внутренний регистр те
кущего адреса периферии / памяти. Стартовый адрес, который
используется для первого обмена, является базовым адресом
периферии / памяти и программируется в регистре DMA_CMARx
или DMA_CPARx.
3. Пост-декремент регистра DMA_CNDTRx, который содержит
число транзакций, которые еще надо выполнить.
Арбитр управляет запросами каналов на основе их приори
тетов и запускает последовательность доступа к периферии / па
мяти.
Приоритетами управляют на двух уровнях:
1. Программный: приоритет каждого канала может быть
сконфигурирован в регистре DMA_CCRx. Есть четыре уровня:
- Very high priority (очень высокий приоритет);
- High priority (высокий приоритет);
- Medium priority (средний приоритет);
- Low priority (низкий приоритет).
2. Аппаратный: если два запроса имеют одинаковый про
граммный приоритет, то канал с более низким порядковым
числом получит приоритет над каналом с более высоким по
рядковым числом - например, канал 2 получит приоритет над
каналом 4. В устройствах семейств HD и XL контроллер DMA1
имеет приоритет над контроллером DMA2.
Процедура конфигурации каналов
Чтобы сконфигурировать DMA-канал channelx (где х - номер
канала), необходимо выполнить следующую последовательность.
1. Задайте адрес регистра периферии в регистре
DMA_CPARx. Данные будут перемещены из / по этому адресу в /
из памяти после события от периферии.
183
2. Задайте адрес памяти в регистре DMA_CMARx. Данные бу
дут записаны в / прочитаны из этой памяти после события
на периферии.
3. Задайте общее число данных, которые надо переместить,
в регистре DMA_CNDTRx.
4. Задайте приоритет канала с помощью битов PL [1:0] в ре
гистре DMA_CCRx.
5. Задайте направление перемещения данных, режим цик
личности, режим инкремента периферии и памяти, размер эле
мента данных периферии и памяти и источник прерывания
(завершение половины или всего обмена) в регистре
DMA_CCRx.
6. Активируйте канал установкой бита ENABLE в регистре
DMA_CCRx.
Как только канал будет разрешен, он сможет обслуживать
DMA-запросы от периферии, подключенной к каналу. Как только
будет передана половина байтов, будет установлен флаг полу
обмена HTIF и будет сгенерировано прерывание, если такое
прерывание разрешено битом HTIE. В конце обмена будет уста
новлен флаг завершения обмена TCIF и будет сгенерировано
прерывание, если такое прерывание разрешено битом TCIE, со
гласно рисунку 101.
Рисунок 101. Запросы прерываний от DMA
Программирование выравнивания данных и порядка следо
вания байтов
Когда значения в битовых полях PSIZE и MSIZE различны, то
184
DMA выполняет некоторое выравнивание данных по 8 байт для
работы с UART, как это описано в таблице ниже.
Рисунок 102. Поведение при программировании ширины дан
ных и порядка следования байтов
Менеджмент обработки ошибок
Генерация ошибки обмена по DMA может возникнуть при
операции чтения или записи в зарезервированном адресном
пространстве. Когда ошибка обмена по DMA происходит во вре
мя доступа на чтение или запись, такой канал автоматически от
ключается посредством аппаратного сброса бита EN в регистре
конфигурации соответствующего канала (DMA_CCRx). Выставля
ется флаг запроса прерывания от ошибки обмена в канале
(TEIF) в регистре DMA_IFR, и генерируется прерывание, если оно
разрешено битом TEIE в регистре DMA_CCRx.
Отображение DMA-запросов:
185
Рисунок 103. Список запросов на DMA1 для каждого канала
Семь запросов от периферии (ТIМх [1,2,3,4], ADC1, SPI1,
SPI / I2S2, I2Сх [1,2] и USARTx [1,2,3]) логически объединяются
(по OR) прежде, чем поступить на DMA1; это означает, что в од
но время может быть разрешен только один запрос. Отображе
но на рисунке 103.
186
Запросы от периферии на DMA можно независимо активи
ровать / деактивировать, программируя бит управления DMA
в регистрах соответствующего внешнего устройства.
Рисунок 104. Сводная таблица запросов на DMA1 для каждого
канала
187
8.2 РЕГИСТРЫ
ИСПОЛЬЗОВАНИЯ DMA
В STM32F1
Регистр статуса прерываний DMA
Bits 31:28 Reserved - зарезервированы.
Bits 27, 23, 19, 15, 11, 7, 3 TEIFx: флаг ошибки обмена в ка
нале х.
Этот бит ставится аппаратно. Очищается программно запи
сью «1» в соответствующий бит регистра DMA_IFCR.
0: нет ошибки обмена в канале х;
1: есть ошибка обмена в канале х.
Bits 26, 22, 18, 14, 10, 6, 2 HTIFx: флаг завершения полови
ны обмена в канале х.
Этот бит ставится аппаратно. Очищается программно запи
сью «1» в соответствующий бит регистра DMA_IFCR.
0: нет события завершения половины обмена в канале х;
1: есть событие завершения половины обмена в канале х.
Bits 25, 21, 17, 13, 9, 5, 1 TCIFx: флаг завершения обмена
в канале х.
Этот бит ставится аппаратно. Очищается программно запи
сью «1» в соответствующий бит регистра DMA_IFCR.
188
0: нет события завершения обмена в канале х;
1: есть событие завершения обмена в канале х.
Bits 24, 20,16,12, 8, 4, 0 GIFx: флаг отвечает за глобальные
прерывания
Этот бит ставится аппаратно. Очищается программно запи
сью «1» в соответствующий бит регистра DMA_IFCR.
0: нет ни одного события прерывания ТЕ, НТ или ТС;
1: есть одно из событий прерывания ТЕ, НТ или ТС.
Очистка флагов прерываний DMA
Bits 31:28 Reserved всегда читается как «0».
Bits 27, 23,19,15,11, 7, 3 CTEIFx: очистка флага ошибки об
мена в канале х.
Этот бит ставится и очищается программно.
0: нет эффекта;
1: очистка соответствующего TEIF-флага в регистре DMA_ISR.
Bits 26, 22, 18, 14, 10, 6, 2 CHTIFx: очистка флага заверше
ния половины обмена в канале х.
Этот бит ставится и очищается программно.
0: нет эффекта;
1: очистка соответствующего HTIF-флага в регистре
DMA_ISR.
189
Bits 25, 21,17,13, 9, 5,1 CTCIFx: очистка флага завершения
обмена в канале х.
Этот бит ставится и очищается программно.
0: нет эффекта;
1: очистка соответствующего TCIF-флага в регистре DMA_ISR.
Bits 24, 20, 16, 12, 8, 4, 0 CGIFx: глобальная очистка флагов
прерывания канала х.
Этот бит ставится и очищается программно.
0: нет эффекта;
1: очистка флагов GIF, TEIF, HTIF и TCIF в регистре DMA_ISR.
Регистр конфигурации каналах DMA
Bits 31:15 Reserved: всегда читается как «0».
Bit 14 МЕМ2МЕМ: режим «память - память».
Этот бит ставится и очищается программно.
0: режим «память - память» отключен;
1: режим «память - память» разрешен.
Bits 13:12 PL [1:0]: уровень приоритета канала.
Этот бит ставится и очищается программно.
00: Low (низкий);
01: Medium (средний);
10: High (высокий);
190
11: Very high (очень высокий).
Bits 11:10 MSIZE [1:0]: размер элемента данных в памяти.
Этот бит ставится и очищается программно.
00: 8-bits;
01:16-bits;
10: 32-bits;
11: Reserved.
Bits 9:8 PSIZE [1:0]: размер элемента данных в периферии.
Этот бит ставится и очищается программно.
00: 8-bits;
01:16-bits;
10: 32-bits;
11: Reserved.
Bit 7 MINC: режим инкремента указателя в памяти.
Этот бит ставится и очищается программно.
0: режим инкремента указателя в памяти отключен;
1: режим инкремента указателя в памяти разрешен.
Bit 6 PINC: режим инкремента указателя в периферии.
Этот бит ставится и очищается программно.
0: режим инкремента указателя в периферии отключен;
1: режим инкремента указателя в периферии разрешен.
Bit 5 CIRC: режим цикличности.
Этот бит ставится и очищается программно.
0: режим цикличности отключен;
1: режим цикличности разрешен.
Bit 4 DIR: направление обмена данных.
Этот бит ставится и очищается программно.
0: чтение из периферии;
1: чтение из памяти.
191
Bit 3 TEIE: разрешение прерывания от ошибки обмена.
Этот бит ставится и очищается программно.
0: прерывание от ТЕ запрещено;
1: прерывание от ТЕ разрешено.
Bit 2 HTIE: разрешение прерывания от события завершения
половины обмена.
Этот бит ставится и очищается программно.
0: прерывание от НТ запрещено;
1: прерывание от НТ разрешено.
Bit 1 TCIE: разрешение прерывания от события завершения
обмена.
Этот бит ставится и очищается программно.
0: прерывание от ТС запрещено;
1: прерывание от ТС разрешено.
Bit 0 EN: разрешение канала.
Этот бит ставится и очищается программно.
0: канал отключен;
1: канал разрешен.
Канал DМАxномер регистра данных
Bits 31:16 всегда читается как «0».
Bits 15:0 NDT [15:0]: размер данных обмена.
192
Число данных, которые будут переданы (от 0 до 65 535).
В этот регистр можно производить запись только тогда, когда ка
нал выключен. Как только канал будет разрешен, этот регистр
можно будет только читать, чтобы определить число байтов,
оставшееся для обмена. Этот регистр декрементируется после
каждой DMA-транзакции.
Как только обмен завершен, этот регистр может либо остать
ся в нуле, либо автоматически перезагрузиться ранее запро
граммированным значением, если канал сконфигурирован в ре
жиме автоперезагрузки (режим цикличности).
Если значение этого регистра «0», никакая транзакция
не может быть обслужена независимо от того, разрешен канал
или нет.
Регистр периферийных адресов канала DMA x
Bits 31:0 РА [31:0]: адрес периферии.
Регистр базового адреса данных в периферии, из / по кото
рому будут читаться / записываться данные. Когда PSIZE =
01 (элемент 16-битный), бит РА [0] игнорируется, доступ автома
тически выравнивается по адресу полуслова. Когда PSIZE =
10 (элемент 32-битный), биты РА [1:0] игнорируются, доступ ав
томатически выравнивается по адресу слова.
193
Регистр адреса памяти каналах DMA
Bits 31:0 МА [31:0]: адрес в памяти. Регистр базового адреса
данных в памяти, из / по которому будут читаться / записываться
данные. Когда MSIZE = 01 (элемент 16-битный), бит МА [0] игно
рируется, доступ автоматически выравнивается по адресу полу
слова. Когда MSIZE = 10 (элемент 32-битный), биты МА [1:0] иг
норируются, доступ автоматически выравнивается по адресу
слова.
194
Рисунок 105. Карта регистров DMA и их значение после сброса
195
Рисунок 105. Продолжение
196
Рисунок 105. Окончание
Еще есть полезный документ по тонким деталям работы
DMA [3].
197
8.3 СОЗДАЕМ ПРОЕКТ
ПО ИСПОЛЬЗОВАНИЮ UART1
В РЕЖИМЕ DMA
Цель нашего текущего проекта - это научиться применять
на практике режим DMA при работе с интерфейсом последова
тельных данных UART на основе уже изученного материала
из разделов 8.1-8.2, а также, конечно, более ранних.
Мы с вами берем пустой проект, где преднастроено по умол
чанию тактирование на 72 МГц, или создаем его заново, если
его нет под рукой. Если заново, то это раздел 3. Нам также необ
ходимо подключить одну из наших плат, BluePill или BluePill+
(значения тут не имеет), к персональному компьютеру через
один из преобразователей уровней USB - UART рисунка 6. Он
может быть абсолютно любым, даже если его нет на этом рисун
ке, главное - в соответствии распиновкой RXD- к TXD- и TXDк RXD- линиям между преобразователем и МК STM32.
Рисунок 106. Код подключения библиотек и определения макро
сов
В первой строке известный нам код, останавливаться не бу
дем. Вторая строка - это подключение файла string. h - заголо
вочный файл стандартной библиотеки языка Си, содержащий
198
функции для работы со строками, оканчивающимися на 0, и раз
личные функции работы с памятью, что нам и будет необходимо
далее для работы функции memcpy ().
Далее то, чего еще не было:
Рисунок 107. Определение и инициализация компонентов буфе
ров
Строка 7
Определение макроса RXSIZE размером в 20 байт. Это
определение численно размера буфера - прием, куда мы бу
дем первично складывать байты, пришедшие по UART
извне МК.
Строка 8
Объявление первичного буфера в виде массива из 20 эле
ментов.
Строка 9
Определение основного буфера в виде массива из 50 эле
ментов, которым можно оперировать в программе далее для ка
кой-либо обработки принятых байтов. Два байта образуют
некий виртуальный кольцевой буфер; о его работе - далее
по тексту программы.
Строка 10
199
Определение и инициализация переменной Index, которая
показывает количество ходов по нашему буферу. Данная пере
менная записана именно с заглавной буквы, т. к. с малой слово
index ранее системой зарезервировано.
Следующая часть кода:
Рисунок 108. Настройка интерфейса UART1
Настройка UART1, приведенная на рисунке 105, практически
полностью совпадает с его настройками из раздела 7.5 и рисун
ков 89-90, поэтому остановимся только на небольшой разнице.
В DMA, как говорилось ранее, мы можем использовать высокие
скорости работы последовательного интерфейса UART, поэтому
в этом примере будем работать не на 9600 б/с, а на более высо
кой скорости - 115 200 б/с. Можно и с максимальной для МК
в 4,5 Мб/с, но нам это в данный момент не требуется. Правда,
при 4,5 Мб/с крайне желательно включить и работать с линией
тактирования Clock-интерфейса, чтобы получился именно
USART, ввиду возможных наводок, длинных линий интерфейса
или возможной неправильной трассировки линий данных плат.
Строки 29-30
Инициализируем скорость в бите BRR как 0x271 для работы
на 115 200 б/с. В следующей строке разрешаем работы на пере
дачу и прием, но не включаем прерывания, как на рисунке
92 в строке 26.
200
Рисунок 109. Код инициализации светодиода на РC13 платы
BluePill
Строки 35-57
Строки закомментированы, но если применяется классиче
ская плата BluePill, то нужно раскомментировать.
Рисунок 110. Код инициализации светодиода на РВ2 платы
BluePill+
Рисунок 111. Код включения DMA
Строки 45-48
201
Включение разрешения передачи и приема данных через
DMA. Нам в примере удобнее работать под отладкой в режиме
приема данных, но пускай будет включена и передача.
Рисунок 112. Код включения DMA
Функция инициализации периферии DMA1. Постарался
в комментариях кратко показать основный смысл каждой стро
ки приведенного кода.
Строка 55
Включение тактирования периферийного блока DMA1.
Строка 58
Работа по приему потока данных в периферию USART1 со
гласно рисунку 101 данной книги. Настраиваем прерывания
по каналу 5, а именно: разрешаем прерывания от события за
вершения обмена, разрешаем прерывания от события заверше
ния половины, разрешаем прерывания от ошибки обмена (пере
дачи).
Строка 61
202
Устанавливаем 0, т. к. направление передачи данных в на
шей схеме - это чтение из периферии, в нашем случае после
довательного интерфейса UART1.
Строка 64
Включаем режим работы Circular mode - циклического бу
фера. У нас будет два буфера (о них я писал ранее), это
ReceivedBuffer [20] и MainBuffer [50].
Строка 67
Приращение адреса периферии должно быть отключено,
а приращение адреса памяти - включено, потому что мы копи
руем данные из регистра данных периферийного устройства
USART-> DR и его адрес должен быть постоянным.
Строка 70
Установка размера данных периферии в 8 бит.
Строка 73
Установка размера данных в памяти в 8 бит.
Строка 76
Установка уровня приоритета в «0», т. к. есть один канал
приема данных (Channel 5). Если необходимо использовать бо
лее одного канала, этот уровень приоритета будет полезен для
установки приоритетов работы каналов.
203
Рисунок 113. Код конфигурации работы DMA1
По сути, здесь мы указываем, откуда, куда и какого размера
данные переносим по DMA1.
Строка 83
Установка переменной datasize - какой размер данных
в потоке (RXSIZE 20).
Строка 86
Установка переменной source - откуда мы копируем данные
в потоке. Далее по тексту программы этот получаемый аргу
мент будет понятен .
Строка 86
Установка переменной destination - куда мы копируем дан
ные в потоке. Далее по тексту программы этот получаемый аргу
мент будет понятен.
Строка 92
Включаем работу DMA1.
Настроив функции DMA_Init () и DMA_Config (), можно ис
пользовать работу DMA1 для любой другой периферии, не толь
ко UART’a.
204
Рисунок 114. Код обработчика прерывания DMA1 канала 5
Это основная функция, в которой крутится весь смысл того,
что мы затеяли в этом разделе :) Попробуем разобрать, и это
не так сложно, как может показаться.
Имя обработчика
взять из ассемблерного файла
startup_stm52f10x_md. s, в котором прописаны все возможные
обработчики прерываний, которые могут быть в данном МК. Это
показано на рисунке ниже.
Рисунок 115. Имя нашего обработчика прерывания
205
Внутри обработчика проверяются два условия его срабаты
вания согласно битам 18 и 17 регистра DMA1-> ISR. Собственно,
это то, что мы включали в строке 58 данной программы. Кроме
ошибки передачи данных, мне сложно ее сымитировать и опи
сать в книге простыми механизмами, поэтому приводить позже
уже не стал.
В строке 99 проверяем появление флага завершения поло
вины обмена в канале 5 (прием потока данных по USART1),
а в строке 113 проверяем появление флага завершения обмена
в канале 5.
Строка 101
После срабатывания прерывания на половину передачи
(DMA1-> ISR) & (1 <<18)) производим побайтно копирование
данных из буфера ReceivedBuffer [0] в MainBuffer [Index] через
использование указателей и размеров 10 байт, т. к. RXSIZE опре
делено макросом как «20».
Строка 102
Очищаем бит прерывания заполнения половины буфера,
чтобы мы смогли выйти отсюда по окончании работы условия
первого if или всего обработчика, по ситуации.
Строки 103-107
Инкрементируем значения индекса (номеров) копируемых
байтов потока и при превышении ими значения 49 обнуляем.
Строка 108
Включаем светодиод на выводе РВ2, если используется пла
та BluePill+. Это делаем для наглядности работы МК вживую. Хо
рошо это видно в режиме отладки при отправке потока байтов
из персонального компьютера в МК.
Строка 113
После срабатывания прерывания
206
на
полной
передаче
(DMA1-> ISR) & (1 <<17)) производим побайтно копирование
данных из буфера ReceivedBuffer [RXSIZE/2] в MainBuffer [Index]
через использование указателей и размеров 10 байт, т. к. RXSIZE
определено макросом как «20». Разница со строкой 101 в том,
что тут начинаем копировать с адрес RXSIZE/2 буфера
ReceivedBuffer [].
Строка 116
Очищаем бит прерывания заполнения всего буфера, чтобы
мы смогли отсюда выйти по окончании работы условия второго
или первого if.
Строки 118-121
Инкрементируем значения индекса (номеров) копируемых
байтов потока и при их превышении значения - 49 обнуляем.
Строка 122
Выключаем светодиод на выводе РВ2, если используется
плата BluePill+. Это делаем для наглядности работы МК вживую.
Хорошо это видно в режиме отладки при отправки потока байт
из персонального компьютера в МК.
Основная часть программы. Конечно, совсем небольшая,
но сколько за ней стоит! Вызов функций конфигурирования
UART1 и DMA.
Строки 131-132
Установка
приоритета
обработчика
прерывания
DMA1_Channel5_IRQn в 0.
Мы ранее говорили, что канал у нас один и уровень приори
тета ни на что в данном примере не влияет.
Строка 135
DMA_Config - функция пересылки данных из регистра DR
в буфер ReceivedBuffer размером RXSIZE.
207
P.S. Суть этого большего количества строк кода в том, что
в момент, когда происходят прерывание на половинное запол
нение первого буфера (ReceivedBuffer) и пересылка его в основ
ной буфер (MainBuffer), происходит также и прием данных
в первый буфер и эти данные не теряются, что, безусловно,
очень важно. Итак, упрощенно говоря, это происходит в цикли
ческом режиме непрерывно, обеспечивая непрерывный прием
потока последовательных данных через UART1 и его пересылку
без загрузки ядра МК.
Режим заполнения буфером подробно и наглядно показан
в моем видео «STM32. CMSIS #10. Работа USART через DMA
в circular mode» по адресу: https://youtu.be/KS3S-ntoL6Y начиная
с временной метки 23:21.
Весь листинг программы представлен ниже.
«include «stm32f10x.h»
«include «string. h» //для работы функции memcpy
«define LED_PB2_0N () GPIOB-> BSRR |= GPI0_BSRR_BS2;
«define LED_PB2_0FF () GPIOB-> BSRR |= GPI0_BSRR_BR2;
«define RXSIZE 20
uint8_t ReceivedBuffer [20];
uint8_t MainBuffer [50];
uint8_t Index = 0;
void UART1 Config (void) // USART1 настройка:
{
// Инициализация портов тактирования. Порт А включили
и подключили использование альтернативных функций. Порты
В и С включили
208
RCC-> APB2ENR |= RCC_APB2ENR_IOPAEN
RCC_APB2ENR_AFI0EN | RCC_APB2ENR_IOPBEN|
RCC_APB2ENR_IOPCEN;
GPIOA-> CRH &= ~GPI0_CRH_CNF9;
GPIOA-> CRH |= GPIO_CRH_CNF9_1;
GPIOA-> CRH |= GPIO_CRH_MODE9_0;
// Настройка
GPIOA-> CRH
GPIOA-> CRH
GPIOA-> CRH
вывода на прием
&= ~GPIO_CRH_CNF10;
|= GPIO_CRH_CNF10_0;
&= ~GPIO_CRH_MODE10;
// Настройка USART1 регистрами
RCC-> APB2ENR |= RCC_APB2ENR_USART1EN;
USART1-> BRR = 0x271;
USART1-> CR1= USART_CR1_UE | USART_CR1_RE;
// настройка вывода PC13 BluePill:
//GPIOC-> CRH &= ~GPIO_CRH_MODE13; //MODE
//GPIOC-> CRH &= ~GPI0_CRH_CNF13; //CNF
//GPIOC-> CRH |= GPIO_CRH_MODE13_1; //2MHz
//GPIOC-> CRH &= ~GPI0_CRH_CNF13;
// Настройка вывода PB2 BluePill+
GPIOB-> CRL &= ~GPIO_CRL_MODE2_0;
GPIOB-> CRL |= GPIO_CRL_MODE2_1;
GPIOB-> CRL &= ~GPIO_CRL_CNF2_0;
GPIOB-> CRL &= ~GPIO_CRL_CNF2_1;
// Включение «Разрешение передачи через DMA»
USART1-> CR3 |= (1 <<7);
// Включение «Разрешение приема через DMA»
USART1-> CR3 |= (1 <<6);
}
void DMA_Init (void)
209
{
// Включите тактирование DMA1
RCC-> AHBENR |= 1 <<0;
// Включите прерывания DMA
DMA1_Channel5-> CCR |= (1 <<1) I (1 <<2) | (1 <<3);
// Установите направление передачи данных
DMA1_Channel5-> CCR &= ~ (1 <<4);
// Включите циклический режим (CIRC)
DMA1_Channel5-> CCR |= 1 <<5;
// Включите увеличение объема памяти (MINC)
DMA1_Channel5-> CCR |= 1 <<7;
// Установите размер данных периферии (PSIZE)
DMA1_Channel5-> CCR &= ~ (3 <<8); // 00:
8 Bit Data
// Установите размер данных в памяти (MSIZE)
DMA1_Channel5-> CCR &= ~ (3 <<10);
// Установите уровень приоритета
DMA1_Channel5-> CCR &= ~ (3 <<12);
}
void DMA_Config (uint32_t source, uint32_t destination,
uintl6_t datasize)
{
// Установите размер данных в регистре CNTR
DMA1_Channel5-> CNDTR = datasize;
// Установите периферийный адрес в регистре PAR
DMA1_Channel5-> CPAR = source;
// Установите адрес памяти в регистре MAR
DMA1_Channel5-> CMAR = destination;
// Включите DMA 1
DMA1_Channel5-> CCR |= 1 <<0;
}
210
void DMA1_Channel5_IRQHandler (void)
{
// Если установлено прерывание на половине
передачи,то
if ((DMA1-> ISR) & (1 <<18))
{
memcpy (&MainBuffer [Index], &ReceivedBuffer [0],
RXSIZE/2);
DMA1-> IFCR |= (1 <<18);
Index = Index+ (RXSIZE/2);
if (Index> 49)
{
Index=0;
}
LED_PB2_0N ();
}
// Если установлено прерывание завершения полной пере
дачи, то
if ((DMA1-> ISR) & (1 <<17))
{
memcpy (&MainBuffer [Index],&ReceivedBuffer
[RXSIZE/2], RXSIZE/2);
DMA1-> IFCR |= (1 <<17); //Очистка бита
прерывания
Index = Index+ (RXSIZE/2);
if (Index> 49)
{
Index=0;
}
LED_PB2_OFF ();
}
}
211
int main ()
{
UART1_Config ();
DMA_Init ();
NVIC_SetPriority (DMA1_Channel5_IRQn, 0);
NVIC_EnableIRQ(DMA1_Channel5_IRQn);
DMA_Config ((uint32_t) &USART1-> DR, (uint32_t)
ReceivedBuffer, RXSIZE);
while (1)
{
}
}
212
9 Вывод последовательной
информации через
интерфейс SWO
9.1 ОСНОВНЫЕ СВЕДЕНИЯ.
SWO. ITM
Микроконтроллеры на базе Cortex-M в одном кристалле
объединяют несколько технологий отладки и трассировки. Это
JTAG и SWD - две комплементарные спецификации, которые
позволяют подключить внешний отладчик к целевому микро
контроллеру. Те же интерфейсы используются для реализации
возможностей трассировки. Трассировка позволяет экспортиро
вать внутренние операции, выполняемые процессором, в ре
альном времени. Это своего рода аппаратная отладка, и она
выполняется с использованием пяти сигналов порта JTAG. Трас
сировка осуществляется благодаря наличию технологии под
названием «встроенная макроячейка трассировки» (Embedded
Trace Macrocell, ЕТМ), но для нее требуются более быстрые
и более продвинутые отладчики, например такие как
ULINKPro, J-Trace. Трассировка ЕТМ - это своего рода техноло
гия мониторинга (sniffing), которая не влияет на производи
тельность микроконтроллера. SEGGER производит отдельную
линейку отладчиков, называемых J-Trace, которые предлагают
трассировку микроконтроллера в реальном времени через ин
терфейс ЕТМ.
Макроячейка
инструментальной
трассировки
(Instrumentation Trace Macrocell, ITM) - менее требовательная
технология трассировки, которая позволяет отправлять про
граммно генерируемые отладочные сообщения через SWD с ис
пользованием специального сигнального I/O, называемого Serial
Wire Output (SWO). Протокол, используемый выводом SWO для
обмена данными с отладчиком, называется Serial Wire Viewer
(SWV). Поддержка SWV недоступна в микроконтроллерах на ба
зе Cortex-M0/0+.
215
По сравнению с другими псевдоотладочными периферийны
ми устройствами, такими как UART / USART, или другими техно
логиями, такими как полухостинг ARM, SWV действительно быст
рый. Скорость передачи данных пропорциональна скорости
микроконтроллера, что позволяет ограничить влияние обмени
ваемых данных на производительность встроенного программ
ного обеспечения. Очевидно, что чем быстрее запускается вы
вод SWO, тем быстрее должен быть отладчик. Вот почему
SEGGER предоставляет несколько версий своего отладчика JLink; дорогие основаны на FPGA, которая позволяет произво
дить выборку входов / выходов SWD на скорости до 100 МГц.
Интегрированный интерфейс ST-LINK со специализированным
микропрограммным обеспечением J-Link может производить
выборку сигнала SWO до 4500 кГц. J-Link Ultra+ способен произ
водить выборку сигналов до 100 МГц.
Пакет CMSIS-Core для ядер Cortex-M3/4/7 предоставляет
необходимую прослойку для обработки протокола SWV. Напри
мер, процедура ITM_SendChar () позволяет отправлять символ,
используя вывод SW0. В инструментарий GNU MCU Eclipse авто
матически интегрируется необходимая логика: если мы устано
вим макрос OS_USE_TRACE_ITM на уровне проекта, мы можем
использовать trace_printf () для вывода сообщений через
порт SWO.
Чтобы правильно декодировать байты, отправленные через
порт SWO, отладчику хоста необходимо знать частоты процес
сора и порта SWO. Последний пропорционален частоте ядра.
Отладчики J-Link обладают методом автоматического определе
ния этих скоростей. Если мы установим оба поля, CPU frequ
и SWO freq, в «0» в конфигурации отладки J-Link, то отладчик
автоматически получит необходимые скорости, когда начнется
сеанс отладки. Однако если наш код изменяет тактовую частоту
во время инициализации микроконтроллера вызовом функ
ции SystemClock_Config (), то вычисленная частота больше
не будет совпадать. Чтобы решить эту проблему, вы можете
указать рабочую частоту процессора в поле CPU frequ и частоту
216
SWO в поле SWO freq. Если вы сомневаетесь в том, какой ука
зать максимальную частоту SWO, то можете воспользовать
ся JLinkSWOViewer, способным вывести правильные значения
конфигурации.
Протокол SWV определяет 32 различных стимулирующих
порта (stimulus ports): порт является «тегом» в сообщении SWV,
используемом для выборочного включения / отключения сооб
щений. В инструментарии GNU MCU Eclipse можно определить
стимулирующий
порт
путем
определения
макро
са OS_INTEGER_TRACE_ITM_STIMULUS_PORT на уровне проекта.
Стимулирующий порт по умолчанию равен 0. Если вы измените
стимулирующий порт, то вам нужно будет изменить параметр
Port mask в настройках конфигурации J-Link. Обратите внима
ние, что параметр Port mask соответствует стимулирующему пор
ту SWV плюс 1 (т.е. если в коде выбран стимулирующий порт 0,
то Port mask должен быть равен 0x1 и т. д.).
Поддержка SWV доступна также в OpenOCD.
217
9.2 СОЗДАЕМ ПРОЕКТ
ПО ПРИМЕНЕНИЮ SWO ДЛЯ
ВЫВОДА ДАННЫХ
За основу возьмем код, написанный в разделе 5.2, где мы
создавали проект по опросу кнопки. Дописываем и донастраиваем этот проект для работы с линией вывода служебной ин
формации SWO.
Произведем настройку линии:
Рисунок 116. Выбор менеджера различных пакетов
После этого появится менеджер пакетов и настроек Keil IDE.
Далее нам нужно выбрать настройки для перенаправления
потоков вывода служебной информации в ITM в наш порт SWO,
чтобы не писать код для этого самостоятельно, т. к. в этом нам
помогает менеджер IDE.
218
Рисунок 117. Выбор настройки ITM и установки выбора
Нужно установить и выбрать соответствующую настройку,
как показано на рисунке 117, затем нажать ОК.
Далее нам нужно выбрать окно с настройками, которое по
казано ниже.
219
Рисунок 118. Настройки МК по умолчанию
Первоначально они выглядят именно как на рисунке 118.
Затем нам нужно внести некоторые изменения, как показано
ниже.
220
Рисунок 119. Внесение изменений в настройки МК
Мы устанавливаем частоту работы нашего ядра, а т. к. в про
екте мы ее не меняли, то она у нас равна 72 МГц. Также ставим
две «птички»: Trace Enable и Use Core Clock. Хотя в некоторых ре
комендациях рекомендуют оставить активным только порт но
мер 0 для работы SWO, мы оставим все остальные настройки
как есть и кликнем ОК.
Добавляем строку для правильной работы функции вывода
printf ():
221
Рисунок 120. Добавление заголовочного файла стандартной
библиотеки языка Си stdio. h
Далее в любом произвольном, но осмысленном месте до
бавляем, что мы хотим вывести, например, как показано ниже.
Рисунок 121. Добавление вывода информации
Далее собираем проект. Необходимо убедиться, что после
сборки нет ошибок, каких-либо опечаток, и зашить программу
в МК, мы с вами уже умеем это делать. Далее нам необходимо
подключить линию SWO от программатора к линии РВЗ платы,
запустить проект под отладчиком и нажать кнопку F5 для запус
ка работы проекта. В окне будет выводиться следующая инфор
мация:
222
Рисунок 122. Получение информации по SWO
Когда мы будем нажимать на кнопку платы, то информация
будет изменяться со включением индикации светодиода платы
BluePill/+.
Рисунок 123. Получение измененной информации по SWO
Если заказчику нельзя передавать весь проект из IDE (допу
223
стим, есть такие особенности), но ему необходимо снять инфор
мацию о работе устройства, то можно воспользоваться бесплат
ной программой STM32 ST-LINK Utility для получения состояния
работы устройства через линию SWO.
Также возможно получать информацию из программы J-Link
SWO Viewer, но при подключении программатора J-Link и уста
новки соответствующих для него драйверов.
Рисунок 124. Получение данных из SWO по STM32 ST-LINK
Utility
Также можно смотреть данные в SWO через программу
STM32CubeProgrammer последних версий.
Посмотреть, как это работает, можно в видео «STM32. CMSIS
#14. Вывод служебной информации через отладочный порт
SWO. IТМ» по адресу: https://youtu.be/qr1klR-CsDM
224
10 Работа с STM32CubeIDE
10.1 СОЗДАЕМ ПУСТОЙ ПРОЕКТЗАГОТОВКУ В STM32CUBEIDE
В этом разделе мы увидим, как создать пустой проект-шаб
лон в родной программе компании STMicroelectronics STM32CubeIDE.
Конечно, STM32CubeIDE обладает рядом недостатков, но,
на мой взгляд, есть как минимум два преимущества перед Keil
IDE: это проприетарность и бесплатность STM32CubeIDE. Ради
последнего пункта точно стоить обратить внимание на нее
в данном издании книги.
Рисунок 125. Программа STM32CubeIDE
Создадим проект с использованием библиотеки CMSIS, ле
жащей в своих специальных раск’ах компании ST, а именно:
STM32Cube_FW_F0_v 1.8.6 и STM32Cube_FW_F0_V1.11.0. Это по
следние версии на момент выхода издания. Они корректируют
227
ся не быстро, в отличие от своей собственной библиотеки HAL,
в которой много ошибок. Эти два пака можно скачать с офици
ального сайта ST.com. Но т. к. с ним, как и со многими подобны
ми сайтами, проблема в доступе из сети Интернет, их можно
взять из Telegram-канала книги. Существуют версии под
Windows, Linux MacOS. В книге мы используем паки под
Windows, как и саму среду разработки STM32CubeIDE, для еди
нообразия.
Рисунок 126. Варианты использования STM32CubeIDE
Создание нового проекта в STM32CubeIDE
Выбираем File -> New -> STM32Project. После того как отра
ботает медленная Java-машина IDE, вы увидите примерно следу
ющие окна:
Выбираем
или
вбиваем
наш
микроконтроллер
STM32F103C8T6 или STM32F103CBT6 и нажимаем Next.
Рисунок 127. Выбираем в меню следующий пункт
228
Рисунок 128. Отмечаем модель имеющегося у нас МК
Появляется следующее окно. В нем вводим произвольное
имя и соответствующие настройки из рисунка.
229
Рисунок 129. Вводим имя нашего проекта и выбираем настрой
ки, как в данном окне
Слева в окне Project Explorer появилось дерево проекта без
нашего кода:
230
Рисунок 130. Вид нашего незаполненного проекта
Сюда будем добавлять файлы из библиотеки CMSIS и созда
вать костяк проекта для работы с МК на библиотеке CMSIS. Да
лее удаляем всё до состояния, показанного ниже.
Рисунок 131. Формируем только необходимое на данный мо
мент данные
Файл линкера оставляем, это файл с расширением.
ld. Остальное удаляем прямо в этом окне через правую кнопку
мыши. Затем нам нужно составить и наполнить дерево собствен
ного проекта для удобной и понятной работы с библиотекой
231
CMSIS. Предлагаю свой вариант дерева. Он не является какимто самым лучшим или правильным - просто удобным, на мой
взгляд. В следующих папках сделаем ответвления в соответ
ствии со следующим рисунком.
Рисунок 132. Структура, которую нам необходимо получить
Делается это просто: кликаем правой кнопкой мыши по име
ни проекта и выбираем Folder или File.
232
Рисунок 133. Создание папок и файлов в нашем дереве
Описание папок:
Startup - в ней будут храниться startup-файл, взятый
из
CMSIS,
с
расширением.
s,
и
файл
линкера
STM32F103CBTX_FLASH. ld;
CMSIS/src и CMSIS/inc - в них будут лежать исходники, фай
лы с расширением, с в папке. src, а заголовочные файлы с рас
ширением. h - в папке inc соответственно. Это файлы из самой
библиотеки CMSIS.
Core/src и Core/inc - в них будут располагаться наши фай
лы, т. е., собственно, файлы, написанные нами. На примере ука
заны файлы main. h и main. с соответственно.
Теперь самое интересное - заполнение нашего шаблона
файлами CMSIS из паков от STMicroelectronics. Берем файл по
следней на текущий момент версии stm32cube_fw_f1_v186:
В Startup: из
Drivers/CMSIS/Device/ST/
STM32F1xx/Source/Templates/gcc/
startup_stm32f103xb. s.
В CMSIS/src: из
Drivers/CMSIS/Device/ST/STM32F 1xx/Source/Templates/
system_stm32f1xx. c
В CMSIS/inc: из
Drivers/CMSIS/Device/ST/STM32F 1xx/lnclude берем 3 файла:
1. stm32f1xx. h
233
2. stm32f103xb. h
3. system_stm32f1xx. h
и еще 4 файла из пака: STM32Cube_FW_F0_V1.11.0 и из:
Drivers\CMSIS\lnclude\cmsis_compi ler. h
Drivers\CMSIS\lnclude\cmsis_gcc. h
Drivers\CMSIS\lnclude\cmsis_version. h
Drivers\CMSIS\lnclude\core_cm3.h
К сожалению, в stm32cube_fw_fl_v186 этих последних фай
лов производитель ST почему-то не положил, а они необходимы.
Так должно выглядеть дерево после всех дополнений в на
шем шаблоне проекта:
Рисунок 134. Наш наполненный проект, но пока без нашего соб
ственного кода
234
Так как были добавлены папки и файлы в них, эти манипуля
ции нужно обязательно зафиксировать в настройках
STM32CubeIDE.
Нажимаем на правую кнопку мыши на имени нашего проекта
(CMSIS_project) -> Properties -> C/C++ Build -> Settings -> Tool
Settings -> MCU GCC Linker -> General - и здесь нужно указать
новое расположение скрипта линкера с помощью кнопки
Browse.
Рисунок 155. Место установки подключения файла-линкера
Также нужно указать пути к файлам проекта: Properties -> С/
C++ General -> Paths and Symbols-> Includes.
235
Рисунок 136. Место подключения папокinc. Путь должен быть
точно таким и у вас
Properties -> C/C++General -> Paths and Symbols -> Source
Location:
Рисунок 137. Место подключения всех остальных папок. Путь
должен быть точно таким и у вас
В Includes пути к папкам inc, а в Source Location логично бы
ло бы к папкам src, но если сделать так, то в дереве проекта эти
папки будут добавлены отдельно, это получается не очень кра
сиво и удобно. Чтобы не загромождать зрительно дерево,
в Source Location можно указать только корневые папки Core,
CMSIS и Startup.
236
Вводим обязательный минимум кода проекта заготовки:
Рисунок 138. Минимальный код проекта заготовки
Рисунок 139. Здесь подключается наш МК платы BluePill+
Для того чтобы проект скомпилировался без ошибок, необ
ходимо раскомментировать в файле stm32f1xx. h строку с назва237
нием нашего микроконтроллера и в main. с добавить функцию
int main с соответствующими строками, как на рисунке 138.
Необходимо сформировать первичные настройки по умолча
нию, как на рисунке 139.
Далее необходимо создать первичные настройки по умолча
нию в следующем окне.
Рисунок 140. Создаем конфигурацию проекта
238
Рисунок 141. Настройки по умолчанию
И нажать кнопку Run. Если программатор не подключен, то
высветится ошибка о его отсутствии:
259
Рисунок 142. Показатель неподключенного и нераспознанного
программатора ST-Link
Далее нажимаем комбинацию горячих клавиш Ctrl+B - про
ект начинает собираться и компилироваться. Если мы всё сдела
ли правильно, то должно появиться окно примерно со следую
щим содержанием.
Рисунок 143. Ориентировочные параметры скомпилированного
проекта
Проект собран, структурирован и готов к написанию какоголибо собственного кода, но об этом подробнее в следующей ча
сти книги.
Для справки. После компиляции должно быть занято при-
240
мерно 1,5 килобайта памяти ОЗУ (RAM). И вспоминаем про стек
и кучу:
Рисунок 144. Заполнение памяти пустого проекта заготовки
Размеры стека и кучи указаны в файле скрипта линкера тот файл, что с расширением. ld в нашем проекте. Их можно
и нужно изменять в соответствии с требованиями проекта. Сей
час пока оставляем значения по умолчанию. Эти значения нахо
дятся в начале файла линкера в виде значений секций
_Min_Heap_Size и _Min_Stock_Size с указанием размера в шестна
дцатеричной форме.
241
10.2 СОЗДАЕМ РАБОЧИЙ ПРОЕКТ
В STM32CUBEIDE
Вставляем весь ранее написанный код из примера о работе
кнопки через вывод РВ2 раздела 5.3 в наш созданный проектзаготовку (шаблон):
242
Рисунок 145. Перенесенный из KEIL IDE в STM32CubelDE код
243
Нажимаем Ctrl+B, и должно появиться окно с примерно сле
дующим содержанием:
Рисунок 146. Лог скомпилированного проекта
С компиляцией всё прекрасно.
Размер занимаемой памяти должен измениться, т. к. проект
уже совсем не пустой.
Далее необходимо запрограммировать нашу BluePill+
и проверить тем не менее работоспособность нашего кода или
созданного проекта в STM32CubeIDE. Кликаем по ниспадающей
кнопке рядом с белым треугольником, затем по имени нашего
проекта согласно рисунку 147:
244
Рисунок 147. Выбираем мышкой имя нашего проекта для про
граммирования платы BluePill+
Процесс программирования аналогичен ранним, и после
окончания можно работать на нашей плате - зажигать светоди
од по нажатию кнопки платы BluePill+.
Рисунок 148. Заполнение памяти заполненного проекта заготов
ки
Все работы проводились в STM32CubeIDE следующей вер
сии:
Настройки компилятора были задействованы следующие:
245
Рисунок 149. Версия рабочей STM32CubeIDE
Рисунок 150. Настройки компилятора STM32CubeIDE
P.S. Вывод достаточно простой. По сути, неважно, в какой
IDE работать, главное - правильно использовать библиотеку
CMSIS. С ней одинаково удобно работать в разных IDE с одина
ковым кодом, как мы видим в этом разделе книги, взяв код один
в один из начала книги, из Keil IDE. Плата также отрабатывает
заложенный в ней функционал.
246
Список литературы
1. https://www.promelec.ru/news/890/
2. Energy Efficiency across Programming Languages. How Do
Energy, Time, and Memory Relate? URL: https://dl.acm.org/doi/
10.1145/3136014.3136031
3. Документ AN2548 от ST.com. URL: https://www.st.com/
resource/en/application_note/an2548-usin g-the
-stm32f0f1f3cxgxlx-series-dma-controller-stmicroelectronics.pdf
249
Об авторе
Николай Русин, магистр техники и технологии
Имеет более чем 20-летний стаж в IT-индустрии. Разраба
тывал изделия для передачи данных на различные расстояния,
системы определения позиционирования для встраиваемых но
симых решений. Принимал участие в проектах разработки
и поставки промышленных изделий московского метрополите
на «Метровагонмаш» - систем контроля скорости, индикации,
мониторинга, связи и работы различных экстренных служб.
Принимал участие в проектировании и производстве систем
управления самолета-амфибии Бе-200.
Автор учебного курса и ведущий лектор курса по разработке
изделий с помощью САПР Altium Designer для Ростовского госу
дарственного университета. Автор учебной серии книг «Освое
ние STM32 самостоятельно», а также персональных курсов
по обучению программированию STM32. Ведет авторский канал
на платформах YouTube, Rutube и VK Video, группу в VK
и Telegram-канал.
253
Николай Русин
Освоение STM32 самостоятельно #1
Создано в интеллектуальной издательской системе Ridero