Embedded HandbookПриложения
Все разделы › X. Проектирование, сети и IoT
STM32F103, STM32F407 · уровень базовый · 14 мин чтения
Открыть в интерактивном справочнике

79. Как рождается устройство: этапы разработки

Предыдущие части справочника — о том, как заставить микроконтроллер работать. Эта — о том, как из работающей платы получается изделие: что решают до первой строки кода, что проверяют перед заказом плат, что закладывают в прошивку ради производства и что происходит после продажи.

Здесь мало регистров и много решений. Но и у решений есть проверяемая часть: бюджет питания считается, требование превращается в тест, а воспроизводимость сборки проверяется компилятором — всё это в разделе пересчитано и собрано.

Как это называется в документации

Оригинал Перевод
requirements, specification требования, техническое задание
functional / non-functional requirement функциональное / нефункциональное требование
stakeholder заинтересованная сторона
power budget бюджет питания
bill of materials (BOM) перечень элементов
schematic, layout схема, разводка платы
ERC, DRC проверка схемы, проверка правил разводки
EVT, DVT, PVT инженерные, конструкторские, производственные испытания
verification / validation верификация / валидация
unit test, hardware-in-the-loop (HIL) модульный тест, испытания на стенде с устройством
reproducible build воспроизводимая сборка
traceability прослеживаемость
design for manufacturing / testing (DFM / DFT) технологичность, контролепригодность
test fixture испытательная оснастка
end of life (EOL), NRND снятие с производства, «не для новых разработок»

79.1 Шесть этапов — и почему это петля

Этап На какой вопрос отвечает Что остаётся на руках
требования что должно делать устройство и в каких рамках техническое задание
проектирование из чего и как оно будет сделано структурная схема, выбор компонентов, схема, плата
программа как оно будет работать прошивка под контролем версий
испытания делает ли оно то, что записано протоколы испытаний
производство как делать его одинаково и много документация для завода, оснастка, тестовая прошивка
поддержка как жить с ним после продажи обновления, разбор отказов

Нарисовать это можно лестницей, но на деле этапы возвращаются друг к другу. Испытания находят ошибку в схеме — назад к проектированию. Прототип показывает, что батарейки не хватит, — назад к требованиям. Правило одно: чем позже найдена ошибка, тем дороже её исправить. Опечатку в требованиях исправляют правкой текста, ошибку в разводке — новой партией плат, ошибку в проданных устройствах — отзывом или обновлением по воздуху.

Шесть этапов разработки, возвраты между ними и растущая цена ошибки


79.2 Требования: только измеримые

Требование, которое нельзя проверить, — пожелание. Сравните:

Пожелание Требование
«кнопка реагирует быстро» «нажатие распознаётся не позже 50 мс, дребезг до 10 мс не даёт ложных нажатий»
«долго работает от батарейки» «год работы от CR2032 при измерении раз в минуту»
«работает на улице» «от −20 до +50 °C, пыле- и влагозащита по заданному классу»
«дешёвое» «перечень элементов не дороже заданной суммы при партии в N штук»

Функциональные требования говорят, что устройство делает: измеряет, передаёт, показывает. Нефункциональные — в каких рамках: питание, размеры, температура, цена, сертификация. Вторые чаще забывают — и именно они потом переворачивают проект.

У каждого требования в задании должен быть способ проверки. Тогда к этапу испытаний готов их перечень: одно требование — одна или несколько проверок. Этот справочник устроен так же: каждое число в тексте пересчитано проверочным скриптом.


79.3 Расчёт на салфетке: можно ли это сделать

Прежде чем выбирать компоненты, требования проверяют грубым расчётом. Пример — «год от батарейки». Исходные данные примера: устройство раз в минуту просыпается на 20 мс и в работе потребляет 10 мА; спит в режиме Stop при 20 мкА; батарейка — 220 мА·ч. Порядок токов режимов для F103 — в разделе 10.

Средний ток — взвешенная по времени сумма:

Iср = (10 мА × 20 мс + 0,020 мА × 59 980 мс) / 60 000 мс ≈ 23,3 мкА
220 мА·ч / 23,3 мкА ≈ 9430 ч ≈ 393 дня

Год набирается впритык, но главное видно из разложения: 86 % среднего тока — сон, а не работа. Ускорить программу вдвое почти бесполезно: при работе 10 мс вместо 20 выходит 423 дня — всего на 8 % дольше. А сон в режиме Standby при 3 мкА даёт 6,3 мкА в среднем и 1448 дней — почти четыре года. Цена — в Standby теряется содержимое ОЗУ, и после пробуждения программа начинается со сброса (раздел 10).

Все эти сроки — верхняя оценка. Паспортная ёмкость CR2032 указана при малом постоянном токе; импульсы по 10 мА сажают напряжение и уменьшают отдаваемую ёмкость, на холоде сильнее, да и саморазряд забирает около процента в год (раздел 53.6). Поэтому в расчёт закладывают запас хотя бы в полтора-два раза и ставят у батарейки конденсатор, который отдаёт импульсы тока. С запасом вдвое Stop даёт 196 дней — требование «год» без Standby не выполнено; Standby — 724 дня, почти два года.

Так расчёт на салфетке меняет требования к схеме и программе раньше, чем куплен первый компонент: нужен режим Standby, нужен датчик, который умеет спать, и нужна проверка тока сна на первом же прототипе.

Средний ток и срок службы в трёх случаях: доля работы и доля сна

⚠️ Проверка правдоподобия. Задача: источник 24 В, лампа и два резистора по 18 Ом, сопротивление лампы — половина резистора, ток 875 мА; найти внутреннее сопротивление источника. Если считать всё соединённым последовательно, выходит 27,43 − 45 = −17,6 Ом: отрицательного сопротивления не бывает — значит, модель неверна. Лампа последовательно с двумя параллельными резисторами даёт 9,4 Ом, все три параллельно — 22,9 Ом. Без схемы у задачи два ответа. Это и есть неоднозначное требование: условие без рисунка. Законы для таких расчётов — в разделах 49 и 50.


79.4 Проектирование: от блоков к плате

Сначала — структурная схема: какие блоки, как связаны, где питание, где интерфейсы. Потом выбор компонентов под блоки, и только потом принципиальная схема и плата.

Выбор микроконтроллера (раздел 2.5): периферия под задачу, память с запасом, корпус, который вы сможете запаять, наличие у поставщиков и статус жизненного цикла. Камень с пометкой NRND — «не для новых разработок» — в новый проект не ставят.

Прототип до своей платы. Отладочная плата (раздел 2.6) и модули на макетке позволяют писать программу и мерить ток, пока своя плата ещё не заказана. Раздел 80 — о таком прототипе.

Своя плата. Схема проверяется ERC, разводка — DRC. Для STM32 при этом не забывают минимальную обвязку и условия запуска (раздел 61), защиту выводов (раздел 62) и то, что понадобится на всех следующих этапах:

Всё это дёшево на схеме и очень дорого, когда плата уже заказана.


79.5 Программа: с первого дня как для серии

Логика — отдельно от железа. Код, который принимает решения, не должен сам лезть в регистры: его вызывают с данными, он возвращает результат. Тогда его можно проверить тестом на компьютере. Пример — требование из 79.2 про кнопку, превращённое в функцию:

#define PRESS_MS 20

typedef struct {
    uint8_t stable;     /* принятое состояние: 1 — нажата */
    uint8_t count;      /* сколько миллисекунд подряд уровень не равен принятому */
} button_t;

/* raw — уровень вывода (1 — нажата), вызов раз в 1 мс. Вернёт 1 ровно
 * один раз — в ту миллисекунду, когда нажатие принято. */
int button_update(button_t *b, int raw)
{
    if (raw == b->stable) {
        b->count = 0;
        return 0;
    }
    if (++b->count < PRESS_MS)
        return 0;
    b->stable = (uint8_t)raw;
    b->count = 0;
    return raw;         /* событие — только нажатие, отпускание молча */
}

На плате её вызывают из прерывания таймера раз в миллисекунду с уровнем вывода (природа дребезга — раздел 50.7). А на компьютере тест подаёт ей заранее заданные последовательности уровней:

Что подано Результат Требование
чистое нажатие одно событие на 20-й мс не позже 50 мс ✓
10 мс дребезга, потом удержание одно событие на 30-й мс от первого касания не позже 50 мс, без ложных ✓
нажатие 15 мс нет события дребезг — не нажатие ✓
удержание 1 с одно событие одно нажатие — одно событие ✓
два нажатия два события ✓
провал контакта на 5 мс посреди удержания одно событие не отпускание ✓

Один и тот же файл собран armclang для Cortex-M3 и M0 без предупреждений и gcc для теста; состояние кнопки — 2 байта ОЗУ. Тест остаётся в проекте: любая правка функции перепроверяется за секунду. Следующий уровень — испытания на стенде, где устройство получает настоящие сигналы от другой платы или прибора.

Версия — в прошивке, и без __DATE__. Устройство должно уметь сказать, какая в нём прошивка. Соблазн — вписать __DATE__ и __TIME__ (раздел 71). Но тогда две сборки одного исходника подряд дают разные файлы, и уже не доказать, из какого исходника собрана прошивка в конкретном устройстве. Проверено:

#ifndef FW_VERSION
#define FW_VERSION "0.0.0-dev"
#endif
const char version[] = FW_VERSION;

Номер задают ключом компилятора, и кавычки должны дойти до него: в оболочке ключ берут в одинарные — '-DFW_VERSION="1.4.2"', в Makefile пишут -DFW_VERSION=\"1.4.2\". Без этого оболочка кавычки съест, FW_VERSION раскроется в 1.4.2 без кавычек, и строка не соберётся. Сам номер удобно брать из системы контроля версий — командой git describe (раздел 109). С таким ключом сборки совпадают, а строка 1.4.2 лежит в объектном файле. Но строку, на которую никто не ссылается, компоновщик выбрасывает (раздел 77.5), и в готовой прошивке её не окажется. Поэтому версию либо используют — отдают по команде в порт, печатают при старте, — либо сохраняют явно: у armlink — ключом --keep=version, у GNU ld — строкой KEEP(*(.rodata.version)) в скрипте компоновки. Проверяют поиском строки в готовом образе — тогда её видно и в файле, и по отладчику (раздел 77.9).

Контроль версий с первой строки, обновление прошивки в поле (раздел 48), сторожевой таймер (раздел 12) и журнал ошибок закладывают сразу — добавить их в готовое изделие труднее, чем в прототип.


79.6 Испытания

Два слова, которые путают:

Испытание Что ищут
функциональное каждая функция по заданию
интеграционное блоки вместе: обмен, синхронизация
долговременное утечки памяти, редкие зависания, сбои раз в сутки
по питанию ток сна, провалы и медленное нарастание питания
температурное уход частоты, АЦП, отказ дисплея на холоде
электромагнитная совместимость помехи от устройства и устойчивость к чужим — для сертификации
пользовательское понятность и удобство

В производстве электроники испытания делят по зрелости изделия:

Стадия Что проверяют На чём
EVT работает ли замысел первые платы, часто с доработками
DVT выполнены ли все требования, проходит ли сертификацию образцы в окончательном виде
PVT даёт ли завод одинаковые годные изделия пробная партия с линии

79.7 Производство

Прошивка на заводе. Программатор по SWD через контактные площадки или встроенный загрузчик по UART (раздел 4.9). После записи — защита чтения байтами конфигурации (раздел 11.6).

Тестовая прошивка. Отдельная программа проверяет собранную плату: питание, кварц, светодиоды, датчики, интерфейсы — и выдаёт «годен / брак» на оснастку. Контрольные точки на плате закладывают ещё на этапе проектирования (79.4) — это и есть контролепригодность.

Серийный номер. У STM32 есть заводской уникальный номер длиной 96 бит. У F407 он лежит по адресу 0x1FFF7A10, рядом — регистр размера Flash 0x1FFF7A22; оба имени (UID_BASE, FLASHSIZE_BASE) объявлены в stm32f407xx.h. В заголовке F1 старого формата stm32f10x.h этих имён нет. У F1 номер лежит с адреса 0x1FFFF7E8, размер Flash — по адресу 0x1FFFF7E0, у G4 — 0x1FFF7590 и 0x1FFF75E0. В каждом семействе адрес свой, поэтому его берут из справочного руководства своего камня (раздел 39).

uint32_t serial32(void)
{
    const volatile uint32_t *uid = (const volatile uint32_t *)UID_BASE;
    return uid[0] ^ uid[1] ^ uid[2];
}

Это три чтения по 32 бита и два XOR. Свёртка 96 бит в 32 может дать совпадение у разных камней; где номер важен, хранят все 12 байт.

Прослеживаемость. Серийный номер связывают с версией прошивки, партией плат и партиями компонентов. Когда из поля придёт отказ, это позволит найти всех «соседей» устройства по партии.

Компоненты. Для каждой позиции перечня — второй поставщик или замена; статус жизненного цикла отслеживают и после запуска серии.


79.8 Поддержка после выпуска


79.9 Команда

Устройство редко делает один человек: схемотехник, конструктор, программист, испытатель, технолог производства, руководитель проекта. Чем больше людей, тем важнее общие документы — задание, схема, перечень элементов, журнал изменений — и общие правила работы.

Подход Суть в одной строке
Agile короткие итерации, каждая заканчивается работающим результатом
Scrum итерации фиксированной длины, план на итерацию, разбор в конце
Kanban доска задач по стадиям и предел одновременной работы
Lean убирать всё, что не добавляет ценности изделию
DevOps сборка, проверка и выпуск — автоматически, при каждом изменении

Для железа итерации медленнее, чем для программ: плату не пересоберёшь за минуту. Поэтому то, что можно проверить без железа, проверяют без него — модульными тестами на компьютере (79.5) и автоматической сборкой при каждом изменении. Этот справочник выпускается так же: ни одна версия не выходит, пока не прошли все проверки.


79.10 Грабли

Требование «быстро» или «долго». Без числа его нельзя ни выполнить, ни проверить.

Батарейки хватает на месяц вместо года. Бюджет питания не считали; ток сна не мерили на прототипе.

Ускорили программу — время работы от батареи не выросло. Средний ток определяет сон, а не работа.

Плата пришла, а прошить её нечем. Не выведены SWD или BOOT0.

Две прошивки с одинаковой версией ведут себя по-разному. Версию не меняли при сборке, или сборка не воспроизводима из-за __DATE__.

Ошибка, исправленная полгода назад, вернулась. Не было теста, который её ловит.

На заводе каждую плату проверяют вручную, по часу. Нет тестовой прошивки и контрольных точек.

Отказ из поля не воспроизводится. Устройство не хранит причину сброса и код отказа.

Камень сняли с производства посреди серии. Не отслеживали статус жизненного цикла, нет замены.

В ответе задачи отрицательное сопротивление. Неверна модель, а не арифметика.


79.11 Итоги

Полные исходники

ver79.c — версия из системы сборки.

/* Раздел 79.5: версия из системы сборки. На строку никто не ссылается — компоновщик её выбросит. */
#ifndef FW_VERSION
#define FW_VERSION "0.0.0-dev"
#endif
const char version[] = FW_VERSION;

Дальше: раздел 80 «Прототип на Arduino: плата, среда, первый скетч».

Открыть в интерактивном справочнике