STM32 и Timer Input Capture. Режим захвата сигнала.

Всем привет! Сегодня мы снова займемся работой с таймерами в STM32 и рассмотрим один из многочисленных режимов работы, о котором раньше, по какой-то причине, у нас на сайте статей не было 🙂 Речь идет о режиме захвата сигнала (Input Capture) – крайне полезном и очень часто используемом режиме. Сначала рассмотрим, в чем, в принципе, заключается работа таймера при захвате сигнала, а затем создадим программу, которая все это использует. Причем просто отслеживать сигнал на входе не так интересно, поэтому усложним задачу – будем измерять параметры подаваемого ШИМ сигнала!

Итак, идея режима Input Capture заключается в следующем. На порт микроконтроллера, который настроен в качестве входа таймера для захвата, подается сигнал. При изменении уровня сигнала в регистр таймера CCRx записывается текущее значение счетного регистра этого таймера. Таким образом, можно отследить момент прихода импульса, либо определить время между двумя последовательными импульсами, либо же, чем мы сейчас и займемся, измерить период и длительность импульса (время включения) ШИМ-сигнала.

У каждого из каналов таймера в STM32 есть свой собственный регистр CCR, в котором и будет сохраняться значение счетчика в момент прихода импульса:

  • Канал 1 – регистр CCR1
  • Канал 2 – регистр CCR2
  • Канал 3 – регистр CCR3
  • Канал 4 – регистр CCR4

Кроме того, два канала таймера можно настроить так, чтобы они оба использовали сигнал с одной и той же ножки контроллера. То есть сигнал у нас один, заходит он на один вывод контроллера, а дальше уже к нему имеют доступ два канала таймера. В этом случае один из каналов настраивается на работу в прямом режиме (direct), а второй – в косвенном (indirect).

Собственно, именно совместная работа каналов таймера нам и потребуется для измерения периода и длительности импульса ШИМ-сигнала:

ШИМ-сигнал.

Алгоритм будет такой:

  • по приходу заднего фронта импульса, 2-ой канал таймера захватывает значение счетчика и сохраняет его в регистр CCR2.
  • по приходу переднего фронта аналогичные действия производит 1-ый канал таймера.
  • и самое главное – нам нужно по приходу переднего фронта считать значения из регистров CCR1 и CCR2 и обнулить счетный регистр таймера CNT.

Таким образом, значение регистра CCR1 будет соответствовать периоду сигнала, а значение CCR2 – длительности импульса:

Измерение периода и длительности импульса ШИМ-сигнала.

Вот и все, приступаем к реализации!

Для начала, выбираем в STM32CubeMx таймер, который будет использоваться для тестирования режима захвата сигнала, например, Timer 2, и настраиваем два из его каналов на работу в прямом и косвенном режимах:

STM32CubeMx режим захвата сигнала.

Обратите внимание, Cube активировал ножку PA0 для работы в качестве входа для таймера:

STM32CubeMx, вход для захвата сигнала.

Далее выставляем “традиционные” настройки таймера, а именно, предделитель частоты и период. В данном примере мы не будем использовать событие переполнения таймера, поэтому период, по большому счету, роли не играет. Но! Длительность периода должна быть больше, чем период измеряемого сигнала, иначе таймер обнулит свой счетчик автоматически еще до прихода фронта и измеренное значение окажется неверным:

Переполнение таймера.

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

Настройки таймера в STM32CubeMx.

При таком значении предделителя частота таймера составит 1 МГц. Тогда один “тик” таймера соответствует 1 мкс. При величине периода 30000 получаем период, равный 30 мс. А значит период подаваемого сигнала для этих конкретных значений не должен превышать 30 мс.

А теперь переходим к специфичным настройкам таймера, которые используются исключительно для режима Input Capture:

Настройки режима захвата сигнала.

Нас тут интересует, в первую очередь, полярность сигнала – передний фронт для канала 1 и задний фронт для канала 2. Кроме того, можно настроить:

  • Prescaler Division Ratio – дополнительный предделитель входного сигнала. Если указать, к примеру, значение 4, то захват будет производиться по каждому 4-му фронту. В данном случае нам необходимо захватывать все импульсы без исключения.
  • Input Filter – позволяет настроить фильтрацию входного сигнала. Работает этот фильтр по стандартной схеме – подтверждает наличие сигнала на входе только по прошествии некоторого времени (некоторого количества циклов таймера). То есть, при изменении сигнала на входе фильтр в течение N циклов наблюдает за новым уровнем сигнала. И, если, в течении этих N отсчетов сигнал сохраняет свой новый уровень, то фильтр передает информацию об изменении уровня дальше. Это позволяет убрать случайные всплески и возможную нестабильность сигнала на входе.

Так, теперь включаем глобальное прерывание таймера и на этом заканчиваем настройку периферии:

Прерывания таймера.

Переходим к сгенерированному проекту! В общем-то, мы обсудили на словах все необходимые действия, теперь просто реализуем в программе. Первый делом включаем выбранные каналы таймера на работу в режиме Input Capture:

  HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
  HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2);

И добавляем callback для прерывания по захвату сигнала:

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if (htim->Instance == TIM2)
	{
		if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
		{
			period = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1);
			pulseWidth = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_2);
      
			TIM2->CNT = 0;
		}
	}
}

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

if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)

Значение в регистре CCR2 второго канала сохраняется аппаратно по приходу заднего фронта. Так что считываем значения CCR1 и CCR2 и получаем период (period) и длительность (pulseWidth) ШИМ-сигнала:

period = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1);
pulseWidth = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_2);

И, конечно, же обнуляем счетный регистр таймера:

TIM2->CNT = 0;

Результаты, соответственно, в отсчетах таймера, что в данном примере составляет 1 мкс. Собираем проект и запускаем отладку! В соответствующих переменных будут отображаться измеренные значения 🙂

Все работает отлично, так что с режимом захвата сигнала заканчиваем, до скорых встреч в новых статьях!

Ссылка на проект – MT_Timer_IC_Example.

Поделиться!

Подписаться
Уведомление о
guest
6 комментариев
старее
новее большинство голосов
Inline Feedbacks
View all comments
Павел
Павел
3 месяцев назад

Результат в микросекундах?

Сергей
Сергей
1 месяц назад

Спасибо вам за ваш труд!
Вы очень помогли мне(уверен, что не только мне) в освоении HAL!
Мне начинает нравиться STM!!!)))))

Сергей
Сергей
Reply to  Aveal
1 месяц назад

Разрешите маленькое примечание:
Фактически нижнюю границу измеряемой частоты можно вычислять, как частота тактирования/counter period.
К примеру для 8000000/65535=122Гц
Если нужно меньше то ставим Prescaler например 4.
После этого частота тактирования станет равна 2000000.
Соответственно нижний порог станет 2000000/65535=30Гц

Last edited 1 месяц назад by Сергей

Присоединяйтесь!

Profile Profile Profile Profile Profile
Vkontakte
Twitter

Язык сайта

Ноябрь 2020
Пн Вт Ср Чт Пт Сб Вс
 1
2345678
9101112131415
16171819202122
23242526272829
30  

© 2013-2020 MicroTechnics.ru