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

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 мс, берётся последний период.

Ток фазы под ШИМ по центру на 20 кГц при скважности 0,75: счётчик TIM1 вверх-вниз, верхний ключ открыт вокруг дна счётчика, нижний — вокруг вершины; ток растёт при открытом верхнем и падает при открытом нижнем, на шунте в нижнем плече напряжение есть только при открытом нижнем; выборка в вершине попадает на средний ток, на дне шунт показывает ноль; запуск АЦП за 59 тактов до вершины

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 Итоги

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

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» — как командовать контроллерами двигателей и инверторами по шине.

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