Железо для программиста: микроконтроллеры, платы, схемы, питание
Зачем программисту вообще лезть в электронику
Когда вы пишете сервис, платформа — это абстракция с документированным API: ядро, драйверы и виртуальная память прячут физику так надёжно, что за всю карьеру можно ни разу о ней не вспомнить. Во встраиваемой разработке платформа — это кусок текстолита с припаянными компонентами, и она сообщает о себе не исключениями, а поведением: программа «иногда не работает», датчик «отвечает через раз», плата «перезагружается, когда включается мотор».
Ключевая мысль всей статьи: железо — это рантайм с электрическим контрактом. У этого контракта три пункта, которых нет ни в одном интерфейсе:
- Напряжение. Логическая единица — не бит, а диапазон напряжений. Выйдете за него в одну сторону — чип не поймёт сигнал, в другую — сгорит.
- Ток. Каждый вывод — источник конечной мощности.
pin = 1не «включает светодиод», а открывает ключ, через который потечёт ток, ограниченный только вашей схемой. - Время. Фронт сигнала не мгновенный. Ёмкость проводов, подтяжки и длина дорожек превращают идеальный меандр из документации в кривую, которую приёмник может прочитать неправильно.
Пока эти три пункта не в голове, отладка превращается в гадание: код выглядит правильным, потому что он правильный, а ошибка живёт в физике. Хорошая новость в том, что программисту нужен не курс электротехники, а компактный практический минимум — примерно то, что помещается в эту статью. Общий контекст трека и то, чем встраиваемая разработка отличается от серверной, разобраны в обзоре трека.
Пять величин, которых хватает на девяносто процентов задач
| Величина | Что это физически | Аналогия с водой | Где встретите | Типичный порядок |
|---|---|---|---|---|
Напряжение U, вольты |
разность потенциалов, «давление» | напор в трубе | уровни логики, питание, пороги | 1,8 / 3,3 / 5 / 12 В |
Ток I, амперы |
поток заряда | расход воды | нагрузка на вывод, бюджет батареи | мкА во сне, мА в работе |
Сопротивление R, омы |
насколько трудно течь | узость трубы | резисторы, подтяжки, шунты | 100 Ом … 100 кОм |
Ёмкость C, фарады |
накопленный заряд | бак-накопитель | развязка, фильтры, задержки фронтов | пФ на линиях, мкФ по питанию |
Мощность P, ватты |
скорость превращения энергии в тепло | работа потока | нагрев регуляторов и ключей | мВт … единицы Вт |
Связывают их два выражения, которых достаточно для повседневных расчётов: U = I · R (закон Ома) и P = U · I. Из них выводится всё остальное — например, что регулятор, роняющий 1,7 В при токе 0,5 А, рассеивает 0,85 Вт и станет горячим настолько, что до него нельзя дотронуться.
Два расчёта, которые встраиваемый разработчик делает каждую неделю, — токоограничивающий резистор и подтяжки шины:
"""Два ежедневных расчёта: резистор для светодиода и подтяжки I2C.
Сложность обоих O(1) — но именно они определяют, будет ли схема работать."""
VDD = 3.3 # напряжение питания логики, вольты
def led_resistor(v_forward: float, current_ma: float) -> tuple[float, float]:
"""Светодиод — не резистор: у него почти фиксированное падение напряжения.
Лишнее напряжение обязан гасить резистор, иначе ток ограничен только
сопротивлением дорожек, и светодиод (а за ним вывод чипа) сгорает."""
r = (VDD - v_forward) / (current_ma / 1000.0)
p_mw = (VDD - v_forward) * (current_ma / 1000.0) * 1000.0
return r, p_mw
for name, vf in (("красный", 1.9), ("зелёный", 2.1), ("синий", 2.9)):
r, p = led_resistor(vf, 5.0)
print(f"{name:8s}: R = {r:6.0f} Ом при 5 мА, рассеиваемая мощность {p:.1f} мВт")
# красный : R = 280 Ом при 5 мА, рассеиваемая мощность 7.0 мВт
# зелёный : R = 240 Ом при 5 мА, рассеиваемая мощность 6.0 мВт
# синий : R = 80 Ом при 5 мА, рассеиваемая мощность 2.0 мВт
def i2c_pullups(c_bus_pf: float, t_rise_ns: float,
i_sink_ma: float = 3.0, v_ol: float = 0.4) -> tuple[float, float]:
"""Подтяжка образует с ёмкостью шины RC-цепочку: чем больше резистор,
тем медленнее фронт. Снизу резистор ограничен током, который ведомый
способен утопить в землю, не поднимая уровень выше V_OL."""
r_max = t_rise_ns * 1e-9 / (0.8473 * c_bus_pf * 1e-12) # 0,8473 = ln(0,7/0,3)
r_min = (VDD - v_ol) / (i_sink_ma / 1000.0)
return r_min, r_max
for mode, c_pf, t_ns in (("100 кГц, короткая шина", 100, 1000),
("400 кГц, короткая шина", 100, 300),
("400 кГц, длинные провода", 400, 300)):
lo, hi = i2c_pullups(c_pf, t_ns)
verdict = f"берите {2200 if hi > 2200 else int(hi)} Ом" if hi > lo else "физически невозможно"
print(f"{mode:26s}: R от {lo:.0f} до {hi:.0f} Ом -> {verdict}")
# 100 кГц, короткая шина : R от 967 до 11802 Ом -> берите 2200 Ом
# 400 кГц, короткая шина : R от 967 до 3541 Ом -> берите 2200 Ом
# 400 кГц, длинные провода : R от 967 до 885 Ом -> физически невозможно
Третья строка — типичная реальная история: шина I2C на макетке с полуметровыми проводами набирает 400 пФ ёмкости, окно допустимых подтяжек схлопывается, и никакой резистор не спасает. Симптом — «I2C работает на 100 кГц и не работает на 400 кГц»; лечение — короткие провода, а не перебор номиналов. Подробнее про сами шины — в статье про протоколы.
Цифровой сигнал — это напряжение с допуском
В коде бит либо ноль, либо единица. На плате есть четыре порога и запрещённая зона между ними:
VIL— максимальное напряжение, которое приёмник гарантированно считает нулём;VIH— минимальное напряжение, которое гарантированно считается единицей;VOL— максимальное напряжение, которое выдаёт передатчик в состоянии нуля;VOH— минимальное напряжение, которое выдаёт передатчик в состоянии единицы.
Для CMOS-логики при питании 3,3 В пороги обычно VIL ≈ 0,3 · VDD = 1,0 В и VIH ≈ 0,7 · VDD = 2,3 В. Между ними — зона, в которой поведение не определено: приёмник может прочитать что угодно, а входной каскад в это время потребляет сквозной ток и греется. Отсюда два практических вывода.
Плавающий вход — это не «ноль по умолчанию», а антенна. Неподключённый вывод в режиме входа набирает наводку от сети и соседних дорожек и «читается» случайно. Лечится подтяжкой — внутренней (десятки килоом, включается одним битом регистра) или внешней. Медленный фронт даёт пачку срабатываний. Пока напряжение ползёт через запрещённую зону, вход может переключиться несколько раз. Поэтому на входах стоит триггер Шмитта: два порога вместо одного и гистерезис между ними. Дребезг механической кнопки — это та же история, только на масштабе миллисекунд: контакт реально замыкается и размыкается десятки раз, и никакой триггер Шмитта тут не поможет — нужен программный или RC-фильтр.
Вывод микроконтроллера изнутри
Строка GPIOA->ODR |= (1 << 5); выглядит как запись в память, а на деле переключает ключ, соединяющий кусок металла с шиной питания. Что стоит между регистром и проводом:
Разберём три решения, которые вы принимаете при настройке каждого вывода.
Push-pull или open-drain. Push-pull активно тянет линию в обе стороны: быстрые фронты, никаких внешних деталей. Но два таких выхода нельзя соединять — если один выдаёт единицу, а другой ноль, между питанием и землёй открывается прямой путь, и ток ограничен только сопротивлением ключей. Open-drain умеет только притягивать линию к нулю, а единицу создаёт внешняя подтяжка. Именно поэтому open-drain — основа шин с несколькими участниками: I2C, общий сигнал прерывания от нескольких датчиков, 1-Wire. Цена — медленные фронты, потому что подтяжка заряжает ёмкость шины через резистор.
Подтяжка вверх, вниз или её отсутствие. Внутренние подтяжки в микроконтроллерах слабые: обычно 30–50 кОм. Для кнопки этого достаточно, для I2C — нет: с 40 кОм и 100 пФ фронт займёт около 3,4 мкс, что убивает даже 100 кГц.
Скорость фронта. У STM32 есть регистр OSPEEDR, и соблазн выставить максимум для всех выводов велик. Не делайте так: крутой фронт на длинной дорожке — это излучение помех и звон, который приёмник считает лишними тактами. Выбирайте минимальную скорость, при которой сигнал успевает.
И главное ограничение, о котором забывают чаще всего. Вывод — источник конечного тока. Даташит STM32F4 разрешает 25 мА на вывод и около 150 мА суммарно на корпус; ATmega328P — 40 мА абсолютного максимума на вывод при рекомендованных 20 мА; GPIO Raspberry Pi — 16 мА на вывод и 50 мА на всю гребёнку. Двигатель с током остановки 500 мА, подключённый напрямую, не «не заработает» — он выведет вывод из строя или уронит питание всей платы. Между выводом и любой нагрузкой мощнее светодиода должен стоять транзистор или драйвер, а на индуктивной нагрузке (мотор, реле, соленоид) — обратный диод, гасящий выброс при отключении. Подробный разбор — в статье про датчики и приводы, а регистры и режимы — в статье про периферию.
Анатомия платы разработчика
Плата разработчика — это микроконтроллер плюс всё, что нужно, чтобы его включить и отладить. Понимание её состава напрямую переносится на собственную плату: там придётся сделать то же самое руками.
Ветка «ловушки» — не шутка, а список того, на чём теряют вечера. Классика: вывод, который вы настраиваете, на этой плате уже занят светодиодом или кварцем; перемычка IDD разомкнута, и чип питается через паразитный путь; на гребёнке рядом стоят 5 В и 3,3 В, и провод сел на соседний контакт. Первое действие при работе с незнакомой платой — открыть её схему (производители публикуют их для всех отладочных плат) и посмотреть, что уже висит на нужных выводах.
Конкретные платы: что где стоит и когда брать
В обзоре трека сравнивались семейства чипов. Здесь — конкретные платы, которые можно купить и включить сегодня, с точки зрения железа: какая логика, откуда питание, есть ли отладчик, что удивит.
| Плата | Чип и ядро | Логика | Отладка на борту | АЦП | Цена | Чем удивит |
|---|---|---|---|---|---|---|
| Arduino Uno R3 | ATmega328P, AVR 8 бит, 16 МГц | 5 В | нет, только загрузчик | 10 бит, 6 каналов | 8–25 USD | 5-вольтовые выводы убивают 3,3-вольтовые датчики; 2 КБ ОЗУ кончаются мгновенно |
| Arduino Uno R4 Minima | RA4M1, Cortex-M4, 48 МГц | 5 В снаружи | SWD выведен | 14 бит + ЦАП | около 20 USD | внутри 3,3 В, наружу 5 В через трансляторы — читайте схему перед подключением |
| STM32 Nucleo-64 (G474RE) | Cortex-M4F, 170 МГц | 3,3 В, часть выводов терпит 5 В | ST-LINK на борту, отделяемый | 12 бит, до 4 Msps | 15–20 USD | лучший старт: отладчик, документация, две гребёнки, питание от USB или внешнее |
| Black Pill (STM32F411) | Cortex-M4F, 100 МГц | 3,3 В | нет, нужен внешний ST-Link | 12 бит | 5–8 USD | встречаются перемаркированные чипы; у старой Blue Pill неверный резистор подтяжки USB |
| ESP32-DevKitC / S3 / C3 | Xtensa или RISC-V, 160–240 МГц | 3,3 В, 5 В не терпит | USB-JTAG на S3 и C3 | 12 бит, нелинейный | 4–10 USD | пики тока до 500 мА при передаче; плохой кабель роняет плату в brownout |
| Raspberry Pi Pico 2 | RP2350, 2 x Cortex-M33, 150 МГц | 3,3 В | нет, но picoprobe из второй Pico | 12 бит, 4 канала | 5–7 USD | встроенный buck-boost: питание от 1,8 до 5,5 В; блоки PIO для своих протоколов |
| Raspberry Pi Zero 2W / 5 | Cortex-A, Linux | 3,3 В на гребёнке | нет | АЦП нет вообще | 15–80 USD | нужен корректный shutdown, иначе рушится файловая система на карте |
Разберём то, что не влезает в таблицу.
STM32 — эталон для обучения и продакшна. Причина не в частоте, а в экосистеме: полное описание всех регистров в reference manual, errata на каждую ревизию кремния, конфигуратор выводов STM32CubeMX и ассортимент от 32-контактных чипов за 1 USD до H7 с двумя ядрами. Практически важная деталь — выводы с пометкой FT (five-volt tolerant): они выдерживают 5 В на входе, но только когда чип запитан, и только в цифровом режиме. Nucleo хорош ещё и тем, что ST-LINK на нём отделяется по линии разлома и превращается в отладчик для ваших собственных плат.
ESP32 — радиомодуль, к которому прилагается процессор. Wi-Fi и BLE на кристалле за несколько долларов не имеют конкурентов, но железные оговорки серьёзные. Передача радио даёт импульсы тока до 500 мА: слабый USB-порт или тонкий кабель вызывают просадку, и встроенный детектор роняет чип в сброс — знаменитый Brownout detector was triggered, который новички лечат перепрошивкой, а надо толстым кабелем и конденсатором. Часть выводов — strapping pins (GPIO0, GPIO2, GPIO12, GPIO15 на классическом ESP32): их уровень в момент сброса выбирает режим загрузки, поэтому повесить на них подтянутый датчик значит однажды не загрузиться. У классического ESP32 второй АЦП физически недоступен, пока работает Wi-Fi, а первый заметно нелинеен и требует заводской калибровки. Выводы 6–11 заняты внешней флеш-памятью и наружу выведены только на некоторых модулях — трогать их нельзя.
Raspberry Pi Pico — самая дешёвая честная плата для учёбы. Блоки PIO — восемь маленьких программируемых конечных автоматов, генерирующих и принимающих сигналы без участия ядра: так делают WS2812, DVI и нестандартные интерфейсы. Отладчик получается из второй Pico за пять долларов (прошивка picoprobe), что закрывает главный барьер входа.
Raspberry Pi — это Linux-компьютер, а не микроконтроллер. Из этого следует всё остальное: у него нет ни одного канала АЦП (нужна внешняя микросхема по SPI), нет предсказуемых таймингов без патчей ядра, и он не переживает внезапного обесточивания без риска для карты. Зато на нём есть камера, сеть, Python и ROS 2 — поэтому в роботах он живёт этажом выше микроконтроллера, а не вместо него. Arduino стоит понимать правильно. Это не «плата для детей», а удачный форм-фактор и огромная библиотека готового кода, за которую платят потерей контроля: digitalWrite() работает в десятки раз медленнее прямой записи в регистр, потому что каждый раз пересчитывает таблицы. Для первого включения датчика — отлично, для управления приводом — нет.
Выбор платы под задачу сводится к нескольким вопросам:
Python-библиотеки, ROS?"} Q1 -->|Да| RPI["Raspberry Pi 5 или Zero 2W
плюс отдельный МК для реального времени"] Q1 -->|Нет| Q2{"Нужны Wi-Fi или BLE
прямо на плате?"} Q2 -->|Да| Q3{"Жёсткие тайминги
одновременно со связью?"} Q3 -->|Нет| ESP["ESP32-C3 или S3:
дёшево, связь из коробки"] Q3 -->|Да| SPLIT["Два чипа: ESP32 на связь,
STM32 на управление, шина между ними"] Q2 -->|Нет| Q4{"Жёсткое реальное время,
приводы, промышленная периферия?"} Q4 -->|Да| STM["STM32: Nucleo для отладки,
G0/G4 для серии"] Q4 -->|Нет| Q5{"Годы работы
от батарейки?"} Q5 -->|Да| NRF["nRF52 или STM32L:
микроамперы во сне, BLE"] Q5 -->|Нет| PICO["Pico на RP2350: учёба, дёшево,
блоки PIO для своих интерфейсов"]
Как читать даташит и не читать его целиком
Документация на чип — не один файл, а иерархия, и половина вопросов новичка вызвана тем, что он ищет ответ не там.
| Документ | О чём | Когда открывать |
|---|---|---|
| Datasheet | выводы, корпуса, электрические параметры, предельные режимы, тайминги | проектирование схемы, расчёт токов и уровней |
| Reference manual | все регистры всей периферии, сотни или тысячи страниц | написание драйвера, разбор непонятного поведения |
| Programming manual | система команд ядра, регистры Cortex-M | ассемблер, разбор HardFault |
| Errata sheet | ошибки конкретной ревизии кремния и обходные пути | когда «по документации должно работать», а не работает |
| Application notes | готовые методики: расчёт кварца, разводка питания, антенны | перед первой своей платой |
| Board user manual и схема платы | что уже разведено на плате разработчика | всегда перед подключением чего-либо |
Внутри даташита ключевые разделы стоит различать жёстко.
Absolute maximum ratings — не режим работы, а граница выживания. Формулировка «stresses beyond those listed may cause permanent damage» означает буквально: превысили — чип может умереть, причём не сразу, а через месяц в поле. Здесь же прячется важный параметр I_INJ — допустимый ток, втекающий в вывод через защитные диоды: у STM32 это ±5 мА на вывод и ±25 мА суммарно.
Recommended operating conditions — диапазон, в котором производитель гарантирует параметры. Между ним и предельными режимами лежит серая зона: работает, но гарантий нет.
Electrical characteristics — таблица, из которой берутся реальные числа. Пример строки: V_OH ≥ VDD − 0,4 В при I_IO = 8 мА. Читается так: при токе 8 мА выход просядет не более чем на 0,4 В, то есть выдаст минимум 2,9 В при питании 3,3 В. Отсюда сразу следует, что светодиод «на 20 мА напрямую с вывода» не получит своих 20 мА — напряжение просядет, и реальный ток окажется меньше расчётного, зато нагрев кристалла окажется больше.
Важная тонкость в колонках: min, typ, max. Проектировать надо по худшему случаю, а не по типичному. Если ток потребления датчика типично 300 мкА, а максимально 1,2 мА, батарейный бюджет считают по 1,2 мА — иначе устройство проживёт вчетверо меньше обещанного.
Errata читать обязательно. Это не формальность: у STM32F1 в списке ошибок несколько дефектов контроллера I2C, требующих обходных путей в драйвере; у ранних ревизий ESP32 есть особенности, зашитые в ECO. Правило простое: если поведение противоречит документации, следующий файл, который вы открываете, — errata на вашу ревизию.
Полезная привычка для чтения: начинайте не с начала, а с pinout и electrical characteristics, а reference manual открывайте по оглавлению нужной периферии. Целиком эти документы не читает никто.
Как читать схему
Схема (schematic) — это граф соединений, а не картинка расположения деталей. Компоненты обозначаются позиционно: R — резистор, C — конденсатор, U — микросхема, Q — транзистор, D — диод, L — катушка или бусина, Y — кварц, J — разъём. Соединения бывают явными (линия) и по имени цепи (net label): два вывода, помеченные SCL, соединены, даже если линия между ними не нарисована — так рисуют, чтобы не превращать схему в клубок. Точка на пересечении означает соединение, её отсутствие — что провода просто пересекаются.
Символы «земля» и «питание» — тоже метки цепей: любые два символа GND на листе означают одну и ту же цепь. Именно поэтому на схемах не рисуют обратные провода — они все ушли в общий полигон.
Вот минимум, без которого микроконтроллер не запустится:
Пройдём по узлам.
Питание и развязка. У чипа обычно несколько выводов VDD и VSS, и это не избыточность: каждый питает свою часть кристалла. У каждого — свой конденсатор 100 нФ, физически рядом. Плюс один объёмный конденсатор 4,7–10 мкФ на плату.
Аналоговое питание VDDA. Его отделяют от цифрового ферритовой бусиной или резистором в несколько ом и фильтруют парой конденсаторов. Причина проста: цифровая часть создаёт на шине питания импульсные помехи, а АЦП измеряет напряжение относительно этой самой шины. Без фильтра младшие два-три бита результата — шум.
Сброс NRST. Подтяжка обычно уже внутри чипа, а конденсатор 100 нФ на землю гасит наводки и задаёт время выхода из сброса. Внешнюю подтяжку добавлять не нужно — она мешает отладчику дёргать линию. Вывод выбора загрузки BOOT0. Определяет, откуда стартует чип: из флеш-памяти или из зашитого в ПЗУ загрузчика. Оставленный в воздухе, он однажды прочитается как единица, и плата «перестанет прошиваться». Резистор 10 кОм на землю стоит десять центов.
Отладочный разъём. SWDIO, SWCLK, земля и желательно NRST с питанием для определения напряжения цели. Выводите его всегда, включая серийные платы: без него единственный способ понять, что происходит внутри, — разбирать изделие. Кварц. Нужен не всегда: внутренний RC-генератор даёт точность около 1%, чего хватает для UART на умеренных скоростях и не хватает для USB (требуется 0,25%), CAN и часов реального времени. Если ставите — конденсаторы подбираются по нагрузочной ёмкости кварца, методика описана в ST AN2867.
Лучший учебник по схемотехнике конкретного чипа — схема его же платы разработчика: ST, Espressif и Raspberry Pi публикуют их вместе с гербер-файлами. Открыть схему Nucleo и сравнить с даташитом полезнее, чем прочитать главу учебника.
Развязывающие конденсаторы: почему десять центов важнее недели отладки
Это тот узел, который программисты пропускают и потом неделю ищут «плавающий баг». Интуиция такая. Когда внутри чипа одновременно переключаются тысячи транзисторов, он выхватывает из шины питания импульс тока за единицы наносекунд. Дорожка от регулятора — это не идеальный провод, а индуктивность порядка одного наногенри на миллиметр, и на индуктивности напряжение равно L · dI/dt. Считаем:
"""Почему конденсатор должен стоять у вывода чипа, а не у регулятора."""
L_TRACE_NH = 50.0 # дорожка длиной 5 см до регулятора, примерно 1 нГн на мм
DI_A = 0.05 # импульс тока 50 мА при переключении шины
DT_S = 5e-9 # фронт 5 нс
drop_without = L_TRACE_NH * 1e-9 * DI_A / DT_S
print(f"просадка питания без локального конденсатора: {drop_without * 1000:.0f} мВ")
C_LOCAL_F = 100e-9 # 100 нФ прямо у вывода VDD
drop_with = DI_A * DT_S / C_LOCAL_F
print(f"просадка при локальном конденсаторе 100 нФ: {drop_with * 1000:.1f} мВ")
# просадка питания без локального конденсатора: 500 мВ
# просадка при локальном конденсаторе 100 нФ: 2.5 мВ
Полвольта просадки на шине 3,3 В — это уже территория, где логика читает мусор, АЦП врёт, а детектор пониженного напряжения перезагружает чип. Конденсатор рядом с выводом работает локальным резервуаром заряда: он отдаёт импульс мгновенно, потому что до него нет длинной индуктивной дорожки.
Отсюда правила, которые не обсуждаются:
- 100 нФ на каждый вывод питания, керамика X7R, корпус 0402 или 0603, расстояние до вывода — миллиметры;
- один объёмный конденсатор 4,7–10 мкФ на плату или на узел, он покрывает медленные броски;
- путь тока — это петля: конденсатор должен возвращать ток в землю по кратчайшему пути, поэтому переходное отверстие на полигон ставят вплотную к площадке;
- чем короче связь, тем лучше — конденсатор на другом краю платы почти бесполезен на наносекундных фронтах.
Симптомы отсутствующей или неудачной развязки узнаваемы: устройство сбрасывается при включении мотора или радио, АЦП шумит на пару младших бит, плата стабильно работает на столе и падает в корпусе, ошибки появляются при подключении осциллографа и исчезают при его отключении.
Питание: дерево от источника до чипа
Питание проектируется как дерево: источник, преобразователи, ветви потребителей. Каждая ветвь имеет напряжение, максимальный ток и требования к шуму.
USB 5 В, Li-Ion 3,0–4,2 В,
CR2032, 12/24 В"] --> PROT["Защита
переполюсовка, предохранитель,
супрессор от выбросов"] PROT --> DEC{"Какой ток
и какая разница
напряжений?"} DEC -->|"до 150 мА,
разница мала"| LDO["Линейный регулятор LDO
простой, тихий,
греется пропорционально разнице"] DEC -->|"сотни мА и больше"| BUCK["Импульсный DC-DC
КПД 85–95%,
шумит, нужна катушка и разводка"] DEC -->|"батарея ниже
напряжения логики"| BOOST["Повышающий или buck-boost
работает от разряженной ячейки"] LDO --> RAIL["Шина 3,3 В"] BUCK --> RAIL BOOST --> RAIL RAIL --> MCU["Микроконтроллер
единицы–десятки мА"] RAIL --> SENS["Датчики
мкА–мА, часто через ключ"] RAIL --> RADIO["Радио
импульсы в сотни мА"] MOT["Силовая часть: моторы, реле
отдельная ветвь от источника"] -.->|"общая только земля"| PROT
Ключевой выбор — линейный регулятор или импульсный. Разница не в удобстве, а в физике: LDO гасит лишнее напряжение, превращая его в тепло.
"""LDO против импульсного преобразователя: куда уходит разница напряжений."""
def ldo(v_in: float, v_out: float, i_out: float, theta_ja: float) -> tuple[float, float, float]:
"""theta_ja — тепловое сопротивление корпуса, °C на ватт.
SOT-23 без полигона: около 250; SOT-223 на меди: около 60."""
p_diss = (v_in - v_out) * i_out
return p_diss, v_out / v_in * 100.0, p_diss * theta_ja
def buck(v_in: float, v_out: float, i_out: float, eff: float = 0.9) -> tuple[float, float]:
p_out = v_out * i_out
return p_out / eff - p_out, eff * 100.0
for current in (0.05, 0.2, 0.5):
p, e, dt = ldo(5.0, 3.3, current, theta_ja=250.0)
pb, eb = buck(5.0, 3.3, current)
print(f"{current * 1000:4.0f} мА: LDO рассеивает {p:.2f} Вт, КПД {e:.0f}%, "
f"перегрев корпуса {dt:5.0f} °C | DC-DC рассеивает {pb:.2f} Вт, КПД {eb:.0f}%")
# 50 мА: LDO рассеивает 0.09 Вт, КПД 66%, перегрев корпуса 21 °C | DC-DC рассеивает 0.02 Вт, КПД 90%
# 200 мА: LDO рассеивает 0.34 Вт, КПД 66%, перегрев корпуса 85 °C | DC-DC рассеивает 0.07 Вт, КПД 90%
# 500 мА: LDO рассеивает 0.85 Вт, КПД 66%, перегрев корпуса 213 °C | DC-DC рассеивает 0.18 Вт, КПД 90%
Перегрев в 213 °C означает не «горячо», а «сработала тепловая защита или сгорело». Практическое правило: до 150–200 мА и при небольшой разнице напряжений — LDO, дальше импульсный. У LDO есть два параметра, о которых забывают: dropout — минимальная разница входа и выхода, при которой он ещё держит напряжение (у обычного 1,5–2 В, у настоящего low-dropout 0,2 В), и Iq — собственное потребление, критичное для батарейных устройств: регулятор с 50 мкА покоя съест батарейку быстрее, чем спящий микроконтроллер.
Что ещё стоит знать про источники:
- USB 5 В — не бесконечный источник: порт без согласования обязан отдавать 100 мА, обычно отдаёт 500 мА, но кабель с тонкими жилами роняет напряжение на несколько десятых вольта при полутора амперах. Больше половины «странных» проблем ESP32 и Raspberry Pi — это кабель.
- Li-Ion даёт 4,2 В заряженной и 3,0 В разряженной — то есть пересекает 3,3 В. Отсюда либо buck-boost, либо питание логики от 3,0 В, либо жизнь с преждевременным отключением.
- CR2032 — источник с высоким внутренним сопротивлением (десятки ом, растущее с разрядом). Импульс 20 мА проседает на полвольта; поэтому рядом с радиомодулем на таблетке всегда стоит конденсатор в десятки микрофарад.
Земля — не «минус», а обратный проводник. Ток всегда течёт по петле, и обратный ток на высоких частотах идёт не кратчайшим путём, а под своей дорожкой — там, где петля минимальна. Отсюда два правила: сплошной земляной полигон вместо тонкой дорожки и «общая земля обязательна» — два устройства, соединённые только сигнальным проводом без земли, не будут работать никогда, потому что у сигнала нет точки отсчёта.
Первое включение: последовательность старта и bring-up
Важно понимать, что происходит между подачей питания и первой инструкцией в main() — тогда «плата не стартует» перестаёт быть загадкой.
и на выводах ничего не происходит Core->>Peri: настройка выводов, запуск внешнего кварца Peri-->>Core: флаг готовности генератора или таймаут Core->>Core: переключение системной частоты, вход в main Peri->>Core: первое прерывание от внешнего мира
Тонкость, которая ловит новичков: если внешний кварц не запустился, а код ждёт его флага в бесконечном цикле, плата выглядит «мёртвой», хотя чип жив и отладчик к нему подключается. Поэтому в стартовом коде всегда должен быть таймаут с откатом на внутренний генератор. Процедура ввода новой платы в строй — отдельный навык, и порядок в нём не случайный:
Смысл порядка в том, что каждый шаг опирается на проверенный предыдущий. Подключать мотор к плате, на которой не измерено питание, — это ставить эксперимент с несколькими неизвестными сразу.
Две вещи стоит уметь делать в коде с самого начала. Первая — узнавать причину последнего сброса: она мгновенно отличает просадку питания от срабатывания сторожевого таймера.
#include <stdint.h>
#include "stm32g0xx.h"
/* Причина последнего сброса — первое, что должна сообщать плата на стенде.
Флаги живут в RCC->CSR, переживают сам сброс и не чистятся автоматически. */
typedef enum {
RST_POWER, /* первое включение или просадка питания */
RST_PIN, /* кнопка или отладчик */
RST_SOFT, /* программный сброс */
RST_IWDG, RST_WWDG, /* сторожевые таймеры: код завис или сбился ритм */
RST_LOWPOWER, /* некорректный выход из режима сна */
RST_UNKNOWN
} reset_cause_t;
reset_cause_t reset_cause(void)
{
const uint32_t csr = RCC->CSR;
RCC->CSR |= RCC_CSR_RMVF; /* снимаем флаги, иначе они накопятся за всю жизнь платы */
/* Порядок важен: флаг внешнего сброса выставляется вместе с другими,
поэтому проверяется последним. */
if (csr & RCC_CSR_LPWRRSTF) return RST_LOWPOWER;
if (csr & RCC_CSR_WWDGRSTF) return RST_WWDG;
if (csr & RCC_CSR_IWDGRSTF) return RST_IWDG;
if (csr & RCC_CSR_SFTRSTF) return RST_SOFT;
if (csr & RCC_CSR_PWRRSTF) return RST_POWER;
if (csr & RCC_CSR_PINRSTF) return RST_PIN;
return RST_UNKNOWN;
}
Вторая — измерять собственное напряжение питания. Внутри чипа есть опорный источник с заводской калибровкой, и это даёт вольтметр без единого внешнего компонента:
/* STM32G0: код VREFINT, снятый на заводе при питании 3,0 В и температуре 30 °C.
Адрес и опорное напряжение отличаются между семействами — сверяйтесь с даташитом. */
#define VREFINT_CAL_ADDR ((volatile uint16_t *)0x1FFF75AAU)
#define VREF_CAL_MV 3000U
/* Опорный источник даёт фиксированное напряжение, а АЦП измеряет его
ОТНОСИТЕЛЬНО питания: чем ниже питание, тем больше код. Отсюда пропорция. */
static inline uint32_t vdda_mv(uint16_t vrefint_raw)
{
if (vrefint_raw == 0u) return 0u; /* канал не измерен: не делим на ноль */
return ((uint32_t)VREF_CAL_MV * (uint32_t)(*VREFINT_CAL_ADDR)) / vrefint_raw;
}
Практическая ценность этих двадцати строк высока: устройство в поле само сообщает, что питание просело до 2,9 В и что перезагрузил его сторожевой таймер, — вместо гадания по симптомам. Инструменты для отладки того, что происходит на выводах, — осциллограф, логический анализатор и SWD — подробно разобраны в статье про отладку и производство.
Как не спалить первую плату
Список составлен по частоте, а не по драматичности:
- 5 В на выводе 3,3-вольтового чипа. Ток через защитный диод уходит в шину питания: в лучшем случае плата ведёт себя странно, в худшем чип умирает. ESP32, RP2040 и Raspberry Pi не терпят 5 В нигде; у STM32 терпят только выводы с пометкой FT и только при поданном питании.
- Отсутствие общей земли. Два устройства, соединённые сигнальным проводом без земли, не имеют общей точки отсчёта. Симптом — «данные приходят, но мусорные».
- Обратная полярность питания. Секунда невнимательности убивает всё на плате. На своих платах ставьте P-канальный MOSFET в цепь питания — он стоит дешевле одного сгоревшего чипа.
- Нагрузка напрямую с вывода. Мотор, реле, лента светодиодов, длинный сервопривод. Между ними и чипом всегда ключ или драйвер.
- Индуктивная нагрузка без обратного диода. При отключении катушки напряжение самоиндукции достигает десятков вольт. Диод параллельно катушке обязателен.
- Питание с двух источников одновременно. USB плюс внешний блок при неснятой перемычке — регуляторы начинают «спорить», ток течёт в обратную сторону. Читайте раздел о перемычках в user manual платы.
- Подача напряжения на выводы обесточенного чипа. Датчик запитан, микроконтроллер нет: ток через защитные диоды частично «оживляет» чип, и он ведёт себя необъяснимо. Правило — питание подавать вместе или сначала на контроллер.
- Замыкание щупом и горячее подключение. Тонкий щуп легко перекидывает соседние выводы, а разъём, воткнутый в запитанную плату, даёт выброс на неправильном контакте. Измеряйте аккуратно, разъёмы соединяйте на обесточенной схеме.
- Статика. Сухой воздух, синтетика, разряд в несколько киловольт — и вывод повреждён неявно: работает, но параметры уплыли. Достаточно коснуться заземлённого корпуса перед работой и не хранить платы в пакете без антистатики.
Отдельная рекомендация, экономящая деньги и время: лабораторный блок питания с ограничением тока. Выставленный лимит в 100 мА превращает короткое замыкание из похорон платы в надпись на дисплее. Это лучшая покупка после отладчика.
Стенд: что покупать и в каком порядке
| Уровень | Что | Цена | Что становится возможным |
|---|---|---|---|
| 0 | плата (Nucleo или Pico), мультиметр, макетка, провода, набор резисторов и конденсаторов, светодиоды | 40–70 USD | мигание, кнопки, простые датчики, измерение напряжений |
| 1 | отладчик (ST-Link, CMSIS-DAP или picoprobe), логический анализатор на 8 каналов, паяльник с регулировкой | 60–120 USD | точки останова, просмотр памяти, декодирование I2C, SPI, UART |
| 1 | лабораторный блок питания с ограничением тока | 50–90 USD | безопасное первое включение, измерение тока потребления |
| 2 | осциллограф от 100 МГц | 250–450 USD | форма фронтов, звон, пульсации питания, дребезг, реальные тайминги |
| 2 | измеритель тока с записью во времени, паяльная станция с феном | 100–300 USD | отладка энергопотребления, монтаж и демонтаж мелких корпусов |
Порядок покупок принципиален. Отладчик важнее осциллографа: без него вы отлаживаете вслепую, а типичные ошибки начинающих — программные. Логический анализатор важнее осциллографа: 90% проблем с шинами — это неправильный адрес, неверный режим SPI или несоблюдённая пауза, и всё это видно на декодированной записи. Осциллограф нужен, когда вопрос перешёл в физику: фронты, помехи, просадки. Мультиметр стоит уметь применять в трёх режимах: напряжение (параллельно, между точкой и землёй), ток (в разрыв цепи — самая частая ошибка новичка в том, что щупы в токовом режиме ставят параллельно и устраивают короткое замыкание) и прозвонка (только на обесточенной схеме).
От макетки к своей плате
Беспаечная макетка удобна и врёт. Её ограничения стоит знать заранее: контактное сопротивление в десятые доли ома и нестабильное, паразитная ёмкость между соседними линиями порядка 2–5 пФ, заметная индуктивность, ток до примерно одного ампера. Следствия: сигналы выше 10–20 МГц на макетке не живут, шины I2C и SPI капризничают, а питание требует конденсатора прямо у чипа. Ещё одна классическая ловушка — шины питания вдоль краёв многих макеток разорваны посередине, и половина схемы просто не запитана. Следующие ступени: перфорированная плата с пайкой (надёжнее, но не для высоких частот), затем своя печатная плата. Порог входа сейчас низкий: KiCad бесплатен и профессионален, а изготовление пяти плат размером 100 x 100 мм у китайских производителей стоит порядка 5–20 USD плюс доставка. Для первой платы разумно взять готовый модуль (ESP32-WROOM, модуль на STM32) вместо россыпи компонентов: модуль уже сертифицирован, имеет разведённую антенну и снимает половину рисков.
Типичные ошибки, которые ловятся на входе
- Считать, что вывод «выдаёт единицу». Он выдаёт напряжение при некотором токе, и оба числа есть в даташите; висящий вход при этом читается случайно.
- Ставить максимальную скорость фронта на все выводы. Помехи и звон вместо стабильной работы.
- Пропускать конденсаторы развязки и экономить на земле. Тонкая дорожка вместо полигона и отсутствие локальной ёмкости дают шум, который потом неделями ищут в коде.
- Проектировать по типичным значениям и игнорировать errata. Считают по колонке max, а при расхождении с документацией открывают список ошибок кремния.
- Не смотреть схему платы разработчика. Половина «странностей» объясняется тем, что вывод уже занят.
- Отлаживать сразу всю систему. Плата вводится в строй по слоям: питание, ядро, периферия, нагрузка.
Мини-итог
Железо перестаёт быть магией, когда сводится к нескольким проверяемым числам. Вывод микроконтроллера — это не бит, а схема: у неё есть режим (push-pull, open-drain, вход, аналог), предельный ток порядка 20 мА и пороги, ниже и выше которых уровень не определён. Плата разработчика — это чип плюс питание, отладка и удобства, и её схема отвечает на большинство вопросов быстрее, чем форум. Питание — это дерево с бюджетом тока: линейный регулятор превращает разницу напряжений в тепло, импульсный — в шум, а конденсатор 100 нФ у вывода питания стоит десять центов и предотвращает неделю отладки. Документация — это иерархия: даташит для электрики, reference manual для регистров, errata для случаев, когда всё «должно работать».
План на ближайшие дни: возьмите плату с отладчиком на борту, откройте её схему рядом с даташитом чипа и найдите на схеме каждый узел из раздела о минимальной обвязке. Затем измерьте мультиметром напряжение на выводе, который вы переключаете из кода, посчитайте ток через светодиод по закону Ома и сравните с измеренным. Расхождение и будет вашим первым уроком в электрической реальности. Продолжение темы на портале — раздел НИР, где роботизированные системы разбираются на уровне архитектуры целиком: определение и классификация роботизированных систем и теоретическое обоснование архитектуры распределённых роботизированных систем. Полезно прочитать после статей про протоколы и робототехнику, когда станет понятно, из каких контроллеров такие системы собираются. Про то, как процессор выполняет ваш код на уровне тактов, есть отдельная статья в треке информатики — Как работает процессор, а про драйверы и ввод-вывод со стороны большой ОС — в треке Операционные системы.
Источники
- Paul Horowitz, Winfield Hill. The Art of Electronics, 3-е издание — https://artofelectronics.net/
- Henry W. Ott. Electromagnetic Compatibility Engineering, заметки автора — https://www.hottconsultants.com/
- Rick Hartley. How to Achieve Proper Grounding, доклад — https://www.youtube.com/watch?v=ySuUZEjARPY
- NXP. I2C-bus specification and user manual, UM10204 — https://www.nxp.com/docs/en/user-guide/UM10204.pdf
- STMicroelectronics. Документация на STM32, даташиты, reference manuals и errata — https://www.st.com/en/microcontrollers-microprocessors/stm32-32-bit-arm-cortex-mcus/documentation.html
- STMicroelectronics. Nucleo-64: схема, user manual UM1724 — https://www.st.com/en/evaluation-tools/nucleo-g474re.html
- Espressif. ESP32 Hardware Design Guidelines — https://docs.espressif.com/projects/esp-hardware-design-guidelines/en/latest/esp32/
- Raspberry Pi. Hardware design with RP2040 — https://datasheets.raspberrypi.com/rp2040/hardware-design-with-rp2040.pdf
- Arduino. Документация на Uno R4 Minima — https://docs.arduino.cc/hardware/uno-r4-minima
- Texas Instruments. Basic Calculation of a Buck Converter’s Power Stage, SLVA477 — https://www.ti.com/lit/an/slva477b/slva477b.pdf
Что дальше
C для встраиваемых систем: память, указатели, volatile, без динамики — от физики выводов переходим к языку, на котором ими управляют: как раскладывается память в чипе, почему регистр периферии обязан быть volatile, что происходит со стеком при его переполнении и как жить без malloc, не теряя выразительности кода.