Перейти к содержимому

Таймеры и широтно-импульсная модуляция: первый запуск двигателя … ↩

Таймеры и широтно-импульсная модуляция: первый запуск двигателя mod. Четверг, 15 октября 2026 22:55 . См. 11

BeeFC · Урок S8 из 33 · Уровень «Старт»

Как таймер микроконтроллера формирует импульсы заданной длительности, какими сигналами управляются регуляторы частоты вращения и сервоприводы, почему выходы одного таймера обязаны работать с одинаковой частотой и как безопасно запустить двигатель в первый раз.

В этом уроке впервые вращается двигатель. До разблокировки на выход не подаётся ни одного импульса, а первый запуск выполняется без воздушного винта.

Содержание

1. Цели урока

Продолжительность урока — четыре часа: один час теории и три часа практики. После урока слушатель:

  • рассчитывает предделитель и период таймера для заданной частоты и разрешения;
  • настраивает канал таймера в режиме широтно-импульсной модуляции;
  • объясняет, почему выходы одного таймера работают с общей частотой и общим протоколом;
  • реализует защиту, которая не допускает импульсов на выходе до разблокировки двигателей;
  • запускает двигатель на стенде и измеряет сигнал логическим анализатором.
↑ К оглавлению

2. Термины и сокращения

ТерминОпределение
широтно-импульсная модуляция (ШИМ)передача значения длительностью импульса при постоянном периоде
предделитель (PSC)регистр, делящий частоту тактирования таймера
регистр автоперезагрузки (ARR)значение, после которого счётчик таймера начинает счёт с нуля
регистр сравнения (CCR)значение, до которого на выходе канала держится высокий уровень
электронный регулятор частоты вращения (ESC)плата управления бесколлекторным двигателем
OneShot125протокол управления регулятором импульсами длительностью 125–250 мкс
разблокировка двигателейперевод системы в состояние, в котором двигатели могут вращаться
калибровка диапазона газазапоминание регулятором минимальной и максимальной длительности импульса
↑ К оглавлению

3. Таймер

Счётчик таймера увеличивается на единицу с частотой \( f_{TIM}/(PSC+1) \). Досчитав до значения регистра ARR, счётчик начинает счёт с нуля. В режиме PWM mode 1 на выходе канала держится высокий уровень, пока значение счётчика меньше значения регистра CCR.

\( f_{PWM} = \frac{f_{TIM}}{(PSC + 1)\,(ARR + 1)}, \qquad t_{имп} = \frac{CCR}{f_{TIM}/(PSC+1)} \)

ТаймерыШинаЧастота тактированияРазрядность
TIM1, TIM8APB2168 МГц16 бит, расширенные возможности
TIM2, TIM5APB184 МГц32 бита
TIM3, TIM4APB184 МГц16 бит
TIM9–TIM11APB2168 МГц16 бит
TIM12–TIM14APB184 МГц16 бит
Режим ШИМ таймера при отсчёте 1 мкс: счётчик CNT, регистры ARR и CCR и сигнал на выходе канала частотой 400 Гц.
Режим ШИМ таймера при отсчёте 1 мкс: счётчик CNT, регистры ARR и CCR и сигнал на выходе канала частотой 400 Гц.
↑ К оглавлению

4. Сигналы управления

СигналЧастотаДлительность импульсаПрименение
сервопривод50 Гц1000–2000 мксрулевые поверхности самолёта, подвес камеры
ШИМ регуляторадо 490 Гц1000–2000 мксрегуляторы с аналоговым входом
OneShot1252 кГц в BeeFC125–250 мксрегуляторы с ускоренным входом
DShotцифровой кадр—урок P6

Расчёт предделителя и периода

Удобно выбрать цену одного отсчёта таймера так, чтобы длительность импульса записывалась без пересчёта. При отсчёте 1 мкс на таймере с частотой 84 МГц предделитель равен 83, и значение CCR равно длительности импульса в микросекундах.

Частота 490 Гц, которую часто называют стандартной для регуляторов, при таком отсчёте точно не получается: \( 1\,000\,000 / 490 \approx 2040{,}8 \). Функция расчёта BeeFC в таком случае возвращает false, а не округляет молча, и выбирается частота 500 Гц с периодом ровно 2000 отсчётов.

Файл core/pwm_calc.h

typedef struct {
    uint32_t psc;   /* значение для регистра TIMx_PSC */
    uint32_t arr;   /* значение для регистра TIMx_ARR */
} pwm_timing_t;

/* timer_hz — частота тактирования таймера, tick_hz — желаемая цена
 * одного отсчёта (разрешение), pwm_hz — частота ШИМ.
 * max_arr — 0xFFFF для 16-битных таймеров, 0xFFFFFFFF для TIM2 и TIM5. */
bool pwm_calc(uint32_t timer_hz, uint32_t tick_hz, uint32_t pwm_hz,
              uint32_t max_arr, pwm_timing_t *out);

/* Длительность импульса в микросекундах переводится в значение CCR. */
uint32_t pwm_us_to_ccr(uint32_t tick_hz, uint32_t pulse_us);
Plain text

Файл core/pwm_calc.c

bool pwm_calc(uint32_t timer_hz, uint32_t tick_hz, uint32_t pwm_hz,
              uint32_t max_arr, pwm_timing_t *out)
{
    if (tick_hz == 0 || pwm_hz == 0) return false;
    if (timer_hz % tick_hz != 0) return false;      /* дробный делитель недопустим */
    if (tick_hz % pwm_hz != 0) return false;

    uint32_t psc = timer_hz / tick_hz - 1u;
    uint32_t ticks = tick_hz / pwm_hz;
    if (psc > 0xFFFFu || ticks == 0 || ticks - 1u > max_arr) return false;

    out->psc = psc;
    out->arr = ticks - 1u;      /* счётчик считает от 0 до ARR включительно */
    return true;
}

uint32_t pwm_us_to_ccr(uint32_t tick_hz, uint32_t pulse_us)
{
    return (uint32_t)(((uint64_t)tick_hz * pulse_us) / 1000000u);
}
Plain text

Файл tests/unit/test_level1.c

void test_pwm_esc_490hz_on_apb1_timer(void)
{
    pwm_timing_t t;
    /* Таймеры шины APB1 тактируются частотой 84 МГц, отсчёт 1 мкс. */
    TEST_ASSERT_TRUE(pwm_calc(84000000u, 1000000u, 490u, 0xFFFFu, &t) == false);
    TEST_ASSERT_TRUE(pwm_calc(84000000u, 1000000u, 500u, 0xFFFFu, &t));
    TEST_ASSERT_EQUAL_UINT32(83, t.psc);
    TEST_ASSERT_EQUAL_UINT32(1999, t.arr);
    TEST_ASSERT_EQUAL_UINT32(1500, pwm_us_to_ccr(1000000u, 1500));
}
Plain text

Для OneShot125 нужно более высокое разрешение. Таймер TIM1 с частотой 168 МГц и предделителем 20 отсчитывает 8 МГц, и импульс длительностью 125 мкс занимает 1000 отсчётов. Весь диапазон газа делится на тысячу ступеней.

↑ К оглавлению

5. Группы выходов

Регистры PSC и ARR общие для всех четырёх каналов таймера. Поэтому выходы одного таймера всегда работают с одинаковой частотой, а значит, и с одинаковым протоколом. Различаются только значения CCR.

Выход платыВыводТаймер и канал
двигатель 1PB0TIM3, канал 3
двигатель 2PB1TIM3, канал 4
двигатель 3PA2TIM2, канал 3
двигатель 4PA3TIM2, канал 4

Таблица составлена по файлу timer_config.cpp платы mamba-f405-mk2 в PX4. Первый и второй двигатели образуют одну группу, третий и четвёртый — другую. Если подключить к третьему выходу сервопривод с частотой 50 Гц, четвёртый выход тоже будет работать на частоте 50 Гц.

Настройка канала таймера

Файл hal/stm32f4/pwm_stm32.c

bool motor_pwm_init(uint32_t pwm_hz)
{
    pwm_timing_t t;
    if (!pwm_calc(84000000u, 1000000u, pwm_hz, 0xFFFFu, &t)) return false;

    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
    (void)RCC->APB1ENR;
    GPIOB->MODER = (GPIOB->MODER & ~(3u << 0)) | (2u << 0);   /* PB0: альтернативная функция */
    GPIOB->AFR[0] = (GPIOB->AFR[0] & ~0xFu) | 2u;             /* AF2 = TIM3 */

    TIM3->PSC  = t.psc;
    TIM3->ARR  = t.arr;
    TIM3->CCR3 = 0;                                  /* до разблокировки — нет импульсов */
    TIM3->CCMR2 = (6u << TIM_CCMR2_OC3M_Pos)         /* режим PWM 1 */
                | TIM_CCMR2_OC3PE;                   /* новое значение CCR — со следующего периода */
    TIM3->CCER = TIM_CCER_CC3E;                      /* включить выход канала */
    TIM3->CR1  = TIM_CR1_ARPE;
    TIM3->EGR  = TIM_EGR_UG;
    TIM3->CR1 |= TIM_CR1_CEN;
    return true;
}

void motor_pwm_write_us(uint32_t pulse_us)
{
    TIM3->CCR3 = pwm_us_to_ccr(1000000u, pulse_us);
}
Plain text

Биты OC3PE и ARPE включают буферизацию: новое значение CCR и ARR вступает в силу только в начале следующего периода. Без буферизации запись в середине периода может дать один импульс неправильной длительности.

↑ К оглавлению

6. Безопасность первого запуска

Регулятор частоты вращения ждёт импульсов минимальной длительности, чтобы перейти в рабочий режим. Пока импульсов нет, он подаёт звуковой сигнал об отсутствии управления. Это безопасное состояние, и BeeFC остаётся в нём до разблокировки.

Калибровка диапазона газа при включении: Многие регуляторы при появлении питания с импульсом максимальной длительности переходят в режим калибровки. После калибровки двигатель может сразу начать вращаться с большой частотой. Поэтому BeeFC после запуска не выдаёт импульсов вовсе, а после разблокировки начинает с 1000 мкс. Значение импульса, превышающее минимальное, при включении питания недопустимо.

Временная защита уровня «Старт» реализуется модулем ядра motor_guard. Полноценная логика разблокировки с проверками появится в уроке P11.

  1. После запуска двигатели заблокированы, на выходе нет импульсов.
  2. Команда консоли arm разблокирует двигатели и устанавливает импульс 1000 мкс.
  3. Команда motor 1 1100 задаёт длительность импульса, значение ограничивается диапазоном 1000–2000 мкс.
  4. Если в течение пяти секунд не поступило ни одной команды, двигатели блокируются автоматически.
  5. Команда disarm и любой символ, полученный во время работы двигателя, кроме цифр команды motor, блокируют двигатели немедленно.

Стенд для первого запуска

Двигатель закрепляется винтами на жёсткой пластине, пластина прижимается к столу струбцинами. Плоскость вращения винта закрывается защитным экраном из поликарбоната или сетки. Питание подаётся от лабораторного источника с ограничением тока или от аккумуляторной батареи через предохранитель.

Последовательность первого запуска: Сначала проверяется сигнал логическим анализатором без регулятора. Затем подключается регулятор с двигателем без воздушного винта. Воздушный винт устанавливается только после того, как проверена автоматическая блокировка через пять секунд.
↑ К оглавлению

7. Практика

Задание S8.1. Расчёт таймера

Продолжительность — 30 минут, выполняется на компьютере.

Реализовать функции pwm_calc и pwm_us_to_ccr.

Критерий автоматической проверки: тесты test_pwm_* пройдены: 500 Гц и 50 Гц на таймере 84 МГц, OneShot125 на таймере 168 МГц, отказ для 490 Гц и для периода, не помещающегося в 16-битный таймер.

Задание S8.2. Сигнал на выходе первого двигателя

Продолжительность — 40 минут, требуется логический анализатор.

Настроить выход первого двигателя своей платы на частоту 500 Гц. Логическим анализатором проверить частоту и длительность импульсов 1000, 1500 и 2000 мкс. Регулятор на этом шаге не подключается.

Критерий автоматической проверки: в модели Renode период сигнала на выходе первого двигателя равен 2000 ± 2 мкс, длительность импульса совпадает с заданной с точностью 1 мкс.

Задание S8.3. Модуль защиты двигателей

Продолжительность — 50 минут, выполняется на компьютере.

Реализовать модуль core/motor_guard по правилам из раздела «Безопасность первого запуска» и подключить его к консоли командами arm, disarm и motor.

Критерий автоматической проверки: скрытые тесты проверяют отсутствие импульсов до разблокировки, ограничение диапазона, автоматическую блокировку через пять секунд модельного времени и блокировку любым посторонним символом.

Задание S8.4. Первый запуск двигателя

Продолжительность — 60 минут, выполняется под наблюдением преподавателя или по видеосвязи.

Подключить регулятор и двигатель без воздушного винта, проверить разблокировку, задание оборотов и автоматическую блокировку. Затем установить воздушный винт и повторить проверку на импульсах до 1200 мкс. Записать в отчёт docs/s8_report.md, при какой длительности импульса двигатель начинает вращаться.

↑ К оглавлению

8. Как это сделано в больших прошивках

  • В Betaflight модуль motor.c выбирает протокол по настройке motorProtocol и обращается к драйверу через таблицу функций motorDevice.vTable. Это способ получить в языке C поведение, похожее на виртуальные методы C++. Драйвер ШИМ находится в pwm_output.c, таблица таймеров F4 — в timer_stm32f4xx.c.
  • В INAV драйвер pwm_output.c построен так же, а выходы распределяет модуль pwm_mapping.c: функция checkPwmTimerConflicts проверяет, не занят ли таймер другой функцией, а назначение «двигатель» или «сервопривод» выдаётся всем выходам таймера сразу.
  • В ArduPilot выходы объединены в группы pwm_group_list[], которые генерируются из описания платы hwdef.dat. Частота задаётся сразу для группы, и метод установки частоты применяет её ко всем каналам группы.
  • В PX4 модуль PWMOut.cpp получает группы из таблицы io_timers файла timer_config.cpp платы. По этой таблице в разделе «Группы выходов» определено, какие двигатели платы mamba-f405-mk2 делят один таймер.
↑ К оглавлению

9. Проверьте себя

  1. Рассчитайте предделитель и период для частоты 50 Гц при отсчёте 1 мкс на таймере 84 МГц.
  2. Почему выходы одного таймера не могут работать с разной частотой?
  3. Почему BeeFC после запуска не выдаёт на двигатели импульсов вовсе?
  4. Зачем включается буферизация регистров CCR и ARR?
  5. Как Betaflight выбирает драйвер протокола двигателей?
↑ К оглавлению

10. Чек-лист самопроверки

Результат: Все пункты выполнены. Можно переходить к уроку S9.
↑ К оглавлению

11. Источники

  1. Справочное руководство RM0090, разделы 17 и 18, таймеры TIM1, TIM8 и TIM2–TIM5.
  2. Указания по применению AN4013: введение в таймеры STM32.
  3. Описание таймеров платы mamba-f405-mk2 в PX4.
  4. Документация ArduPilot: подключение регуляторов и двигателей.
↑ К оглавлению

12. Что дальше

  • Следующий урок: S9. Аналого-цифровой преобразователь: напряжение и ток аккумуляторной батареи.
  • Полный перечень уроков приведён в статье «Курс BeeFC: содержание уровня „Старт“».

Проверено: 13.10.2026

↑ К оглавлению
  • мод. : Четверг, 15 октября 2026