В последнее время на хабре появилось много статей по STM32 (). В комментариях неоднократно упоминается сложность STM32 по сравнению с AVR. Эта тема особенно влияет на новичков, которые хотят начать изучение микроконтроллеров, и, видя такое мнение, выбирают для изучения AVR. Давайте разберемся, так ли сложен этот зверь - STM32?
Для этого выберем недорогой вариант платы и напишем прошивку в десяток-другой байт (да-да, мигание светодиодом в 2 килобайта сродни «Hello world» в сотни килобайт x86 для неумех). Также научимся писать программы на любом языке программирования для STM32.
Вступление
Какой тип микроконтроллеров изучать? Этот вопрос, по-моему, аналогичен вопросам типа «Какой язык программирования изучать?», «Какой иностранный язык учить?». ИМХО, изучать нужно тот, который нужнее в данную минуту, для данной задачи. Когда знаешь что-то одно, изучение второго дастся намного легче, а на счет третьего и не задумаешься.Итак, в чем же сложность STM32? Наиболее часто звучит мнение о сложности программирования его периферии. Количество и тип периферии STM32 и AVR примерно одинаков. Конфигурирование ее также не сильно отличается. Так в чем же сложность? В микроконтроллерах STM32 всю периферию нужно предварительно включать. Вот и вся сложность.
Я сравниваю AVR с общественными зданиями: все двери нараспашку, везде мониторы сверкают рекламой и свет горит, а STM32 с личным домом: хочешь телевизор посмотреть - включи сначала, потом переключай каналы, захотел пи-пи - открой дверь и включи там свет, руки помыть - открой воду, и так далее. Не верите? Убедимся вместе.
Обзор платы
Я выбрал самую дешевую плату из предложенных на aliexpress (рисунок выше). Чуть дороже $2, 180 рублей в декабре 2015. На борту минимальная обвязка: два кварцевых резонатора - высокочастотный на 8МГц и часовой на 32.768Гц, кнопка «сброс», два джампера выбора режима загрузки, пара светодиодов - на питание и на ножке PC13 и набор разъемов: microUSB, отладочный, две гребенки для всех выводов микроконтроллера.Дешевле только купить все детали, сделать самому плату и спаять. Чем шить и отлаживать? Если есть ST-LINK, то лучше им, нет - не беда, есть еще несколько вариантов, например через USB-USART переходник, нет и его - можно напрямую через USB, правда нужно самому написать драйвер для такого случая, никто пока не озаботился. ST-LINK достаточно дешев, да и входит во все платы серии DISCOVERY. Вот и я воспользовался таким.
Подключаем питание, светодиод весело мигает, плата исправна. Скачиваем и устанавливаем программу-программатор (масло-масляное) «STM32 ST-LINK Utility» (все программы и документы берем на сайте производителя). Пытаемся считать прошивку… Программа защищена от чтения. Видимо, недаром все говорят о сложности написания программ для STM32, даже китайцы защитили эту сверхсложную программу от взлома. Или там спрятана закладка-вирус? Разбираться не будем, снимаем защиту и получаем девственно чистый микроконтроллер STM32F103C8T6.
Первая программа
Давайте тоже помигаем светодиодом, сделаем, так сказать, реверс-инжиниринг в уме родной прошивки. Чем? Чтобы не городить споры по выбору среды разработки, я это сделаю в родной Visual Studio Community. Мне кажется, для Windows лучше для мужчины нет.Как там программа мигания для ардуины? Конфигурируем ножку на выход и в цикле переключаем ее с нуля на единицу и обратно.
А как будет выглядеть она же для STM32? Намного сложнее. Сначала включим свет в комнате конфигурации ножек микроконтроллера, а затем «Конфигурируем ножку на выход и в цикле переключаем ее с нуля на единицу и обратно». Я понимаю, сложно… Но мы справимся.
В документе «RM0008. Reference Manual» на наш микроконтроллер посмотрим карту памяти для нужных нам регистров.
- Пойдем простым и логическим ходом.
- Пойдем вместе.
1. Включим тактирование порта C (наш светодиод висит на ножке 13 порта C). Смотрим документ. Нужный нам регистр RCC_ABP2ENR (переводим: регистр сброса и тактирования - вторая низкоскоростная шина периферии). Адрес порта - 0x40021018, нужный бит IOPCEN (порт ввода-вывода C - бит разрешения) четвертый - 0x00000010.
Отступление
У микроконтроллеров все как у взрослых процессоров. Есть высокоскоростная шина AHB aka «Северный мост» и низкоскоростная APB aka «Южный мост». Сам процессор микроконтроллера умеет все для ускорения работы: имеет предвыборку команд, конвейер выполнения команд. Нет кеша, но процессор не намного быстрее памяти, и чтение-запись в память успевает выполняться за один такт. Так что, можно сказать, вся память микроконтроллера - это один большой кеш. Ладно-ладно, не один и не большой. Два маленьких кеша.
Вся периферия отображена (маппирована) на адресное пространство. По сравнению с x86 нет команд in-out, но и Intel оставил их только для совместимости, сейчас они практически не используются.
2. Сконфигурируем ножку на вывод. Смотрим документ. Нужный нам регистр GPIOC_CRH (переводим: регистр порта ввода-вывода C - конфигурационный регистр для старшей половины ножек). Адрес порта - 0x40011004, за конфигурацию каждой ножки отвечают 4 бита, значение для переключения ножки на выход - 0001b, для ножки 13 значение - 0x00100000.
3. Как переключить логическое значение на ножке. Смотрим документ. Нужный нам регистр GPIOC_ODR (переводим: регистр порта ввода-вывода C - регистр вывода данных). Адрес порта - 0x4001100С, его значение напрямую выводится в ножки микроконтроллера, для ножки 13 значение - 0x00002000. Все готово для написания программы (не забыть выложить проект на github):
Int main(void)
{
*((int*)0x40021018) = 0x00000010; // RCC_APB2ENR = RCC_APB2ENR_IOPCEN
*((int*)0x40011004) = 0x00100000; // GPIOC_CRH = MODER_OUTPUT_13
while(1)
{
*((volatile int*)0x4001100C) ^= 0x00002000; // GPIOC_ODR ^= BIT_13
int i; for (i=1000000; i>0; i--) ;
}
}
extern int _eram;
__attribute__ ((section(".isr_vector")))
int g_pfnVectors =
{
(int)&_eram, // начальное значение стека
(int)main // Reset Handler
};
С векторами прерываний, надеюсь все понятно? Мы используем только два из них, поэтому незачем занимать память пустышками. Все остальные прерывания включаются программно, не включали - значит они никогда не сработают. Исключение - третий вектор HardFault, если случилось - микроконтроллер неисправен или сбоит, для простых проектов (не космос-авиация, не медицина) можно не обрабатывать.
Это учебный проект, конечно следует оформить все адреса как символические константы в отдельный h-файл с большим количеством дефайнов, как это сделано в CMSIS. Можно взять их и приспособить для своих нужд. Для компиляции использую gcc, прошивка с помощью «STM32 ST-LINK Utility». Прошивка заняла 56 байт (привет, ассемблер).
Еще отступление
Еще одно утверждение о сложности STM32 - мало документации на русском языке. Спорно. Необходимы только два документа - Datasheet и Reference Manual на нужный микроконтроллер. Язык на котором он написан сложно назвать английским. Я изучал язык по непереведенным игрушкам, уровень английского остался на том же уровне, но даташиты я читаю без проблем, незнакомые термины понятны из контекста.
Вроде много получилось, тогда на сегодня все.
Во второй половине расскажу о программировании STM32 на любом языке программирования.
Теги: STM32, микроконтроллеры-это-просто, ардуино-не-надо
Любой микроконтроллер stm32 можно прошивать через USART_1 и другие интерфейсы, подробно смотрите в . Для этого в МК есть специальный системный загрузчик, который зашивается в System memory (спец. область памяти) на этапе производства, его нельзя удалить или изменить. Это загрузчик инициализируется путём «подтягивания» пина BOOT_0 к «плюсу», после чего он ожидает поступления прошивки.
Через USART можно загружать любые.bin или.hex файлы.
Описание сделано на примере платы Blue Pill , однако всё сказанное справедливо для любого stm32 .
Фирменные платы типа Discovery и Nucleo тоже можно прошивать через USART.
Для работы потребуется USB to UART конвертер…
Если такой штуковины нет, то есть два варианта превращения ардуины в конвертер…
превращение
1
. Замкнуть RESET
на GND
(может не прокатить)
.
2
. Загрузить в неё вот такой незамысловатый скетч:
Void setup() { pinMode(0, INPUT); pinMode(1, INPUT); } void loop() {}
Чтобы проверить работает ли ардуина в качестве конвертера, замкните RX и TX, откройте и отправьте какие-нибудь символы. Если они вернулись, значит всё работает.
Перед прошивкой необходимо подтянуть пин BOOT0 к «плюсу», это переведёт МК в режим «системного бутлоадера». На описываемой плате это осуществляется перестановкой джампера…
Если плата фирменная
На фирменных платах (Discovery, Nucleo) тоже нужно подтянуть пин BOOT0 к плюсу, но просто так это сделать не получится. Возьмите схему вашей платы и найдите там соответствующие перемычки…
Искать надо что-то вроде этого:
Не помню от какой платы эта схема, но суть такая: в данном случае пин BOOT_0 подтянут к «минусу» через резистор R16 и перемычку SB16. Если убрать перемычку SB16, то пин подтянется к «плюсу» через резистор R17 и можно прошивать. После прошивки надо вернуть всё на место.
Перемычка может выглядеть как резистор.
Соединяем конвертер и STM следующим образом…
картинка
Конвертер RX
<-> PA9
STM
Конвертер TX
<-> PA10
STM
Конвертер GND
<-> GND
STM
… и подключаем конвертер и STM к компьютеру.
Инструкция по прошивке для
Если хотите, можете взять с сайта ST (в самом низу страницы).
Нажмите Reset на плате.
Перейдите в папку Flash Loader Demonstrator и запустите Flash Loader Demonstrator.exe
Выбираем СОМ-порт конвертера и жмем Next ...
WARNING Remove Protection (защита от записи)
Такое сообщение…
… означает, что плата, залочена. Ничего страшного нет, просто снимите защиту кнопкой Remove protection и следом нажмите ОК .
Защита снята.
Если выдаёт ещё какие-то ошибки, то либо не нажали Reset, либо что-то с конвертером.
Если светофор даёт зеленый свет, то смело жмите Next ...
Жмем Next ...
В пункте Download to device указываем путь к нужному.bin или.hex файлу и жмем Next ...
Всё готово, верните джампер в исходное положение и нажмите Reset.
Инструкция по прошивке для
Скачайте и распакуйте куда-нибудь.
Из папки соответствующей вашей ОС скопируйте файл stm32flash в домашнюю папку.
Эта утилита есть в репах ubuntu, в других дистрах не знаю, поэтому выкладываю.
Перед прошивкой всегда нажимайте Reset на плате.
В терминале дайте команду:
./stm32flash -w /home/dima/myfile.bin -v -g 0x0 /dev/ttyUSB0
Название и путь к файлу у вас будет свой. HEX-файлы тоже можно прошивать этой утилитой.
Если будет ругаться на права, тогда дайте команду…
Sudo chmod 777 /dev/ttyUSB0
… или запускайте утилиту через sudo .
Защита от записи
Если появилось вот такое сообщение…
… тогда дайте по очереди две команды:
./stm32flash -u /dev/ttyUSB0 ./stm32flash -k /dev/ttyUSB0
Первая снимает защиту от записи, вторая от чтения.
Если у Вас стоял ардуиновский загрузчик, то он будет затёрт, чтоб его востановить просто залейте описанным выше способом этот
В своем проекте я использую микроконтроллер STM32F103C8 и фреймворк stm32duino . Этот клон Ардуино предлагает специальный бутлоадер, который позволяет заливать прошивку через USB, без использования внешних компонентов типа ST-Link или USB-UART переходника.
Сегодня мне понадобилось поработать с голым контроллером из-под CooCox и без stm32duino. Но вот в чем проблема. Даже простая моргалка лампочкой влитая через этот бутлоадер не работает.
Давайте разбираться. Возможно, мои выкладки покажутся кому-то банальностью. Но я только начинаю изучать контроллеры STM32 и на поиск проблемы убил как минимум полдня. Вдруг эта статья сократит кому-то время разработки.
Я ничего не имею против ST-Link и других отладчиков. Но в моем готовом устройстве его не будет, но точно будет USB. Почему бы сразу не заложить возможность обновлять прошивку через USB? Лично я нахожу этот способ удобным. тем более что все равно у меня уже подключен шнурок по которому идет питание и USB Serial.
Давайте посмотрим как работает бутлоадер. Для начала на примере контроллеров AVR. Почему я о нем вспомнил? Я переходил с Arduino и подсознательно ожидал такого же поведения. Но в STM32 оказалось все по другому. Потому хочу рассказать о разнице этих двух микроконтроллеров.
Итак. В микроконтроллерах AVR ATMega под бутлоадер можно зарезервировать некоторое количество памяти ближе к концу флеша. С помощью fuse битов можно регулировать с какого адреса будет стартовать программа. Если бутлоадера нет - программа стартует с адреса 0x0000. Если бутлоадер есть - он запускается с некоторого другого адреса (скажем, в ATMega32 с 0x3C00, если размер бутлоадера выбран 2к).
Когда бутлоадер сделал свои дела он передает управление основной программе с адреса 0x0000. Т.е. программа всегда стартует с адреса 0x0000. Компилятор и линковщик работают с учетом того, что код будет находится в начале адресного пространства.
В микроконтроллерах STM32 все не так. Все программы стартуют с адреса 0x0800000. Бутлоадер не является чем-то таким особенным. Это такая же программа, которая стартует с того же самого начального адреса. В процессе работы бутлоадер может принять прошивку (через USB или UART, считать с флешки, принять со спутника, достать из подпространства, whatever...) и записать ее по адресам выше чем находится сам загрузчик. Ну и, конечно же, в конце своей работы передать управление основной программе.
Так вот при компиляции прошивки нужно знать куда же бутлоадер запишет прошивку и соответствующим образом скорректировать адреса.
На этом с теорией все. Переходим к практике. Ниже пошаговая инструкция как прикрутить USB загрузчик к микроконтроллерам серии STM32F1xx, а может быть и к некоторым другим тоже.
Есть, правда, некоторые ограничения по схемотехнике. Тут я, к сожалению, не силен. ЯТП нужен подтягивающий резистор 1.5к для порта PA12 (он же USB D+). Это позволяет загрузчику в нужные моменты времени подключаться и отключаться от USB.
- Указать линкеру стартовый адрес. В CooCox это делается в настройках проекта, вкладка Link, раздел Memory Areas, Адрес IROM1 Start Address. Бутлоадер занимает первые 8 килобайт, значит стартовый адрес прошивки будет 0x0800000 + 0x2000 = 0x08002000. Поле Size, наверное, тоже стоит уменьшить на 8к.
- Где нибудь вначале программы перед инициализацией периферии сделать вызов
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x2000);
"maple_upload.bat" COM20 2 1EAF:0003 "Path\To\Firmware.bin"
Вместо COM20 нужно подставить свой порт куда прицепился микроконтроллер.
Заливатор штука очень нежная, относительных путей не любит. так что путь к прошивке нужно указывать полностью.
1EAF:0003 - это VID и PID
2 - это параметр AltID, который указывает что прошивку нужно заливать по адресу 0x08002000 (читать ).
Из-за этого может возникнуть неудобство. Если микроконтроллер заглючил и повис, то он уже не слушает порт. Следовательно он не может услышать ключевую последовательность и перегрузиться в бутлоадер. Тогда только ресет в помощь.
На этом все. Надеюсь моя статья прольет свет на то, как работает загрузчик в STM32 и как можно загружать прошивку через USB порт. К сожалению порог вхождения по прежнему высок, но вдруг кому-то моя статья поможет его преодолеть.
Ну, вот мы и дошли до одного из самых интересных этапов, — как же залить в контроллер готовую прошивку и оживить наконец нашу железяку.
Вообще-то прошивать контроллеры STM32 можно по-разному, но мы рассмотрим самый простой вариант прошивки — через последовательный интерфейс с помощью фирменной утилиты Flash Loader Demonstrator. Эта утилита совершенно бесплатна и её можно скачать как с официального , так и .
Как мы ранее уже говорили, — в системной области памяти контроллера зашит bootloader. Именно он и позволяет записать прошивку во flash-память через последовательный интерфейс.
Детально bootloader описан в документе AN2606 (CD00167594.pdf), а используемый им протокол — в документе AN3155 (CD00264342.pdf). Это для тех, кто хочет исчерпывающей информации, а мы рассмотрим процесс прошивки через bootloader вкратце.
Для начала вам нужно скачать и установить себе на компьютер утилиту Flash Loader Demonstrator.
Итак, ногу Tx контроллера нужно соединить с ногой Rx преобразователя, а ногу Rx контроллера — с ногой Tx преобразователя. Кроме этого, контроллер нужно запитать и обеспечить нужные уровни на ногах BOOT0 (pin 44), BOOT1 (pin 20). Для запуска bootloadera, который расположен в system memory, ногу BOOT1 нужно подтянуть к «земле», а BOOT0 — к «питанию» (табличка справа).
Подтяжку для BOOT0, BOOT1 лучше делать не жёсткую, а джамперами, чтобы можно было легко выбирать режимы загрузки (например, переключившись после заливки проги в режим загрузки из flash, можно будет эту прогу сразу и потестить).
Схема подключения показана ниже.
Чётность и количество бит данных уже настроены как надо, скорость можно менять — контроллер в процессе инициализации обмена настраивается на выбранную скорость автоматически, com-порт нужно выбрать тот, который создался при подключении USB-to-COM преобразователя к компьютеру (наш преобразователь создаёт при подключении виртуальный com-порт, полностью имитирующий настоящий аппаратный). После того, как всё настроили — жмём «Next».
О том, что всё нормально и соединиться с контроллером удалось, нам сообщит зелёный сигнал светофора на следующей страничке. Если связь не установится — на эту страничку нас вообще не пустят, сообщив, что контроллер не отвечает.
При установлении связи программа автоматически определит сколько у контроллера flash-памяти и защищена ли эта память от чтения. Нажав кнопку «Remove protection» защиту можно снять, но при этом содержимое флеша будет стёрто (предыдущая записанная туда прошивка уничтожится). Жмём «Next».
В следующем окне нам предлагают выбрать тип программируемого камня (хотя непонятно зачем, — он и так автоматически определяется), а также показывают для нашего камня PID, карту flash-памяти, и версию bootloader-а. Просто жмём «Next».
В следующем окне нужно выбрать, что мы собственно хотим с нашим контроллером делать. Тут возможны следующие варианты: Erase (стереть), Download to device (загрузить в контроллер прошивку), Upload from device (считать прошивку с контроллера), Enable/Disable Flash protection (включить/выключить защиту flash-памяти), Edit option bytes (изменить байты опций). Соответственно, если мы хотим залить прошивку — выбираем Download to device, потом жмём на квадрат с тремя точками и выбираем в проводнике файл с прошивкой, которую надо залить, после чего опять жмём «Next».
На следующей странице внизу появится прогресс-бар, в котором будет показан ход выполнения процедуры загрузки. После того, как вся прошивка будет загружена в контроллер, этот прогресс-бар станет зелёным и в нём белыми буквами будет написано: «Download operation finished successfully» (операция загрузки успешно завершена). После этого, можно нажать кнопку «Close» и закрыть Flash Loader Demonstrator
Всё, теперь чтобы залитая программа начала выполняться нам останется только настроить контроллер на загрузку из flash (BOOT0 = 0, BOOT1 — любой уровень) и перезагрузить его.
Этот клон Ардуино предлагает специальный бутлоадер, который позволяет заливать прошивку через USB, без использования внешних компонентов типа ST-Link или USB-UART переходника.
Сегодня мне понадобилось поработать с голым контроллером из-под CooCox и без stm32duino. Но вот в чем проблема. Даже простая моргалка лампочкой влитая через этот бутлоадер не работает.
Давайте разбираться. Возможно, мои выкладки покажутся кому-то банальностью. Но я только начинаю изучать контроллеры STM32 и на поиск проблемы убил как минимум полдня. Вдруг эта статья сократит кому-то время разработки.
Я ничего не имею против ST-Link и других отладчиков. Но в моем готовом устройстве его не будет, но точно будет USB. Почему бы сразу не заложить возможность обновлять прошивку через USB? Лично я нахожу этот способ удобным. тем более что все равно у меня уже подключен шнурок по которому идет питание и USB Serial.
Давайте посмотрим как работает бутлоадер. Для начала на примере контроллеров AVR. Почему я о нем вспомнил? Я переходил с Arduino и подсознательно ожидал такого же поведения. Но в STM32 оказалось все по другому. Потому хочу рассказать о разнице этих двух микроконтроллеров.
Итак. В микроконтроллерах AVR ATMega под бутлоадер можно зарезервировать некоторое количество памяти ближе к концу флеша. С помощью fuse битов можно регулировать с какого адреса будет стартовать программа. Если бутлоадера нет - программа стартует с адреса 0x0000. Если бутлоадер есть - он запускается с некоторого другого адреса (скажем, в ATMega32 с 0x3C00, если размер бутлоадера выбран 2к).
Когда бутлоадер сделал свои дела он передает управление основной программе с адреса 0x0000. Т.е. программа всегда стартует с адреса 0x0000. Компилятор и линковщик работают с учетом того, что код будет находится в начале адресного пространства.
В микроконтроллерах STM32 все не так. Все программы стартуют с адреса 0x0800000. Бутлоадер не является чем-то таким особенным. Это такая же программа, которая стартует с того же самого начального адреса. В процессе работы бутлоадер может принять прошивку (через USB или UART, считать с флешки, принять со спутника, достать из подпространства, whatever...) и записать ее по адресам выше чем находится сам загрузчик. Ну и, конечно же, в конце своей работы передать управление основной программе.
Так вот при компиляции прошивки нужно знать куда же бутлоадер запишет прошивку и соответствующим образом скорректировать адреса.
На этом с теорией все. Переходим к практике. Ниже пошаговая инструкция как прикрутить USB загрузчик к микроконтроллерам серии STM32F1xx, а может быть и к некоторым другим тоже.
Есть, правда, некоторые ограничения по схемотехнике. Тут я, к сожалению, не силен. ЯТП нужен подтягивающий резистор 1.5к для порта PA12 (он же USB D+). Это позволяет загрузчику в нужные моменты времени подключаться и отключаться от USB.
- Указать линкеру стартовый адрес. В CooCox это делается в настройках проекта, вкладка Link, раздел Memory Areas, Адрес IROM1 Start Address. Бутлоадер занимает первые 8 килобайт, значит стартовый адрес прошивки будет 0x0800000 + 0x2000 = 0x08002000. Поле Size, наверное, тоже стоит уменьшить на 8к.
- Где нибудь вначале программы перед инициализацией периферии сделать вызов
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x2000);
UPDATE 17.05.2018: В современной версии STM32Cube функции NVIC_SetVectorTable() нет. Вместо этого можно в файле system_stm32f1xx.c (или аналогичного для другого микроконтроллера) исправить дефайн VECT_TAB_OFFSET
"maple_upload.bat" COM20 2 1EAF:0003 "Path\To\Firmware.bin"
Вместо COM20 нужно подставить свой порт куда прицепился микроконтроллер.
Заливатор штука очень нежная, относительных путей не любит. так что путь к прошивке нужно указывать полностью.
1EAF:0003 - это VID и PID
2 - это параметр AltID, который указывает что прошивку нужно заливать по адресу 0x08002000 (читать ).
Из-за этого может возникнуть неудобство. Если микроконтроллер заглючил и повис, то он уже не слушает порт. Следовательно он не может услышать ключевую последовательность и перегрузиться в бутлоадер. Тогда только ресет в помощь.
На этом все. Надеюсь моя статья прольет свет на то, как работает загрузчик в STM32 и как можно загружать прошивку через USB порт. К сожалению порог вхождения по прежнему высок, но вдруг кому-то моя статья поможет его преодолеть.