Встраиваемые системы

Таймеры и прерывания

Каждый раз когда нажимается кнопка на клавиатуре, чип контроллера не сидит в цикле проверяя «нажата ли?» - он занимается другими задачами и узнаёт о нажатии через прерывание за микросекунды. Без прерываний не было бы отзывчивых интерфейсов, точного времени в часах, управления BLDC моторами в дронах и безопасного ABS в автомобилях.

  • **ABS в автомобиле**: датчики колёс генерируют прерывания тысячи раз в секунду. MCU (обычно ARM Cortex-M4) должен обработать каждое за <100 мкс, иначе блокировка колеса - потеря управления.
  • **Arduino/ESP32 сервоуправление**: Arduino IDE скрывает сложность, но `Servo.write()` за кулисами настраивает Timer1 на 50 Hz PWM с точностью ±1 мкс через прерывания overflow.
  • **RTOS задачи**: FreeRTOS на STM32 использует SysTick таймер (1 мс прерывание) для переключения задач. Без этого таймера невозможна многозадачность на bare-metal системах.

Прерывания: что это и зачем

Без прерываний микроконтроллер может только polling: крутиться в цикле и постоянно проверять состояние периферии. На STM32 с частотой 168 MHz: CPU тратит тысячи циклов на проверку «нажата ли кнопка?», пока полезная работа стоит. Прерывание решает это принципиально иначе.

**Прерывание (Interrupt)** - аппаратный сигнал, который приостанавливает текущее выполнение программы и передаёт управление специальному обработчику (ISR). После ISR выполнение возобновляется с той же точки. Источники: таймер сработал, UART получил байт, GPIO изменил состояние, ADC завершил преобразование.

Векторная таблица прерываний (IVT) - массив указателей на ISR-функции, хранящийся в начале Flash-памяти. ARM Cortex-M имеет до 240 внешних прерываний + 16 системных (SysTick, HardFault, NMI). Каждое прерывание имеет приоритет - более приоритетные могут прерывать обработку менее приоритетных.

Что происходит с регистрами CPU когда возникает прерывание?

ISR: правила написания обработчиков прерываний

Interrupt Service Routine (ISR) - функция, вызываемая при прерывании. Главное правило: ISR должна быть **максимально короткой**. Пока выполняется ISR, другие прерывания могут быть заблокированы (или задержаны), что критично для real-time систем.

  • **volatile**: все переменные, разделяемые между ISR и основным кодом, должны быть `volatile` - иначе компилятор кэширует значение в регистре.
  • **Только флаги**: ISR устанавливает флаг/записывает данные в буфер. Обработка - в main loop или задаче RTOS.
  • **Никаких blocking calls**: no `delay()`, no `printf()`, no mutex.lock() в ISR - блокировка в ISR = deadlock.
  • **Атомарность**: доступ к многобайтным переменным требует отключения прерываний или atomic операций.
  • **Очистка флага**: аппаратный флаг прерывания нужно очищать вручную (в STM32: `__HAL_TIM_CLEAR_FLAG()`).

Переменная `count` обновляется в ISR и читается в main(). Нужен ли `volatile`?

Режимы работы таймеров

Таймер в микроконтроллере - счётчик, тактируемый от системной частоты. STM32F4 имеет 14 таймеров с различными возможностями. Базовый принцип: таймер считает от 0 до ARR (Auto-Reload Register), при переполнении генерирует прерывание или событие.

  • **Up counting**: 0 → ARR → overflow → 0. Прерывание при overflow. Используется для периодических задач.
  • **Down counting**: ARR → 0 → underflow → ARR. Прерывание при underflow.
  • **Up-Down (center-aligned)**: 0 → ARR → ARR → 0. Используется для симметричного PWM.
  • **One-pulse mode**: однократный отсчёт и стоп. Для генерации одиночных импульсов.
  • **Input capture**: захват значения счётчика при событии на входе. Для измерения частоты и длительности импульсов.
  • **Output compare**: действие при совпадении счётчика с CCR (compare register). Основа PWM.

На STM32 с частотой APB1 = 84 MHz нужно прерывание каждые 10 мс. Prescaler=840-1, ARR=?

PWM: широтно-импульсная модуляция

**PWM (Pulse Width Modulation)** - техника управления мощностью через изменение скважности цифрового сигнала. Duty cycle D% = ton/T * 100%. Средняя мощность = D% * Vmax. Серво-мотор ожидает импульс 1-2 мс при периоде 20 мс (50 Hz): 1 мс = 0° (crайнее левое), 1.5 мс = 90° (центр), 2 мс = 180°.

В STM32 PWM реализуется через Output Compare в режиме PWM1/PWM2. CCR (Capture Compare Register) задаёт момент переключения. Если счётчик < CCR: выход HIGH. Если >= CCR: LOW. Duty cycle = CCR/ARR * 100%.

BLDC моторы (в дронах DJI, электроскутерах) управляются трёхфазным PWM от ESC (Electronic Speed Controller). ESP32 имеет встроенный LEDC (LED Controller) модуль для PWM с разрешением до 16 бит - идеально для диммирования LED и управления моторами.

Прерывания всегда быстрее polling - нужно использовать прерывания везде

При очень высокой частоте событий (>1 MHz) overhead на context save/restore делает polling быстрее. DMA (Direct Memory Access) часто лучший выбор для высокоскоростных передач

ARM Cortex-M: вход в прерывание занимает 12-70 циклов CPU (context save + pipeline flush). На 168 MHz это 70-400 нс. Если UART работает на 10 Mbit/s (один байт каждые 800 нс), прерывание на каждый байт невозможно - нужен DMA.

PWM сигнал с частотой 1 кГц и duty cycle 30% управляет LED. Какова средняя яркость?

Ключевые идеи

  • **Прерывание** останавливает CPU, сохраняет контекст, выполняет ISR, восстанавливает контекст - всё автоматически на ARM Cortex-M.
  • **ISR правила**: короткая, только флаги/буфер, volatile для shared переменных, никаких блокировок.
  • **Таймер** = счётчик с prescaler и ARR. f_timer = f_cpu / (PSC+1), f_event = f_timer / (ARR+1).
  • **PWM** = Output Compare: duty cycle = CCR/ARR * 100%. 50 Hz для серво, 1-20 кГц для моторов, >100 Hz для LED диммирования.

Связанные темы

Таймеры и прерывания - фундамент для всей real-time периферии:

  • ADC и аналоговые интерфейсы — ADC завершение преобразования генерирует прерывание; DMA + таймер-триггер = непрерывная оцифровка без участия CPU
  • RTOS и планировщик задач — FreeRTOS/Zephyr используют SysTick таймер (1 мс прерывание) как основу планировщика задач

Вопросы для размышления

  • При частоте прерываний 100 кГц overhead context save занимает значимую долю CPU времени на Cortex-M4 168 MHz. При каком пороге частоты прерываний переходить на DMA?
  • ISR устанавливает флаг, main() читает его. Нужна ли атомарная операция при чтении флага на 32-битном ARM Cortex-M если флаг - uint8_t?
  • PWM с duty cycle 50% управляет нагревательным элементом. Почему низкая частота PWM (1 Hz) лучше для нагревателя, но хуже для LED-диммера?

Связанные уроки

  • os-02-processes
Таймеры и прерывания

0

1

Войти