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°.
Кривую почти не нужно считать — её заменяют прямыми (асимптотами):
- АЧХ — ноль децибел до среза, дальше прямая вниз на 20 дБ за декаду. Сильнее всего прямые врут на самом срезе — на 3,01 дБ; на половине среза и на двух срезах — на 0,97 дБ.
- ФЧХ — 0° до десятой доли среза, −90° после десяти срезов, между ними прямая через −45° на срезе. Наибольшая ошибка — 5,71°, на концах отрезка.
Фаза на низкой частоте — это просто задержка. На 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 бит). Итог:
- Всегда 32 точки на период — на высоких частотах хорошо, на низких плохо. На 100 Гц частота отсчётов 3,2 кГц, шаг 312 мкс втрое длиннее τ = 100 мкс, цепь успевает почти установиться на каждой ступеньке, и поправка на полшага перестаёт работать: фаза −6,14° против −3,60° по формуле.
- Точек столько, чтобы fs была не ниже 100 кГц (до 1024 на период) — от 100 Гц до 10 кГц ошибка не больше 0,03 дБ и 0,3° для обеих цепей, одного RC и двух через буфер. Шум АЦП в 1 младший разряд сдвигает замер не больше чем на 0,012 дБ и 0,3°, и ошибка остаётся в тех же пределах: свёртка по 16 периодам его усредняет.
Цифры для проверки на плате: таблица ЦАП 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 Итоги
- Децибелы: 0,707 — −3,01; 0,5 — −6,02; 0,1 — −20; первый порядок спадает на 20 дБ за декаду (6,02 на октаву).
- RC 10 кОм и 10 нФ: срез 1591,5 Гц, на нём −3,01 дБ и −45°; асимптоты врут не больше 3,01 дБ и 5,71°; ниже среза цепь — задержка на τ = 100 мкс.
- Два звена через буфер: 40 дБ на декаду, −3 дБ на 1024,3 Гц; без буфера 595,6 Гц и −9,54 дБ на срезе.
- Против наложения одиночный RC бессилен: 5,74 раза на 9 кГц при нужных 78,3 дБ.
- Фильтр на ОУ с усилением 10 и полосой ОУ 1 МГц: срез 1566 Гц вместо 1591,5, на 1 МГц фаза 37° вместо 90°.
- АЧХ снимается связкой ЦАП → цепь → АЦП от одного таймера; с поправкой на ступеньку и fs не ниже 100 кГц ошибка до 0,03 дБ и 0,3°.
Полные исходники
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 «Полупроводники: диоды, стабилитроны, биполярные транзисторы» — детали, у которых нелинейность не помеха, а рабочий инструмент.