Embedded HandbookПриложения
Все разделы › XXVI. От макета к изделию
STM32F103 · уровень средний · 18 мин чтения
Открыть в интерактивном справочнике

206. Сертификация: подтверждение соответствия и радиомодули

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

Числа раздела — из двух прогонов: сравнение «модуль против своего радио» и экономика пре-комплаенса — _verify/cert206/cost.py в условных единицах с явными допущениями; тестовые режимы rf_test.c собраны и прогнаны на ПК (stm-lab:1) и собраны для Cortex-M3 (stm-arm:1).

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

Оригинал Перевод
conformity assessment подтверждение соответствия: допуск изделия к продаже
declaration of conformity декларация соответствия: изготовитель заявляет и отвечает сам
accredited laboratory аккредитованная лаборатория: её протокол признаёт регулятор
test report протокол испытаний: результаты по каждому виду воздействия
technical file технический файл: комплект доказательств — схемы, расчёты, протоколы
intentional radiator преднамеренный излучатель: изделие передаёт радиосигнал само
modular approval модульное одобрение: одобрение радиомодуля переносится на изделие
host product изделие-носитель: прибор, в который встроен готовый модуль
continuous wave (CW) немодулированная несущая: «голый» сигнал для измерений

206.1 Продажа прибора — это подтверждение соответствия

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

Системы подтверждения на разных рынках устроены похоже, но зовутся по-разному:

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

⚠️ Соответствие подтверждают у изделия в сборе — таким, каким его видит покупатель: с корпусом, штатными кабелями и блоком питания. «Сертифицированная плата» без корпуса — не термин: поменяли корпус или блок питания — по смыслу поменяли изделие (206.6).

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


206.2 Что проверяют у электроники

У электронного изделия три типовых экзамена, и все три уже знакомы по соседним разделам:

Карта «какое изделие → какие испытания», по смыслу:

Изделие ЭМС Безопасность Радиочастоты
датчик на батарейке, без радио да минимальная нет
прибор с питанием от сети да полная нет
изделие с готовым сертифицированным модулем да, как носитель по питанию закрыты одобрением модуля
изделие со своим радио да по питанию полный цикл

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

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


206.3 Радиомодуль или своё радио

«Своё радио» — это не только трансивер, разведённый на вашей плате; микроконтроллер со встроенным радио (семейства STM32WB и STM32WL, приложения A17–A18) — с точки зрения испытаний тоже ваше радио: вы изготовитель передатчика со всеми вытекающими. Альтернатива — готовый модуль с модульным одобрением: его радиочасть уже испытана изготовителем модуля, и одобрение переносится на ваше изделие-носитель — при соблюдении условий из документов модуля: антенна из его списка (или встроенная), питание в допуске, и прошивка не трогает радиопараметры за разрешёнными пределами.

Честная арифметика — в условных единицах, потому что цены лабораторий и модулей меняются от рынка к рынку; важны не суммы, а отношение (прогон cost.py, допущения в нём же):

Точка равновесия — серия, на которой своё радио окупается: N* = C / Δ (у.е. / (у.е./шт) = шт):

        C\Δ      100      200      400
     500000     5000     2500     1250
    1000000    10000     5000     2500
    2000000    20000    10000     5000
    3000000    30000    15000     7500

По всей сетке допущений N — от 1 250 до 30 000 изделий; в центральной точке (C = 1 000 000, Δ = 200) — 5 000. На серии в 1 000 изделий модуль обходится в 200 000 у.е. переплаты против миллиона входа в своё радио — в 5 раз дешевле, и это ещё без месяцев на итерации ВЧ-платы и без риска пересдачи. Отсюда правило мелкой серии: радио покупают модулем*, а своё радио строят, когда счёт идёт на десятки тысяч штук — либо когда модуль физически не подходит по габариту, потреблению или диапазону.

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

🔧 Как выбирать модуль. До разводки платы открыть документы модуля и проверить четыре вещи: одобрения есть именно для ваших рынков (одобрение не переносится между системами); ваша антенна — встроенная или из списка модуля; одобрение живо, а модуль не в конце жизненного цикла (иначе пересертификация приедет к вам); на плате модуля выведены тестовые площадки и есть тестовые режимы — лаборатория скажет спасибо.


206.4 Лаборатория: что попросит и что зашить в прошивку

Собранный к испытаниям комплект похож на список из 203.7, но для радиоизделия к нему добавляется пункт, о котором чаще всего вспоминают уже на стенде. Лаборатория попросит:

Штатная прошивка для измерений почти бесполезна: стек передаёт короткими пакетами со случайными паузами, слушает эфир, прыгает по каналам и засыпает. Измерителю же нужно стабильное худшее состояние: несущая без модуляции на заданном канале — включая крайние, пакеты вплотную, чистый приём и точка наибольшего потребления. Эти режимы зашивают заранее и дёргают через сервисную консоль мини-проектов (196–198) — вся радиочасть у нас уже за драйвером radio_reg(), поэтому команды сводятся к таблице:

struct cmd {
    const char *name;                      /* слово команды консоли */
    void (*run)(int ch);
    uint8_t need_ch;                       /* команде нужен номер канала */
};

static const struct cmd cmds[] = {
    { "cw",   rf_cw,     1 },              /* эмиссия: несущая на канале */
    { "pkt",  rf_pkt,    1 },              /* занятая полоса «как в жизни» */
    { "rx",   rf_rx,     1 },              /* стойкость приёмника */
    { "off",  rf_off,    0 },              /* вернуть штатный режим */
    { "max",  load_max,  0 },              /* наибольшее потребление */
    { "stat", stat_show, 0 },              /* жив / сколько перезапусков */
};

Каждый обработчик — несколько записей в регистры радиомодуля; номера регистров у вашего модуля будут свои, из даташита:

static void rf_cw(int ch)                  /* несущая без модуляции */
{
    radio_reg(R_CHAN, (uint8_t)ch);
    radio_reg(R_PWR, PWR_MAX);             /* испытывают на максимуме мощности */
    radio_reg(R_MODE, M_TX_CW);
    reply("cw: канал ", (uint32_t)ch, ", несущая без модуляции, мощность максимум");
}

Прогон на ПК с заглушкой вместо радио (rf_test.c, условный модуль на 16 каналов): консоль отвечает на каждую команду и не пускает за пределы сетки каналов —

> cw 15
  радио <- регистр 0x02 = 15
  радио <- регистр 0x03 = 8
  радио <- регистр 0x01 = 3
cw: канал 15, несущая без модуляции, мощность максимум
> cw 16
? канал: 0..15
> stat
stat: жив, перезапусков 0

Команда stat — это критерий «жив / перезапустился» из 203.7: без неё кратковременный сбой на стенде стойкости никто не заметит. Для Cortex-M3 весь набор — шесть команд с разбором строки — собрался в 1103 байта кода и 84 байта ОЗУ: дешевле одного часа стенда.

⚠️ Тестовые режимы не должны уехать заказчику. Прибор, который командой в консоль превращается в непрерывный передатчик на максимальной мощности, в бою недопустим — да и одобрение модуля действует, пока прошивка не выводит радиопараметры за разрешённые пределы. Для испытаний собирают отдельную прошивку (флаг сборки, как NO_HOST_TEST в rf_test.c) или запирают консоль сервисной перемычкой; версию испытанной прошивки записывают — она попадёт в протокол.


206.5 Пре-комплаенс: отсеять дома

Формальный цикл испытаний — заметные деньги и очередь на недели, поэтому приезжать в лабораторию с необследованным изделием — способ заплатить дважды. Стенд из 203.6 — зажигалка, реле-зуммер, приёмник, для радиоизделий плюс SDR-свисток, которым видно несущую и её гармоники «до и после» доработки, — отсеивает грубые дыры: изделие, падающее от разряда в корпус; импульсник, слышный приёмником через всю комнату; прошивку без тестовых режимов, наконец.

Арифметика та же, что у всего раздела, — отношения, не суммы (прогон cost.py): полный цикл испытаний — порядка 100 000 у.е.; провал означает доработку и повторный визит — по смыслу 30–50 % цикла и потерянные месяцы; домашний стенд — порядка 1 000 у.е., то есть около 1 % цикла. Одна проблема, пойманная дома, окупает стенд в 30–50 раз. Что стенд не покажет — тоже из 203.6: запас в децибелах и маску полосы подтверждает только нормированная площадка.

Разумный порядок для изделия с радио: свой стенд → при сомнениях день измерений без протокола (многие лаборатории так умеют — это дешевле формального цикла) → формальный цикл с протоколами. Каждый шаг фильтрует дыры, которые на следующем стоили бы на порядок дороже.


206.6 После испытаний: маркировка, документы, изменения

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

Маркировка. Знак соответствия рынка — на изделие или упаковку (на своём рынке — знак обращения ЕАЭС, на европейском — знак CE), рядом — модель, изготовитель и всё, что требует ваша система; у изделий с модулем внутри по смыслу встречается и надпись «содержит модуль такой-то» с номером одобрения модуля. Что именно писать — из документов вашей системы и модуля, а не из чужих фотографий корпусов.

Технический файл. Декларация без доказательств — просто заявление: в файле живут протоколы, схемы, описание, инструкция, фото, перечень из 199 и версия испытанной прошивки. Хранят его годами — по смыслу порядка десяти лет; при проверке или претензии это ваша единственная защита.

Изменения изделия. Соответствие подтверждено для конкретной конфигурации — и каждое изменение сверяют с ней:

Что поменяли Что делать
деталь на равноценную замену из перечня запись в журнале изменений
импульсный источник, разводку платы, корпус повторить затронутую часть испытаний
антенну или радиомодуль радиочасть заново: одобрение модуля больше не покрывает
прошивку: логика, интерфейс запись + свои тесты (201)
прошивку: тактовые, фронты, мощность радио по смыслу это новое изделие — на измерения

Серийное производство обязано соответствовать испытанному образцу: замены «аналогом» в закупке (199.2) проходят через разработчика, а не решаются на складе.


206.7 Грабли

«Модуль сертифицирован — значит, и изделие сертифицировано». Нет: модульное одобрение закрывает только радиочасть. Изделие-носитель всё равно проходит свои экзамены — ЭМС в сборе и безопасность по питанию; строка таблицы 206.2 короче соседних, но не пустая.

Поставили антенну «получше» — одобрение перестало действовать. Антенна с большим усилением вне списка модуля делает вас изготовителем нового передатчика — со всем циклом радиоиспытаний. Антенну выбирают из документов модуля до разводки, а не по наличию на складе.

Модуль одобрен — но не для вашего рынка. Одобрение FCC не действует в ЕАЭС, ЕАЭС — в Европе, и наоборот: это разные системы с разными протоколами. Список одобрений модуля сверяют со списком рынков изделия — письменно, до заказа партии модулей.

Лаборатория просит несущую — а прошивка умеет «отправить пакет». Штатный стек не умеет CW на заданном канале и пакеты вплотную; дописывать тестовые режимы через переписку со стендом — самые дорогие человеко-часы проекта. Шесть команд из 206.4 пишутся за вечер до поездки.

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

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


206.8 Итоги

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

rf_test.c — тестовые режимы радиочасти для лаборатории.

/* Раздел 206.4: тестовые режимы радиочасти для лаборатории.
 *
 * Штатный стек передаёт пакетами, слушает эфир и уходит в сон — лаборатории
 * этого мало: ей нужны непрерывная несущая на заданном канале, пакеты
 * вплотную, чистый приём и точка наибольшего потребления. Эти режимы
 * зашиваются в прошивку заранее и дёргаются командами сервисной консоли
 * (разделы 196–198). Здесь, на компьютере, вместо радиомодуля — заглушка,
 * печатающая записи в его регистры; сами команды и разбор — те же, что
 * поедут в изделие.
 *
 * Сборка на ПК:  gcc -O2 -Wall -Wextra -o rf_test rf_test.c && ./rf_test
 * Для кристалла: arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -Os -DNO_HOST_TEST -c rf_test.c
 */
#include <stdint.h>
#ifndef NO_HOST_TEST
#include <stdio.h>
#endif

void con_out(const char *s);               /* строка в консоль: у изделия — UART */
void radio_reg(uint8_t reg, uint8_t val);  /* запись регистра радиомодуля — драйвер */

uint32_t reset_count;                      /* сколько раз перезапускались: журнал, 12.4 */
static uint8_t no_sleep;                   /* 1 = не спать: точка наибольшего потребления */

enum { NCHAN = 16, PWR_MAX = 8 };          /* каналы и шаги мощности нашего модуля */

/* регистры условного модуля — у вашего будут свои, из даташита */
enum { R_MODE = 0x01, R_CHAN = 0x02, R_PWR = 0x03 };
enum { M_SLEEP = 0, M_RX = 1, M_TX_PKT = 2, M_TX_CW = 3 };

static char *dec(char *p, uint32_t v)      /* число в строку, без printf */
{
    char t[10];
    int n = 0;
    do { t[n++] = (char)('0' + v % 10); v /= 10; } while (v);
    while (n)
        *p++ = t[--n];
    *p = 0;
    return p;
}

static void reply(const char *a, uint32_t n, const char *b)
{
    static char line[80];
    char *p = line;
    while (*a) *p++ = *a++;
    p = dec(p, n);
    while (*b) *p++ = *b++;
    *p = 0;
    con_out(line);
}

static void rf_cw(int ch)                  /* несущая без модуляции */
{
    radio_reg(R_CHAN, (uint8_t)ch);
    radio_reg(R_PWR, PWR_MAX);             /* испытывают на максимуме мощности */
    radio_reg(R_MODE, M_TX_CW);
    reply("cw: канал ", (uint32_t)ch, ", несущая без модуляции, мощность максимум");
}

static void rf_pkt(int ch)                 /* пакеты вплотную, без пауз протокола */
{
    radio_reg(R_CHAN, (uint8_t)ch);
    radio_reg(R_PWR, PWR_MAX);
    radio_reg(R_MODE, M_TX_PKT);
    reply("pkt: канал ", (uint32_t)ch, ", пакеты вплотную, пауза 0");
}

static void rf_rx(int ch)                  /* чистый приём: стойкость приёмника */
{
    radio_reg(R_CHAN, (uint8_t)ch);
    radio_reg(R_MODE, M_RX);
    reply("rx: канал ", (uint32_t)ch, ", только приём, передатчик выключен");
}

static void rf_off(int ch)
{
    (void)ch;
    no_sleep = 0;
    radio_reg(R_MODE, M_SLEEP);
    con_out("off: радио в сон, изделие в штатный режим");
}

static void load_max(int ch)               /* для замера питания и провалов сети */
{
    (void)ch;
    no_sleep = 1;                          /* главный цикл не зовёт WFI */
    rf_cw(NCHAN / 2);
    con_out("max: ядро без сна, передатчик включён — снимайте потребление");
}

static void stat_show(int ch)              /* критерий «жив» для стойкости, 203.7 */
{
    (void)ch;
    reply("stat: жив, перезапусков ", reset_count, "");
}

struct cmd {
    const char *name;                      /* слово команды консоли */
    void (*run)(int ch);
    uint8_t need_ch;                       /* команде нужен номер канала */
};

static const struct cmd cmds[] = {
    { "cw",   rf_cw,     1 },              /* эмиссия: несущая на канале */
    { "pkt",  rf_pkt,    1 },              /* занятая полоса «как в жизни» */
    { "rx",   rf_rx,     1 },              /* стойкость приёмника */
    { "off",  rf_off,    0 },              /* вернуть штатный режим */
    { "max",  load_max,  0 },              /* наибольшее потребление */
    { "stat", stat_show, 0 },              /* жив / сколько перезапусков */
};

static int str_eq(const char *a, const char *b)
{
    while (*a && *a == *b) { a++; b++; }
    return *a == *b;
}

void rf_console(const char *line)          /* вызвать из приёма сервисной консоли */
{
    char word[8];
    unsigned wl = 0;
    int ch = -1;
    const char *p = line;
    while (*p && *p != ' ' && wl + 1 < sizeof word)
        word[wl++] = *p++;
    word[wl] = 0;
    if (*p == ' ') {                       /* необязательный номер канала */
        ch = 0;
        for (p++; *p >= '0' && *p <= '9'; p++)
            ch = ch * 10 + (*p - '0');
    }
    for (unsigned i = 0; i < sizeof cmds / sizeof cmds[0]; i++)
        if (str_eq(word, cmds[i].name)) {
            if (cmds[i].need_ch && (ch < 0 || ch >= NCHAN)) {
                con_out("? канал: 0..15");
                return;
            }
            cmds[i].run(ch);
            return;
        }
    con_out("? команды: cw pkt rx off max stat");
}

#ifndef NO_HOST_TEST
void con_out(const char *s)
{
    printf("%s\n", s);
}

void radio_reg(uint8_t reg, uint8_t val)
{
    printf("  радио <- регистр 0x%02X = %u\n", reg, val);
}

int main(void)
{
    static const char *script[] = {
        "cw 0", "cw 15", "cw 16", "pkt 7", "rx 7",
        "max", "off", "stat", "qq",
    };
    for (unsigned i = 0; i < sizeof script / sizeof script[0]; i++) {
        printf("> %s\n", script[i]);
        rf_console(script[i]);
    }
    printf("no_sleep после off: %u\n", no_sleep);
    return 0;
}
#endif

Дальше: раздел 206а «Сертификация в ЕАЭС по шагам» — регламенты, декларация или сертификат, схема, знак EAC.

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