
Как известно на микроконтроллерах STM32 можно генерировать PWM сигналы. Это всегда применяют для регулирования яркости свечения светодиодов, управления температурой нагревателей, управления крутящим моментом на моторах. При этом легко регулировать частоту, заполнение и инвертировать фазу, меняя полярность. Однако как непрерывно регулировать фазу на первый взгляд даже не ясно. Фаза сигнала это то насколько он смещен вдоль оси Х. В карте регистров аппаратных таймеров STM32 просто нет регистра, который отвечает за фазу сигнала.
Вы, конечно, можете сделать полностью программный PWM, написать функцию вычисления PWM семпла.
uint8_t calc_pwm_sample_num(uint64_t time_us, uint32_t period_ms, float duty, int32_t phase_ms) { uint8_t val = 0; if(100.0f < duty) { duty = 100.0f; } float cur_time_ms = ((float)time_us) / 1000.0f; int32_t time_saw = (phase_ms + ((int32_t)cur_time_ms)) % period_ms; int32_t threshold = (int32_t)(((float)(period_ms)*duty) / 100.0f); if(threshold < time_saw) { val = 0; } else { val = 1; } return val; }
Однако не получится генерировать PWM на высокой частоте, да и стабильность программного PWM оставляет желать лучшего. Программный PWM нагружает процессор.
В чем проблема?
Проблема в том, что если вы запустите на STM32 два или более аппаратных PWM сигнала на одинаковой частоте, то скорее всего сигналы окажутся далеко не синфазными. Более того, предсказать образовавшуюся фазу PWM не удастся. Это зависит от множества факторов: частоты процессора, организации проекта и скорости исполнения кода и пр.
Почему нужно регулировать фазу PWM?
1--> Для тестировочного оборудования. Двумя PWM сигналами вы можете аппаратно имитировать сигнал квадратурного энкодера .

2--> Для формирования пары sin / cos сигнала в квадратурном смесителе (с фазовым сдвигом 90° относительно другого).

3--> Для управления асинхронными BLDC двигателями вам надо три PWM смещенных по фазе на 120 градусов.

4--> Для управления бензиновыми форсунками (или свечой зажигания). Частота у них всех одинаковая, а фаза срабатывания у всех разная.
5--> Для аппаратной генерации сигнала гетеродина (LO) в квадратурном смесителе в SDR обработке входного сигнала с ADC.
6--> Специально сделанное рассогласование фаз PWM сигналов (например в нагревателях или светильниках) помогает уменьшить электромагнитные помехи в цепях и, тем самым, пройти испытания на электромагнитную совместимость прибора.
Теория
Фаза - это то, на сколько два сигнала смещены друг относительно друга вдоль оси Х. То есть по времени. Может измеряться в секундах или в градусах.
Основная идея управления фазой в том, что фаза управляется регистром компаратора от другого таймера. Назовем его опорный таймер (или Master-таймер), фаза которого условно принимается за ноль. В момент воображаемого прерывания по превышению счетчика значения опорного компаратора следует обнулить счетчик таймера B. Master-таймер генерирует на своем выводе опорный ШИМ-сигнал.
Таким образом PWM на таймере B сместится относительно PWM на таймере A. В результате на улицу у вас будут выходить два PWM сигнала смещенных друг относительно друга по фазе.

В CLI (Command Line Interface) у вас может быть отдельная команда для управления фазой.

Она принимает номер PWM и абсолютное значение фазы в секундах (или градусах). Это значение может принимать числа от нуля (синфазные с опорным) до значения периода (360 deg).

Достоинства
++ Можно непрерывно регулировать фазу с погрешностью равной 1/(значение периода).
++ Нет прерываний
++ Нет остановки таймеров на время изменения фазы
Недостатки
--два аппаратных таймера должны работать на одной частоте.
--для управления фазой таймера B надо проникать в настройки таймера А. Происходит нарушение модульности.
--Сложность настройки связи между таймерами. Сложность поддержки кода.
--На один опорный PWM вы можете посадить только 4 ведомых PWM, так как всего только 4 компаратора (в реальности на STM32 только один).
--Если Вы решите обнулять ведомый таймер в обработчике прерывания по компаратору, то получите заметную просадку по производительности своего основного приложения. Всё таки аппаратные PWM работают на высоких частотах.
Практическая часть
Что ж, c теорией покончено. Попробуем теперь провернуть это на реальной учебно-тренировочной электронной плате JZ-F407VET6.

Есть несколько способов регулировать фазу:
0) У STM32 таймеров есть бит полярности. Это значит, что вы можете атомарно инвертировать PWM сигнал. Это равносильно смещению фазы на пол периода (на 180 градусов). Таким образом можно буквально одним битом генерировать BPSK модуляцию на обыкновенном аппаратном PWM. Преимущество в том, что не нужен второй таймер. Недостаток в том, что нет плавности изменения фазы.
1) Наивный способ - это просто подменить значение счетчика таймера PWM3 на новое значение относительно счетчика таймера TIM8 . Алгоритм прост. Берем текущее значение из регистра TIM8_CNT, добавляем к нему желаемый прирост разности фаз и результат тут же быстро прописываем в регистр TIM4_CNT. Код очень простой.
float timer_tick_get_s(uint8_t num) { float tick_s = -1; float bus_clock = (float)timer_bus_clock_get(num); if(0.0 < bus_clock) { uint32_t prescaler = timer_prescaler_get(num); tick_s = ((float)(prescaler + 1)) / bus_clock; } return tick_s; } static int32_t TimerPhaseUsToCompareValue(uint8_t num, int32_t phase_us) { int32_t phase_value = 0; float tick_s = timer_tick_get_s(num); float phase_s = USEC_2_SEC(phase_us); float phase_value_f = (phase_s / tick_s); phase_value = (int32_t) phase_value_f; return phase_value; } uint32_t timer_counter_get(uint8_t num) { uint32_t timer_cnt32 = 0; TIM_TypeDef* TIMx = timer_get_ptr(num); if(TIMx) { timer_cnt32 = TIMx->CNT; } return timer_cnt32; } bool timer_counter_set(uint8_t num, uint32_t value) { bool res = false; TimerInfo_t* Info = TimerGetInfo(num); if(Info) { Info->TIMx->CNT = value; res = true; } return res; } bool pwm_phase_set_counter_adjust(uint8_t num, int32_t phase_us) { bool res = false; PwmHandle_t *Node = PwmGetNode(num); if(Node) { int32_t compare_value = TimerPhaseUsToCompareValue(Node->PhaseComparator.timer, phase_us); int32_t counter_base = (int32_t) timer_counter_get(Node->PhaseComparator.timer); int32_t value = counter_base + compare_value; res = timer_counter_set(Node->TimChan.timer, (uint32_t) value); LOG_INFO(PWM, "SetPhaSW:%d us,compareValue:%d,CNTMaster:%u,CNTslave%u", phase_us, compare_value,counter_base,value); } return res; }
Таким образом можно на лету регулировать фазу. Для PWM2 на частоте 1 kHz погрешность установки фaзы составила 3us. Это 0.29 %. Эта погрешность обусловлена тем, что отработка функций тоже требует своего времени. В сумме на код самой установку фазы набегает 2.8us

3) У STM32 есть правильный способ регулирования фазы. Это механизм составного таймера. Позволяет регулировать фазу без остановки таймера.

Согласно спецификации таймером 4 можно управлять при помощи таймера 8. Для этого надо сконфигурировать таймер 4 на режим ведомого устройства и указать, что надо брать события от IRT2 (от TIM3)

Вот справка из спецификации про то какие Master таймеры могут быть у каждого конкретного slave таймера. У таймеров 6, 7, 10, 11, 13, 14 вообще нет хозяина. На них получится регулировать фазу только вторым неточным способом.

План таков. Запустить два таймера PWM сигнала TIM8 и TIM4 на частоте 1kHz и заполнением 50%. Фазу PWM на TIM4 непрерывно двигать относительно PWM на первом компараторе TIM8.
Роль |
Название сигнала |
Таймер |
Канал |
GPIO |
PinMux |
Заполнение,% |
Freq, Hz |
Фаза, deg |
Master |
PWM2 |
TIM8 |
CH1 |
PС7 |
3 |
50 |
1000 |
0 |
Slave |
PWM3 |
TIM4 |
CH2 |
PB7 |
2 |
50 |
1000 |
90 |
Первый канал каждого таймера может выдавать наружу (то есть на ведомый таймер) события. Каждый раз, когда счетчик превысит значение первого компаратора сработает событие, которое может кто-нибудь принять. Надо в настройки мастер таймера прописать HAL-овскую константу TIM_TRGO_OC1 (Capture or a compare match 1 is used as trigger output (TRGO)). Slave таймер надо сконфигурировать в режим сброса (MODE_RESET) и указать источник INTERNAL_TRIGGER_3 (который ассоциирован с TIM8).
#include "timer_config.h" const TimerConfig_t TimerConfig[] = { { .num = TIMER_NUM_LO_BASE, .interrupt_on = false, .cnt_period_ns = 1000, .period_s = 0.001f, .name = "LocalOscillatorBase", .valid = true, .on_off = true, .dir = TIMER_CNT_DIR_UP, .role = TIMER_ROLE_MASTER, .master_out_trigger = TIMER_MASTER_OUT_TRG_OC1, }, { .num = TIMER_NUM_LO, .slave_input_trigger = TIMER_SLAVE_IN_TRIG_INTERNAL_TRIGGER_3, .slave_mode = TIMER_SLAVE_MODE_RESET, .role = TIMER_ROLE_SLAVE, .dir = TIMER_CNT_DIR_UP, .interrupt_on = false, .cnt_period_ns = 1000, .period_s = 0.001f, .name = "LocalOscillator", .valid = true, .on_off = true, .slave_trigger_polarity = TIMER_SLAVE_TRIGGER_POLARITY_INVERTED, .slave_trigger_prescaler = 1, .slave_trigger_filter = 1, }, }; TimerHandle_t TimerInstance[] = { { .num = TIMER_NUM_LO_BASE, .valid = true, }, { .num = TIMER_NUM_LO, .valid = true, }, }; COMPONENT_GET_CNT(Timer, timer)
Таким образом после инициализации TIM4 будет сбрасываться при срабатывании компаратора на первом канале таймера 8.
Достоинство этого метода в том, что нет нужды в прерываниях. Всё происходит полностью аппаратно. ARM процессор участвует только в момент записи нового числа в первый компаратор таймера 8, в функции установки фазы.
Диагностика фазы PWM
Вот я собрал прошивку и настроил несколько каналов PWM.

Далее логическим анализатором вижу сигнал, после настройки симфазности

Как видно, погрешность установки нулевой фазы вовсе отсутствует

Можно настроить противофазу

Режим энкодера. Всё, что захотите.

Перед вами сырые значения регистров таймера 4 и таймера 8, при которых работает управление фазы. Этого более чем достаточно, чтобы понять суть применённых настроек.
53:12-->timer_diag_raw_reg 4 3199.137,+31698,256,I,[Timer],Base:0x40000800,Cnt:20 +-----+--------------------+-------+------+------------+------------+-------------------------------------------+ | N | Name |offset | size | Addr | ValHex | ValBin | +-----+--------------------+-------+------+------------+------------+-------------------------------------------+ | 1 | TIMx_CR1 | 0x000 | 0 | 0x40000800 | 0x00000081 | 0b0000_0000|0000_0000|0000_0000|1000_0001 | | 2 | TIMx_CR2 | 0x004 | 4 | 0x40000804 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 3 | TIMx_SMCR | 0x008 | 4 | 0x40000808 | 0x00000034 | 0b0000_0000|0000_0000|0000_0000|0011_0100 | | 4 | TIMx_DIER | 0x00c | 4 | 0x4000080c | 0x00000005 | 0b0000_0000|0000_0000|0000_0000|0000_0101 | | 5 | TIMx_SR | 0x010 | 4 | 0x40000810 | 0x0000005f | 0b0000_0000|0000_0000|0000_0000|0101_1111 | | 6 | TIMx_EGR | 0x014 | 4 | 0x40000814 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 7 | TIMx_CCMR1 | 0x018 | 4 | 0x40000818 | 0x00006800 | 0b0000_0000|0000_0000|0110_1000|0000_0000 | | 8 | TIMx_CCMR2 | 0x01c | 4 | 0x4000081c | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 9 | TIMx_CCER | 0x020 | 4 | 0x40000820 | 0x00000010 | 0b0000_0000|0000_0000|0000_0000|0001_0000 | | 10 | TIMx_CNT | 0x024 | 4 | 0x40000824 | 0x00000139 | 0b0000_0000|0000_0000|0000_0001|0011_1001 | | 11 | TIMx_PSC | 0x028 | 4 | 0x40000828 | 0x00000052 | 0b0000_0000|0000_0000|0000_0000|0101_0010 | | 12 | TIMx_ARR | 0x02c | 4 | 0x4000082c | 0x000003f3 | 0b0000_0000|0000_0000|0000_0011|1111_0011 | | 13 | TIMx_RCR | 0x030 | 4 | 0x40000830 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 14 | TIMx_CCR1 | 0x034 | 4 | 0x40000834 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 15 | TIMx_CCR2 | 0x038 | 4 | 0x40000838 | 0x000001f9 | 0b0000_0000|0000_0000|0000_0001|1111_1001 | | 16 | TIMx_CCR3 | 0x03c | 4 | 0x4000083c | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 17 | TIMx_CCR4 | 0x040 | 4 | 0x40000840 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 18 | TIMx_BDTR | 0x044 | 4 | 0x40000844 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 19 | TIMx_DCR | 0x048 | 4 | 0x40000848 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 20 | TIMx_DMAR | 0x04c | 4 | 0x4000084c | 0x00000081 | 0b0000_0000|0000_0000|0000_0000|1000_0001 | +-----+--------------------+-------+------+------------+------------+-------------------------------------------+ 53:19-->timer_diag_raw_reg 8 3204.547,+5410,257,I,[Timer],Base:0x40010400,Cnt:20 +-----+--------------------+-------+------+------------+------------+-------------------------------------------+ | N | Name |offset | size | Addr | ValHex | ValBin | +-----+--------------------+-------+------+------------+------------+-------------------------------------------+ | 1 | TIMx_CR1 | 0x000 | 0 | 0x40010400 | 0x00000081 | 0b0000_0000|0000_0000|0000_0000|1000_0001 | | 2 | TIMx_CR2 | 0x004 | 4 | 0x40010404 | 0x00000030 | 0b0000_0000|0000_0000|0000_0000|0011_0000 | | 3 | TIMx_SMCR | 0x008 | 4 | 0x40010408 | 0x00000080 | 0b0000_0000|0000_0000|0000_0000|1000_0000 | | 4 | TIMx_DIER | 0x00c | 4 | 0x4001040c | 0x00000005 | 0b0000_0000|0000_0000|0000_0000|0000_0101 | | 5 | TIMx_SR | 0x010 | 4 | 0x40010410 | 0x0000001f | 0b0000_0000|0000_0000|0000_0000|0001_1111 | | 6 | TIMx_EGR | 0x014 | 4 | 0x40010414 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 7 | TIMx_CCMR1 | 0x018 | 4 | 0x40010418 | 0x00006800 | 0b0000_0000|0000_0000|0110_1000|0000_0000 | | 8 | TIMx_CCMR2 | 0x01c | 4 | 0x4001041c | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 9 | TIMx_CCER | 0x020 | 4 | 0x40010420 | 0x00000010 | 0b0000_0000|0000_0000|0000_0000|0001_0000 | | 10 | TIMx_CNT | 0x024 | 4 | 0x40010424 | 0x00000265 | 0b0000_0000|0000_0000|0000_0010|0110_0101 | | 11 | TIMx_PSC | 0x028 | 4 | 0x40010428 | 0x000000a6 | 0b0000_0000|0000_0000|0000_0000|1010_0110 | | 12 | TIMx_ARR | 0x02c | 4 | 0x4001042c | 0x000003ec | 0b0000_0000|0000_0000|0000_0011|1110_1100 | | 13 | TIMx_RCR | 0x030 | 4 | 0x40010430 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 14 | TIMx_CCR1 | 0x034 | 4 | 0x40010434 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 15 | TIMx_CCR2 | 0x038 | 4 | 0x40010438 | 0x000001f6 | 0b0000_0000|0000_0000|0000_0001|1111_0110 | | 16 | TIMx_CCR3 | 0x03c | 4 | 0x4001043c | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 17 | TIMx_CCR4 | 0x040 | 4 | 0x40010440 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 18 | TIMx_BDTR | 0x044 | 4 | 0x40010444 | 0x00008000 | 0b0000_0000|0000_0000|1000_0000|0000_0000 | | 19 | TIMx_DCR | 0x048 | 4 | 0x40010448 | 0x00000000 | 0b0000_0000|0000_0000|0000_0000|0000_0000 | | 20 | TIMx_DMAR | 0x04c | 4 | 0x4001044c | 0x00000081 | 0b0000_0000|0000_0000|0000_0000|1000_0001 | +-----+--------------------+-------+------+------------+------------+-------------------------------------------+ 53:24-->
Сравнение способов установки фазы PWM сигнала
Способ управления фазой PWM |
Достоинство |
Недостаток |
программный PWM |
гибкость настройки |
процессор участвует |
Инверсия полярности |
процессор не участвует |
нет непрерывной настройки |
подмена счетчика |
есть погрешность установок |
процессор участвует |
master-slave |
+нет прерываний |
-надо два таймера |
Результат
Непрерывное регулирование фазы PWM с минимальной возможной погрешностью более чем возможная задача. Готовый бинарь demo прошивки JZ-F407VET6 можно скачать тут.
Или можете собрать PWM-phase-demo из исходников
Учебные тестировочные сборки для PCB JZ-F407VET6 в репозитории trunk
https://github.com/aabzel/trunk/tree/main
Вот конкретная сборка PWM-phase-demo проекта
https://github.com/aabzel/trunk/tree/main/source/projects/jz_f407vet6_pwm_phase_demo_gcc_m
Все конфигурации для платы JZ-F407VET6 лежат тут
https://github.com/aabzel/trunk/tree/main/source/boards/jz_f407vet6
Все конфигурации для микроконтроллера stm32f407ve тут
https://github.com/aabzel/trunk/tree/main/source/microcontroller/stm32f407ve
Логика шатания фазы описана отдельным модулем
https://github.com/aabzel/trunk/tree/main/source/applications/pwm_phase_demo
Драйвер PWM лежит тут
https://github.com/aabzel/trunk/tree/main/source/mcal/mcal_common/pwm
https://github.com/aabzel/trunk/tree/main/source/mcal/mcal_stm32f4/pwm
Драйвер Timer лежит в source/mcal
https://github.com/aabzel/trunk/tree/main/source/mcal/mcal_common/timer
https://github.com/aabzel/trunk/tree/main/source/mcal/mcal_stm32f4/timer
Остальное можно найти утилитой grep по репозиторию.
Словарь
Сокращение |
Расшифровка |
PWM |
pulse-width modulation |
BPSK |
Binary Phase Shift Keying |
MCAL |
Microcontroller Abstraction Layer |
LO |
Local Oscillator |
STM32 |
STM 32 bit |
Ссылки
Название |
URL |
Demo прошивка регулирования фазы |
https://github.com/aabzel/Artifacts/tree/main/jz_f407vet6_pwm_phase_demo_gcc_m |
How to Generate 3-Phase PWM Using Synchronized STM32 Timers |
https://controllerstech.com/stm32-timers-6-timer-synchronization-generate-3-phase-pwm/ |
Обзор учебно-тренировочной платы JZ-F407VET6 (или электронная парта) |
|
Каскадный Таймер на STM32 (или Таймер с Прицепом) @danil_12345 |
|
Аналитика по таймерам STM32 |
|
STM32 PWM Phase Shift (Timer Synchronized) + Example Code |
https://deepbluembedded.com/stm32-pwm-phase-shift-timer-synchronized-example-code/ |
Вопросы
1--Как в прошивке микроконтроллера прочитать мгновенное значение на выходе аппаратного PWM ведь но согласно PinMux не подключен к GPIO ?
2--Можно ли на STM32 в случае схемы Master-Slave сделать регулирование фазы на Slave PWM сигнале без использования прерываний микроконтроллера и остановки счета, чтобы не затормаживать основную прошивку? Да. Надо использовать схему Master-slave. Генерировать по первому компаратору мастером события сброса для slave таймера.
Комментарии (10)

Pyhesty
18.04.2026 12:58тема полезная, странно, что мало комментариев но видимо не все дочитали до конца
меня засмущали вопросы после выводов, а где ответы?
Вопросы
1--Как в прошивке микроконтроллера прочитать мгновенное значение на выходе аппаратного PWM?
2--Можно ли на STM32 в случае схемы Master-Slave сделать регулирование фазы на Slave PWM сигнале без использования прерываний микроконтроллера и остановки счета, чтобы не затормаживать основную прошивку?ну и результат (или выводы статьи) напомнили студенческую работу
Результат
Удалось научиться
Автор, вопрос поднят хороший, хорошо описано и понятно, но результат и странные вопросы после выводов смазывают впечатление. Ставлю +, очень надеюсь на следуюшие тех статьи от вас на основе железа доступного на озон для любителей) тот же stm32 nano
и да, не нашел исходники примера на git, без общедоступных исходников сильно снижается ценность вашей работы

aabzel Автор
18.04.2026 12:58не нашел исходники примера на git, без общедоступных исходников сильно снижается ценность вашей работы
Тестировочные сборки в репозитории trunk
https://github.com/aabzel/trunk/tree/main
Вот конкретная сборка Demo проектаhttps://github.com/aabzel/trunk/tree/main/source/projects/jz_f407vet6_pwm_phase_demo_gcc_m
Все конфигурации для платы jz-f407vet6 лежат тут
https://github.com/aabzel/trunk/tree/main/source/boards/jz_f407vet6Логика шатания фазы описана отдельным модулем
https://github.com/aabzel/trunk/tree/main/source/applications/pwm_phase_demoДрайвер PWM лежит тут
https://github.com/aabzel/trunk/tree/main/source/mcal/mcal_common/pwm
https://github.com/aabzel/trunk/tree/main/source/mcal/mcal_stm32f4/pwmДрайвер Timer лежит в source/mcal
https://github.com/aabzel/trunk/tree/main/source/mcal/mcal_common/timer
https://github.com/aabzel/trunk/tree/main/source/mcal/mcal_stm32f4/timer

aabzel Автор
18.04.2026 12:58тема полезная, странно, что мало комментариев
Автор, вопрос поднят хороший, хорошо описано и понятно,Спасибо.

aabzel Автор
18.04.2026 12:58на основе железа доступного на озон для любителей) тот же stm32 nano
PCB JZ-F407VET6 тоже продается на OZON
Обзор учебно-тренировочной платы JZ-F407VET6 (или электронная парта)
https://habr.com/ru/articles/988494/(K N E X) 1 компл. Совет по развитию промышленного управления STM32F407VET6 Learning 485 Dual CAN Ethernet Интернет вещей STM32


aabzel Автор
18.04.2026 12:58тема полезная, странно, что мало комментариев но видимо не все дочитали до конца
По факту мало кому реально надо управлять фазой PWM. Я работаю программистом MK 14 лет и замечал, что 60-80 % программистов МК даже не знают, что такое фаза сигнала. Знают только про частоту, амплитуду и скважность.
Мало комментариев как раз и доказывают, что мало кто понял "об чём речь" и "Про что пирожки?" .

Pyhesty
18.04.2026 12:58да, тема узкоспециализированная, тем более выше ценность вашей статьи, понятно, что есть досада, что еë мало оценили, так как в основе здесь обитают программисты ПК и сейчас все сфокусировались и необоснованно "залайкивают" статьи про ИИ.
но буду ждать ваших статей по узким и широкопрофильным темам по МК. Там вопросов, мало известных, вагон и все они достойны, если не книги, то статьи на хабр.
и отдельное спасибо за ответ и ссылки выше

nixtonixto
18.04.2026 12:58У всех STM32 есть TIM1, специально заточенный под многофазные нагрузки. 3-фазным двигателем он способен управлять полностью аппаратно, с нулевой ошибкой по фазе. А у некоторых контроллеров он ещё и тактируется повышенной частотой, что увеличивает разрядность.


KarmaCraft
Полезная статья, может пригодится
aabzel Автор
Спасибо.