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

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

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

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, TIM8 APB2 168 МГц 16 бит, расширенные возможности
TIM2, TIM5 APB1 84 МГц 32 бита
TIM3, TIM4 APB1 84 МГц 16 бит
TIM9–TIM11 APB2 168 МГц 16 бит
TIM12–TIM14 APB1 84 МГц 16 бит
Режим ШИМ таймера при отсчёте 1 мкс: счётчик CNT, регистры ARR и CCR и сигнал на выходе канала частотой 400 Гц.
Режим ШИМ таймера при отсчёте 1 мкс: счётчик CNT, регистры ARR и CCR и сигнал на выходе канала частотой 400 Гц.
↑ К оглавлению

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

СигналЧастотаДлительность импульсаПрименение
сервопривод 50 Гц 1000–2000 мкс рулевые поверхности самолёта, подвес камеры
ШИМ регулятора до 490 Гц 1000–2000 мкс регуляторы с аналоговым входом
OneShot125 2 кГц в BeeFC 125–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.

Выход платыВыводТаймер и канал
двигатель 1 PB0 TIM3, канал 3
двигатель 2 PB1 TIM3, канал 4
двигатель 3 PA2 TIM2, канал 3
двигатель 4 PA3 TIM2, канал 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

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