57б. Ток двигателя: шунт, усилитель, выборка в нужный момент ШИМ
Раздел 57а управлял двигателем и на каждом периоде ШИМ «измерял токи фаз» — одной строкой. Здесь — как это делается: куда ставят шунт, как его десятки милливольт превращают в полную шкалу АЦП, в какой момент периода брать отсчёт, чтобы получить средний ток без фильтра, и как тот же сигнал выключает ШИМ при перегрузке раньше, чем программа что-то заметит.
Встроенные ОУ и компараторы G4 описаны в разделе 44, защита по току на них — в 44.8. Здесь те же блоки работают на двигатель: шунт в нижнем плече, ток обоих знаков, выборка, привязанная к ШИМ.
Опоры: даташит G474 (DS12288, таблица 80 — ОУ, характеристики АЦП,
предельные режимы) и справочное руководство RM0440 (таблицы связей 65,
71, 72, регистры TIM1, АЦП, ЦАП) из пака Keil STM32G4xx_DFP 1.6.0, заголовок
stm32g474xx.h оттуда же. Ток фазы и усилитель посчитаны моделью на ПК:
ngspice в образе stm-lab:1, макромодель ОУ из раздела 164 с числами
из DS12288, каталог _verify/mcur57c. Код настройки собран armclang для
G474. Обмотка 2 Ом / 1 мГн и 12 В — двигатель из 57.5; шунт 10 мОм — как
в 44.8; порог 8 А — пример.
Как это называется в документации
Оригинал Перевод shunt resistor, current sense шунт, датчик тока low-side / in-line / single (DC-link) shunt шунт в нижнем плече / в линии фазы / в общей шине phase current ток фазы programmable gain amplifier (PGA) with external bias усилитель с программируемым усилением и смещением injected channel, JSQR, JEXTSEL инжектированный канал АЦП, его очередь и источник запуска trigger output 2 (TRGO2, MMS2) второй выход запуска таймера center-aligned PWM ШИМ с выравниванием по центру trigger conversion latency (tLATR) задержка от запуска до начала выборки blanking гашение выхода компаратора на время переключения break input (BKIN, tim_brk_cmp) аварийный вход таймера
57б.1 Куда ставить шунт
| Место | Что видит | Синфазное на шунте | Чем усиливать |
|---|---|---|---|
| нижнее плечо, шунт под каждым нижним ключом | ток фазы, пока открыт нижний ключ | около нуля | встроенный ОУ G4 |
| в линии фазы, между мостом и обмоткой | ток фазы всегда | скачет от 0 до 12 В с каждым фронтом ШИМ | внешний усилитель для такого синфазного |
| в общей шине, один на мост | в каждом состоянии ключей — ток одной из фаз или ноль | около нуля | встроенный ОУ, но ток фаз восстанавливать по двум отсчётам за период |
Встроенный ОУ G4 принимает на входах только 0…VDDA (DS12288, таблица 80), поэтому шунт в линии фазы ему не по силам. Общий шунт экономит детали, но требует сдвигать фронты ШИМ, чтобы успеть измерить, — отдельная тема, здесь не разобранная. Дальше — шунт в нижнем плече, 10 мОм.
У нижнего шунта две особенности, из которых следует весь раздел. Первая: ток через него идёт только при открытом нижнем ключе — значит, отсчёт надо брать именно тогда. Вторая: знак. Ток, который вытекает из фазы в обмотку, при открытом нижнем ключе идёт через шунт снизу вверх, от земли, — напряжение на шунте отрицательное. В модели при токе +1,5 А на шунте −14,992 мВ, при −1,5 А — +15,006 мВ. Усилителю нужны оба знака.
57б.2 Пульсации тока и где брать отсчёт
Модель — одна фаза моста на 12 В: обмотка 2 Ом / 1 мГн (двигатель из 57.5) к средней точке 6 В, которая заменяет нейтраль трёхфазного двигателя. ШИМ 20 кГц по центру: счётчик TIM1 идёт вверх-вниз, верхний ключ открыт вокруг «дна» счётчика, нижний — вокруг вершины. Для сравнения тот же ток при ШИМ по фронту. ngspice считает 10 мс, берётся последний период.
| D | Средний ток | Размах пульсаций | Ошибка выборки в вершине | Ошибка выборки по фронту |
|---|---|---|---|---|
| 0,25 | −1,5000 А | 112,5 мА | −0,58 мА | −55,8 мА |
| 0,5 | 0 | 150,0 мА | −0,93 мА | −75,0 мА |
| 0,75 | +1,5000 А | 112,5 мА | −0,82 мА | −56,7 мА |
| 0,9 | +2,4000 А | 54,0 мА | −0,42 мА | −27,4 мА |
Размах 150 мА при D = 0,5 — тот же, что в 57.5. Главное в последних двух столбцах. Ток на каждом участке меняется почти по прямой, а у отрезка прямой середина равна среднему. Середина открытого нижнего ключа — вершина счётчика, и отсчёт в ней даёт средний за период ток с ошибкой меньше миллиампера — без всякого фильтра. Остаток ошибки — от того, что из-за сопротивления обмотки ток идёт не по прямой, а по экспоненте.
Отсчёт по фронту — при ШИМ по фронту, в момент обновления счётчика — попадает на излом пульсаций: ошибка — половина размаха, до 75 мА. К тому же фронт — это момент переключения ключей, и на шунте в это время выброс (раздел 44.6, гашение).
На дне счётчика, в середине открытого верхнего ключа, ток тоже равен
среднему, но шунт в нижнем плече показывает ноль: ток через него не
идёт. Отсюда ловушка с запуском по обновлению счётчика. В режиме по
центру при RCR = 0 обновление бывает и на вершине, и на дне (RM0440:
счётчик повторений уменьшается и при переполнении, и при опустошении) —
каждый второй отсчёт будет нулём.
57б.3 Запуск АЦП от TIM1: TRGO2 и сдвиг выборки
Запускать нужно один раз за период и чуть раньше вершины. У TIM1 для
этого есть второй выход запуска TRGO2 с полем MMS2 в CR2: код 0111
выводит на него tim_oc4refc — опорный сигнал четвёртого канала, не
связанного с выводами. Канал 4 в режиме ШИМ 2 активен, пока счётчик выше
CCR4, — импульс вокруг вершины, и его фронт запускает АЦП. По таблице
65 RM0440 у инжектированных каналов ADC1/2 источник 8 — tim1_trgo2.
Насколько раньше вершины? Числа для 170 МГц:
| Что | Значение |
|---|---|
| TIM1 по центру, 20 кГц | ARR = 4250: вверх и вниз — 8500 тактов, 50 мкс |
| такт АЦП | HCLK/4 = 42,5 МГц, CKMODE = 11 |
задержка от запуска до выборки, tLATR |
не более 2,125 такта АЦП — 50,0 нс (DS12288, CKMODE = 11) |
| выборка | 12,5 такта — 294,1 нс |
| запуск раньше вершины | 344,1 нс — 59 тактов TIM1: CCR4 = 4191 |
Выборка кончается ровно в вершине: после неё АЦП преобразует уже удержанное напряжение. Такт АЦП берут синхронный, от HCLK: по RM0440 в синхронных режимах у задержки от запуска таймером нет дрожания, и 59 тактов — всегда 59.
А если не угадать? Ошибка выборки со сдвигом после вершины — по тому, что увидит АЦП на шунте:
| Сдвиг | D = 0,75 | D = 0,9 |
|---|---|---|
| 0,344 мкс | −3,91 мА | −4,14 мА |
| 1 мкс | −9,81 мА | −11,21 мА |
| 2 мкс | −18,78 мА | −21,98 мА |
| 5 мкс | −45,58 мА | −2400,00 мА |
Ошибка — наклон тока в вершине (−9,00 А/мс при D = 0,75) на сдвиг. Не учесть задержку и выборку вовсе — это 344 нс, единицы миллиампер при токе в полтора ампера. Опасно другое: при D = 0,9 нижний ключ открыт всего 5 мкс, и сдвиг на 5 мкс выводит выборку из окна — АЦП видит ноль вместо 2,4 А.
TIM1->PSC = 0;
TIM1->ARR = PWM_ARR;
TIM1->CR1 = (1u << TIM_CR1_CMS_Pos) | TIM_CR1_ARPE; /* вверх-вниз, режим 1 */
TIM1->CCR4 = PWM_CCR4;
TIM1->CCMR2 = (TIM1->CCMR2 & ~(TIM_CCMR2_OC4M_Msk | TIM_CCMR2_CC4S_Msk))
| (OC_PWM2 << TIM_CCMR2_OC4M_Pos) | TIM_CCMR2_OC4PE;
TIM1->CR2 = (TIM1->CR2 & ~TIM_CR2_MMS2_Msk) | (MMS2_OC4REFC << TIM_CR2_MMS2_Pos);
ADC12_COMMON->CCR = (ADC12_COMMON->CCR & ~ADC_CCR_CKMODE_Msk) | (CKMODE_HCLK_4 << ADC_CCR_CKMODE_Pos);
ADC1->SMPR2 = (ADC1->SMPR2 & ~ADC_SMPR2_SMP12_Msk) | (SMP_12_5 << ADC_SMPR2_SMP12_Pos);
ADC1->JSQR = (0u << ADC_JSQR_JL_Pos) /* одно преобразование */
| (JEXTSEL_T1TRGO2 << ADC_JSQR_JEXTSEL_Pos)
| (JEXTEN_RISING << ADC_JSQR_JEXTEN_Pos)
| (12u << ADC_JSQR_JSQ1_Pos); /* ADC1_IN12 — PB1 */
Инжектированный канал удобен тем, что живёт отдельно от обычной очереди:
сканирование прочих каналов с DMA (раздел 14.6) идёт своим ходом, а
отсчёт тока прерывает его в назначенный момент. Включение АЦП — регулятор,
калибровка, ADEN — как в 15а.3.
57б.4 Усилитель: PGA −15 со смещением от DAC3
На шунте ±0,1 В, а АЦП нужна шкала 0…3,3 В с нулём тока посередине. Это
делает OPAMP3 в режиме PGA с инвертирующим усилением и смещением
(RM0440, раздел про режим PGA со смещением): шунт — на вывод VINM0
(PB2) через внутренний резистор R1, на вход «+» — напряжение с DAC3_CH2.
Для инвертирующего включения на делителе ×16:
Uвых = 16·Uсм − 15·Uш.
Смещение DAC3 кодом 128 — 3,3 · 128 / 4096 = 0,103125 В, и при нулевом токе на выходе ровно 1,65 В. Ток из фазы даёт отрицательное Uш — выход растёт; обратный ток — падает. Выход не подходит к шинам ближе 0,1 В (DS12288), значит шкала — ±10,33 А. Шаг — 0,806 мВ на код АЦП, это 5,37 мА.
Почему инвертирующее. В нём входы самого ОУ стоят на смещении 0,103 В,
внутри допустимых 0…VDDA, а шунт видит только вывод VINM0 через
резистор 10 кОм. Отрицательные −0,1 В на выводе — в пределах абсолютного
максимума VSS − 0,3 В (DS12288). В неинвертирующем включении шунт шёл
бы прямо на вход ОУ, и отрицательный знак тока был бы вне диапазона.
Что говорит модель (ngspice, R1 = 10 кОм, R2 = 150 кОм из таблицы 80,
открытое усиление 95 дБ): при нулевом токе 1,6498 В, при −0,1 В на
шунте 3,1493 В, усиление 14,996. Разброс резисторов — ±15 % каждый, но
усиление −15 даташит держит в ±1 %. Смещение ОУ ±1,5 мВ (44.2) на выходе
умножается на 16: ±24 мВ, то есть ±160 мА. Поэтому код нуля
code_zero снимают при стоящем двигателе и вычитают из каждого отсчёта.
DAC3->MCR = (DAC_MODE_INT << DAC_MCR_MODE1_Pos) | (DAC_MODE_INT << DAC_MCR_MODE2_Pos)
| DAC_MCR_HFSEL_1; /* AHB 170 МГц */
DAC3->DHR12R1 = DAC_TRIP;
DAC3->DHR12R2 = DAC_BIAS;
DAC3->CR |= DAC_CR_EN1 | DAC_CR_EN2;
OPAMP3->CSR = (VPSEL_DAC << OPAMP_CSR_VPSEL_Pos) /* «+» — смещение 0,103 В */
| (VMSEL_PGA << OPAMP_CSR_VMSEL_Pos)
| (PGA_INV_15 << OPAMP_CSR_PGGAIN_Pos) /* выход = 16·Uсм − 15·Uш */
| OPAMP_CSR_OPAMPxEN; /* выход на PB1: и в АЦП, и в COMP1 */
Почему OPAMP3. По таблицам 71 и 72 RM0440 его выход — PB1, а PB1 — это
и вход АЦП ADC1_IN12, и вход «+» компаратора COMP1. Один сигнал идёт и
регулятору, и защите. Смещение — DAC3_CH2: из ЦАП у OPAMP3 на входе «+»
только он. Режим ЦАП 011 — выход только на внутренние блоки, без буфера.
Перевод кода в миллиамперы — целыми числами:
static inline int32_t phase_mA(uint16_t code, uint16_t code0)
{
return ((int32_t)code - (int32_t)code0) * 22000 / 4095;
}
Функция собрана gcc на ПК и сверена с моделью: 1,5 А — код 2327 и 1498 мА, −10 А — код 186 и −10003 мА.
57б.5 Окно измерения и фильтр
Когда открывается нижний ключ, на шунте ступенька — от нуля до −i·Rш, и выход ОУ должен успеть к ней прийти до выборки. Модель — ступенька −0,1 В (10 А), выход проходит 1,5 В и должен встать в полкода АЦП, 0,403 мВ:
| ОУ | GBW, скорость нарастания | Установление | Нижний ключ открыт не меньше | Предельная D |
|---|---|---|---|---|
| типовой | 13 МГц, 6,5 В/мкс | 1,01 мкс | 2,61 мкс | 0,948 |
| худший по даташиту | 7 МГц, 2,5 В/мкс | 2,46 мкс | 5,51 мкс | 0,890 |
Окно считается так: выборка кончается в вершине, значит от открытия нижнего ключа до её начала — половина его времени минус выборка, и туда должно уместиться установление. При большей скважности ток этой фазы в этом периоде не измерить. Для трёх фаз это не беда: хватает двух токов (57а.7), а у фазы с самой большой скважностью нижний ключ открыт меньше всех — её и вычисляют по двум другим. Защитный интервал ещё сужает окно — считать надо по настоящим фронтам.
Фильтр. Аппаратный RC здесь почти невозможен. В режиме PGA
конденсатор ставят на вывод VINM1 параллельно R2 = 150 кОм, и его
постоянная времени должна уложиться в окно с запасом на установление до
полкода — в ln(8190) раз: при D = 0,9 окно 2,206 мкс, постоянная не
больше 0,245 мкс, конденсатор не больше 1,63 пФ. Фильтром служит сама
выборка в середине: она уже даёт средний ток. Шум остального сглаживает
регулятор.
57б.6 Защита по току: COMP1 гасит ШИМ
Тот же выход ОУ на PB1 идёт на «+» COMP1, порог — DAC3_CH1. Ток из фазы 8 А — это 1,65 + 15 · 0,01 · 8 = 2,85 В на выходе, код ЦАП 2,85 / 3,3 · 4096 = 3537; с этим кодом порог — 7,9976 А. Гистерезис 20 мВ на выходе ОУ — это 133 мА тока.
COMP1->CSR = (INM_DAC3_CH1 << COMP_CSR_INMSEL_Pos)
| (INP_PB1 << COMP_CSR_INPSEL_Pos)
| (HYST_20MV << COMP_CSR_HYST_Pos)
| (BLANK_TIM1_OC5 << COMP_CSR_BLANKING_Pos) /* выброс при переключении не считается */
| COMP_CSR_EN;
TIM1->AF1 |= TIM1_AF1_BKCMP1E;
TIM1->BDTR |= TIM_BDTR_BKP | TIM_BDTR_BKE; /* авария — единицей (раздел 44.8) */
Выход компаратора через BKCMP1E приходит на аварийный вход TIM1, и
MOE сбрасывается без участия ядра (57а.6). Полярность BKP и гашение
импульсом TIM1 OC5 — как в 44.6 и 44.8.
Две оговорки. Компаратор здесь ловит только ток из фазы — выход ОУ выше порога. Обратный ток уводит выход вниз, и для него нужен второй компаратор; PB1 в таблице 71 — вход только COMP1, так что второму компаратору выход ОУ заводят проводником на плате. И главное: шунт в нижнем плече слеп, пока открыт верхний ключ — замыкание фазы на землю в это время он не увидит. От такого замыкания защищают шунт в общей шине или защита драйвера ключей.
57б.7 Грабли
Каждый второй отсчёт — ноль. АЦП запущен по обновлению TIM1 в режиме
по центру при RCR = 0: обновление и на вершине, и на дне, а на дне
нижний ключ закрыт. Запуск — от TRGO2 по фронту OC4REF, один раз за
период.
Ток «плавает» на полразмаха пульсаций. Отсчёт взят на фронте ШИМ: ошибка до 75 мА в модели. Выборка — в середине открытого нижнего ключа.
При большой скважности ток фазы — ноль или мусор. Нижний ключ открыт меньше, чем нужно усилителю на установление: 2,61 мкс у типового ОУ, 5,51 мкс у худшего. Эту фазу считать по двум другим.
Ток при стоящем двигателе — не ноль. Смещение ОУ ×16 — до ±160 мА. Код нуля снимать при остановке и вычитать.
Знак тока перевёрнут. Ток из фазы делает шунт в нижнем плече отрицательным; инвертирующий PGA возвращает знак. В неинвертирующем включении отрицательное напряжение уходит за вход ОУ.
Конденсатор «от шума» в обратной связи PGA. Уже 1,63 пФ при R2 = 150 кОм съедают всё окно при D = 0,9.
Защита не сработала при замыкании на землю. Нижний шунт не видит тока, пока открыт верхний ключ. Нужна вторая линия защиты.
Порог защиты считают от 1,65 В, а ноль ушёл. Порог — код ЦАП, он не знает про смещение ОУ. Запас порога должен покрывать ±160 мА.
57б.8 Итоги
- Шунт в нижнем плече — 10 мОм, синфазное около нуля, встроенный ОУ справляется; ток виден только при открытом нижнем ключе, и ток из фазы даёт на шунте минус.
- ШИМ по центру и отсчёт в вершине счётчика дают средний ток: ошибка меньше 1 мА против 27…75 мА при отсчёте на фронте.
- Запуск:
TRGO2=OC4REF(MMS2= 0111), инжектированный канал ADC1 с источником 8;CCR4= 4191 приARR= 4250 — на 344 нс раньше вершины. - OPAMP3 в режиме PGA −15 со смещением DAC3 кодом 128: ноль тока — 1,65 В, шкала ±10,33 А, 5,37 мА на код; смещение ОУ до ±160 мА снимают калибровкой нуля.
- Установление после открытия нижнего ключа — 1,01…2,46 мкс: предельная скважность 0,948 у типового ОУ и 0,890 у худшего.
- COMP1 с порогом 3537 (8 А) через
BKCMP1Eгасит ШИМ без ядра; нижний шунт слеп при открытом верхнем ключе.
Полные исходники
mc57c.c — ток фазы на STM32G474: PGA, выборка по TIM1, защита COMP1.
/* 57б: ток фазы на STM32G474 — шунт в нижнем плече, OPAMP3 в режиме PGA −15 со смещением от DAC3,
выборка инжектированным каналом ADC1 по TIM1 TRGO2 в вершине счётчика, защита COMP1 → авария TIM1.
Шунт — на PB2 (OPAMP3_VINM0), выход ОУ — PB1: он же ADC1_IN12 и COMP1_INP (RM0440, табл. 71 и 72).
Собирается armclang: -DSTM32G474xx -include stm32g4xx.h. */
#include <stdint.h>
#include "mc57c.h"
/* ── биты, на которые опирается код: из заголовка пака ───────────────── */
_Static_assert(TIM_CR2_MMS2_Pos == 20u && TIM_CR2_MMS2_Msk == (0xFu << 20), "MMS2 — биты 23:20 TIMx_CR2");
_Static_assert(ADC_JSQR_JL_Pos == 0u && ADC_JSQR_JEXTSEL_Pos == 2u && ADC_JSQR_JEXTEN_Pos == 7u && ADC_JSQR_JSQ1_Pos == 9u,
"JSQR: JL, JEXTSEL, JEXTEN, JSQ1");
_Static_assert(ADC_CCR_CKMODE_Pos == 16u && ADC_SMPR2_SMP12_Pos == 6u, "CKMODE — 17:16 CCR, SMP12 — 8:6 SMPR2");
_Static_assert(OPAMP_CSR_VPSEL_Pos == 2u && OPAMP_CSR_VMSEL_Pos == 5u && OPAMP_CSR_PGGAIN_Pos == 14u, "OPAMP_CSR");
_Static_assert(COMP_CSR_INMSEL_Pos == 4u && COMP_CSR_INPSEL_Pos == 8u && COMP_CSR_HYST_Pos == 16u
&& COMP_CSR_BLANKING_Pos == 19u, "COMP_CSR");
_Static_assert(DAC_MCR_MODE1_Pos == 0u && DAC_MCR_MODE2_Pos == 16u, "DAC_MCR: MODE1, MODE2");
_Static_assert(TIM_CCMR2_OC4M_Pos == 12u, "OC4M[2:0] — биты 14:12 CCMR2");
#define MMS2_OC4REFC 7u /* 0111: tim_oc4refc — на tim_trgo2 */
#define JEXTSEL_T1TRGO2 8u /* ADC1/2, инжектированные: 8 — tim1_trgo2 */
#define JEXTEN_RISING 1u
#define CKMODE_HCLK_4 3u /* синхронно, HCLK/4 = 42,5 МГц: без дрожания от запуска таймером */
#define SMP_12_5 2u /* 12,5 такта = 294 нс */
#define OC_PWM2 7u /* 0111: вверх — активен при CNT > CCR, вниз — при CNT > CCR */
#define VPSEL_DAC 3u /* у OPAMP3 — DAC3_CH2 */
#define VMSEL_PGA 2u
#define PGA_INV_15 11u /* 01011: −15, вход через R1 с VINM0 (PB2) */
#define DAC_MODE_INT 3u /* 011: только на внутренние блоки, без буфера */
#define INM_DAC3_CH1 4u /* у COMP1 — DAC3_CH1 (раздел 44.6) */
#define INP_PB1 1u /* второй вход COMP1: PB1 */
#define HYST_20MV 2u
#define BLANK_TIM1_OC5 1u
_Static_assert(PGA_INV_15 == (8u | 3u), "+8 — инвертирующее с VINM0, 3 — делитель ×16");
_Static_assert(PWM_CCR4 == 4191u && DAC_BIAS == 128u && DAC_TRIP == 3537u, "числа — из модели");
/* Усилитель и опоры: DAC3_CH2 — смещение, DAC3_CH1 — порог защиты. */
void current_amp_init(void)
{
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN | RCC_AHB2ENR_DAC3EN | RCC_AHB2ENR_ADC12EN;
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN | RCC_APB2ENR_TIM1EN;
GPIOB->MODER |= (3u << (1 * 2)) | (3u << (2 * 2)); /* PB1, PB2 — аналоговые */
DAC3->MCR = (DAC_MODE_INT << DAC_MCR_MODE1_Pos) | (DAC_MODE_INT << DAC_MCR_MODE2_Pos)
| DAC_MCR_HFSEL_1; /* AHB 170 МГц */
DAC3->DHR12R1 = DAC_TRIP;
DAC3->DHR12R2 = DAC_BIAS;
DAC3->CR |= DAC_CR_EN1 | DAC_CR_EN2;
OPAMP3->CSR = (VPSEL_DAC << OPAMP_CSR_VPSEL_Pos) /* «+» — смещение 0,103 В */
| (VMSEL_PGA << OPAMP_CSR_VMSEL_Pos)
| (PGA_INV_15 << OPAMP_CSR_PGGAIN_Pos) /* выход = 16·Uсм − 15·Uш */
| OPAMP_CSR_OPAMPxEN; /* выход на PB1: и в АЦП, и в COMP1 */
}
/* ШИМ по центру и запуск АЦП в вершине счётчика — в середине открытого нижнего ключа. */
void pwm_adc_sync_init(void)
{
TIM1->PSC = 0;
TIM1->ARR = PWM_ARR;
TIM1->CR1 = (1u << TIM_CR1_CMS_Pos) | TIM_CR1_ARPE; /* вверх-вниз, режим 1 */
TIM1->CCR4 = PWM_CCR4;
TIM1->CCMR2 = (TIM1->CCMR2 & ~(TIM_CCMR2_OC4M_Msk | TIM_CCMR2_CC4S_Msk))
| (OC_PWM2 << TIM_CCMR2_OC4M_Pos) | TIM_CCMR2_OC4PE;
TIM1->CR2 = (TIM1->CR2 & ~TIM_CR2_MMS2_Msk) | (MMS2_OC4REFC << TIM_CR2_MMS2_Pos);
ADC12_COMMON->CCR = (ADC12_COMMON->CCR & ~ADC_CCR_CKMODE_Msk) | (CKMODE_HCLK_4 << ADC_CCR_CKMODE_Pos);
ADC1->SMPR2 = (ADC1->SMPR2 & ~ADC_SMPR2_SMP12_Msk) | (SMP_12_5 << ADC_SMPR2_SMP12_Pos);
ADC1->JSQR = (0u << ADC_JSQR_JL_Pos) /* одно преобразование */
| (JEXTSEL_T1TRGO2 << ADC_JSQR_JEXTSEL_Pos)
| (JEXTEN_RISING << ADC_JSQR_JEXTEN_Pos)
| (12u << ADC_JSQR_JSQ1_Pos); /* ADC1_IN12 — PB1 */
ADC1->IER |= ADC_IER_JEOCIE;
/* включение АЦП (регулятор, калибровка, ADEN) — раздел 15а; затем: */
ADC1->CR |= ADC_CR_JADSTART;
NVIC_EnableIRQ(ADC1_2_IRQn);
}
/* Перегрузка: COMP1 сравнивает выход ОУ с порогом и через аварийный вход гасит ШИМ. */
void overcurrent_init(void)
{
COMP1->CSR = (INM_DAC3_CH1 << COMP_CSR_INMSEL_Pos)
| (INP_PB1 << COMP_CSR_INPSEL_Pos)
| (HYST_20MV << COMP_CSR_HYST_Pos)
| (BLANK_TIM1_OC5 << COMP_CSR_BLANKING_Pos) /* выброс при переключении не считается */
| COMP_CSR_EN;
TIM1->AF1 |= TIM1_AF1_BKCMP1E;
TIM1->BDTR |= TIM_BDTR_BKP | TIM_BDTR_BKE; /* авария — единицей (раздел 44.8) */
}
volatile uint16_t code_zero = 2048; /* снимается при стоящем двигателе */
volatile int32_t i_phase_mA;
void ADC1_2_IRQHandler(void)
{
ADC1->ISR = ADC_ISR_JEOC;
i_phase_mA = phase_mA((uint16_t)ADC1->JDR1, code_zero);
}
Дальше: раздел 57в «VESC и OpenInverter: силовая электроника под управлением по CAN» — как командовать контроллерами двигателей и инверторами по шине.