Embedded HandbookПриложения
Все разделы › VIII. Электроника вокруг микроконтроллера
STM32F407 · уровень средний · 12 мин чтения
Открыть в интерактивном справочнике

50а. Цепь по частоте: АЧХ, ФЧХ и диаграмма Боде

В разделе 50 RC-цепь разобрана во времени, а по частоте — одной формулой среза и правилом «20 дБ на декаду». Здесь та же цепь целиком: как меняются с частотой амплитуда и фаза, почему их рисуют в децибелах на логарифмической оси, что происходит, когда звеньев два, чем RC перед АЦП помогает и чем не помогает, как ведёт себя фильтр на ОУ и как снять частотную характеристику самим микроконтроллером — ЦАП подаёт синус, АЦП меряет ответ.

Опоры: справочное руководство RM0090 Rev 19 (поля TSEL1 в DAC_CR и EXTSEL в ADC_CR2) и даташит DS8626 Rev 9 на F405/407 (выходной буфер ЦАП, ёмкость выборки АЦП) — оба из пака Keil. Каждая цепь посчитана трижды: формулой, ngspice 39 (образ stm-lab:1, ОУ — макромодель раздела 164) и моделью измерения на ПК, где работает тот же код на Си, что и в прошивке.

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

Оригинал Перевод
frequency response частотная характеристика
magnitude response амплитудно-частотная характеристика, АЧХ
phase response фазочастотная характеристика, ФЧХ
Bode plot диаграмма Боде
cutoff (corner) frequency частота среза
roll-off, dB/decade спад, дБ на декаду
aliasing наложение спектров
anti-aliasing filter фильтр перед АЦП против наложения
frequency sweep качание частоты, свип
zero-order hold удержание отсчёта (ступенька ЦАП)

50а.1 Что показывают АЧХ и ФЧХ

На вход линейной цепи подают синус. На выходе — синус той же частоты, но другой амплитуды и сдвинутый во времени. Отношение амплитуд — это АЧХ, сдвиг в градусах периода — ФЧХ. Обе зависят только от частоты, поэтому их удобно записать одним комплексным числом. Для RC-цепи раздела 50:

H(f) = 1 / (1 + j · f / fc),   fc = 1 / (2 · π · R · C)
|H| — во сколько раз ослаблена амплитуда, arg H — сдвиг фазы

Амплитуду считают в децибелах: 20 · lg(K). Так удобнее по двум причинам: цепи, включённые одна за другой, перемножают коэффициенты, а децибелы просто складываются; и на одном графике помещаются и 1, и 0,0001. Опорные точки стоит помнить:

Во сколько раз Децибелы
0,707 (корень из половины) −3,01
0,5 −6,02
0,1 −20
0,01 −40
2 +6,02
10 +20

Частоту откладывают по логарифмической оси: декада — шаг в 10 раз, октава — в 2 раза. Спад 20 дБ на декаду — это те же 6,02 дБ на октаву.


50а.2 Диаграмма Боде RC-цепи

Возьмём R = 10 кОм и C = 10 нФ: срез 1591,5 Гц. Формула и ngspice дают одно и то же:

Частота АЧХ, дБ ФЧХ
0,1 среза −0,043 −5,71°
0,5 среза −0,969 −26,57°
срез −3,010 −45,00°
2 среза −6,990 −63,43°
10 срезов −20,04 −84,29°
100 срезов −40,00 −89,43°

ngspice находит точку −3,01 дБ ровно на 1591,5 Гц, на срезе фаза −45,00°, на десяти срезах −20,04 дБ и −84,29°.

Диаграмма Боде RC-цепи 10 кОм и 10 нФ и двух таких звеньев через буфер: срез 1,59 кГц на уровне −3,01 дБ, спад 20 и 40 дБ на декаду, фаза −45° и −90° на срезе, асимптоты пунктиром

Кривую почти не нужно считать — её заменяют прямыми (асимптотами):

Фаза на низкой частоте — это просто задержка. На 50 Гц цепь сдвигает сигнал на −1,80°, то есть на 99,97 мкс, а постоянная времени RC — 100 мкс: ниже среза RC-цепь запаздывает на τ, не искажая формы.


50а.3 Второй порядок: два звена подряд

Поставим два одинаковых RC-звена подряд. Если между ними буфер (повторитель на ОУ), коэффициенты просто перемножаются: H². Спад становится 40 дБ на декаду (ngspice: −40,09 дБ на десяти срезах, −80,00 дБ на ста), фаза на срезе −90° и дальше уходит к −180°. Но и полоса сужается: −3 дБ теперь не на 1591,5, а на 1024,3 Гц — на 0,6436 среза одного звена, а на самом срезе уже −6,02 дБ.

Без буфера второе звено нагружает первое, и получается уже другая цепь:

H(x) = 1 / (1 − x² + 3 · j · x),   x = f / fc

Добротность у неё 1/3, полоса ещё уже: −3 дБ на 0,3742 среза, 595,6 Гц (ngspice — те же 595,6 Гц). На срезе −9,54 дБ вместо −6,02 с буфером, фаза −90°. Далеко от среза разницы нет — те же −40 дБ на декаду (−80,00 дБ на ста срезах) и фаза к −180° (−178,28° там же).

Два RC подряд — самый простой фильтр второго порядка, но и самый вялый: перегиб у него пологий. Крутой перегиб при том же порядке даёт схема Саллена–Ки из раздела 178, там же — как выбирать добротность.


50а.4 RC перед АЦП и наложение спектров

АЦП, который берёт отсчёты с частотой fs, не отличает частоту f от |f − k · fs|. При fs = 10 кГц помеха 9 кГц в отсчётах выглядит как честный сигнал 1 кГц, и отфильтровать её программой уже нельзя. Поэтому всё, что выше fs/2, нужно задавить до АЦП.

Сколько даёт наша RC-цепь со срезом 1,59 кГц:

Помеха RC Два RC через буфер
5 кГц (половина fs) −10,36 дБ, в 3,30 раза −20,72 дБ
9 кГц (видна как 1 кГц) −15,18 дБ, в 5,74 раза −30,36 дБ

ngspice подтверждает все четыре числа. А чтобы помеха полной амплитуды ушла ниже половины младшего разряда 12-битного АЦП, нужно 78,3 дБ. Вывод: одиночный RC у входа АЦП — не фильтр против наложения. Его работа — держать заряд для конденсатора выборки (у F407 это 4 пФ по DS8626, подробно — 50.6 и 180) и снять часть широкополосного шума. От наложения спасают иначе: поднимают fs в разы выше полосы сигнала и фильтруют уже цифрой, либо ставят фильтр высокого порядка (178.5).


50а.5 Фильтр на ОУ: усиление и срез сразу

Инвертирующий усилитель с конденсатором параллельно резистору обратной связи: R1 = 10 кОм, R2 = 100 кОм, C = 1 нФ. Усиление −R2/R1 = −10 (+20 дБ), срез 1 / (2π · R2 · C) — те же 1591,5 Гц. Питание однополярное 3,3 В, опора — середина 1,65 В (раздел 162).

Частота Идеальный ОУ ОУ с полосой 1 МГц (ngspice)
10 Гц 20,00 дБ, 179,6° 20,00 дБ, 179,6°
1591,5 Гц 16,99 дБ, 135,0° 16,92 дБ, 134,5°
100 кГц −15,96 дБ, 90,9° −16,10 дБ, 85,2°
1 МГц −35,96 дБ, 90,1° −36,03 дБ, 37,3°

Фаза здесь отсчитана с учётом инверсии: 180° на низкой частоте, к 90° выше среза. Уровень −3 дБ от 20 дБ — это 16,99 дБ; у реального ОУ он достигается на 1566 Гц, на 1,6 % ниже расчётного: у самого усилителя конечная полоса (произведение усиления на полосу — 1 МГц в модели), и на частотах, где запас усиления мал, она добавляет свой полюс. Хорошо видно по фазе: на 1 МГц идеал держит 90°, а модель уже 37° — фильтр становится второго порядка. Правило простое: полоса ОУ должна быть много больше произведения усиления на срез (здесь 10 · 1,59 кГц), иначе срез и спад поедут. Про запас по фазе — раздел 163.


50а.6 Снять АЧХ самим микроконтроллером

Генератор и осциллограф не нужны: у F407 есть ЦАП и АЦП. ЦАП выдаёт синус по таблице из M точек, цепь его пропускает, АЦП берёт отсчёт с выхода. Оба запускаются одним событием — обновлением TIM2: в RM0090 это TSEL1 = 100 в DAC_CR и EXTSEL = 0110 в ADC_CR2. Таймер задаёт частоту отсчётов fs, частота синуса — fs / M.

void bode_trigger_setup(uint32_t arr)
{
    TIM2->ARR = arr;                                            /* fs = 84 МГц / (ARR + 1) */
    TIM2->CR2 = TIM_CR2_MMS_1;
    DAC->CR = DAC_CR_TSEL1_2 | DAC_CR_TEN1 | DAC_CR_DMAEN1 | DAC_CR_EN1;
    ADC1->CR2 = ADC_CR2_EXTEN_0 | ADC_CR2_EXTSEL_1 | ADC_CR2_EXTSEL_2 | ADC_CR2_DMA | ADC_CR2_DDS | ADC_CR2_ADON;
    TIM2->CR1 = TIM_CR1_CEN;
}

Таймеры шины APB1 при 168 МГц ядра считают от 84 МГц (раздел 13). Потоки DMA для таблицы и отсчётов — как в разделе 9. Выждав восемь периодов на установление, берут 16 целых периодов и сворачивают их с опорными синусом и косинусом той же фазы, что у таблицы ЦАП:

    for (int k = 0; k < n; k++) {
        float x = (float)adc[k] - mean;
        i += x * s_tab[k % m];
        q += x * c_tab[k % m];
    }
    float amp = 2.0f * sqrtf(i * i + q * q) / (float)n;
    float ph = atan2f(q, i) * 57.295780f;
    if (zoh) {
        const float x = 3.14159265f / (float)m;
        amp /= sinf(x) / x;                    /* ступенька ослабляет первую гармонику: sinc */
        ph += 180.0f / (float)m;               /* и запаздывает на полшага */
    }

Постоянную составляющую (середину 2048) вычитают, остаются амплитуда и фаза первой гармоники. Поправка zoh нужна потому, что ЦАП выдаёт не синус, а ступеньки: при M = 32 они ослабляют первую гармонику в 0,99839 раза (−0,0140 дБ) и сдвигают на полшага — 5,625°. Без поправки ФЧХ при 32 точках и частоте от 1 кГц врёт на 5,3–5,9°.

Модель на ПК собирает тот же файл gcc и кормит его отсчётами точной модели цепи (ступенька ЦАП, RC, отсчёт в момент запуска, 12 бит). Итог:

Цифры для проверки на плате: таблица ЦАП 448…3648 — внутри 0x0E0…0xF1C, где буфер ЦАП по DS8626 даёт полный размах; обновлять ЦАП паспорт разрешает до 1 млн раз в секунду при малых шагах, а у нас fs не выше 320 кГц (но на верхних частотах шаги таблицы крупные — сотни кодов, и установление буфера на них здесь не проверено); нагрузка буфера — не меньше 5 кОм, а вход RC-цепи никогда не бывает меньше R = 10 кОм. На 1591,5 Гц при M = 32 делитель таймера выходит 1649, и частота получается 1591,87 Гц — считать формулу надо для неё, а не для заказанной. Памяти при M = 1024: опорные синус и косинус — 8 КБ, таблица ЦАП — 2 КБ, 16 периодов отсчётов — 32 КБ.


50а.7 Грабли

Два звена по 1,59 кГц, а срез на 1 кГц. −3 дБ у каскада — это не срез каждого звена: через буфер 1024 Гц, без буфера 596 Гц. Считают общую АЧХ, а не звенья по отдельности.

Второе RC-звено без буфера — другая цепь. На срезе −9,54 дБ вместо −6,02: второе звено нагружает первое. Либо буфер между звеньями, либо считать каскад как одну цепь.

RC у входа АЦП считают защитой от наложения. На частоте 9 кГц при fs = 10 кГц он ослабляет помеху всего в 5,74 раза, а нужно 78 дБ. Помеха ложится в полосу сигнала навсегда.

Фазу меряют по ЦАП без поправки на ступеньку. При 32 точках — лишние 5,6° запаздывания, которых в цепи нет.

Свип с 32 точками на низких частотах. Когда шаг ЦАП длиннее постоянной времени цепи, модель «синус плюс полшага» ломается; фазу уводит на градусы. Держат fs много выше среза, увеличивая число точек.

Свёртка не по целому числу периодов. Опорные синус и косинус перестают быть ортогональны, амплитуда и фаза плывут от места обрыва. Число отсчётов — строго M × число периодов.

От фильтра на ОУ ждут спада до бесконечности. На частотах, где полоса ОУ кончается, добавляется его собственный полюс: на 1 МГц фаза 37° вместо 90°, срез ушёл на 1,6 % вниз.


50а.8 Итоги

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

bode50b.c — АЧХ и ФЧХ цепи, снятые ЦАП и АЦП микроконтроллера.

/* bode50b.c — раздел 50а: АЧХ и ФЧХ цепи, снятые самим микроконтроллером.
 *
 * ЦАП выдаёт синус по таблице из m точек, АЦП берёт отсчёт с выхода цепи по тому же
 * запуску таймера. После целого числа периодов свёртка с опорными sin и cos даёт амплитуду
 * и фазу первой гармоники. Файл собирается двумя способами:
 *   - armclang для STM32F407 (-DSTM32F407xx -include stm32f4xx.h): математика + запуск от TIM2;
 *   - gcc на ПК (bode_host.c включает этот файл): та же математика на отсчётах модели цепи.
 */
#include <stdint.h>
#include <math.h>

#define BODE_MMAX 1024     /* точек синуса на период — не больше */
#define BODE_MID 2048      /* середина шкалы 12 бит */
#define BODE_AMP 1600      /* амплитуда, коды: 448…3648 — внутри 0x0E0…0xF1C, где буфер ЦАП линеен */

static float s_tab[BODE_MMAX], c_tab[BODE_MMAX];

/* Таблица для ЦАП на m точек и опорные синус и косинус той же фазы. */
void bode_tables(uint16_t *dac, int m)
{
    for (int k = 0; k < m; k++) {
        float w = 6.2831853f * (float)k / (float)m;
        s_tab[k] = sinf(w);
        c_tab[k] = cosf(w);
        dac[k] = (uint16_t)lrintf(BODE_MID + BODE_AMP * s_tab[k]);
    }
}

typedef struct { float gain_db, phase_deg; } bode_point;

/* n отсчётов АЦП — целое число периодов по m точек. zoh = 1: поправка на ступеньку ЦАП. */
bode_point bode_measure(const uint16_t *adc, int n, int m, int zoh)
{
    float mean = 0.0f, i = 0.0f, q = 0.0f;
    for (int k = 0; k < n; k++)
        mean += adc[k];
    mean /= (float)n;
    for (int k = 0; k < n; k++) {
        float x = (float)adc[k] - mean;
        i += x * s_tab[k % m];
        q += x * c_tab[k % m];
    }
    float amp = 2.0f * sqrtf(i * i + q * q) / (float)n;
    float ph = atan2f(q, i) * 57.295780f;
    if (zoh) {
        const float x = 3.14159265f / (float)m;
        amp /= sinf(x) / x;                    /* ступенька ослабляет первую гармонику: sinc */
        ph += 180.0f / (float)m;               /* и запаздывает на полшага */
    }
    bode_point p = { 20.0f * log10f(amp / BODE_AMP), ph };
    return p;
}

#ifdef STM32F407xx
/* Один запуск на двоих: TIM2 TRGO по обновлению, ЦАП (TSEL1 = 100) и АЦП (EXTSEL = 0110)
 * берут его оба. Потоки DMA для таблицы и отсчётов — как в разделе 9, здесь не показаны. */
_Static_assert(DAC_CR_TSEL1_2 == (4UL << DAC_CR_TSEL1_Pos), "TSEL1 = 100 — TIM2 TRGO (RM0090, DAC_CR)");
_Static_assert((ADC_CR2_EXTSEL_1 | ADC_CR2_EXTSEL_2) == (6UL << ADC_CR2_EXTSEL_Pos), "EXTSEL = 0110 — TIM2 TRGO (RM0090, ADC_CR2)");
_Static_assert(TIM_CR2_MMS_1 == (2UL << TIM_CR2_MMS_Pos), "MMS = 010 — TRGO по обновлению");

void bode_trigger_setup(uint32_t arr)
{
    TIM2->ARR = arr;                                            /* fs = 84 МГц / (ARR + 1) */
    TIM2->CR2 = TIM_CR2_MMS_1;
    DAC->CR = DAC_CR_TSEL1_2 | DAC_CR_TEN1 | DAC_CR_DMAEN1 | DAC_CR_EN1;
    ADC1->CR2 = ADC_CR2_EXTEN_0 | ADC_CR2_EXTSEL_1 | ADC_CR2_EXTSEL_2 | ADC_CR2_DMA | ADC_CR2_DDS | ADC_CR2_ADON;
    TIM2->CR1 = TIM_CR1_CEN;
}
#endif

Дальше: раздел 51 «Полупроводники: диоды, стабилитроны, биполярные транзисторы» — детали, у которых нелинейность не помеха, а рабочий инструмент.

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