Таймеры STM32: управление временем в микроконтроллере, примеры

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Разработчики и инженеры-встраиваемых систем
  • Студенты и обучающиеся в области программирования и электроники
  • Специалисты, интересующиеся микроконтроллерами STM32 и их применением

    Точное измерение времени и генерация сигналов с высокой точностью — критически важные задачи для многих встраиваемых систем. Таймеры в микроконтроллерах STM32 предоставляют мощный инструментарий, позволяющий решать эти задачи без перегрузки центрального процессора. От простой задержки в несколько миллисекунд до сложного управления двигателями через ШИМ — всё это становится возможным благодаря грамотному использованию таймеров. В статье я раскрою архитектуру таймеров STM32, методы их настройки и практические примеры применения. 🔄⏱️

Освоив базовые принципы работы с таймерами на STM32, вы можете значительно расширить свои навыки в области программирования встраиваемых систем. Это отличный трамплин для изучения других областей программирования. Для тех, кто хочет развиваться в сфере разработки, Обучение Python-разработке от Skypro открывает двери в мир высокоуровневого программирования, где ваши знания низкоуровневых систем станут конкурентным преимуществом при создании комплексных технических решений.

Архитектура и возможности таймеров STM32

Таймеры в микроконтроллерах STM32 представляют собой сложные периферийные устройства, способные выполнять широкий спектр задач по измерению времени и генерации сигналов. Их архитектура тщательно продумана инженерами ST Microelectronics для обеспечения максимальной гибкости при минимальном вмешательстве центрального процессора. 🧩

Микроконтроллеры семейства STM32 содержат различные типы таймеров, каждый из которых оптимизирован для определённых задач:

  • Базовые таймеры – простейшие счётчики с возможностью генерации прерываний
  • Таймеры общего назначения – многофункциональные устройства с поддержкой различных режимов счёта, захвата и сравнения
  • Расширенные таймеры – наиболее функциональные, с поддержкой комплементарных выходов, мёртвого времени и других продвинутых функций для управления силовыми преобразователями
  • Низкопотребляющие таймеры – работают даже в режимах пониженного энергопотребления

Все таймеры STM32 построены вокруг счётчика, который может инкрементироваться или декрементироваться с заданной частотой. Предделитель (prescaler) и автоматическая перезагрузка (auto-reload) позволяют точно настраивать период таймера.

Тип таймера Разрядность Каналы захвата/сравнения Дополнительные возможности
Базовый (TIM6, TIM7) 16 бит 0 Только счёт и прерывания
Общего назначения (TIM2-TIM5) 16/32 бит 4 ШИМ, захват, энкодер
Расширенный (TIM1, TIM8) 16 бит 4 Комплементарные выходы, мёртвое время
Низкого потребления (LPTIM) 16 бит 1-2 Работа в режимах пониженного энергопотребления

Особую ценность таймерам STM32 придают блоки захвата/сравнения (Capture/Compare). Они позволяют:

  • Фиксировать момент поступления внешнего события (захват)
  • Генерировать прерывание или сигнал при совпадении счётчика с заданным значением (сравнение)
  • Формировать ШИМ-сигналы различной конфигурации
  • Измерять длительность импульсов и частоту внешних сигналов

Большинство таймеров STM32 также поддерживают различные режимы синхронизации, что позволяет создавать каскады таймеров для решения комплексных задач измерения и генерации сигналов.

Пошаговый план для смены профессии

Инициализация таймеров в проектах STM32: базовые шаги

Алексей Петров, инженер-разработчик встраиваемых систем

Помню свой первый проект с использованием таймеров STM32. Мне требовалось реализовать точное измерение интервалов времени для системы сбора данных. Казалось бы, простая задача, но я потратил целый день, пытаясь понять, почему мой таймер не запускается. Оказалось, я забыл включить тактирование периферии в RCC! Такая элементарная ошибка, но она часто встречается у новичков. Теперь я всегда проверяю включение тактирования первым делом при отладке таймеров. Этот опыт научил меня быть более методичным при инициализации периферии STM32 — составлять мысленный чек-лист и следовать ему шаг за шагом.

Инициализация таймеров на микроконтроллерах STM32 требует последовательного выполнения определённых шагов. Хотя для упрощения разработки мы часто используем библиотеки HAL или CubeMX, понимание базовых принципов инициализации критически важно. 🔧

Процесс настройки таймера можно разделить на несколько ключевых этапов:

  1. Включение тактирования – без этого шага таймер просто не будет работать
  2. Настройка предделителя и периода – определяет частоту работы таймера
  3. Конфигурация режима работы – выбор направления счёта, режима счёта и т.д.
  4. Настройка каналов – если требуется использование ШИМ или захвата
  5. Конфигурация прерываний – если требуются обработчики событий таймера
  6. Запуск таймера – активация счётчика

Рассмотрим базовый пример инициализации таймера TIM2 для генерации прерывания с периодом 1 мс:

c
Скопировать код
// 1. Включаем тактирование таймера TIM2
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;

// 2. Настраиваем предделитель для получения тактовой частоты 1 МГц
// Предположим, что частота шины APB1 равна 72 МГц
TIM2->PSC = 72 – 1; // Предделитель = 72, получаем 1 МГц

// 3. Настраиваем период таймера на 1 мс (1000 тактов при частоте 1 МГц)
TIM2->ARR = 1000 – 1;

// 4. Разрешаем прерывание по переполнению таймера
TIM2->DIER |= TIM_DIER_UIE;

// 5. Настраиваем приоритет и разрешаем прерывание в NVIC
NVIC_SetPriority(TIM2_IRQn, 1);
NVIC_EnableIRQ(TIM2_IRQn);

// 6. Запускаем таймер
TIM2->CR1 |= TIM_CR1_CEN;

При использовании библиотеки HAL тот же процесс выглядит более структурированным:

c
Скопировать код
TIM_HandleTypeDef htim2;

void MX_TIM2_Init(void)
{
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72 – 1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 1000 – 1;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim2);

HAL_TIM_Base_Start_IT(&htim2);
}

// Обработчик прерывания
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2)
{
// Действия по прерыванию таймера
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
}
}

При инициализации таймеров критически важно правильно рассчитать значения предделителя (PSC) и автоматической перезагрузки (ARR). Эти параметры определяются по формулам:

Параметр Формула расчёта Пример
Частота таймера Ftim = Fclk / (PSC + 1) 72 МГц / 72 = 1 МГц
Период таймера Ttim = (ARR + 1) / Ftim 1000 / 1 МГц = 1 мс
Частота прерываний Fint = Ftim / (ARR + 1) 1 МГц / 1000 = 1 кГц
Максимальное значение счётчика 2^16-1 для 16-битных таймеров 65535

Важно помнить о возможных ограничениях:

  • Предделитель и значение автоматической перезагрузки для 16-битных таймеров не могут превышать 65535
  • Реальная частота тактирования таймера зависит от конфигурации PLL и делителей тактовой частоты
  • Некоторые таймеры (например, TIM2 и TIM5 в определённых сериях STM32) являются 32-битными, что позволяет использовать гораздо большие значения счётчика

Режимы работы таймеров: от счётчика до ШИМ

Таймеры STM32 поддерживают множество режимов работы, что делает их одним из самых гибких периферийных устройств в микроконтроллерах этого семейства. Каждый режим оптимизирован для решения определённого класса задач, от простого отсчёта времени до сложного управления силовой электроникой. 💡

Основные режимы работы таймеров включают:

  • Базовый счётчик – инкрементирование или декрементирование значения с заданной частотой
  • Режим входного захвата (Input Capture) – измерение длительности внешних импульсов
  • Режим выходного сравнения (Output Compare) – генерация сигналов по достижении счётчиком заданного значения
  • Широтно-импульсная модуляция (ШИМ) – генерация сигналов с регулируемой скважностью
  • Режим энкодера – декодирование сигналов инкрементальных энкодеров
  • One-pulse mode – генерация одиночного импульса заданной длительности

Рассмотрим подробнее реализацию некоторых из этих режимов.

Базовый счётчик

Простейший режим работы таймера – счётчик, который может использоваться для создания задержек или периодических событий. Пример конфигурации с использованием HAL:

c
Скопировать код
// Инициализация таймера как простого счётчика
void ConfigureBasicTimer(void)
{
htim3.Instance = TIM3;
htim3.Init.Prescaler = 72 – 1;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 1000 – 1;
HAL_TIM_Base_Init(&htim3);

// Запуск таймера
HAL_TIM_Base_Start(&htim3);
}

// Создание программной задержки с использованием таймера
void TimerDelay_ms(uint32_t ms)
{
uint32_t startTick = __HAL_TIM_GET_COUNTER(&htim3);

while((__HAL_TIM_GET_COUNTER(&htim3) – startTick) < ms)
{
// Ждем нужное количество миллисекунд
}
}

Режим захвата (Input Capture)

Режим захвата позволяет измерять длительность внешних импульсов или периодов сигналов. Этот режим часто используется для измерения частоты, длительности импульса или фазового сдвига.

c
Скопировать код
// Настройка таймера в режиме захвата
void ConfigureInputCapture(void)
{
TIM_IC_InitTypeDef sConfigIC;

htim2.Instance = TIM2;
htim2.Init.Prescaler = 72 – 1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0xFFFF; // Максимальное значение для 16-битного таймера
HAL_TIM_IC_Init(&htim2);

// Конфигурация канала 1 на захват по нарастающему фронту
sConfigIC.ICPolarity = TIM_ICPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1);

// Запуск захвата
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
}

// Обработчик прерывания при захвате
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2)
{
uint32_t captureValue = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
// Обработка захваченного значения
}
}

Широтно-импульсная модуляция (ШИМ)

ШИМ – один из наиболее востребованных режимов таймеров STM32, позволяющий управлять мощностью нагрузки, яркостью светодиодов, скоростью двигателей и многими другими параметрами. STM32 поддерживает несколько режимов ШИМ:

  1. Edge-aligned PWM – счётчик считает вверх до ARR, затем сбрасывается
  2. Center-aligned PWM – счётчик считает вверх до ARR, затем вниз до 0
  3. Combined PWM – специальный режим для расширенных таймеров, позволяющий создавать сложные последовательности сигналов

Пример настройки таймера для генерации ШИМ-сигнала:

c
Скопировать код
// Настройка таймера для генерации ШИМ-сигнала
void ConfigurePWM(void)
{
TIM_OC_InitTypeDef sConfigOC;

htim1.Instance = TIM1;
htim1.Init.Prescaler = 72 – 1;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 1000 – 1; // 1000 шагов для ШИМ (0.1% разрешение)
HAL_TIM_PWM_Init(&htim1);

// Конфигурация канала 1
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 500; // Скважность 50%
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);

// Запуск ШИМ
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
}

// Функция для изменения скважности ШИМ
void SetPWMDuty(uint32_t dutyCycle) // dutyCycle: 0-1000
{
if(dutyCycle > 1000) dutyCycle = 1000;

__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, dutyCycle);
}

Режим энкодера (Encoder Mode)

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

c
Скопировать код
// Настройка таймера в режиме энкодера
void ConfigureEncoderMode(void)
{
TIM_Encoder_InitTypeDef sEncoderConfig;

htim4.Instance = TIM4;
htim4.Init.Prescaler = 0;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 65535; // Максимальное значение для 16-битного таймера

// Конфигурация режима энкодера
sEncoderConfig.EncoderMode = TIM_ENCODERMODE_TI12;
sEncoderConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
sEncoderConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
sEncoderConfig.IC1Prescaler = TIM_ICPSC_DIV1;
sEncoderConfig.IC1Filter = 10; // Фильтрация помех

sEncoderConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
sEncoderConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
sEncoderConfig.IC2Prescaler = TIM_ICPSC_DIV1;
sEncoderConfig.IC2Filter = 10; // Фильтрация помех

HAL_TIM_Encoder_Init(&htim4, &sEncoderConfig);

// Запуск режима энкодера
HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_ALL);
}

// Чтение положения энкодера
int32_t ReadEncoderPosition(void)
{
return (int32_t)__HAL_TIM_GET_COUNTER(&htim4);
}

Выбор конкретного режима работы таймера зависит от требований приложения. Часто один и тот же таймер может использовать разные режимы для различных каналов одновременно, что позволяет максимально эффективно использовать ресурсы микроконтроллера.

Программирование прерываний таймеров STM32

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

Таймеры STM32 могут генерировать прерывания по различным событиям:

  • Обновление (Update) – при переполнении счётчика или его сбросе
  • Захват (Capture) – когда происходит захват значения по внешнему событию
  • Сравнение (Compare) – когда счётчик достигает заданного значения сравнения
  • Триггер (Trigger) – по событию от другого таймера
  • Ошибка (Break) – по сигналу экстренного отключения (для расширенных таймеров)

Иван Соколов, ведущий инженер-программист

В проекте управления многоосевой системой позиционирования мы столкнулись с интересной проблемой. Система периодически сбоила — двигатели останавливались без видимых причин. Всё выглядело так, будто микроконтроллер зависал. Мониторинг показал, что проблема в конфликте прерываний — прерывание от одного таймера блокировалось другим, более приоритетным. Мы решили эту проблему, пересмотрев архитектуру прерываний. Для каждого таймера установили соответствующий приоритет с учётом критичности функций. Добавили защиту от длительных вычислений в обработчиках — критичные операции вынесли в основной цикл, а в прерываниях только устанавливали флаги. Эта реорганизация полностью устранила проблему.

Рассмотрим базовую настройку прерывания по переполнению таймера с использованием регистров:

c
Скопировать код
// Настройка прерывания по обновлению таймера
void ConfigureTimerInterrupt(void)
{
// 1. Включаем тактирование таймера
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;

// 2. Настраиваем предделитель и период
TIM3->PSC = 72 – 1; // Предделитель на 72 МГц -> 1 МГц
TIM3->ARR = 1000 – 1; // Период 1 мс

// 3. Разрешаем прерывание по обновлению
TIM3->DIER |= TIM_DIER_UIE;

// 4. Настраиваем приоритет и разрешаем прерывание в NVIC
NVIC_SetPriority(TIM3_IRQn, 2); // Устанавливаем приоритет 2
NVIC_EnableIRQ(TIM3_IRQn); // Разрешаем прерывание

// 5. Запускаем таймер
TIM3->CR1 |= TIM_CR1_CEN;
}

// Обработчик прерывания
void TIM3_IRQHandler(void)
{
if(TIM3->SR & TIM_SR_UIF)
{
// Сбрасываем флаг прерывания
TIM3->SR &= ~TIM_SR_UIF;

// Действия по прерыванию
GPIOB->ODR ^= GPIO_ODR_OD5; // Переключаем светодиод на PB5
}
}

Тот же функционал с использованием HAL выглядит следующим образом:

c
Скопировать код
TIM_HandleTypeDef htim3;

// Настройка прерывания с использованием HAL
void ConfigureTimerInterruptHAL(void)
{
htim3.Instance = TIM3;
htim3.Init.Prescaler = 72 – 1;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 1000 – 1;
HAL_TIM_Base_Init(&htim3);

// Запуск таймера с прерываниями
HAL_TIM_Base_Start_IT(&htim3);
}

// Обработчик прерывания HAL
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM3)
{
// Действия по прерыванию
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); // Переключаем светодиод на PB5
}
}

При работе с прерываниями таймеров важно учитывать следующие аспекты:

  1. Приоритеты прерываний – правильная настройка приоритетов критически важна для стабильной работы системы
  2. Время выполнения обработчиков – длительные операции в обработчиках могут нарушить работу системы
  3. Флаги прерываний – необходимо корректно сбрасывать флаги, чтобы избежать зацикливания прерываний
  4. Конфликты ресурсов – если несколько прерываний используют общие ресурсы, необходима синхронизация

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

Событие прерывания Флаг в регистре SR Бит разрешения в DIER Типичное применение
Обновление (Update) TIMSRUIF TIMDIERUIE Периодические задачи, таймауты
Захват канал 1 (Capture CH1) TIMSRCC1IF TIMDIERCC1IE Измерение длительности импульсов
Сравнение канал 1 (Compare CH1) TIMSRCC1IF TIMDIERCC1IE Генерация сигналов в заданное время
Триггер (Trigger) TIMSRTIF TIMDIERTIE Синхронизация нескольких таймеров

Стоит отметить, что в STM32 также существует возможность генерации DMA-запросов вместо прерываний, что может быть полезно для высокоскоростной передачи данных без участия процессора.

Практические проекты с использованием таймеров STM32

Теоретические знания о таймерах STM32 приобретают реальную ценность только при их практическом применении. Рассмотрим несколько проектов, демонстрирующих решение типичных задач с использованием различных возможностей таймеров. 🛠️

Проект 1: Универсальный генератор импульсов

Этот проект демонстрирует использование таймера для генерации прямоугольных импульсов с программируемой частотой и скважностью. Такой генератор может применяться для тестирования различных устройств, генерации тактовых сигналов или управления периферийными устройствами.

c
Скопировать код
// Инициализация таймера TIM1 для генерации ШИМ
void PulseGenerator_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;

// Включение тактирования
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();

// Конфигурация GPIO для выхода ШИМ (PA8 = TIM1_CH1)
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

// Настройка таймера
htim1.Instance = TIM1;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim1);

// Настройка канала ШИМ
TIM_OC_InitTypeDef sConfigOC;
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);

// Запуск ШИМ
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);

// Разрешение выходов для расширенного таймера
__HAL_TIM_MOE_ENABLE(&htim1);
}

// Установка частоты и скважности генератора
void PulseGenerator_SetParams(uint32_t frequency, uint8_t dutyCycle)
{
uint32_t period, pulse;
uint16_t prescaler;

// Расчёт параметров для заданной частоты
if(frequency <= 1000) // Низкие частоты (до 1 кГц)
{
prescaler = 7200 – 1; // 72 МГц / 7200 = 10 кГц
period = 10000 / frequency – 1;
}
else if(frequency <= 10000) // Средние частоты (до 10 кГц)
{
prescaler = 720 – 1; // 72 МГц / 720 = 100 кГц
period = 100000 / frequency – 1;
}
else // Высокие частоты (выше 10 кГц)
{
prescaler = 72 – 1; // 72 МГц / 72 = 1 МГц
period = 1000000 / frequency – 1;
}

// Расчёт длительности импульса для заданной скважности
pulse = (period + 1) * dutyCycle / 100;

// Применение новых параметров
__HAL_TIM_SET_PRESCALER(&htim1, prescaler);
__HAL_TIM_SET_AUTORELOAD(&htim1, period);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, pulse);
}

Проект 2: Частотомер на основе захвата

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

c
Скопировать код
volatile uint32_t captureValue1 = 0;
volatile uint32_t captureValue2 = 0;
volatile uint8_t captureDone = 0;

// Инициализация таймера TIM2 для захвата
void FrequencyMeter_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;

// Включение тактирования
__HAL_RCC_TIM2_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();

// Конфигурация GPIO для входа захвата (PA0 = TIM2_CH1)
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

// Настройка таймера
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72 – 1; // 72 МГц / 72 = 1 МГц
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0xFFFFFFFF; // Максимальное значение для 32-битного таймера
HAL_TIM_IC_Init(&htim2);

// Настройка канала захвата
TIM_IC_InitTypeDef sConfigIC;
sConfigIC.ICPolarity = TIM_ICPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1);

// Запуск захвата с прерываниями
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
}

// Обработчик прерывания захвата
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
if(captureDone == 0)
{
captureValue1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
captureDone = 1;
}
else
{
captureValue2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
captureDone = 2;
}
}
}

// Измерение частоты
float FrequencyMeter_GetFrequency(void)
{
float frequency = 0.0f;

if(captureDone == 2)
{
// Расчет периода в тактах таймера
uint32_t period;
if(captureValue2 > captureValue1)
period = captureValue2 – captureValue1;
else
period = (0xFFFFFFFF – captureValue1) + captureValue2 + 1;

// Расчет частоты (F = 1/T)
frequency = 1000000.0f / period; // 1 МГц = 1000000 Гц

// Подготовка к следующему измерению
captureDone = 0;
}

return frequency;
}

Проект 3: Управление сервоприводом

Этот проект демонстрирует использование таймера для управления стандартным сервоприводом, что применяется в робототехнике, моделизме и автоматизации. Сервоприводы требуют специального ШИМ-сигнала с периодом около 20 мс и длительностью импульса от 1 до 2 мс для управления положением.

c
Скопировать код
// Инициализация таймера для управления сервоприводом
void Servo_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;

// Включение тактирования
__HAL_RCC_TIM4_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();

// Конфигурация GPIO для выхода ШИМ (PB6 = TIM4_CH1)
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

// Настройка таймера для периода 20 мс
htim4.Instance = TIM4;
htim4.Init.Prescaler = 72 – 1; // 72 МГц / 72 = 1 МГц
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 20000 – 1; // 20000 тактов = 20 мс
HAL_TIM_PWM_Init(&htim4);

// Настройка канала ШИМ
TIM_OC_InitTypeDef sConfigOC;
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 1500; // 1.5 мс – среднее положение
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1);

// Запуск ШИМ
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
}

// Установка угла сервопривода (0-180 градусов)
void Servo_SetAngle(uint8_t angle)
{
uint32_t pulse;

// Ограничение угла от 0 до 180 градусов
if(angle > 180) angle = 180;

// Преобразование угла в длительность импульса
// 0° = 1000 мкс, 90° = 1500 мкс, 180° = 2000 мкс
pulse = 1000 + (angle * 1000 / 180);

// Установка новой длительности импульса
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, pulse);
}

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

Важно помнить, что эффективное использование таймеров часто требует комбинирования различных режимов и возможностей. Например, один таймер может одновременно генерировать ШИМ-сигнал и периодические прерывания, что позволяет создавать сложные алгоритмы управления с минимальной нагрузкой на процессор.

Мастерство работы с таймерами STM32 открывает широкие возможности для создания точных, эффективных и надёжных встраиваемых систем. От простых задач измерения времени до сложного управления силовой электроникой — таймеры остаются одним из наиболее мощных и универсальных периферийных устройств в арсенале разработчика. Внимание к деталям при их настройке и глубокое понимание принципов работы позволит вам создавать решения, оптимальные по производительности, энергопотреблению и стоимости.

Читайте также

Проверь как ты усвоил материалы статьи
Пройди тест и узнай насколько ты лучше других читателей
Какую функцию выполняет предделитель в настройке базового таймера на STM32?
1 / 5

Загрузка...