Вредоносное ПО (malware) - это назойливые или опасные программы,...
![Лучшие утилиты для удаления вирусов и вредоносных программ](https://i2.wp.com/webhelper.info/images/danger.jpg)
Опубліковано 09.08.2016
Микроконтроллеры STM32 приобретают все большую популярность благодаря своей мощности, достаточно разнородной периферии, и своей гибкости. Мы начнем изучать , используя бюджетную тестовую плату, стоимость которой не превышает 2 $ (у китайцев). Еще нам понадобится ST-Link программатор, стоимость которого около 2.5 $ (у китайцев). Такие суммы расходов доступны и студентам и школьникам, поэтому именно с такого бюджетного варианта я и предлагаю начать.
Этот микроконтроллер не является самым мощным среди STM32
, но и не самый слабый. Существуют различные платы с STM32
, в томе числе Discovery
которые по цене стоят около 20 $. На таких платах почти все то же, что и на нашей плате, плюс программатор. В нашем случае мы будем использовать программатор отдельно.
Итак, начнем с того, что попробуем прошить микроконтроллер. Это можно сделать с помощью через USART, или с помощью программатора ST-Link .
Скачать тестовый файл для прошивки можно . Программа мигает светодиодом на плате.
В системной памяти STM32 есть Bootloader . Bootloader записан на этапе производстве и любой микроконтроллер STM32 можно запрограммировать через интерфейс USART с помощью USART-USB переходника. Такие переходники чаще всего изготавливают на базе популярной микросхем FT232RL . Прежде всего подключим переходник к компьютеру и установим драйвера (если требуется). Скачать драйвера можно с сайта производителя FT232RL – ftdichip.com . Надо качать драйвера VCP (virtual com port). После установки драйверов в компьютере должен появиться виртуальный последовательный порт.
Подключаем RX и TX выходы к соответствующим выводам USART1 микроконтроллера. RX переходника подключаем к TX микроконтроллера (A9). TX переходника подключаем к RX микроконтроллера (A10). Поскольку USART-USB имеет выходы питания 3.3В подадим питания на плату от него.
Чтобы перевести микроконтроллер в режим программирования, надо установить выводы BOOT0 и BOOT1 в нужное состояние и перезагрузить его кнопкой Reset или выключить и включить питание микроконтроллера. Для этого у нас есть перемычки. Различные комбинации загоняют микроконтроллер в различные режимы. Нас интересует только один режим. Для этого у микроконтроллера на выводе BOOT0 должно быть логическая единица, а на выводе BOOT1 – логический ноль. На плате это следующее положение перемычек:
После нажатия кнопки Reset
или отключения и подключения питания, микроконтроллер должен перейти в режим программирования.
Если используем USB-UART переходник, имя порта буде примерно такое /dev/ttyUSB0
Результат:
Результат:
Stm32flash 0.4 http://stm32flash.googlecode.com/ Using Parser: Raw BINARY Interface serial_posix: 57600 8E1 Version: 0x22 Option 1: 0x00 Option 2: 0x00 Device ID: 0x0410 (Medium-density) - RAM: 20KiB (512b reserved by bootloader) - Flash: 128KiB (sector size: 4x1024) - Option RAM: 16b - System RAM: 2KiB Write to memory Erasing memory Wrote and verified address 0x08012900 (100.00%) Done. Starting execution at address 0x08000000... done.
При использовании программатора ST-Link выводы BOOT0 и BOOT1 не используются и должны стоять в стандартном положении для нормальной работы контроллера.
(Книжка на русском языке)
Device family | Product type | Device subfamily | Pin count | Flash memory size | Package | Temperature range |
---|---|---|---|---|---|---|
STM32
= ARM-based 32-bit microcontroller | F = General-purpose
L = Ultra-low-power TS = TouchScreen W = wireless system-on-chip | 60 = multitouch resistive 103 = performance line | F = 20 pins G = 28 pins K = 32 pins T = 36 pins H = 40 pins C = 48/49 pins R = 64 pins O = 90 pins V = 100 pins Z = 144 pins I = 176 pins B = 208 pins N = 216 pins | 4 = 16 Kbytes of Flash memory 6 = 32 Kbytes of Flash memory 8 = 64 Kbytes of Flash memory B = 128 Kbytes of Flash memory Z = 192 Kbytes of Flash memory C = 256 Kbytes of Flash memory D = 384 Kbytes of Flash memory E = 512 Kbytes of Flash memory F = 768 Kbytes of Flash memory G = 1024 Kbytes of Flash memory I = 2048 Kbytes of Flash memory | H = UFBGA N = TFBGA P = TSSOP T = LQFP U = V/UFQFPN Y = WLCSP | 6 = Industrial temperature range, –40…+85 °C.
7 = Industrial temperature range, -40…+ 105 °C. |
STM32 | F | 103 | C | 8 | T | 6 |
Если вы получили плату с STM32F103, а программатор ее не видит, это означает, что китайцы защитили Флеш память микроконтроллера. Вопрос “зачем?” оставим без внимания. Чтобы снять блокировку, подключим UART переходник, будем программировать через него. Выставляем перемычки для программирования и поехали:
Я это буду делать из под Ubuntu с помощью утилиты stm32flash.
1. Проверяем видно ли микроконтроллер:
Sudo stm32flash /dev/ttyUSB0
Должны получить что-то такое:
Stm32flash 0.4 http://stm32flash.googlecode.com/ Interface serial_posix: 57600 8E1 Version: 0x22 Option 1: 0x00 Option 2: 0x00 Device ID: 0x0410 (Medium-density) - RAM: 20KiB (512b reserved by bootloader) - Flash: 128KiB (sector size: 4x1024) - Option RAM: 16b - System RAM: 2KiB
2. Снимаем защиту от чтения а затем от записи:
Sudo stm32flash -k /dev/ttyUSB0 stm32flash 0.4 http://stm32flash.googlecode.com/ Interface serial_posix: 57600 8E1 Version: 0x22 Option 1: 0x00 Option 2: 0x00 Device ID: 0x0410 (Medium-density) - RAM: 20KiB (512b reserved by bootloader) - Flash: 128KiB (sector size: 4x1024) - Option RAM: 16b - System RAM: 2KiB Read-UnProtecting flash Done. sudo stm32flash -u /dev/ttyUSB0 stm32flash 0.4 http://stm32flash.googlecode.com/ Interface serial_posix: 57600 8E1 Version: 0x22 Option 1: 0x00 Option 2: 0x00 Device ID: 0x0410 (Medium-density) - RAM: 20KiB (512b reserved by bootloader) - Flash: 128KiB (sector size: 4x1024) - Option RAM: 16b - System RAM: 2KiB Write-unprotecting flash Done.
Теперь можно нормально работать с микроконтроллером.
Однажды, заехав в очередную съемную квартиру, я столкнулся с определенным неудобством, которое достаточно сильно напрягало: выключатель света в основной комнате оказался за шкафом-стенкой, который был прикручен к стене, и его перестановка была невозможна т.к. на это требовалось значительно много времени и сил. Решить данную проблему хотелось очень сильно и в голову пришла одна мысль: сделать дистанционный пульт для управления освещением!Именно с идеи создания собственного пультика для управления светом в комнате и началось моё увлечение электроникой, микроконтроллерами и различными радиоустройствами.
После этого я начал изучать данную тему, знакомиться с основами электроники, примерами устройств, узнавать, как люди реализуют подобного рода устройства. Поискав информацию на тему того, с чего можно было бы начать изучение микроконтроллеров я узнал о том, что такое Arduino, с чем их едят, о том, как с ними работать. Легкое решение выглядело весьма привлекательно, ведь насколько я понял на тот момент, код собирается на раз-два. Но сделав вывод, что я не узнаю, что творится внутри микроконтроллера за рамками Arduino-скетчей я решил поискать более интересный вариант, который подразумевал глубокое изучение и погружение в дебри микроконтроллерной техники.
В компании, в которой я работаю, имеется отдел разработки, и я решил обратиться к инженерам чтобы они направили меня на путь истинный и показали с чего можно было бы начать решение своей задачи. Меня решительно отговорили от изучения Arduino и у меня в руках оказалась неведомая и непонятная зеленая платка на которой виднелись надписи, буковки, разные электронные компоненты.
Всё это для меня на тот момент показалось непостижимо сложным, и я даже пришел в некоторое смятение, но от реализации поставленной задачи отказываться не собирался. Так я познакомился с семейством микроконтроллеров STM32 и платой STM32F0-Discovery, после изучения которых мне хотелось бы сваять свой девайс под нужные мне цели.
К моему большому удивлению, такого большого комьюнити, статей, примеров, различных материалов по STM не было в таком же изобилии как для Arduino. Конечно, если поискать найдется множество статей «для начинающих» где описано, как и с чего начать. Но на тот момент мне показалось, что все это очень сложно, не рассказывались многие детали, интересные для пытливого ума новичка, вещи. Многие статьи хоть и характеризовались как «обучение для самых маленьких», но не всегда с их помощью получалось достичь требуемого результата, даже с готовыми примерами кода. Именно поэтому я решил написать небольшой цикл статей по программированию на STM32 в свете реализации конкретной задумки: пульт управления освещением в комнате.
Во-первых, решающую роль сыграло отношение цена-функционал, разницу видно даже между одним из самых дешевых и простых МК от ST и достаточно «жирной» ATMega:
Во-вторых, я предварительно для себя старался определить набор умений и навыков, которые бы я получил к моменту, когда я достигну требуемого результата. В случае если бы я решил использовать Arduino – мне было бы достаточно скопировать готовые библиотеки, накидать скетч и вуаля. Но понимание того, как работают цифровые шины, как работает радиопередатчик, как это всё конфигурируется и используется – при таком раскладе мне бы не пришло бы никогда. Для себя я выбрал самый сложный и тернистый путь, чтобы на пути достижения результата – я бы получил максимум опыта и знаний.
В-третьих, любой STM32 можно заменить другим STM32, но с лучшими характеристиками. Причем без изменения схемы включения.
В-четвертых, люди, занимающиеся профессиональной разработкой больше склонны к использованию 32-разрядных МК, и чаще всего это модели от NXP, Texas Instruments и ST Microelectronics. Да и мне можно было в любой момент подойти к своим инженерам из отдела разработки и разузнать о том, как решить ту или иную задачу и получить консультацию по интересующим меня вопросам.
После Keil спросит нас какой МК будет использоваться в проекте. Выбираем нужный нам МК и нажимаем ОК .
Для того, чтобы сконфигурировать параметры проекта и настроить наш программатор нужно правым кликом по Target 1 открыть соответствующее меню.
Для удобства можно настроить параметр, отвечающий за то, чтобы МК сбрасывался автоматически после перепрошивки. Для этого нужно поставить галочку в поле Reset and Run .
В Keil имеется удобный навигатор по проекту, в котором мы можем видеть структуру проекта, необходимые для работы справочные материалы, в т. ч. те, которые мы уже скачали к себе на компьютер до этого (схема Discovery, datasheet, reference manual), список функций, использованных в проекте и шаблоны для быстрой вставки разных языковых конструкций языка программирования.
Что ж, теперь мы можем приступить к созданию нашей программы.
Первым делом, необходимо подключить к нашему исполняемому файлу заголовочный документ нашего семейства микроконтроллеров. Добавим в файл main.c строки следующего содержания, данная программа заставить попеременно моргать наши светодиоды:
/* Заголовочный файл для нашего семейства микроконтроллеров*/
#include "stm32f0xx.h"
/* Тело основной программы */
int main(void)
{
/* Включаем тактирование на порту GPIO */
RCC->AHBENR |= RCC_AHBENR_GPIOCEN;
/* Настраиваем режим работы портов PC8 и PC9 в Output*/
GPIOC ->MODER = 0x50000;
/* Настраиваем Output type в режим Push-Pull */
GPIOC->OTYPER = 0;
/* Настраиваем скорость работы порта в Low */
GPIOC->OSPEEDR = 0;
while(1)
{
/* Зажигаем светодиод PC8, гасим PC9 */
GPIOC->ODR = 0x100;
for (int i=0; i<500000; i++){} // Искусственная задержка
/* Зажигаем светодиод PC9, гасим PC8 */
GPIOC->ODR = 0x200;
for (int i=0; i<500000; i++){} // Искусственная задержка
}
}
После того, как мы написали нашу программу, настала пора скомпилировать код и загрузить прошивку в наш МК. Чтобы скомпилировать код и загрузить можно воспользоваться данным меню.
Данная статья является первой в планируемом цикле статей по изучению программирования микроконтроллеров. Изучая различные материалы я отметил, что практически все они начинаются с того, что новичку предлагается скачать (или использовать идущую со средой разработки) библиотеку для работы с периферийными устройствами и использовать ее для написания своей первой программы (обычно мигание светодиодом).
Меня это сильно удивило. Если верить данным статьям, для программирования не обязательно даже читать документацию к программируемому контроллеру. Меня же учили премудростям «железного программирования» совершенно иначе.
В этой статье, путь от фразы «Да, я хочу попробовать!» до радостного подмигивания светодиода, будет значительно длиннее чем у других авторов. Я постараюсь раскрыть аспекты программирования микроконтроллеров, которые прячутся за использованием библиотечных функций и готовых примеров.
Если вы намерены серьезно изучать программирование микроконтроллеров данная статья для вас. Возможно, она может заинтересовать и тех, кто вдоволь наигрался с Arduino и хочет получить в свои руки все аппаратные возможности железа.
Будем считать, что с типом микроконтроллера мы разобрались. Но на рынке представлен огромнейший спектр различных модификаций от разных производителей. Они отличаются по множеству параметров - от размера флеш памяти до количества аналоговых входов. Для каждой задачи выбор стоит производить индивидуально. Ни каких общих рекомендаций тут нет и быть не может. Отмечу лишь, что стоит начинать изучение с МК производителей имеющих как можно больший ассортимент. Тогда, при выборе МК для определенной задачи достаточно велик шанс, что из представленного ассортимента вам что-нибудь да подойдет.
Я остановил свой выбор на STM32 (хотя и считаю, что лучше начинать изучение с МК от TexasInstruments - очень грамотно составлена документация), потому что они широко распространены среди российских разработчиков электроники. При возникновении проблем и вопросов вы сможете без труда найти решения на форумах. Еще одним плюсом является богатый выбор демонстрационных плат как от производителя, так и от сторонних организаций.
Сам я использую демонстрационную плату STM3220G-EVAL и программатор J-Link PRO . Но для начала, будет вполне достаточно STM32F4DISCOVERY , которую можно купить без особых проблем за небольшую сумму.
Все примеры будут именно для отладочной платы STM32F4DISCOVERY . На данном этапе нам будет совершенно не важно, что этой плате стоит МК на базе ядра Cortex-M4. В ближайшее время мы не будем использовать его особенности и преимущества над Cortex-M3. А как там будет дальше - посмотрим.
Если у вас есть в наличии любая другая плата на базе STM32F2xx/STM32F4xx, вы сможете работать с ней. В изложении материала я постараюсь максимально подробно описывать почему мы делаем именно так, а не иначе. Надеюсь ни у кого не возникнет проблем с переносом примеров на другое железо.
Почему платная среда разработки?
Возможно, кто-то будет не совсем доволен тем, что я предлагаю использовать платную среду разработки, но в IAR есть возможность получить временную лицензию без ограничения функционала, либо безлимитную лицензию с ограничением по размеру кода (32КБ для МК это очень много).
Помимо этого, сразу замечу, что для некоторых МК не существует бесплатных сред разработки. И к сожалению эти МК в некоторых областях незаменимы.
Перед нами появится пустой проект с main файлом.
Теперь необходимо настроить проект для начала работы с «нашим» МК и отладчиком. На плате STM32F4DISCOVERY установлен MK STM32F407VG . Его необходимо выбрать в свойствах проекта (General Options->Target->Device):
При выборе целевого программируемого процессора происходит загрузка его описания, что дает широкие возможности для отладки (об этом будет идти речь ниже). Кроме того, автоматически присоединяется конфигурационный файл с описанием доступного адресного пространства для линкера. Если будет необходимо, мы затронем тему конфигурационного файла линкера в следующих статьях.
После этого необходимо настроить отладчик. Отладка программы происходит непосредственно «в железе». Производится это с помощью JTAG отладчика. Более подробнее ознакомиться с тем, как это происходит можно на Википедии . На плату STM32F4DISCOVERY интегрирован отладчик ST-LINK/V2. Для работы с отладчиком необходимо выбрать его драйвер в меню Debugger->Setup->Driver
. Так же необходимо указать, что отладка должна производиться непосредственно в железе. Для этого необходимо поставить флаг Debugger->Download->Use flash loader(s)
Для тех, кто увидел слово Simulator
Теоретически, IAR позволяет отлаживать программы с использованием симулятора. Но я ни разу на практике не встречал его использования.
Теперь проект готов для работы (программирования, заливки и отладки).
Не будем отходить от классики. Первым проектом будет мигающий светодиод. Благо на плате их предостаточно.Что же это означает с точки зрения программирования? Первым делом необходимо изучить принципиальную схему демонстрационной платы и понять как «заводится» светодиод.
доступен на сайте производителя. В данном описании даже есть отдельный раздел про светодиоды на плате -4.4 LEDs
. Для примера, будем использовать User LD3
. Найдем его на схеме:
Простейший анализ схемы говорит о том, что для того, что бы «зажечь» светодиод необходимо на пин МК подать «1» (которая для данного МК соответствует 3.3В). Выключение производится подачей на этот пин «0». На схеме этот пин обозначается PD13 (это, наверное, самая важная информация из этого документа).
В итоге, мы можем написать «ТЗ» для нашей первой программы:
Программа для МК должна переводить состояние пина МК PD13 из состояния «0» в состояние «1» и обратно с некоторой периодичностью, различимой для человеческого глаза (важное замечание, если моргать светодиодом слишком часто глаз может этого не различить).
Начнем с того, что любой МК включает ядро, память и периферийные блоки. Думаю, что с памятью пока все понятно. Упомяну лишь, в STM32 есть флеш память в которой хранится программа МК (в общем случае это не верное утверждение, программа может храниться во внешней энергонезависимой памяти, но пока это опустим) и другие данные, в том числе и пользовательские. Так же есть SRAM - оперативная память.
Ядро - часть микроконтроллера, осуществляющая выполнение одного потока команд. В нашем МК тип ядра - Cortex-M4. Ядро МК можно сравнить с процессором в ПК. Оно умеет только выполнять команды и передавать данные другим блокам (в этом сравнении не учитываются процессоры с интегрированными графическими ускорителями).
При этом производитель МК не разрабатывает ядро. Ядро покупается у компании ARM Limited . Главное отличие между различными МК - в периферии.
Периферийные блоки - блоки осуществляющие взаимодействие с «внешним миром» или выполняющие специфические функции, недоступные ядру МК. Современные МК (в том числе и STM32) содержат огромный спектр периферийных блоков. Периферийные блоки предназначены для решения различных задач, от считывания значения напряжения с аналогового входа МК до передачи данных внешним устройствам по шине SPI.
В отличии от ядра МК периферийные блоки не выполняют инструкции. Они лишь выполняют команды ядра. При этом участие ядра при выполнении команды не требуется.
Пример
В качестве примера можно привести блок UART, который предназначен для приема и передачи данных от МК внешним устройствам. От ядра необходимо лишь сконфигурировать блок и отдать ему данные для передачи. После этого ядро может дальше выполнять инструкции. На плечи же периферийного блока ложится управление соответствующим выводом МК для передачи данных в соответствии с протоколом. Периферийный блок сам переводит выход МК в необходимое состояние «0» или «1» в нужный момент времени, осуществляя передачу.
ВАЖНО: После записи данных в спецрегистр и последующем чтении вы можете получить совершенно иные данные. Например, передача данных блоку UART для отправки, и считывание данных, полученных блоком от внешнего устройства, осуществляется с помощью одного и того же регистра.
Спецрегистры обычно разделены на битовые поля. Один (или несколько) бит управляют определенным параметром периферийного блока, обычно независимо. Например, разные биты одного регистра управляют состоянием разных выходов МК.
Запись данных по адресу в памяти
Предположим, что читая описание периферийного блока, мы поняли, что для его корректной работы необходимо записать в него число 0x3B. Адрес спецрегистра 0x60004012. Регистр 32-битный.
Если вы сразу не знаете как это сделать, попробую описать цепочку рассуждений для получения правильной команды.
Значение 0x60004012 есть не что иное, как значение указателя на ячейку памяти. Нужно именно это и указать в нашей программе, тоесть сделать преобразование типов согласно синтаксису языка C:
(unsigned long*)(0x60004012)
Таким образом, у нас есть указатель на элемент. Теперь нужно в этот элемент записать необходимое значение. Делается это разыменовыванием указателя. Таким образом получаем правильную команду:
*(unsigned long*)(0x60004012) = 0x3B;
Установка произвольных бит в 1
Предположим, что необходимо установить «1» в 7 и 1 биты по адресу 0x60004012, при этом не изменив значение всех остальных бит в регистре. Для этого необходимо использовать бинарную операцию |. Сразу приведу правильный ответ:
*(unsigned long*)(0x60004012) |= 0x82;
Обратите внимание на 2 факта. Биты считаются с нулевого, а не с первого. Данная операция на самом деле занимает неменее 3 тактов - считывание значения, модификация, запись. Иногда это не допустимо, поскольку между считыванием и записью значение одного из бит, которые нам запрещено изменять, могло быть изменено периферийным блоком. Незабывайте про эту особенность, иначе могут полезть баги, которые крайне сложно отловить.
Установка произвольных бит в 0
Предположим, что необходимо установить «0» в 7 и 1 биты по адресу 0x60004012, при этом не изменив значение всех остальных бит в регистре. Для этого необходимо использовать бинарную операцию &. Сразу приведу правильный ответ:
*(unsigned long*)(0x60004012) &= 0xFFFFFF7D;
Или его более простою запись (не переживайте за лишнюю операцию, компилятор все заранее посчитает даже при минимальной оптимизации):
*(unsigned long*)(0x60004012) &= (~0x82);
В первую очередь необходимо определиться с какими блоками предстоит работать. Для это достаточно изучит разделы 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 достойно отдельной статьи. Если у читателей возникнет желание, я подробнее освещу этот раздел в одной из следующих статей.
Для получения адреса регистра, необходимо к начальному значению адресного пространства блока RCC прибавить Addr. offset нужного регистра. Addres offset указывается и в описании регистра (см. скриншот выше).
В итоге, мы определили адрес регистра RCC_AHB1ENR - 0x4002 3830.
Сейчас же наша задача научиться управлять состоянием пинов МК. Перейдем сразу к описанию регистров GPIO.
Как видно из описания для совершения требуемой нам настройки необходимо записать значение 01b в 26-27 биты регистра GPIOx_MODER . Адрес регистра можно определить тем же методом, что описан выше.
Используем регистр 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» на соответствующем выходе МК. Этот метод предпочтительнее предыдущего, если необходимо установить определенное значение на пине «МК», а не изменить его.
Для того, чтобы этого избежать, обычно используется счетчик циклов, а переключение состояние пина МК происходит при прохождении программы определенного числа циклов.
void main()
{
//Enable port D clocking
*(unsigned long*)(0x40023830) |= 0x8;
//little delay for GPIOD get ready
volatile unsigned long i=0;
i++; i++; i++;
i=0;
//Set PD13 as General purpose output
*(unsigned long*)(0x40020C00) = (*(unsigned long*)(0x40020C00)& (~0x0C000000)) | (0x04000000);
while(1)
{
i++;
if(!(i%2000000))
{
//Turn LED ON
*(unsigned long*)(0x40020С14) |= 0x2020;
}
else if(!(i%1000000))
{
//Turn LED OFF
*(unsigned long*)(0x40020С14) &= ~0x2000;
}
}
}
Но и тут не обойдется без проблем, с изменением количества команд выполняемых внутри цикла, будет меняться период мигания светодиодом (или период выполнения других команд в цикле). Но на данном этапе мы не можем с этим бороться.
Но помимо этого, присутствует возможность просмотра значений регистров ядра, спецрегистров периферийных блоков (View->Register) и т.п.
Я настоятельно рекомендую ознакомиться с возможностями дебаггера во время изучения программирования МК.
Спасибо всем, кто прочитал мой пост, получилось значительно больше чем я ожидал в начале.
Жду ваших комментариев и аргументированной критики. Если у прочитавших возникнет желание - постараюсь продолжить цикл статей. Возможно у кого-то есть идеи по поводу тем, которые стоило бы осветить - я был бы рад их услышать.
Данная статья является первой в планируемом цикле статей по изучению программирования микроконтроллеров. Изучая различные материалы я отметил, что практически все они начинаются с того, что новичку предлагается скачать (или использовать идущую со средой разработки) библиотеку для работы с периферийными устройствами и использовать ее для написания своей первой программы (обычно мигание светодиодом).
Меня это сильно удивило. Если верить данным статьям, для программирования не обязательно даже читать документацию к программируемому контроллеру. Меня же учили премудростям «железного программирования» совершенно иначе.
В этой статье, путь от фразы «Да, я хочу попробовать!» до радостного подмигивания светодиода, будет значительно длиннее чем у других авторов. Я постараюсь раскрыть аспекты программирования микроконтроллеров, которые прячутся за использованием библиотечных функций и готовых примеров.
Если вы намерены серьезно изучать программирование микроконтроллеров данная статья для вас. Возможно, она может заинтересовать и тех, кто вдоволь наигрался с Arduino и хочет получить в свои руки все аппаратные возможности железа.
Будем считать, что с типом микроконтроллера мы разобрались. Но на рынке представлен огромнейший спектр различных модификаций от разных производителей. Они отличаются по множеству параметров - от размера флеш памяти до количества аналоговых входов. Для каждой задачи выбор стоит производить индивидуально. Ни каких общих рекомендаций тут нет и быть не может. Отмечу лишь, что стоит начинать изучение с МК производителей имеющих как можно больший ассортимент. Тогда, при выборе МК для определенной задачи достаточно велик шанс, что из представленного ассортимента вам что-нибудь да подойдет.
Я остановил свой выбор на STM32 (хотя и считаю, что лучше начинать изучение с МК от TexasInstruments - очень грамотно составлена документация), потому что они широко распространены среди российских разработчиков электроники. При возникновении проблем и вопросов вы сможете без труда найти решения на форумах. Еще одним плюсом является богатый выбор демонстрационных плат как от производителя, так и от сторонних организаций.
Сам я использую демонстрационную плату STM3220G-EVAL и программатор J-Link PRO . Но для начала, будет вполне достаточно STM32F4DISCOVERY , которую можно купить без особых проблем за небольшую сумму.
Все примеры будут именно для отладочной платы STM32F4DISCOVERY . На данном этапе нам будет совершенно не важно, что этой плате стоит МК на базе ядра Cortex-M4. В ближайшее время мы не будем использовать его особенности и преимущества над Cortex-M3. А как там будет дальше - посмотрим.
Если у вас есть в наличии любая другая плата на базе STM32F2xx/STM32F4xx, вы сможете работать с ней. В изложении материала я постараюсь максимально подробно описывать почему мы делаем именно так, а не иначе. Надеюсь ни у кого не возникнет проблем с переносом примеров на другое железо.
Почему платная среда разработки?
Возможно, кто-то будет не совсем доволен тем, что я предлагаю использовать платную среду разработки, но в IAR есть возможность получить временную лицензию без ограничения функционала, либо безлимитную лицензию с ограничением по размеру кода (32КБ для МК это очень много).
Помимо этого, сразу замечу, что для некоторых МК не существует бесплатных сред разработки. И к сожалению эти МК в некоторых областях незаменимы.
Перед нами появится пустой проект с main файлом.
Теперь необходимо настроить проект для начала работы с «нашим» МК и отладчиком. На плате STM32F4DISCOVERY установлен MK STM32F407VG . Его необходимо выбрать в свойствах проекта (General Options->Target->Device):
При выборе целевого программируемого процессора происходит загрузка его описания, что дает широкие возможности для отладки (об этом будет идти речь ниже). Кроме того, автоматически присоединяется конфигурационный файл с описанием доступного адресного пространства для линкера. Если будет необходимо, мы затронем тему конфигурационного файла линкера в следующих статьях.
После этого необходимо настроить отладчик. Отладка программы происходит непосредственно «в железе». Производится это с помощью JTAG отладчика. Более подробнее ознакомиться с тем, как это происходит можно на Википедии . На плату STM32F4DISCOVERY интегрирован отладчик ST-LINK/V2. Для работы с отладчиком необходимо выбрать его драйвер в меню Debugger->Setup->Driver
. Так же необходимо указать, что отладка должна производиться непосредственно в железе. Для этого необходимо поставить флаг Debugger->Download->Use flash loader(s)
Для тех, кто увидел слово Simulator
Теоретически, IAR позволяет отлаживать программы с использованием симулятора. Но я ни разу на практике не встречал его использования.
Теперь проект готов для работы (программирования, заливки и отладки).
Не будем отходить от классики. Первым проектом будет мигающий светодиод. Благо на плате их предостаточно.Что же это означает с точки зрения программирования? Первым делом необходимо изучить принципиальную схему демонстрационной платы и понять как «заводится» светодиод.
доступен на сайте производителя. В данном описании даже есть отдельный раздел про светодиоды на плате -4.4 LEDs
. Для примера, будем использовать User LD3
. Найдем его на схеме:
Простейший анализ схемы говорит о том, что для того, что бы «зажечь» светодиод необходимо на пин МК подать «1» (которая для данного МК соответствует 3.3В). Выключение производится подачей на этот пин «0». На схеме этот пин обозначается PD13 (это, наверное, самая важная информация из этого документа).
В итоге, мы можем написать «ТЗ» для нашей первой программы:
Программа для МК должна переводить состояние пина МК PD13 из состояния «0» в состояние «1» и обратно с некоторой периодичностью, различимой для человеческого глаза (важное замечание, если моргать светодиодом слишком часто глаз может этого не различить).
Начнем с того, что любой МК включает ядро, память и периферийные блоки. Думаю, что с памятью пока все понятно. Упомяну лишь, в STM32 есть флеш память в которой хранится программа МК (в общем случае это не верное утверждение, программа может храниться во внешней энергонезависимой памяти, но пока это опустим) и другие данные, в том числе и пользовательские. Так же есть SRAM - оперативная память.
Ядро - часть микроконтроллера, осуществляющая выполнение одного потока команд. В нашем МК тип ядра - Cortex-M4. Ядро МК можно сравнить с процессором в ПК. Оно умеет только выполнять команды и передавать данные другим блокам (в этом сравнении не учитываются процессоры с интегрированными графическими ускорителями).
При этом производитель МК не разрабатывает ядро. Ядро покупается у компании ARM Limited . Главное отличие между различными МК - в периферии.
Периферийные блоки - блоки осуществляющие взаимодействие с «внешним миром» или выполняющие специфические функции, недоступные ядру МК. Современные МК (в том числе и STM32) содержат огромный спектр периферийных блоков. Периферийные блоки предназначены для решения различных задач, от считывания значения напряжения с аналогового входа МК до передачи данных внешним устройствам по шине SPI.
В отличии от ядра МК периферийные блоки не выполняют инструкции. Они лишь выполняют команды ядра. При этом участие ядра при выполнении команды не требуется.
Пример
В качестве примера можно привести блок UART, который предназначен для приема и передачи данных от МК внешним устройствам. От ядра необходимо лишь сконфигурировать блок и отдать ему данные для передачи. После этого ядро может дальше выполнять инструкции. На плечи же периферийного блока ложится управление соответствующим выводом МК для передачи данных в соответствии с протоколом. Периферийный блок сам переводит выход МК в необходимое состояние «0» или «1» в нужный момент времени, осуществляя передачу.
ВАЖНО: После записи данных в спецрегистр и последующем чтении вы можете получить совершенно иные данные. Например, передача данных блоку UART для отправки, и считывание данных, полученных блоком от внешнего устройства, осуществляется с помощью одного и того же регистра.
Спецрегистры обычно разделены на битовые поля. Один (или несколько) бит управляют определенным параметром периферийного блока, обычно независимо. Например, разные биты одного регистра управляют состоянием разных выходов МК.
Запись данных по адресу в памяти
Предположим, что читая описание периферийного блока, мы поняли, что для его корректной работы необходимо записать в него число 0x3B. Адрес спецрегистра 0x60004012. Регистр 32-битный.
Если вы сразу не знаете как это сделать, попробую описать цепочку рассуждений для получения правильной команды.
Значение 0x60004012 есть не что иное, как значение указателя на ячейку памяти. Нужно именно это и указать в нашей программе, тоесть сделать преобразование типов согласно синтаксису языка C:
(unsigned long*)(0x60004012)
Таким образом, у нас есть указатель на элемент. Теперь нужно в этот элемент записать необходимое значение. Делается это разыменовыванием указателя. Таким образом получаем правильную команду:
*(unsigned long*)(0x60004012) = 0x3B;
Установка произвольных бит в 1
Предположим, что необходимо установить «1» в 7 и 1 биты по адресу 0x60004012, при этом не изменив значение всех остальных бит в регистре. Для этого необходимо использовать бинарную операцию |. Сразу приведу правильный ответ:
*(unsigned long*)(0x60004012) |= 0x82;
Обратите внимание на 2 факта. Биты считаются с нулевого, а не с первого. Данная операция на самом деле занимает неменее 3 тактов - считывание значения, модификация, запись. Иногда это не допустимо, поскольку между считыванием и записью значение одного из бит, которые нам запрещено изменять, могло быть изменено периферийным блоком. Незабывайте про эту особенность, иначе могут полезть баги, которые крайне сложно отловить.
Установка произвольных бит в 0
Предположим, что необходимо установить «0» в 7 и 1 биты по адресу 0x60004012, при этом не изменив значение всех остальных бит в регистре. Для этого необходимо использовать бинарную операцию &. Сразу приведу правильный ответ:
*(unsigned long*)(0x60004012) &= 0xFFFFFF7D;
Или его более простою запись (не переживайте за лишнюю операцию, компилятор все заранее посчитает даже при минимальной оптимизации):
*(unsigned long*)(0x60004012) &= (~0x82);
В первую очередь необходимо определиться с какими блоками предстоит работать. Для это достаточно изучит разделы 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 достойно отдельной статьи. Если у читателей возникнет желание, я подробнее освещу этот раздел в одной из следующих статей.
Для получения адреса регистра, необходимо к начальному значению адресного пространства блока RCC прибавить Addr. offset нужного регистра. Addres offset указывается и в описании регистра (см. скриншот выше).
В итоге, мы определили адрес регистра RCC_AHB1ENR - 0x4002 3830.
Сейчас же наша задача научиться управлять состоянием пинов МК. Перейдем сразу к описанию регистров GPIO.
Как видно из описания для совершения требуемой нам настройки необходимо записать значение 01b в 26-27 биты регистра GPIOx_MODER . Адрес регистра можно определить тем же методом, что описан выше.
Используем регистр 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» на соответствующем выходе МК. Этот метод предпочтительнее предыдущего, если необходимо установить определенное значение на пине «МК», а не изменить его.
Для того, чтобы этого избежать, обычно используется счетчик циклов, а переключение состояние пина МК происходит при прохождении программы определенного числа циклов.
void main()
{
//Enable port D clocking
*(unsigned long*)(0x40023830) |= 0x8;
//little delay for GPIOD get ready
volatile unsigned long i=0;
i++; i++; i++;
i=0;
//Set PD13 as General purpose output
*(unsigned long*)(0x40020C00) = (*(unsigned long*)(0x40020C00)& (~0x0C000000)) | (0x04000000);
while(1)
{
i++;
if(!(i%2000000))
{
//Turn LED ON
*(unsigned long*)(0x40020С14) |= 0x2020;
}
else if(!(i%1000000))
{
//Turn LED OFF
*(unsigned long*)(0x40020С14) &= ~0x2000;
}
}
}
Но и тут не обойдется без проблем, с изменением количества команд выполняемых внутри цикла, будет меняться период мигания светодиодом (или период выполнения других команд в цикле). Но на данном этапе мы не можем с этим бороться.
Но помимо этого, присутствует возможность просмотра значений регистров ядра, спецрегистров периферийных блоков (View->Register) и т.п.
Я настоятельно рекомендую ознакомиться с возможностями дебаггера во время изучения программирования МК.
Спасибо всем, кто прочитал мой пост, получилось значительно больше чем я ожидал в начале.
Жду ваших комментариев и аргументированной критики. Если у прочитавших возникнет желание - постараюсь продолжить цикл статей. Возможно у кого-то есть идеи по поводу тем, которые стоило бы осветить - я был бы рад их услышать.
Система тактирования STM32.
Сегодня речь пойдет о системе тактирования микроконтроллеров STM 32. Если вы ещё не знаете что такое такт, частота и вообще не затрагивали до этого системы тактирования, . Хоть по данной ссылке и рассматривается система тактирования микроконтроллера AVR , понятия определенные в уроке по ссылке, применимы и к системе тактирования микроконтроллеров STM 32.
Итак, приступим!
Рассматривать систему тактирования будем на примере микроконтроллера STM 32F 303VCT 6, который установлен в отладочной плате STM 32 F 3 DISCOVERY .
Взглянем на общую структуру системы тактирования:
Как мы видим, система тактирования STM 32, на порядок сложнее системы тактирования микроконтроллеров AVR, не смотря на то, что на рисунке отражена лишь основная её часть.
Давайте разбираться!
Рассматривать схему следует слева направо. Во-первых, мы должны выбрать основной источник тактирования контроллера. Выбирать будем между HSI и HSE.
HSE -Внешний высокочастотный генератор. Источником тактирования для него служит внешний тактовый сигнал (Input frequency ), который как мы видим по схеме, может быть от 4 до 32 МГц. Это может быть кварцевый резонатор, тактовый генератор и так далее.
HSI - Внутренний высокочастотный генератор. В микроконтроллерах STM 32 F 3 является RC цепочкой с частотой 8МГц. Точность значительно ниже внешнего генератора HSE.
Каждый из данных источников тактирования может быть соединен с PLL . Однако перед подачей на PLL сигнал с HSI будет уменьшен в 2 раза. Сигнал HSE в свою очередь, может подаваться на PLL без изменений, либо быть уменьшен в определенное количество раз, по желанию пользователя.
PLL Clock - Система Фазовой Автоподстройки Частоты (ФАПЧ). Позволяет умножить входной сигнал HSI или HSE в необходимое количество раз.
С PLL сигнал может быть подан на системную шину, максимальная частота которой 72МГц. Либо, на системную шину может быть подан сигнал HSE или HSI напрямую, то есть без преобразования PLL .
Системная тактовая частота SYSCLK , тактирует все основные шины микроконтроллера, через соответствующие делители, как мы видим на схеме выше. Следует учитывать, что максимальная тактовая частота некоторых шин ниже SYSCLK . Поэтому, перед подачей тактового сигнала SYSCLK на шину, следует поделить его соответствующим делителем. Если этого не сделать, микроконтроллер зависнет.
Для настройки тактирования можно прибегнуть к ручной правке регистров, либо воспользоваться библиотечными функциями. Мы воспользуемся библиотекой.
Настроим нашу отладочную плату STM 32 F 3 DISCOVERY на работу с тактовой частотой 72 МГц.
Создадим и настроим проект в Keil uVision . .
Добавим следующий код:
#include "stm32f30x_gpio.h" #include "stm32f30x_rcc.h" void InitRCC() { RCC_HSEConfig(RCC_HSE_ON); //Enable HSE while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET) ; //Waiting for HSE //Set Flash latency FLASH->ACR |= FLASH_ACR_PRFTBE; FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); FLASH->ACR |= (uint32_t)((uint8_t)0x02); RCC_PREDIV1Config(RCC_PREDIV1_Div1);//PREDIV 1 Divider = 1 RCC_PLLConfig(RCC_PLLSource_PREDIV1,RCC_PLLMul_9);//Set PREDIV1 as source for PLL,And set PLLMUL=9 RCC_PLLCmd(ENABLE);//Enable PLL while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) ;//Waiting for PLL RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//Set PLL as SYSCLK Soucre RCC_HSICmd(DISABLE);//Disable HSI } int main(void) { RCC_ClocksTypeDef RCC_Clocks; InitRCC(); RCC_GetClocksFreq (&RCC_Clocks); __NOP (); while (1) { } }
#include "stm32f30x_gpio.h" #include "stm32f30x_rcc.h" void InitRCC () RCC_HSEConfig (RCC_HSE_ON ) ; //Enable HSE while (RCC_GetFlagStatus (RCC_FLAG_HSERDY ) == RESET ) ; //Waiting for HSE //Set Flash latency FLASH -> ACR |= FLASH_ACR_PRFTBE ; FLASH -> ACR &= (uint32_t ) ((uint32_t ) ~ FLASH_ACR_LATENCY ) ; FLASH -> ACR |= (uint32_t ) ((uint8_t ) 0x02 ) ; RCC_PREDIV1Config (RCC_PREDIV1_Div1 ) ; //PREDIV 1 Divider = 1 RCC_PLLConfig (RCC_PLLSource_PREDIV1 , RCC_PLLMul_9 ) ; //Set PREDIV1 as source for PLL,And set PLLMUL=9 RCC_PLLCmd (ENABLE ) ; //Enable PLL while (RCC_GetFlagStatus (RCC_FLAG_PLLRDY ) == RESET ) ; //Waiting for PLL RCC_SYSCLKConfig (RCC_SYSCLKSource_PLLCLK ) ; //Set PLL as SYSCLK Soucre RCC_HSICmd (DISABLE ) ; //Disable HSI int main (void ) RCC_ClocksTypeDef RCC_Clocks ; InitRCC () ; RCC_GetClocksFreq (& RCC_Clocks ) ; NOP () ; while (1 ) |
В основной функции main , объявлена структура RCC _ ClocksTypeDef . Данная структура содержит в себе поля, отражающие текущую тактовую частоту определенных частей контроллера.
Затем в основной функции вызывается функция InitRCC ,которая настраивает тактирование контроллера. Рассмотрим её подробнее.
Командой RCC _ HSEConfig (RCC _ HSE _ ON ), мы включаем HSE .На его включение необходимо время, поэтому необходимо подождать пока не будет установлен флаг RCC _ FLAG _ HSERDY . Делаем мы это в цикле while (RCC _ GetFlagStatus (RCC _ FLAG _ HSERDY ) == RESET ) .
Затем мы производим настройку задержки флеш памяти. Это необходимо делать при работе системной шины на частотах свыше 36 МГц!
После настройки задержки выбираем предделитель PLL . Командой RCC _ PREDIV 1 Config (RCC _ PREDIV 1_ Div 1) мы устанавливаем предделитель на 1. Командой RCC _ PLLConfig (RCC _ PLLSource _ PREDIV 1, RCC _ PLLMul _9 ) выбирам HSE как источник частоты для PLL и выбираем умножение в 9 раз. Остается только влючить PLL командой RCC _ PLLCmd (ENABLE ), и ожидать установки флага RCC _ FLAG _ PLLRDY ,в цикле while . Тем самым мы обеспечиваем необходимую временную задержку для включения PLL . После этого выбираем PLL как источник системной частоты SYSCLK командой RCC _ SYSCLKConfig (RCC _ SYSCLKSource _ PLLCLK ). Предделители шин трогать не будем, поэтому шины AHB ,APB 1,APB 2 будут работать на частотах 72,36 и 72 МГц соответственно.
Остается лишь выключить внутреннюю RC цепочку командой RCC _ HSICmd (DISABLE ).
После выполнения функции InitRCC , в основном цикле прошивки заполним структуру RCC _ ClocksTypeDef , что позволит нам узнать, правильно ли мы настроили систему тактирования. Делаем мы это командой RCC_GetClocksFreq (&RCC_Clocks).
Посмотреть значения тактовых частот контроллера можно в режиме отладки, установив точку останова на команде __ NOP () что означает, пустую команду. Данную команду часто добавляют для удобства отладки.
Подключаем отладочную плату STM32 F3 DISCOVERY , собираем прошивку, прошиваем плату и наконец, заходим в режим отладки, нажав кнопку Start /Stop debug session (Ctrl +F 5). Установив точку останова на функции __ NOP ,и добавив структуру RCC _Clocks в Watch ,запускаем исполнение прошивки, нажав F 5. В результате видим:
Частоты настроены правильно, и микроконтроллер теперь работает на частоте 72 Мгц.
Итак, как Вы поняли из сегодняшнего урока, система тактирования STM 32 достаточно мощна и гибка для удовлетворения потребностей Ваших проектов. Потратив время на её настройку - Вы достигнете прекрасных результатов!
Спасибо за внимание! Ваши вопросы как обычно в комментариях!
Любое копирование, воспроизведение, цитирование материала, или его частей разрешено только с письменного согласия администрации MKPROG .RU . Незаконное копирование, цитирование, воспроизведение преследуется по закону!