Показаны сообщения с ярлыком STM32. Показать все сообщения
Показаны сообщения с ярлыком STM32. Показать все сообщения

суббота, 2 мая 2026 г.

STM32 FreeRTOS blue pill

 Изучим бесплатную операционную ситему для встраиваемых систем FreeRTOS. Это не такая операционная система как те что устанавливаются на персональные компьютеры либо одноплатные вроде Raspberry pi. FreeRTOS это многозадачная операционная система реального времени для микроконтроллеров. В ней нет многого того что есть в обычных операционных системах но зато есть многозадачность. Что может быть полезным для случаев при которых микроконтроллер должен выполнять некоторое количество функций. Естественно микроконтроллер не может реально параллельно выполнять много задач одновременно. Если это конечно не такие задачи возможность выполнения которых предусмотрена на аппаратном уровне например DMA. В общем случае, задачи выполняются последовательно и на каждую выделяется определенное время. FreeRTOS является библиотекой которую можно подключить к проекту. Подключив её, можно будет проще реализовывать многозадачность чем при написании кода с нуля самостоятельно. В интернете есть один интересный проект, хорошо подходящий для изучения данной операционной системы на плате blue pill с микроконтроллером stm32f103. Найти его можно по ссылке https://github.com/sbodd/bluepill/tree/master. На странице проекта есть небольшая инструкция по его запуску:

  1. install the toolchain and libarary.
  2. change the TOOLROOT path in Makefile.common
  3. change arm-none-eabi-objcopy path in mkdump.sh
  4. run bash mkdump.sh (it will compile and flash final binary to bluepill, will ask for root password while flashing)
  5. go to app dir and type make clean to remove generated bins and object files

Правда мне пришлось кое что сделать дополнительно чтобы проект собрался. Но в целом больших проблем не было. Всё что пришлось сделать, большего чем в инструкции, это изменить пути в ещё некотором количестве переменных т.к. toolchain был установлен путем копирования и распаковки в рандомный каталог без прописывания системных переменных. Для начала нужно зайти в папку в которую требуется установить проект, открыть её в командной строке
и вписать команду клонирования проекта из гита.

sudo git clone https://github.com/sbodd/bluepill.git

Помимо самого проекта, также понадобиться toolchain для микроконтроллеров stm32. Просто так его скачать не получилось (запретили качать из моего региона) поэтому пришлось скачать его сложным способом и добавить, на всякий случай, к себе на яндекс диск.

https://disk.yandex.ru/d/b14nePpO9Jjo5g

После скачивания, можно разархивировать данный тулчейн например утилитой tar.

sudo tar -xf  gcc-arm-none-eabi-7-2018-q2-update-linux.tar.bz2

Далее понадобиться изменить пару файлов проекта. Это Make файл и скрипт сборки проекта.


В Make файле нужно поменять все пути до тулчейна на корректные

Тоже самое проделать в скрипте сборки проекта + можно убрать загрузку stlink ом если нет stlinkа.
Каждый файл нужно сохранить. В редакторе nano для этого есть комбинация клавиш ctrl+o.
Теперь всё готово для сборки проекта и можно начинать исследовать исходный код.
он содержится по пути корень->Src->FreeRTOS->main.c
В начале, как всегда, подключаются заголовочные файлы библиотек.

Ниже объявляются хэндлеры задач. Задачи у нас будет две, для начала для простоты, поэтому и хандлера всего два.
Ещё ниже объявляются прототипы функций задач.
И ещё есть некоторое колличество прототипов функций инициализации и вспомогательных функций.
В функции main вызывается функция сброса настроек тактирования
и функция обновления тактирования ядра.
Также функция инициализации периферии. В данном случае это инициализация uart1.

В данном проекте микроконтроллер выполняет всего две задачи. Вывод одного текста в uart и вторая задача, вывод другого текста в uart.
Название функции, в которой будет задача, передается первым параметром в функцию xTaskCreate()
Вторым параметром в эту функцию передается просто название задачи кторое нигде больше не используется а служит просто для удобства и понятности.

Далее задается размер стэка задачи. Насколько я понял это то ко
личество оперативной памяти, в словах, которое задача может использовать в стэке.

Следующий параметр это параметр который может быть передан в задачу. Т.к. пока параметров нет то передаем туда нуль
Предпоследний парметр это приоритет задачи. Чем меньше цифра тем выше приоритет. 
И последний параметр это адрес, ранее обявленного, хэндлера задачи.

Данный хэндлер впоследствии может быть использован для управления задачей, например для её завершения или приостановки.
Всё тоже самое проделывается теперь для второй задачи.
 После запускается шэдулер который, собственно говоря, и выполняет эти задачи.
В задачах обычно имеются бесконечные циклы и поэтому по идее выполнение программы никогда не должно выходить дальше шедулера но если вдруг это произойдет то дальше есть обычный бесконечный пустой цикл.
Также имеется функция инициализации периферии и пара вспомогательных функций для отправки сообщений в uart.
Теперь можно рассмотреть сами задачи. Функции ывполняюшие задачи должны принимать один указатель на тип void и ничего не возвращать т.е. возвращать void
В этих функциях д.б. бесконечные циклы. В данном случае одна из задач посылает один текст в uart с одной задержкой.
Вторая задача посылает в uart другой текст с другой задержкой между отправками.
Чтобы запустить сборку проекта, нужно зайти в папку со скриптом запуска проекта и запустить его специальной командой.
После, если сборка прошла успешно, можно найти прошивку в формате .bin там же где находиться исходный код. И загрузить её в микроконтроллер например через FlashLoaderDemonstrator или stlink. 


Также есть видеогайд коорый можно посмотреть



Адрес биткоин кошелька для поддержки - bc1qlhrmmkh77x2lzhqe4lt9qwkglswj64tsqt2l5g

воскресенье, 5 апреля 2026 г.

STM32 DMA CMSIS UART to GPIO

 Одной из не обязательных но тем не менее весьма полезных функций в микроконтроллерах STM32 является DMA (Direct Memory Access) т.е. дословно "прямой доступ к памяти". В микроконтроллерах такую функцию обычно выполняет специальный контроллер. Также таких контроллеров может быть больше одного. DMA контроллер может выполнять передачу данных из одной области памяти в другую область минуя центральный процессор, который в этот момент может выполнять какие то более сложные функции. Поскольку передача данных это очень частая в практике и очень ресурсозатратная операция то наличие DMA контроллера позволяет значительно повысить производительности микроконтроллера в целом. Даже с учетом того что шина данных используемая и процессором и DMA контроллером одна и это отчасти несколько ограничивает эффективность такого распараллеливания, в целом всё равно использование DMA имеет смысл. Например нам нужно чтобы данные поступающие из UART или SPI попадали в массив расположенный в оперативной памяти. Возьмем для начала UART1 и попробуем через DMA передать данные в GPIO для наглядности. Как обычно сперва добавляем заголовочные файлы библиотек и макросы. Использовать макросы не рекомендуется для лучшей удобочитаемости кода. Однако они всё таки могут быть полезны. Например можно использовать макросы для простой и быстрой настройки каких либо часто используемых режимов какой либо периферии. Ниже пример макросов для настройки на самые часто используемые режимы GPIO и включение тактирования. 

Данные будем брать из UART1 и передавать в порт B а чтобы было видно результат, можно подключить трехцветный светодиод на свободные пины порта B

Порт B используется потому что на порту A работает UART1 и передача данных туда, может помешать работе UART1. Далее, как обычно, настраиваем пины и uart (о том как это делается см. предыдущие уроки по STM32) но только на этот раз дополнительно устанавливаем 6й бит третьего управляющего регистра UART чтобы включить DMA приемник.

USART1->CR3 |= 1<<6;

В используемом микроконтроллере имеется 2 DMA контроллера. У первого есть 7 каналов у второго 5. Каждый канал может проводить только определенные запросы от определенной периферии. Для того чтобы выяснить какая периферия каким каналам соответствует есть две таблицы в мануале. Одна для первого DMA вторая для второго. Т.к. UART1 есть на DMA1 то нас интересует первая таблица. Из неё мы находим нужный нам канал. Это пятый канал первого DMA по тому что на нем uart1 приемник.

DMA1_Channel5

Также ещё важно не забыть включить тактирование DMA1.

RCC->AHBENR |= 1;

В регистр периферии DMA1 передаем адрес регистра данных UART1. В регистр памяти DMA1 передаем адрес порта B. Т.о. обозначив направление передачи данных.

// 2. Указываем адрес периферии (регистр данных UART1)

DMA1_Channel5->CPAR = (uint32_t)&(USART1->DR);

// 3. Указываем адрес назначения (регистр ODR порта GPIOB)

DMA1_Channel5->CMAR = (uint32_t)&(GPIOB->ODR);

В регистр количества пересылок записываем единицу по тому что больше пока не требуется.

DMA1_Channel5->CNDTR = 1;

В конфигурационном регистре устанавливаем только биты 5 для циклического режима т.е. неоднократной передачи и бит 1 для включения DMA1. Остальное установлено по умолчанию так как нужно.

DMA1_Channel5->CCR = 1<<5 | 1;

Основной цикл пуст (при желании его можно заполнить), прерывания тоже не используются.  Можно сказать что всю нужную работу, в данном случае, выполняет только DMA и если всё заработает то значит работает DMA. Полный код:

В итоге всё заработало. После загрузки прошивки, визуально светодиод начал реагировать на поступающие на плату символы.

Адрес биткоин кошелька для поддержки блога - bc1qlhrmmkh77x2lzhqe4lt9qwkglswj64tsqt2l5g


пятница, 15 марта 2024 г.

Системный таймер STM32

 Микроконтроллеры STM32 содержат некоторое количество таймеров. Таймеры делятся на типы и отличаются друг от друга. Существуют, ранее рассмотренные, таймеры общего назначения (напр. TIM2 и TIM3), продвинутые таймеры (TIM1 почти такие же как таймеры общего назначения но с большим количеством дополнительных функций), сторожевые таймеры и системные таймеры (обычно системный таймер только один). Системный таймер самый простой и может использоваться когда не нужен какой либо сложный функционал. Данный тип таймера имеет некоторое небольшое количество регистров. Например регистр счетчика. В этом регистре находиться число которое показывает до скольки досчитал данный таймер. Считать он может только вниз т.е. только декрементировать (уменьшать на единицу) число в данном регистре. Этот регистр 23х разрядный:


После того как число в этом регистре достигло нуля, происходит запись числа в этот регистр из регистра перезагрузки:

Т.е. можно сказать что в этом регистре находиться число того сколько раз считает таймер прежде чем произойдет его переполнение. После того как произошло переполнение, данного таймера, может быть вызвано прерывание. Чтобы разрешить прерывание нужно установить единицу в бит
1 регистра настройки таймера:

У данного таймера всего один регистр настройки в котором для настройки есть всего три бита. Также есть один флаг. Бит 1 (как было показано выше) отвечает за разблокировку прерывания по переполнению (других прерываний у данного таймера нет). Этот бит д.б. установлен в единицу чтобы прерывание срабатывало. Также есть бит 0 для включения таймера:
И бит 2 для выбора внутреннего источника тактирования:

единица, в данном бите, означает тактирование от внутреннего источника, ноль означает тактирование от внешнего источника. Т.е. для настройки таймера (если не нужно внешнее тактирование) все три бита нужно заполнить единицами. Предделителя у данного таймера нет поэтому снизить частоту возникновения прерывания можно например дополнительным программным счетчиком или же например установить внешнее низкочастонтное тактирование от например микросхемы - таймера 555 или от пина этого же микроконтроллера (правда в этом случае придется задействовать какой либо другой таймер если нужна точность). 
Видео по данной теме:



Адрес биткоин кошелька для поддержки канала - bc1qlhrmmkh77x2lzhqe4lt9qwkglswj64tsqt2l5g

суббота, 4 ноября 2023 г.

Правильная настройка таймера на STM32 CMSIS режим импульса

 Таймеры микроконтроллеров STM32 имеют функцию одиночного импульса. Эта функция похожа на обычное использование таймера с циклической выдачей импульсов но только + к этому добавляется автоматическая аппаратная остановка сразу после выдачи первого импульса. Настраивается данная функция очень просто. Для этого просто устанавливается один третий бит регистра CR1 в единицу. Однако при использовании данного режима и обычной настройке (какая была описана в статье -> https://electe.blogspot.com/2021/12/stm32f103c8t6-cmsis_24.html) будет заметна такая проблема как срабатывание прерывания таймера сразу после старта микроконтроллера. Это может быть большой проблемой т.к. некоторые действия будут выполняться в незапланированный промежуток времени. Чтобы этого избежать нужно выполнить определенную последовательность действий при настройке таймера. Рассмотрим код:

Константой DELAY_MS  задается время задержки в миллисекундах. В константу DEL записывается значение для записи в регистр для создания задержки записанной в константе DELAY_MS . Следующий макрос с конструкцией if проверяет константу на не выход за допустимый предел значения. Если предел превышен то компилятор выдаст ошибку и компиляция не произойдет. В этом случае нужно уменьшить задержку. В основной функции производиться настройка пинов. Пин PC13 (со встроенным светодиодом на плате "blue pill") настраивается на выход в режиме push-pull. Пин PB8 настраивается на вход с подтяжкой к плюсу для подключения кнопки между этим пином и землей для запуска таймера по нажатию данной кнопки. При нажатии на кнопку запускается таймер, через некоторое время (заданное константой DELAY_MS ) срабатывает прерывание и светодиод меняет свое состояние вслед за изменением своего состояния пином PC13 микроконтроллера. Правильная последовательность настройки таймера выглядит так:

 1) - записать, что надо, прерывание еще не разрешать,

 2) - установить искусственно флаг UG в регистре EGR - это обновит PSC|ARR новыми значениями.

3) - сбросить установившиеся флаги прерываний

4) - только теперь разрешить прерывания и запустить таймер.

Посмотреть работу платы blue-pill, в таком режиме, можно на видео:



 источник -> https://electronix.ru/forum/index.php?app=forums&module=forums&controller=topic&id=120406


воскресенье, 4 декабря 2022 г.

STM32 CMSIS внешние прерывания и счетчик дребезга контактов

Внешние прерывания могут быть полезны например когда нужно зафиксировать кратковременные импульсы на пине микроконтроллера, по тому что обычный опрос пина не обязательно будет происходить в нужный момент времени. Для демонстрации полезности внешних прерываний, на микроконтроллере STM32, можно изготовить например счетчик дребезга контактов. Ранее, в данный блог, уже выкладывалась статья о внешних прерываниях для ATtiny2313 и настройке их на ассемблере. Такой счетчик показывает что дребезг есть при нажати на кнопку и даже при её отпускании, хотя при отпускании он заметно слабее и даже иногда может отсутствовать, хотя это зависит от конструкции кнопки. Счетчик дребезга контактов также может использоваться как генератор случайных чисел т.к. количество дребезга практически невозможно предсказать. М.б. на него можно, в некоторой степени повлиять например магнитом, но например в тандеме с псевдослучаным программным генератором, счетчик дребезга контактов может быть одновременно и устойчивым к внешним физическим воздействиям воздействиям и генерировать реально случайные числа.  Схема может быть например такой:

Рисунок 1 - Счетчик дребезга контактов кнопки

В микроконтроллере STM32F103 на плате "blue pill" может одновременно применяться не более 16ти датчиков внешних прерываний. Каждое из этих 16ти прерываний может быть переключено между портами к пинам соответствующим номеру прерывания т.е. например первое прерывание может быть на пине PA1 или PB1 (или PC1 если бы он был на данном микроконтроллере (на других микроконтроллерах STM32, с большим количеством пинов, это может быть)). По умолчанию все внешние прерывания работают на порту А поэтому, чтобы меньше возиться с настройками, можно использовать для внешних прерываний только пины порта A это сделает настройку внешних прерываний не сложной. Как обычно, нужно включить тактирование порта А в регистре APB2ENR, сделать это можно командой:

Выбранный пин (пин А2) надо настроить на вход с альтернативной функцией и подтяжкой к +питания, сделать это можно командами:

Установить прерывания по восходящему фронту для пина А2 можно установкой в 1 бита 2 регистра XTI_RTSR:

Для этого в коде можно написать команду:

Для того чтобы аналогично включить прерывания по спаду, можно использовать регистр XTI_FTSR:


Для разрешения внешних прерываний пина 2, бит 2 регистра EXTI_IMR

 устанавливается в 1:

Теперь чтобы прерывание сработало осталось разрешить его в NVIC командой:

NVIC_EnableIRQ (EXTI2_IRQn);

После этого можно написать функцию-обработчик прерывания:

Записью единицы в бит 2 регистра EXTI_PR сбрасывается флаг внешнего прерывания 2.

counter++  -инкремент счетчика дребезга контактов.

Помимо описанных выше команд, также понадобиться работа с UARTом (см. передача по UART - https://electe.blogspot.com/2022/01/uart-stm32f103c8-cmsis.html и прием по UART - https://electe.blogspot.com/2022/01/uart-stm32f103c8-cmsis_7.html) для вывода получения результатов и некоторые другие команды. Полный текст программы можно скопировать из поля:

Результат работы счетчика дребезга контактов можно посмотреть на видео:


КАРТА БЛОГА (содержание)

суббота, 3 сентября 2022 г.

STM32 CMSIS ADC использование нескольких каналов АЦП

 Микроконтроллеры STM32 (например stm32f103c8), как правило, имеют некоторое количество каналов аналого-цифрового преобразователя (АЦП(ADC)) что может быть использовано например для снятия показаний например с некоторого количества потенциометров или аналоговых датчиков. О том как снимать показания с одного потенциометра уже имеется статья в данном блоге. Если взять код от туда и дополнить его 3мя строками инициализации АЦП то можно получить устройство которое снимает показания с двух потенциометров.

 Далее аналогично можно расширить устройство на больше количество каналов. Есть некоторое количество способов использования нескольких каналов но наиболее простой в настройке - это использование прерывистого режима. Микроконтроллер stm32f103c8 (установленный в популярной плате "plue pill") может автоматически поочередно переключать, своим мультиплексором, свой АЦП между разными каналами но если это регулярные каналы то регистр данных у них один и данные в нем будут также автоматически перезаписываться после автоматического переключения, поэтому чтобы "спокойно" забрать результат из регистра данных нужно чтобы после преобразования наступила пауза. Если включить прерывистый режим, установкой бита 11 регистра CR1,

то автоматическое переключение между каналами будет прерываться после того как конверсия пройдет с каналами количество которых установлено битами 13-15 данного регистра. По умолчанию там записаны нули, что означает что перерыв будет после конверсии по каждому одному каналу - а это именно тот режим который нужен т.к. если поставить больше то данные от  каналов будут перезаписываться и из регистра данным можно будет забрать результат только от последнего канала. 

Далее нужно регистрами SQR1,SQR2,SQR3 настроить количество и номера каналов на которые будет переключаться АЦП. 


Для настройки количества каналов используются биты 20...23 регистра SQR1, 0b0000 - означает 1 канал, 0b0001 - 2 канала и т.д.



Для того чтобы вторым каналом был второй, по счету, канал нудно в биты 5....9 регистра SQR1 записать единицу т.е. записать единицу в бит 5 (т.к. остальные, по умолчанию, нули).



Если нужно использовать 3 канала то аналогично настраиваются биты 10...15 данного канала (не забыв при этом про настройку количества). Если регистр SQR3 закончился то дальше идет переход на SQR2 а после на SQR1 до битов настройки количества. После добавления данных 3х строк программа уже будет выдавать в последовательный порт числа поочередно с одного и с другого каналов. Для большего удобства и вывода показаний в два столбца можно добавить специальную конструкцию.



Полный код можно скопировать из текстового поля:


Посмотреть результат работы программы можно на видео:


КАРТА БЛОГА (содержание)









среда, 31 августа 2022 г.

АЦП (ADC) на stm32f103, Си и cmsis

 Аналого цифровой преобразователь (далее АЦП) является важным периферийным устройством в микроконтроллере т.к. аналоговые сигналы, наряду с цифровыми, часто используются. Примеров ситуаций при которых используются аналоговые сигналы можно привести много. Это например может быть какой нибудь датчик температуры, напряжения, токовый шунт, джойстик, резистивная кнопка и т.д. АЦП микроконтроллеров плохи тем что напряжения подаваемые на пин микроконтроллера часто небольшие (особенно для STM32 это 3.3В) а также тем что они могут уступать по быстродействию и/или разрядности специализированным микросхемам. Первый недостаток устраняется использованием резистивного делителя напряжения а второй тем что это не всегда имеет большое значение а также большим количеством настроек аппаратного АЦП данного микроконтроллера для управления точностью, скоростью и количеством каналов. Начать изучение АЦП микроконтроллеров STM32 желательно с чего нибудь простого. Например можно подключить к нему какой нибудь простой потенциометр и попытаться определить напряжение на среднем его выводе а результат выводить например по uart на пк и смотреть его в мониторе последовательного порта среды разработки Arduino IDE. Для начала нужно выбрать пин микроконтроллера к которому можно подключить средний вывод потенциометра. Для этого можно глянуть таблицу в даташите:

Если выбрать пин PA0 то дальше настройка потенциометра будет немного проще чем если выбрать другой пин. Т.к, при таком варианте, не нужно будет настраивать регистры SQR. Схема может быть например такой:
Сопротивление резистора может быть другим но главное чтобы оно не было слишком низким чтобы не сделать слишком большую нагрузку на цепи питания. Конденсатор нужен для уменьшения погрешности измерения путем ослабления помех. Как обычно для периферийных устройств для АЦП нужно включить тактирование, но только теперь помимо этого нужно установить делитель частоты т.к. АЦП не может работать на частоте более 14МГц. Чтобы например сделать деление на 8 нужно записать единицы в 14й и 15й биты регистра конфигурации тактирования.
Далее, как обычно (см. предыдущие уроки), настраивается тактирование периферийных устройств (GPIO, АЦП и UART) а также режимы работы GPIO (в данном случае пин PA0 настраивается на альтернативный вход) и UART1 настраивается на передачу.
Далее если бы использовался бы пин не PA0 а другой то тогда бы пришлось настраивать регистры SQR. В данном микроконтроллере этих регистров 3 штуки ADC_SQR1, ADC_SQR2 и ADC_SQR3. В биты 20-23 регистра ADC_SQR1 записывается количество измерений за один проход. АЦП в данном микроконтроллере не один и каналов АЦП больше одного а также АЦП дает погрешность для уменьшения которой может понадобиться усреднение значения с некоторого количества измерений. В остальные биты данных регистров, за исключением битов 24-31 регистра ADC_SQR1, записываются номера каналов на которых будут производиться измерения. Первый из них записывается в биты 0...4 регистра ADC_SQR3 далее записываются остальные каналы. Если регистр ADC_SQR3 заканчивается то далее аналогично продолжается запись в регистр ADC_SQR2. Если и ADC_SQR2 тоже заканчивается то далее заполняется регистр ADC_SQR1 до бита 20 т.к. с этого бита начинается запись количества каналов. Т.к. по умолчанию в этих регистрах нули то это означает что измерение будет проводиться один раз на нулевом канале поэтому в данном случае данные регистры можно не трогать.
Есть ещё регистр настройки времени выборки ADC_SMPR2
Можно пробовать изменять значение в этом регистре чтобы повлиять на точность измерений. Работа АЦП может быть запущена разными способами. Для установки запуска программным способом нужно в регистр в биты 17...19 регистра ADC_CR2 записать единицы. 
Также нужно установить бит 20 данного регистра в единицу для разрешения внешнего запуска.
Для включения АЦП, в единицу устанавливается бит 0 данного регистра.
Для запуска калибровки АЦП нужно установить бит 2 данного регистра. 
По окончании калибровки данный бит сам сброситься. Чтобы преждевременно не начать работу с АЦП можно организовать проверку данного бита в цикле.
Для запуска АЦП, устанавливается бит 22 данного регистра



Об окончании аналого цифрового преобразования свидетельствует бит 1 регистра ADC_SR


Теперь можно забрать результат преобразования из регистра данных ADC1_DR
Чтобы увидеть результат можно его преобразовать в символы и отослать по UART на компьютер. Полный код программы можно скачать из текстового поля:

Данный урок в видеоформате можно посмотреть на видео:
КАРТА БЛОГА (содержание)