Beremiz — это интегрированная среда разработки для ПЛК с открытым исходным кодом, которая полностью соответствует стандарту МЭК-61131-3. Beremiz опирается на открытые стандарты, которые не зависят от целевых устройств. Так что вы можете превратить любой процессор в ПЛК. Также Beremiz включает инструменты для создания HMI и подключения ваших программ PLC к наблюдению, базам данным или полевым шинам.
Введение
Для программирования ПЛК используются 5 языков, описанных стандартом МЭК 61131-3:
LD — релейно-контактные схемы;
FBD — функциональные блоковые диаграммы;
SFC — последовательностные функциональные диаграммы;
ST — структурированный текст;
IL (Instruction List) — список инструкций.
Тем не менее ведущие производители ПЛК используют собственные варианты этих языков, несовместимых с этим стандартом. Например, у фирмы Siemens это соответственно языки:
LAD, FBD, SFC, SCL, STL. Исключением являются компании, использующие в качестве сред программирования ISAGRAF и Codesys. Обе эти среды полностью соответствуют стандарту МЭК 61131-3.
Но и ISAGRAF и Codesys накладывают ограничение на аппаратную платформу. Сам производитель ПЛК не может внести изменения в исходный сред исполнения для портирования их на новую аппаратную платформу.
Beremiz же являясь свободным программным обеспечением, может быть модифицирован производителем ПЛК для работы на новой аппаратной платформе. Гибкость в изменении существующих и добавлении новых компонентов достигается с помощью языка Python (и соответствующих библиотек для пользовательского интерфейса, работы с сетью и т.д.) и xsd (XML Schema) файлов, применяемых для описания компонентов среды разработки: модулей работы с компиляторами целевой архитектуры, плагинов внешних источников данных и т.д.
Beremiz используется:
1. ПАО «ИНЭУМ им. И.С.Брука» в качестве среды разработки и исполнения на ПЛК серии СМ1820М на базе отечественных микропроцессоров «Эльбрус» и SPARC, а также линеек, основанных на микропроцессорах ARM и x86. www.sm1820.com.ru
2. ООО НПК «Нуклерон» (г. Пермь) тестирует линейки программируемых реле NUC-24x/251. В основе программируемых реле используется микроконтроллер STM32F105 для модели NUC-242 и STM32F205 для остальных. Также поддерживается плата STM32F4DISCOVERY. В качестве среды разработки используется YAPLC-IDE, представляющая собой набор расширений для среды Beremiz. Для ПЛК параллельно разрабатывается среда исполнения YAPLC-RTE. Исходные коды YAPLC доступны на github: github.com/nucleron/YAPLC
3. ООО НТЦ «Арго» МУР 1001.3 СВ argoivanovo.ru/catalog/index.php?IBL=22&ID=184120
4. ООО «НГП Информ» ngpinform.ru/means/programmiruemye-kontrollery-i-raspredelennye-sistemy-vvoda-vyvoda/modulnye-plk/
5. Smarteh LPC-2.MC8 www.smarteh.si/
Полный список компаний, использующих Beremiz: www.beremiz.org/apps
Особенности Beremiz
Среда разработки Beremiz позволяет работать в конфигурационном режиме и в режиме исполнения прикладной программы. В конфигурационном режиме происходит создание прикладной программы, написание алгоритмов и логики её основных программных модулей и их связывание с внешними модулями УСО (устройство связи с объектом). В режиме исполнения прикладная программа передаётся на целевое устройство и может быть запущена с режимом отладки и без отладки.
Основными компонентами Beremiz являются:
— редактор PLCOpen для текстовых (IL и ST) и графических языков (FBD, LD, SFC) стандарта IEC 61131-3;
компилятор MatIEC, преобразующий логику и алгоритмы программных модулей (из которых состоит прикладная программа), описанных на языках стандарта IEC 61131-3, в эквивалентный С-код;
— механизм плагинов, позволяющий связывать внешние источники данных, такие как модули УСО (их параметры, состояния), SCADA-системы с логикой и алгоритмами программных модулей;
— средства отладки прикладной программы в режиме исполнения;
— элементы для создания человеко-машинного интерфейса управления прикладной программой.
Таким образом Beremiz преобразует LD, FBD, SFC или IL в код на ST, а MatIEC конвертирует ST в C. Код С компилируется на конечную платформу.
Видео:Программирование ПЛК на FBD с нуля. Урок 1. Первая программаСкачать
STM32F4: GNU AS: Программирование на ассемблере (Часть 1)
Обсуждение статьи после прочтения или задать вопросы можно в VK: vk.com/topic-200545792_46641834
Так же теперь (2021 год) я написал небольшой редактор для программ на ассемблере, начинать читать можно с Редактор ассемблера для ARM микроконтроллеров для компилятора gnu as. Старт там же можно будет и создавать проект в более удобном формате нежели описано в этой и нескольких последующих статьях. При этом рекомендую все таки ознакомится со всеми статьями по этой тематике в моих публикациях, так как не везде я повторяю прошлые материалы.
Это моя первая статья для сообщества Хабрахабр и написать ее я решил про то что сейчас волнует меня самого: написание программ для микроконтроллеров STM32 (семейство АRМ) на языке ассемблера. Я использую отладочную плату на основе микроконтроллера STM32F407 (STM32F4 Discovery, Open407I-C), но статья будет не менее полезна и для программирования других микроконтроллеров STM32.
После поисков по интернету так и не удалось найти сколь нить понятного для новичка способа написания прошивок для STM32- и ARM- контроллеров вообще на языке ассемблера. Нет, конечно любой поисковик по сочетанию «STM32 ассемблер» дает очень много результатов, но после внимательного их изучения выясняется что 98% результатов поиска ведут на описание сред в которых по заверению производителей языком программирования микроконтроллеров является С, С++, Assembler.
Более того, как только Вы начинаете искать информацию о том как же все таки программировать на ассемблере в конкретной среде для конкретного микроконтроллера — выясняется, что «гуру не пишут проекты на ассемблере», и в средах ассемблер используется максимум для написания процедур и функций требующих максимального быстродействия, или генерации кода содержащего специфические команды микроконтроллера аналог которых не предлагается языком Си (С++ или библиотеками) in-line вставками.
Дальше было еще интереснее, я выяснил что компиляторов ассемблера на ARM платформу существует как минимум два: GNU AS, и ARMASM. И у них различные форматы исходных файлов… Нет, конечно команды ассемблера одинаковые (слава богу), но вот как они пишутся, как указываются операнды, особенно служебные токены — отличаются так, что компиляция без «танцев» исходных файлов GNU AS на ARMASM (и наоборот) становится невозможна. Причем если GNU AS используется в бесплатно распространяемых средах (например CooCox), то ARMASM идет в составе далеко не самой дешевой среды Keil MDK. Насколько мне удалось выяснить для кода размером не более 32 кб среда является бесплатной, но не думаю что 32 кб для 32-разрядного микроконтроллера является каким то выдающимся размером для прошивки: немного кода работающего с дисплеем, немного шрифтов под дисплей, образов иконок — и 32 кб может и не хватить. Сообщество любителей нашло выход — это использование отдельных образов кода по 32 кб и потом объединение их различными способами — но как то хочется нормальной разработки, а не попыток «впихнуть и распределить невпихуемое» (использование пиратских программ мне не интересно). Есть ли ограничение на размер кода у самого компилятора ARMASM или это ограничение Keil MDK — я не стал выяснять, не очень приятно проделать работу основываясь на данных что все бесплатно, и в середине какого нить уже не тестового проекта получить ошибку компиляции из-за ограничений по размеру…
Таким образом, несмотря на то что в сети проектов написанных под ARMASM больше (по крайней мере так мне показалось исходя из результатов поиска), я решил разобраться с GNU AS — который бесплатен для любых размеров прошивок.
В результате поисков ресурсов (другие 2% результатов по запросу «STM32 ассемблер») на тему ассемблера для STM32 я наткнулся на отсутствие какого то начального состояния у многочисленных авторов. У кого то хорошо описаны инструкции ARM, у кого то сделаны попытки раскрыть параметры ассемблера и линковщика, где то описана минимальная (по мнению автора статьи) конфигурация от которой можно оттолкнуться — но вся эта информация так раскидана — что для начального вхождения практически не пригодна, тем более когда в проекте появляется что-то новое и вы просто не понимаете какие исправления нужно внести чтобы все «заработало»… Особенно тяжело если вы раньше не задумывались о том что делает IDE при компиляции ваших проектов. Теоретически конечно все представляют, что есть ассемблер, линковщик и другие вспомогательные утилиты, но вот какие файлы настроек, ключи для запуска, файлы для работы нужны — все это как то на уровне «ликбеза».
Поэтому, цель первого этапа — создать с нуля минимальную конфигурацию программных и настроечных файлов для возможности написания программ на языке ассемблера.
Я постараюсь описать все более менее последовательно, чтобы Вы могли не только просто повторить то, что я сделал, но и сделать собственные настройки для своего микроконтроллера, а так же менять эти настройки когда это будет необходимо (это пожалуй самое главное!). Вообще не думаю что все что я написал нужно повторять — просто внимательно прочитайте! Это тот путь который вы должны были бы пройти если бы начинали разбираться во всем сами. Я не буду описывать каким образом я дошел до того или иного шага, это сборная «солянка» от поиска в интернете, просмотра примеров других авторов, разбирательства в файлах которые генерирует CoIDE (CooCox), это не интересно и не нужно, но то что узнал в ключевых моментах, и что реально будет нужно для написания своих проектов я постараюсь отметить.
Для начала необходимо скачать пакет разработчика GNU GCC (из него можно взять программы компилятора ассемблера), это можно сделать например отсюда (справа выбираем пакет в зависимости от операционной системы).
Этот пакет можно распаковать на диск (я распаковал в каталог gcc каталога CooCox, у меня этот пакет используется и для программирования в среде CooCox).
Нужные нам файлы я выделил:
Теперь небольшое отступление для тех кто не задумывался о том как происходит преобразование написанной программы в исполняемый код.
Программа написанная в текстовом редакторе (это может быть как блокнот, так и среды разработки по типу CooCox, Keil) сначала компилируется ассемблером.
Все более менее серьезные программы состоят из различных частей которые размещаются в разных исходных файлах программы, эти части могут иметь различное назначение (код, константы, данные), располагаться в различных областях памяти (FLASH, SRAM, Backup SRAM и т. д.) — поэтому мало просто откомпилировать программу, нужно еще правильно расположить ее части в памяти микроконтроллера. Этим важным делом занимается линковщик — в зависимости от схемы линковки он располагает части программы по так называемым сегментам которые и представляют собой различные области памяти микроконтроллера.
Нам осталась самая малость: разобраться как скомпилировать и как распределить части нашей прошивки.
Здесь очень желательно чтобы вы представляли с чего начинается исполнение программы у микроконтроллера STM32F4, если это не откровение для вас можете пропустить пару абзацев:
При включении микроконтроллер:
(*) Адреса разделены пробелом для удобства чтения, в программах правильно писать конечно же 0x08000000
Исходя из выявленных шагов находим в документации на контроллер в каких адресах располагается SRAM у STM32F4 (или у Вашего микроконтроллера, так как различные микроконтроллеры имеют различный объем памяти). Для моего микроконтроллера открываем «RM0090 Reference manual» (для других микроконтроллеров ST посмотрите здесь).
Смотрим оглавление на предмет «чего нить про память»:
Идем на страницу 59
Внимательно читаем! У нашего микроконтроллера есть два банка памяти SRAM1 и SRAM2 размерами 112 кб и 16 кб. Другая интересующая нас информация находится на странице 68.
У микроконтроллера проекта STM32F407 фактически 3 блока памяти расположенных в разных областях адресного пространства:
Таким образом указатель стека нужно будет установить на значение 0x20020000. Получаем следующую «карту» памяти:
Со вторым пунктом проще — мы укажем адрес следующий после нашей таблицы переходов. Поскольку один указатель у нас занимает 4 байта, таких указателей у нас 2 — получается что программу мы можем начинать писать с адреса 0x08000008.
Получаем следующую табличку:
Адрес | Значение | Примечание |
0x08000000 | 0x20020000 | Значение для загрузки в SP (указатель стека) |
0x08000004 | 0x08000008 | Значение для перехода по сбросу (включению) |
Значения указателей 4-х байтные, формат этот называется word (это вам должно быть известно из курса программирования на любом языке)
Теперь немного «шаманства». Дело в том что платформа ARM имеет очень большое количество микропроцессоров, с различными форматами команд. Для того чтобы определить какой формат команды используется используются 2 младших бита адреса команды. Так вот младший бит установленный в «1» показывает, что команда подлежащая исполнению указана в формате Thumb. В случае, если оба младших бита сброшены — микропроцессор считает что нужно исполнить команду в формате ARM. Для микроконтроллеров STM32 при осуществлении переходов допустим только набор команд Thumb! В случае если мы попробуем «заставить» микроконтроллер перейти на команду в формате ARM — произойдет ошибка!
Если посмотреть двухзначное представление полученного нами адреса то мы увидим что в нашем адресе «закодирована» система команд ARM.
Младший байт адреса (0x08) в двоичном виде:
Бит | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
Значение | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 |
Флаг типа команды | часть адреса | Флаг | ||||||
Полубайт | 0 | 9 |
Запоминаем правило: при указании указателей в таблице векторов прерываний необходимо увеличивать значение адреса на 1
В качестве программы будем использовать примитивный бесконечный цикл. Чтобы не отправлять Вас в «копание» в документации — это команда «B».
Если вы задумались об ассемблере, или писали на ассемблере для AVR (или i8080, или Z80), то наверное команда «JMP» восьмибитников вам знакома, так вот «B» ее аналог.
Получается что наша программа должна выглядеть следующим образом:
Займемся средой компиляции. Компилятор ассемблера имеет название arm-none-eabi-as.exe. Вызывая компилятор мы должны указать ему файл нашей программы, и файл в который он должен сохранить результат компиляции. Если запустить компилятор с ключем —help, можно найти какие для этого нужны ключи, берем минимально необходимый ключ -о:
Нашу программу мы разместим в файле main.asm. Файл-результат работы компилятора называется «объектный файл», общепринятое расширение таких файлов «.о» — пусть будет называться main.o. Таким образом компилятор мы вызовем строкой:
Но это еще не все (к сожалению) — дело в том что этот компилятор может быть использован для различных семейств и типов микроконтроллеров и процессоров и мы до сих пор не указали ему какой же микроконтроллер собираемся использовать. Исходя из того что нам сообщает сам компилятор его устроит указание семейства cortex-m4.
Указать можно двумя путями:
Получаем следующую программу:
Теперь, запустив компилятор командой:
мы увидим что в папке появился файл main.o — таким образом компиляция прошла успешно, объектный файл создан.
Для полноты привожу содержимое файла make_project.bat на данном этапе:
Теперь из файла main.o при помощи программы-линковщика нужно сделать файл прошивки. Для программы-линковщика необходим файл настройки в котором собственно говоря и будет описано какую часть нашей программы куда нужно поместить.
Здесь могу рекомендовать почитать статью на этом же ресурсе habrahabr.ru/post/191058 — там можно найти один из примеров карты линковщика, есть и другие ресурсы где можно посмотреть какими бывают файлы линковщика, но к сожалению простых решений нигде не предлагается.
Один из самых простых файлов линковщика приведенный по ссылке выше выглядит так:
Для того чтобы понимать что, как и зачем, давайте попробуем составить свою карту линковщика с нуля, используя данную как подсказку (вместе со статьей автора ее написавшего, ну и подглядывайте в интернет — там море информации на эту тему).
ИТАК, первое и самое заметное: карта линковщика состоит как бы из двух больших секций MEMORY и SECTIONS
В первом блоке (MEMORY) указывается какая память установлена в микроконтроллере, в каких адресах, и какого размера. Адреса и размеры SRAM мы уже выясняли, а вот для FLASH памяти эту информацию нужно поискать в том же «RM0090 Reference manual» или просто вспомнить сколько flash памяти у вашего микроконтроллера (например по полному наименованию микроконтроллера).
После указания имени типа памяти (произвольно, кто то пишет ROM / RAM, кто то MEMORY, я пишу FLASH / SRAM) указываются режимы доступа к памяти: для FLASH памяти R — чтение, X-исполнение. И далее начальный адрес памяти в адресном пространстве и размер.
Должно получиться примерно так:
Конструкция блока SECTIONS в общем виде строиться следующим образом:
Еще раз резюмирую: у нас есть два разных набора секций: один набор секций — это секции описанные в исходных файлах нашей программы, другой набор секций — это секции которые будут записаны в прошивке. Соответственно сначала объявляем секцию прошивки, и внутри указываем какие секции описанные в исходных файлов в нее входят. Секции прошивки называются так как называются, секции в исходных файлах — называем мы сами (в определенных пределах к сожалению, но об этом позже).
Пока вам (и мне) повезло, у нас только одна секция прошивки и одна секция исходного файла — исполняемый код.
Примечания пишутся внутри скобок /* */, все примечания пишутся «для себя» и «чтобы не забыть». Этот текст карты линковщика я поместил в файл stm32f40_map.ld.
Я намеренно упростил нашу карту линковщика по максимуму чтобы вы могли понять идею ее написания. Несмотря на упрощение (которое наверняка мне не простят «гуру» программирования и напишут в комментариях, что так писать нельзя) эта карта линковщика для нашего примера полностью работоспособна (а иначе не стоило и мучаться с ее написанием)!
Теперь, коль у нас появились секции — совершенно очевидным становиться необходимость указания в прошивке что и к какой секции относиться! Делается это при помощи команды .section, секция у нас одна .text, правим наш main.asm.
Секция «вроде бы» должна заканчиваться командой «.end», я еще не выяснил до конца нужна это команда на самом деле или нет и поэтому добавил в конце.
Файл исходного текста нашей программы теперь выглядит вот так:
Еще одно отступление: для нашего примера можно не указывать секции вовсе! Дело в том, что если секции не указаны, то при компиляции и последующей линковке будет выбрана «самая подходящая»… Конечно не стоит надеяться на компилятор и лучше самостоятельно указывать что и где должно быть размещено.
Линковщик это утилита arm-none-eabi-ld.exe, при попытке запросить помощь запуская линковщик с ключем -–help выходит не маленькая «портянка», но мы не будем строить монстроидальный запуск, нам нужно только сделать линковку полученной после ассемблера прошивки.
Используем ключ -T для указания файла карты памяти, и ключ -o для задания имени файла-прошивки, это будет файл с расширением .elf.
Чтобы руками не набирать правим наш make_project.bat:
После запуска у нас появляется файл с расширением .elf.
Теперь нужно сделать остановку и поразмыслить — а что же мы получили? Ну да, ассемблер «что то» скомпилировал (файл main.o), а линковщик на основании этого сделал «какую-то» прошивку (файл main.elf), но как нам проверить, что у нас получилось?
По логике действий мы должны залить прошивку в микроконтроллер и посмотреть на результат… И тут мы вспоминаем что прошивка то у нас ничего не делает — поэтому никакого мигающего светодиода не будет! Вообще ничего не будет! Внешне, по пинам микроконтроллера мы не узнаем работает он, или прошивка оказалась ошибочной — и он завис.
По нашей программе проверку работы микроконтроллера пока сделать нельзя. А что можно? — с файлом прошивки .elf — мы даже не можем посмотреть что и куда линковщик расположил (посмотреть хотелось бы не утилитами, а чем нить вроде того же FAR, чтобы уж «точно и железно», перед тем как мы пойдем дальше нужно быть уверенными что на текущий момент все сделано верно).
Значит нужно сделать из прошивки .elf прошивку в формате .bin. Бинарный формат прошивки это фактически байты которые будут загружены в контроллер.
Сделать это можно при помощи утилиты arm-none-eabi-objcopy.exe. Ключем -O мы задаем формат который мы хотим получить, допустимы 2 значения: binary и ihex. Правим наш make_project.bat:
Запускаем и получаем «вожделенный» файлик output.bin. Размер файла output.bin 10 байт, в FAR можно запустить его просмотр (F3) и в этом режиме переключиться в просмотр кода (F4):
Вот теперь нам есть что анализировать:
Нашел все таки документ в котором описано как кодируются команды ARM. Скачать можно по ссылке ARMv7-M Architecture Reference Manual.
У нас код команды E7 FE — 16-ти битный, значит сразу идем на страницу 127, где будут определены старшие биты команд:
Код нашей команды: 1110 0111 1111 1110, в указанном выше списке это последняя строчка «11100x Unconditional Branch, see B on page A7-207», значит идем на страницу 207.
Следовательно закодирована команда B относительным адресом 111 1111 1110. Как рассчитать адрес перехода можно понять из этого документа
у нас получается:
1111 1111 1100 => 111 1111 1110
Наша команда полностью:
(код команды)=11100 (адрес)=111 1111 1110 => разобьем «по правильному»: 1110 0111 1111 1110 = E7 FE
На этом текущее повествование прекратим и подведем итоги:
Видео:Программирование МК на FBDСкачать
Начинаем изучать Cortex-M на примере STM32
Данная статья является первой в планируемом цикле статей по изучению программирования микроконтроллеров. Изучая различные материалы я отметил, что практически все они начинаются с того, что новичку предлагается скачать (или использовать идущую со средой разработки) библиотеку для работы с периферийными устройствами и использовать ее для написания своей первой программы (обычно мигание светодиодом).
Меня это сильно удивило. Если верить данным статьям, для программирования не обязательно даже читать документацию к программируемому контроллеру. Меня же учили премудростям «железного программирования» совершенно иначе.
В этой статье, путь от фразы «Да, я хочу попробовать!» до радостного подмигивания светодиода, будет значительно длиннее чем у других авторов. Я постараюсь раскрыть аспекты программирования микроконтроллеров, которые прячутся за использованием библиотечных функций и готовых примеров.
Если вы намерены серьезно изучать программирование микроконтроллеров данная статья для вас. Возможно, она может заинтересовать и тех, кто вдоволь наигрался с Arduino и хочет получить в свои руки все аппаратные возможности железа.
Выбор микроконтроллера
Многие могут сказать, что начинать изучение микроконтроллеров лучше с AVR, PIC, 8051 или чего-то еще. Вопрос многогранный и спорный. Я знаю достаточно примеров, когда люди изучив Cortex-M, программировали AVR, ARM7 и т.д. Сам же я начинал с Cortex-M3. Если перед вами стоит определенная задача, в интернете достаточно много информации со сравнением различных типов микроконтроллеров и решаемых с их помощью задач. На хабре этот вопрос тоже поднимался, например тут.
Будем считать, что с типом микроконтроллера мы разобрались. Но на рынке представлен огромнейший спектр различных модификаций от разных производителей. Они отличаются по множеству параметров — от размера флеш памяти до количества аналоговых входов. Для каждой задачи выбор стоит производить индивидуально. Ни каких общих рекомендаций тут нет и быть не может. Отмечу лишь, что стоит начинать изучение с МК производителей имеющих как можно больший ассортимент. Тогда, при выборе МК для определенной задачи достаточно велик шанс, что из представленного ассортимента вам что-нибудь да подойдет.
Я остановил свой выбор на STM32 (хотя и считаю, что лучше начинать изучение с МК от TexasInstruments — очень грамотно составлена документация), потому что они широко распространены среди российских разработчиков электроники. При возникновении проблем и вопросов вы сможете без труда найти решения на форумах. Еще одним плюсом является богатый выбор демонстрационных плат как от производителя, так и от сторонних организаций.
Что необходимо для изучения?
К сожалению, для начала программирования МК не достаточно одного лишь ПК. Придется где-то раздобыть демонстрационную плату и программатор. Хотя это и уменьшает конкуренцию на рынке труда.
Сам я использую демонстрационную плату STM3220G-EVAL и программатор J-Link PRO. Но для начала, будет вполне достаточно STM32F4DISCOVERY, которую можно купить без особых проблем за небольшую сумму.
Все примеры будут именно для отладочной платы STM32F4DISCOVERY. На данном этапе нам будет совершенно не важно, что этой плате стоит МК на базе ядра Cortex-M4. В ближайшее время мы не будем использовать его особенности и преимущества над Cortex-M3. А как там будет дальше — посмотрим.
Если у вас есть в наличии любая другая плата на базе STM32F2xx/STM32F4xx, вы сможете работать с ней. В изложении материала я постараюсь максимально подробно описывать почему мы делаем именно так, а не иначе. Надеюсь ни у кого не возникнет проблем с переносом примеров на другое железо.
Среда разработки
Процесс установки я описывать не буду.
С чего начать?
Создание проекта
Для начала создадим пустой проект. IAR позволяет создать проекты на ASM, C и C++. Мы будем использовать C.
Перед нами появится пустой проект с main файлом.
При выборе целевого программируемого процессора происходит загрузка его описания, что дает широкие возможности для отладки (об этом будет идти речь ниже). Кроме того, автоматически присоединяется конфигурационный файл с описанием доступного адресного пространства для линкера. Если будет необходимо, мы затронем тему конфигурационного файла линкера в следующих статьях.
После этого необходимо настроить отладчик. Отладка программы происходит непосредственно «в железе». Производится это с помощью JTAG отладчика. Более подробнее ознакомиться с тем, как это происходит можно на Википедии. На плату STM32F4DISCOVERY интегрирован отладчик ST-LINK/V2. Для работы с отладчиком необходимо выбрать его драйвер в меню Debugger->Setup->Driver. Так же необходимо указать, что отладка должна производиться непосредственно в железе. Для этого необходимо поставить флаг Debugger->Download->Use flash loader(s)
Теперь проект готов для работы (программирования, заливки и отладки).
«ТЗ» для первого проекта
Подведем промежуточный итог: МК и отладочная плата выбраны, проект подготовлен. Пора определиться с задачей.
Простейший анализ схемы говорит о том, что для того, что бы «зажечь» светодиод необходимо на пин МК подать «1» (которая для данного МК соответствует 3.3В). Выключение производится подачей на этот пин «0». На схеме этот пин обозначается PD13 (это, наверное, самая важная информация из этого документа).
В итоге, мы можем написать «ТЗ» для нашей первой программы:
Программа для МК должна переводить состояние пина МК PD13 из состояния «0» в состояние «1» и обратно с некоторой периодичностью, различимой для человеческого глаза (важное замечание, если моргать светодиодом слишком часто глаз может этого не различить).
Прежде чем приступать к программированию, или немного теории
Прежде чем приступить к реализации нашего ТЗ, необходимо понять как производится управление МК.
Начнем с того, что любой МК включает ядро, память и периферийные блоки. Думаю, что с памятью пока все понятно. Упомяну лишь, в STM32 есть флеш память в которой хранится программа МК (в общем случае это не верное утверждение, программа может храниться во внешней энергонезависимой памяти, но пока это опустим) и другие данные, в том числе и пользовательские. Так же есть SRAM — оперативная память.
Ядро — часть микроконтроллера, осуществляющая выполнение одного потока команд. В нашем МК тип ядра — Cortex-M4. Ядро МК можно сравнить с процессором в ПК. Оно умеет только выполнять команды и передавать данные другим блокам (в этом сравнении не учитываются процессоры с интегрированными графическими ускорителями).
При этом производитель МК не разрабатывает ядро. Ядро покупается у компании ARM Limited. Главное отличие между различными МК — в периферии.
Периферийные блоки — блоки осуществляющие взаимодействие с «внешним миром» или выполняющие специфические функции, недоступные ядру МК. Современные МК (в том числе и STM32) содержат огромный спектр периферийных блоков. Периферийные блоки предназначены для решения различных задач, от считывания значения напряжения с аналогового входа МК до передачи данных внешним устройствам по шине SPI.
В отличии от ядра МК периферийные блоки не выполняют инструкции. Они лишь выполняют команды ядра. При этом участие ядра при выполнении команды не требуется.
Взаимодействие ядра с периферийным блоком
Взаимодействие ядра МК с периферийным блоком осуществляется с помощью спецрегистров (есть еще взаимодействие через механизм прерываний и DMA, но об этом в следующих постах). С точки зрения ядра это просто участок памяти с определенным адресом, вот только на самом деле это не так. Запись данных в спецрегистр эквивалентна передаче команды или данных периферийному блоку. Считывание — получение данных от блока или считывание его состояния. Описание периферийных блоков и их спецрегистров занимает львиную долю описания МК.
ВАЖНО: После записи данных в спецрегистр и последующем чтении вы можете получить совершенно иные данные. Например, передача данных блоку UART для отправки, и считывание данных, полученных блоком от внешнего устройства, осуществляется с помощью одного и того же регистра.
Спецрегистры обычно разделены на битовые поля. Один (или несколько) бит управляют определенным параметром периферийного блока, обычно независимо. Например, разные биты одного регистра управляют состоянием разных выходов МК.
Вспоминаем С
Если вы гуру в языке C, то можете смело пропускать данный раздел. Он предназначен в первую очередь для тех, кого учили (или ктоучился сам) программировать для ПК. Опыт показывает, что люди часто не помнят важных команд. Здесь я вкратце напомню про побитовые операции и работу напрямую с памятью по ее адресу.
Запись данных по адресу в памяти
Предположим, что читая описание периферийного блока, мы поняли, что для его корректной работы необходимо записать в него число 0x3B. Адрес спецрегистра 0x60004012. Регистр 32-битный.
Если вы сразу не знаете как это сделать, попробую описать цепочку рассуждений для получения правильной команды.
Значение 0x60004012 есть не что иное, как значение указателя на ячейку памяти. Нужно именно это и указать в нашей программе, тоесть сделать преобразование типов согласно синтаксису языка C:
Таким образом, у нас есть указатель на элемент. Теперь нужно в этот элемент записать необходимое значение. Делается это разыменовыванием указателя. Таким образом получаем правильную команду:
Установка произвольных бит в 1
Предположим, что необходимо установить «1» в 7 и 1 биты по адресу 0x60004012, при этом не изменив значение всех остальных бит в регистре. Для этого необходимо использовать бинарную операцию |. Сразу приведу правильный ответ:
Обратите внимание на 2 факта. Биты считаются с нулевого, а не с первого. Данная операция на самом деле занимает неменее 3 тактов — считывание значения, модификация, запись. Иногда это не допустимо, поскольку между считыванием и записью значение одного из бит, которые нам запрещено изменять, могло быть изменено периферийным блоком. Незабывайте про эту особенность, иначе могут полезть баги, которые крайне сложно отловить.
Установка произвольных бит в 0
Предположим, что необходимо установить «0» в 7 и 1 биты по адресу 0x60004012, при этом не изменив значение всех остальных бит в регистре. Для этого необходимо использовать бинарную операцию &. Сразу приведу правильный ответ:
Или его более простою запись (не переживайте за лишнюю операцию, компилятор все заранее посчитает даже при минимальной оптимизации):
Некоторые особенности программ для МК
Здесь я постараюсь описать некоторые особенности программ для МК, которые важно помнить. Вещи достаточно очевидные, но все же.
У программы нет конца
В отличии от большинства программ для ПК, программа для МК не должна заканчиваться, НИКОГДА! А что собственно должен будет делать МК после завершения вашей программы? Вопрос, практически, риторический. Поэтому не забываем убедиться в том, что вы не забыли вечный цикл. При желании, можно перевести МК в режим сна.
Пользуйтесь целочисленными переменными
Не смотря на то, что мы используем МК с ядром Cortex-M4, который аппаратно выполняет операции над числами с плавающей точкой, советую вам отказаться от их использования. В МК без поддержки таких операций время вычислений будет просто огромным.
Откажитесь от динамического выделения памяти
Это только совет. Причина проста — памяти мало. Я не раз встречался с библиотеками, в которых были «медленные утечки» памяти. Было очень неприятно, когда после нескольких недель стабильной работы МК зависал с ошибкой. Лучше заранее продумать архитектуру своей программы так, чтобы не пришлось использовать динамическое выделение памяти.
Если же все-таки хочется использовать — внимательно изучите работу менеджера памяти или пишите свой.
Приступаем к работе!
Работа над программой для МК всегда начинается с чтения документации. Для нашего МК Reference manual доступен на сайте производителя. Страниц много, но все читать пока не нужно. Как уже было сказано, большую часть документации составляет описание периферийных блоков и их регистров. Так же хочу обратить внимание на то, что этот Reference Manual написан не для одного МК, а для нескольких линеек. Это говорит о том, что код будет переносим при переходе на другие МК в этих линейках (если конечно не пытаться использовать периферийные блоки которых нет в используемом МК).
В первую очередь необходимо определиться с какими блоками предстоит работать. Для это достаточно изучит разделы Introduction и Main features.
Непосредственное управление состоянием пинов МК осуществляется с помощью блока GPIO. Как указано в документации в МК STM32 может быть до 11 независимых блоков GPIO. Различные периферийные блоки GPIO принято называть портами. Порты обозначаются буквам от A до K. Каждый порт может содержать до 16 пинов. Как мы отметили ранее, светодиод подключается к пину PD13. Это означает, что управление этим пином осуществляется периферийным блоком GPIO порт D. Номер пина 13.
Ни каких других периферийных блоков на это раз нам не понадобится.
Управление тактированием периферийных блоков
Для снижения электропотребления МК практически все периферийные блоки после включения МК отключены. Включение/выключение блока производится подачей/прекращением подачи тактового сигнала на его вход. Для корректной работы, необходимо сконфигурировать контроллер тактового сигнала МК, чтобы необходимому периферийному блоку поступал тактовый сигнал.
Важно:Периферийный блок не может начать работу сразу после включения тактового сигнала. Необходимо подождать несколько тактов пока он «запустится». Люди, использующие библиотеки для периферийных устройств, зачастую даже не знают об этой особенности.
За включение тактирования периферийных блоков отвечают регистры RCC XXX peripheral clock enable register.На месте XXX могут стоять шины AHB1, AHB2, AHB3, APB1 и APB2. После внимательного изучения описания соответствующих регистров, можно сделать вывод о том, тактирование периферийного блока GPIOD включается установкой «1» в третий бит регистра RCC AHB1 peripheral clock enable register (RCC_AHB1ENR):
Теперь необходимо разобраться с тем, как узнать адрес самого регистра RCC_AHB1ENR.
Замечание: Описание системы тактирования МК STM32 достойно отдельной статьи. Если у читателей возникнет желание, я подробнее освещу этот раздел в одной из следующих статей.
Определение адресов спецрегистров
Определение адресов спецрегистров необходимо начинать с чтения раздела Memory map в Reference manual. Можно заметить, что каждому блоку выделен свой участок адресного пространства. Например, для блока RCC это участок 0x4002 3800 — 0x4002 3BFF:
Перейдя по ссылке к Register map блока RCC находим строчкку с интересующим нас регистром RCC_AHB1ENR:
Для получения адреса регистра, необходимо к начальному значению адресного пространства блока RCC прибавить Addr. offset нужного регистра. Addres offset указывается и в описании регистра (см. скриншот выше).
В итоге, мы определили адрес регистра RCC_AHB1ENR — 0x4002 3830.
Блок GPIO
Для общего ознакомления с блоком GPIO я настоятельно рекомендую полностью прочитать соответствующий раздел Reference Manual. Пока можно не особо обращать внимание на Alternate mode. Это оставим на потом.
Сейчас же наша задача научиться управлять состоянием пинов МК. Перейдем сразу к описанию регистров GPIO.
Режим работы
В первую очередь необходимо установить режим работы 13 пина порта D как General purpose output mode, что означает что блок GPIO будет управлять состоянием пина МК. Управление режимом работы пинов МК производитсяс помощью регистра GPIO port mode register (GPIOx_MODER) (x = A..I/J/K):
Как видно из описания для совершения требуемой нам настройки необходимо записать значение 01b в 26-27 биты регистра GPIOx_MODER. Адрес регистра можно определить тем же методом, что описан выше.
Настройка параметров работы выходных пинов порта GPIO
Установка значения на пине МК
Наконец-то мы подошли к моменту управления состоянием выхода МК. Для утановки выходного значения на определенном пине МК есть два метода.
Используем регистр GPIO port bit set/reset register (GPIOx_BSRR)
Запись «0» или «1» в биты 0-16 приводят к соответствующему изменению состояния пинов порта. Для того, чтобы установить определенное значение на выходе одного или нескольких пинов МК и не изменить состояния остальных, необходимо будет пользоваться операцией модификации отдельных бит. Такая операция выполняется не менее чем за 3 такта. Если же необходимо в часть битов записать 1, а в другие 0, то понадобится не менее 4 тактов. Данный метод предпочтительнее всего использовать для изменения состояния выхода на противоположное, если его изначальное состояние не известно.
GPIO port bit set/reset register (GPIOx_BSRR)
В отличии от предыдущего метода, запись 0 в любой из битов данного регистра не приведет ни к чему (да и вообще, все биты write-only!). Запись 1 в биты 0-15 приведет к установке «1» на соответствующем выходе МК. Запись 1 в биты 16-31 приведет к установке «0» на соответствующем выходе МК. Этот метод предпочтительнее предыдущего, если необходимо установить определенное значение на пине «МК», а не изменить его.
Зажигаем светодиод!
Найдя адреса всех необходимых регистров, можно написать программу, которая включает светодиод:
Можно компилировать (Project->Compile) и заливать (Project->Download->Download active application). Или запустить отладку (Project->Dpwnload and Debug) и начать выполнение (F5).
Светодиод загорелся!
Мигаем светодиодом
Мигание светодиода есть ни что иное, как попеременное включение и выключение с задержкой между этими действиями. Самый простой способ — поместить включение и выключение в вечный цикл, а между ними вставить задержку.
Значение 1000000 в задержке подобрано экспериментально так, чтобы период мигания светодиода был различим глазом, но и не был слишком велик.
Оптимизируем алгоритм
Минусом выбранного подхода миганием светодиодом является то, что ядро МК большую часть времени проводит в пустых циклах, хотя мог бы заниматься чем-нибудь полезным (в нашем примере других задач нет, но в будущем они появятся).
Для того, чтобы этого избежать, обычно используется счетчик циклов, а переключение состояние пина МК происходит при прохождении программы определенного числа циклов.
Но и тут не обойдется без проблем, с изменением количества команд выполняемых внутри цикла, будет меняться период мигания светодиодом (или период выполнения других команд в цикле). Но на данном этапе мы не можем с этим бороться.
Немного об отладке
IAR позволяет осуществлять отладку приложения непосредственно в железе. Все выглядит практически так же, как и отладка приложения для ПК. Есть режим пошагового выполнения, входа в функцию, просмотр значения переменных (В режиме отладки View->Watch->Watch1/4).
Но помимо этого, присутствует возможность просмотра значений регистров ядра, спецрегистров периферийных блоков (View->Register) и т.п.
Я настоятельно рекомендую ознакомиться с возможностями дебаггера во время изучения программирования МК.
Несколько слов в заключение
Спасибо всем, кто прочитал мой пост, получилось значительно больше чем я ожидал в начале.
Жду ваших комментариев и аргументированной критики. Если у прочитавших возникнет желание — постараюсь продолжить цикл статей. Возможно у кого-то есть идеи по поводу тем, которые стоило бы осветить — я был бы рад их услышать.
💥 Видео
STM32 - программирование для начинающих. Пошагово. CubeMX CubeIDEСкачать
STM32. Урок 2. Reference Manual. Знакомство с мануал. Работа с регистрами.Скачать
Программа FBD для PLC на запуск двигателя звезда-треугольникСкачать
Знакомство с языком FBD для ArduinoСкачать
STM32 программирование для начинающих. Пошагово. С чего начать? Какой язык выбрать?Какой МК выбрать?Скачать
STM32 или AVR. Как изучать программирование микроконтроллеров.Скачать
STM32 1 урокСкачать
РАЗРАБОТКА ПРОГРАММЫ УПРАВЛЕНИЯ НА ЯЗЫКЕ FBD |ПРАКТИЧЕСКОЕ ЗАНЯТИЕ 1Скачать
Simatic FBD ч.1 - Общее ознакомление. Булевы операцииСкачать
Язык программирования для микроконтроллеров STM32Скачать
Китайские STM32. Поддельные электронные компоненты, микросхемы. Что с этим делать?Скачать
Visual Studio + VisualGDB + STM32. Пробуем Китайские STM32.Скачать
Программирование и отладка поддельных STM32 в среде CubeIDEСкачать
Программирование ПЛК на FBD с нуля. Урок 5. Типы данных и арифметикаСкачать