Академический Документы
Профессиональный Документы
Культура Документы
Все потоки Разработка Администрирование Дизайн Менеджмент Маркетинг Научпоп Войти Регистрация
Данная статья является первой в планируемом цикле статей по изучению программирования Обратная сторона Open Source-славы:
микроконтроллеров. Изучая различные материалы я отметил, что практически все они начинаются с как угрожают автору curl
того, что новичку предлагается скачать (или использовать идущую со средой разработки) библиотеку 34,1k 86
для работы с периферийными устройствами и использовать ее для написания своей первой программы
(обычно мигание светодиодом). Работа сознания: что такое пятна
Габора?
Меня это сильно удивило. Если верить данным статьям, для программирования не обязательно даже 45,3k 49
читать документацию к программируемому контроллеру. Меня же учили премудростям «железного
программирования» совершенно иначе. Глава Microsoft заявил об имеющихся
доказательствах причастности России
к хакерской атаке на SolarWinds
В этой статье, путь от фразы «Да, я хочу попробовать!» до радостного подмигивания светодиода, будет
3,9k 14
значительно длиннее чем у других авторов. Я постараюсь раскрыть аспекты программирования
микроконтроллеров, которые прячутся за использованием библиотечных функций и готовых примеров.
5 книг Стругацких
Если вы намерены серьезно изучать программирование микроконтроллеров данная статья для вас.
6,4k 17
Возможно, она может заинтересовать и тех, кто вдоволь наигрался с Arduino и хочет получить в свои
руки все аппаратные возможности железа.
Мир JavaScript в 2021 году
Выбор микроконтроллера 12,2k 10
— от размера флеш памяти до количества аналоговых входов. Для каждой задачи выбор стоит
производить индивидуально. Ни каких общих рекомендаций тут нет и быть не может. Отмечу лишь, что
стоит начинать изучение с МК производителей имеющих как можно больший ассортимент. Тогда, при
выборе МК для определенной задачи достаточно велик шанс, что из представленного ассортимента вам
что-нибудь да подойдет.
Я остановил свой выбор на STM32 (хотя и считаю, что лучше начинать изучение с МК от
TexasInstruments — очень грамотно составлена документация), потому что они широко распространены
среди российских разработчиков электроники. При возникновении проблем и вопросов вы сможете
без труда найти решения на форумах. Еще одним плюсом является богатый выбор демонстрационных
плат как от производителя, так и от сторонних организаций.
Все примеры будут именно для отладочной платы STM32F4DISCOVERY. На данном этапе нам будет
совершенно не важно, что этой плате стоит МК на базе ядра Cortex-M4. В ближайшее время мы не
будем использовать его особенности и преимущества над Cortex-M3. А как там будет дальше —
посмотрим.
Если у вас есть в наличии любая другая плата на базе STM32F2xx/STM32F4xx, вы сможете работать с ней.
В изложении материала я постараюсь максимально подробно описывать почему мы делаем именно так,
а не иначе. Надеюсь ни у кого не возникнет проблем с переносом примеров на другое железо.
Среда разработки
Как уже неоднократно упоминалось, для ARM микроконтроллеров существует достаточное количество
сред разработки, как платных так и не очень. И снова хочется опустить полемику по этому поводу. Я
использую IAR Embedded Workbench for ARM 6.60. Все примеры будут именно в этой среде. Если вам по
душе (или в вашей организации используется) что-то другое (Keil, Eclipse, CCS, CooCoc и т.д.) то это вам
тоже не очень помешает. На особенности, связанные именно со средой разработки, я буду обращать
отдельное внимание.
Почему платная среда разработки?
С чего начать?
Создание проекта
Для начала создадим пустой проект. IAR позволяет создать проекты на ASM, C и C++. Мы будем
использовать C.
Теперь необходимо настроить проект для начала работы с «нашим» МК и отладчиком. На плате
STM32F4DISCOVERY установлен MK STM32F407VG . Его необходимо выбрать в свойствах проекта
(General Options->Target->Device):
При выборе целевого программируемого процессора происходит загрузка его описания, что дает
широкие возможности для отладки (об этом будет идти речь ниже). Кроме того, автоматически
присоединяется конфигурационный файл с описанием доступного адресного пространства для линкера.
Если будет необходимо, мы затронем тему конфигурационного файла линкера в следующих статьях.
Не будем отходить от классики. Первым проектом будет мигающий светодиод. Благо на плате их
предостаточно.Что же это означает с точки зрения программирования? Первым делом необходимо
изучить принципиальную схему демонстрационной платы и понять как «заводится» светодиод.
User manualдоступен на сайте производителя. В данном описании даже есть отдельный раздел про
светодиоды на плате -4.4 LEDs. Для примера, будем использовать User LD3. Найдем его на схеме:
Простейший анализ схемы говорит о том, что для того, что бы «зажечь» светодиод необходимо на пин
МК подать «1» (которая для данного МК соответствует 3.3В). Выключение производится подачей на этот
пин «0». На схеме этот пин обозначается PD13 (это, наверное, самая важная информация из этого
документа).
Начнем с того, что любой МК включает ядро, память и периферийные блоки. Думаю, что с памятью
пока все понятно. Упомяну лишь, в STM32 есть флеш память в которой хранится программа МК (в
общем случае это не верное утверждение, программа может храниться во внешней энергонезависимой
памяти, но пока это опустим) и другие данные, в том числе и пользовательские. Так же есть SRAM —
оперативная память.
Ядро — часть микроконтроллера, осуществляющая выполнение одного потока команд. В нашем МК тип
ядра — Cortex-M4. Ядро МК можно сравнить с процессором в ПК. Оно умеет только выполнять
команды и передавать данные другим блокам (в этом сравнении не учитываются процессоры с
интегрированными графическими ускорителями).
При этом производитель МК не разрабатывает ядро. Ядро покупается у компании ARM Limited. Главное
отличие между различными МК — в периферии.
ВАЖНО: После записи данных в спецрегистр и последующем чтении вы можете получить совершенно
иные данные. Например, передача данных блоку UART для отправки, и считывание данных, полученных
блоком от внешнего устройства, осуществляется с помощью одного и того же регистра.
Спецрегистры обычно разделены на битовые поля. Один (или несколько) бит управляют определенным
параметром периферийного блока, обычно независимо. Например, разные биты одного регистра
управляют состоянием разных выходов МК.
Вспоминаем С
Если вы гуру в языке C, то можете смело пропускать данный раздел. Он предназначен в первую очередь
для тех, кого учили (или ктоучился сам) программировать для ПК. Опыт показывает, что люди часто не
помнят важных команд. Здесь я вкратце напомню про побитовые операции и работу напрямую с
памятью по ее адресу.
Предположим, что читая описание периферийного блока, мы поняли, что для его корректной работы
необходимо записать в него число 0x3B. Адрес спецрегистра 0x60004012. Регистр 32-битный.
Если вы сразу не знаете как это сделать, попробую описать цепочку рассуждений для получения
правильной команды.
Значение 0x60004012 есть не что иное, как значение указателя на ячейку памяти. Нужно именно это и
указать в нашей программе, тоесть сделать преобразование типов согласно синтаксису языка C:
(unsigned long*)(0x60004012)
Таким образом, у нас есть указатель на элемент. Теперь нужно в этот элемент записать необходимое
значение. Делается это разыменовыванием указателя. Таким образом получаем правильную команду:
Предположим, что необходимо установить «1» в 7 и 1 биты по адресу 0x60004012, при этом не изменив
значение всех остальных бит в регистре. Для этого необходимо использовать бинарную операцию |.
Сразу приведу правильный ответ:
Обратите внимание на 2 факта. Биты считаются с нулевого, а не с первого. Данная операция на самом
деле занимает неменее 3 тактов — считывание значения, модификация, запись. Иногда это не
допустимо, поскольку между считыванием и записью значение одного из бит, которые нам запрещено
изменять, могло быть изменено периферийным блоком. Незабывайте про эту особенность, иначе могут
полезть баги, которые крайне сложно отловить.
Предположим, что необходимо установить «0» в 7 и 1 биты по адресу 0x60004012, при этом не изменив
значение всех остальных бит в регистре. Для этого необходимо использовать бинарную операцию &.
Сразу приведу правильный ответ:
Или его более простою запись (не переживайте за лишнюю операцию, компилятор все заранее
посчитает даже при минимальной оптимизации):
Приступаем к работе!
Работа над программой для МК всегда начинается с чтения документации. Для нашего МК Reference
manual доступен на сайте производителя. Страниц много, но все читать пока не нужно. Как уже было
сказано, большую часть документации составляет описание периферийных блоков и их регистров. Так
же хочу обратить внимание на то, что этот Reference Manual написан не для одного МК, а для
нескольких линеек. Это говорит о том, что код будет переносим при переходе на другие МК в этих
линейках (если конечно не пытаться использовать периферийные блоки которых нет в используемом
МК).
В первую очередь необходимо определиться с какими блоками предстоит работать. Для это достаточно
изучит разделы Introduction и Main features.
За включение тактирования периферийных блоков отвечают регистры 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 достойно отдельной статьи. Если у читателей
возникнет желание, я подробнее освещу этот раздел в одной из следующих статей.
Перейдя по ссылке к Register map блока RCC находим строчкку с интересующим нас регистром
RCC_AHB1ENR:
Для получения адреса регистра, необходимо к начальному значению адресного пространства блока
RCC прибавить Addr. offset нужного регистра. Addres offset указывается и в описании регистра (см.
скриншот выше).
Блок 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 port output speed register (GPIOx_OSPEEDR) — задается скорость работы выхода
Мы не будем менять данных параметров, поскольку нас вполне устраивают значения по умолчанию.
Запись «0» или «1» в биты 0-16 приводят к соответствующему изменению состояния пинов порта. Для
того, чтобы установить определенное значение на выходе одного или нескольких пинов МК и не
изменить состояния остальных, необходимо будет пользоваться операцией модификации отдельных
бит. Такая операция выполняется не менее чем за 3 такта. Если же необходимо в часть битов записать 1,
а в другие 0, то понадобится не менее 4 тактов. Данный метод предпочтительнее всего использовать
для изменения состояния выхода на противоположное, если его изначальное состояние не известно.
В отличии от предыдущего метода, запись 0 в любой из битов данного регистра не приведет ни к чему
(да и вообще, все биты write-only!). Запись 1 в биты 0-15 приведет к установке «1» на соответствующем
выходе МК. Запись 1 в биты 16-31 приведет к установке «0» на соответствующем выходе МК. Этот метод
предпочтительнее предыдущего, если необходимо установить определенное значение на пине «МК», а
не изменить его.
Зажигаем светодиод!
Найдя адреса всех необходимых регистров, можно написать программу, которая включает светодиод:
void main()
{
//Enable port D clocking
*(unsigned long*)(0x40023830) |= 0x8;
while(1);
}
Мигаем светодиодом
Мигание светодиода есть ни что иное, как попеременное включение и выключение с задержкой между
этими действиями. Самый простой способ — поместить включение и выключение в вечный цикл, а
между ними вставить задержку.
void main()
{
//Enable port D clocking
*(unsigned long*)(0x40023830) |= 0x8;
while(1)
{
//Turn LED ON
*(unsigned long*)(0x40020C14) |= 0x2000;
//Delay
for( i=0; i<1000000 ;++i );
//Turn LED OFF
*(unsigned long*)(0x40020C14) &= ~0x2000;
//Delay
for( i=0; i<1000000 ;++i );
}
}
Значение 1000000 в задержке подобрано экспериментально так, чтобы период мигания светодиода был
различим глазом, но и не был слишком велик.
Оптимизируем алгоритм
Минусом выбранного подхода миганием светодиодом является то, что ядро МК большую часть
времени проводит в пустых циклах, хотя мог бы заниматься чем-нибудь полезным (в нашем примере
других задач нет, но в будущем они появятся).
Для того, чтобы этого избежать, обычно используется счетчик циклов, а переключение состояние пина
МК происходит при прохождении программы определенного числа циклов.
void main()
{
//Enable port D clocking
*(unsigned long*)(0x40023830) |= 0x8;
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;
}
}
}
Но и тут не обойдется без проблем, с изменением количества команд выполняемых внутри цикла, будет
меняться период мигания светодиодом (или период выполнения других команд в цикле). Но на данном
этапе мы не можем с этим бороться.
Немного об отладке
IAR позволяет осуществлять отладку приложения непосредственно в железе. Все выглядит практически
так же, как и отладка приложения для ПК. Есть режим пошагового выполнения, входа в функцию,
просмотр значения переменных (В режиме отладки View->Watch->Watch1/4).
Спасибо всем, кто прочитал мой пост, получилось значительно больше чем я ожидал в начале.
Жду ваших комментариев и аргументированной критики. Если у прочитавших возникнет желание —
постараюсь продолжить цикл статей. Возможно у кого-то есть идеи по поводу тем, которые стоило бы
осветить — я был бы рад их услышать.
29,0 0,0
Карма Рейтинг
@ ezh213b
Пользователь
Опрос
СРЕДНЯЯ ЗАРПЛАТА В IT
Поэтому я бы не стал говорить что не важно, что юзать — F1xx или F4xx, у начинающих запросто могут
Судьба предателя, угнавшего
вылезти проблемы, оттого, что, скажем, они пишут код как для F1xx, пытаются его использовать на F4xx, но не новейший МиГ-25 в Японию
включили мэпинг периферии.
130k 718
P.S. Магические числа в коде — это адЪ. Хоть бы константы именованные юзали.
Pet-проект для джуна. Или зачем и как
выбрать pet project. (+личный опыт)
3,6k 2
ezh213b 24 марта 2014 в 13:16 0
На счет магических чисел я тоже вас поддерживаю, но считаю, на первых шагах код должен выглядеть
именно так. Отчасти из-за того, что при желании воспользоваться «готовым» куском кода вам придется
лезть в описание регистров МК.
В дальнейшем, конечно же, в своих постах я уйду от прямого ковыряния в регистрах и буду пользоваться
библиотекой.
или
Во-вторых, каким образом ваш код больше стимулирует к изучению документации, чем код с
использованием дефайнов от ST?
RCC->AHB1ENR |= RCC_AHB1ENR_IOPDEN;
Человеку в любом случае придется полезть в документацию, чтобы узнать, что это за регистр AHB1ENR
и что за флаг RCC_AHB1ENR_IOPDEN, так зачем тогда намеренно портить код?
Как вы, наверное, знаете в большинстве начальных программ для МК быстро конфигурируется
периферия, и дальше уже используются дефайны для работы с определенными регистрами (даже без
использования библиотек). И этих регистров не так много. Так вот, используя определения, люди
часто забывают о том, что на самом деле происходит с их коде. На курсах программирования МК
часто встречались случаи, когда студенты делали записи:
А по прошествии недели спрашивали: «Какой хэддер нужно подключить, чтобы заработала функция
EnableLED1?».
Выносить что-либо в макросы стоит только когда вы на 100% уверены, что знаете как это работает.
Все выше сказанное только мое мнение, а не руководство к действию. Каждый сам решает как ему
оформлять код. По этому поводу написано миллионы страниц текста.
2) По поводу:
RCC->AHB1ENR |= RCC_AHB1ENR_IOPDEN
RCC->AHB1ENR |= RCC_AHB1Periph_SRAM1;
Регистры у F4xx и F1xx отличаются, у тех же GPIO, допустим. Настраиваются по-разному, у F1xx режим с
настройками упиханы в общий регистр, у F4xx — отдельные регистры.
Ещё ощутимо различается настройка AF in, т. к. на F1xx используется IN_FLOATING, а на F4xx AF. Плюс
ремаппер, да.
Я как-то час убил пытаясь сопоставить логику работы кода по инициализации UART в примере на TI'шный
stellaris launchpad и в мануале на него же. В итоге оказалось что алгоритм в примере был не правильный, но в
частном случае «по умолчанию» он таки работал.
От TI, если мне не изменяет память. Я никогда не заморачивался Keil'ами и IAR'ами, потому что «vim'а
хватит везде» :-)
У меня, наверно, было тяжелое детство, приходилось дебажить раннюю загрузку linux миганиями
светодиода, так что я вполне могу пережить без этих вот новомодных отладчиков (а по сути — я
никогда профессионально разработкой ПО в windows не занимался, потому нет привычки ко
всяким армовым иде).
Вообще есть openocd и gdb, их всегда хватает для отладки. Gdb вообще очень функционален и
удобен, если к нему попривыкнуть. А компилировать проект из Makefile — это вообще на порядок
очевиднее чем IDE должно быть, потому что видно, что происходит.
В первом примере как-то не понадобилось. Но вот когда дойдем до считывания данных из регистров —
вот тогда затронем этот вопрос.
Segger J-link PRO стоит 998$, как то не очень годится для DIY творчества
shop-us.segger.com/J_Link_PRO_p/8.12.00.htm
Хехе. У меня брат один такой уже умер от прошивки 4.80. Как говорится, работает — не трогай.
Отдал убитый клон старшим товарищам, может и вылечат. Вроде там надо слово GDBFULL
обязательно писать большими буквами.
Segger J-Link очень хорошо подходит к дешовым платам типа LC Technology по 7.99$
www.lctech-inc.com/Hardware/Default.aspx?Id=6e665f5e-20d7-4986-94da-bd5a89058f72
www.aliexpress.com/store/group/Cortex-Study-Board/524881_251395501.html
Все дело должно работать на Linux Arch (пришлось несколько пакетов переместить из AUR в [community]).
В общем после курения мануалов и интернетов получилась вот такое вот минималистическое приложение
(конечно же диодиком моргать) github.com/anatol/stm32f4-examples/tree/master/blink01
Какой смысл не использовать CMSIS (для STM32 проще взять и выдрать её из SPL), которая
минималистична и даёт готовые define для адресов периферии? Проверить, что это возможно?
Удобство падает, вероятность сделать ошибку растет, больше внимания и усилий тратится не на логику и
control flow программы (а при наличии мало-мальски нетривиального набора прерываний он уже
довольно сложен), а на аккуратное вбивание адресов и смещений из datasheet'а.
Опять же это не спасет от, например, неправильной задержки после инициализации RCC (которая в
разных версиях для разных шин — разная). Или от попадания на какой-нибудь баг из errata.
Вы Anatol Pomozov?
Да! Выучить вещи такие инициализация устройства, инициализация приложения (bss/data секции); что
есть вектор прерываний, как работает линковка…
Постойте, но текст вроде не для тех, кто хочет научиться программировать? Он для тех, кто хочет
пощупать новую архитектуру. Думаю, читатель уже должен представлять, что такое библиотека и как
с ней работать, а уж заглянуть в файл с дефайнами и подавно.
Да, в рамках обучения это полезно. У вас, например, нет важных хэндлеров в таблице векторов
прерываний (NMI, HardFault, MemManage, BusFault, UsageFault, SVC, DebugMon, PendSV). В процессе
отладки stlink2 умеет сваливаться в HardFault, а у вас undefined behavior, т. к. секция .text будет лежать
в той части, где должна быть таблица векторов прерываний. Пока исполнительных устройств нет,
может и ничего, но при их наличии — довольно неприятно.
Обучение это прежде всего формирование понимания и правильных навыков. Потом, в процессе
реальной работы, когда важна скорость выдачи продукта, можно обвешиваться какими угодно
IDE и бирюльками. Но на данном этапе, я считаю, только так…
При чём здесь IDE? В нормальной IDE вы можете использовать make для сборки, написать
руками скрипт для линковщика и т. п.
Использование визардов IDE на начальном этапе (без понимания того, что они генерируют) —
зло.
Я вообще считаю, что обучаться Си лучше на простом контроллере, например, AVR. Ассемблер
там простой, спецрегистров немного, периферии — тоже.
Что до магических чисел. Я, например, как адепт старой школы, на автопилоте всем
константам прописываю мнемонические названия.
Я приверженец мнения, что Си можно изучить только работая, как минимум с памятью,
на низком уровне. Будет это на x86, x51, AVR, PIC, ARM — не суть важно. На x86, разве что,
порог вхождения ниже за счёт легкой работы с libc.
Правильные навыки, что там, что там по большей части одинаковы. Если, конечно, не
замахиваться на какой-нибудь MISRA C, который ещё ужесточает ограничения.
Прямо убить контроллер — это, конечно, надо постараться. А вот из-за набитой с
опечаткой чужой программки, как-то пришлось полдня потом промудохаться, чтобы
привести контроллер в чувство. Было дело… Он был уже запаян на плату…
Сравнивая с habrahabr.ru/post/189484/ бирюльки в виде IAR появляются уже у вас :-) Впрочем я
нисколько не дискредитирую вашу статью — тема разбора reference manual у вас раскрыта
лучше.
Пардон, но лично я давно и счастливо сижу на Linux'e, и IAR стараюсь обходить стороной.
Лично моё отношение к подобным IDE сугубо отрицательное. Правда, я не занимаюсь
микроконроллерами профессионально. Для меня это хобби…
Да. знаю таких товарищей. Они обычно от Linux'а шарахаются как от огня. Этим и
объясняется их «снисходительность» к «сборкам вручную» и «мейкфайлам»…
Каких либо серьезных аргументов за IAR' ы и Keil'ы я от них так и не услышал. Обычно
приводят аргумент с интеграцией с инструментами сквозного проектирования, типа
Altium и иже сними… Когда например, port mappings может генерироваться в
зависимости от разводки платы. И еще пара специфических фишек, которые на деле
ими мало используются…
Зато, послушаешь рассказы как глючат эти IAR'ы порой, это вообще пипец… Когда про
цену на Keil/IAR'ы говорить начинаешь они тоже грустнеют… В общем всё говорит за
то, что это больше вопрос привычки а не каких-то реальных профитов…
Моя информация, вероятно, устарела, но, вроде, в Keil uVision (могу путать с IAR, т. к.
в то время работал в одном, делал хоббийные проекты в другом) для x51 в 2006,
примерно, году не было:
— нормальной отладки (да-да, embedded среда),
— почти не было подсветки синтаксиса,
— навигации по коду (например, перейти к объявлению/реализации, перейти в
заголовочный файл),
— автодополнения (имен файлов, названий функций, полей в структурах).
Ага, так вот кому надо сказать за перемещение arm-none-eabi-gcc и иже с ним в community repo…
Спасибо… )
Присоединяюсь =)
Я бы не рекомендовал напрямую работать с регистрами, особенно для новичков. У STM есть неплохая
прослойка в виде библиотек, и читаемость кода выше, и портирование кода на другие версии процессора
будет проще. Конечно полезно знать, что делает библиотечная функция, но совсем не обязательно это
помнить — всегда можно посмотреть, открыв ее код или define
Ох, помню как я втыкал в архитектуру F1, а потом заказал себе F4, и долго пытался понять разницу работы и
победить компилятор :)). Потом прозрел и читаю документацию можно сказать прямо в заголовках. Пишу на
кокосе. Уже объяснил паре товарищей как работать с кокосом и программированием под
микроконтроллеры. :) Один радиолюбитель был, какие же у него были азартные глаза когда он понял что он
делает и что может сделать :)
А обучение мое довольно жестокое, грызи, грызи, грызи. Вот это глянь и пойми почему так, а не этак. Так что
я за либы от stm как никак а переделки малы, и можно даже окружиться в своих либах дефайнами по
платформе что бы компилилось и под f1 и под f4. Конечно не универсальное решение, но вполне сгодится
для общих задач. ЗА статью определенно +, все таки описывает многие аспекты программирования для
начинающих.
«Как видно из описания для совершения требуемой нам настройки необходимо записать значение 01b в 26-
27 биты регистра GPIOx_MODER. Адрес регистра можно определить тем же методом, что описан выше.»
Помогите вкурить, как число можно в биты впихнуть?
САМОЕ ЧИТАЕМОЕ
© 2006 – 2021 «Habr» Настройка языка О сайте Служба поддержки Мобильная версия