Апр 022013
 

В данном режиме обнаружение изменения уровня сигнала на входе микроконтроллера приводит к “захвату” содержимого счетного регистра таймера. Это позволяет измерить период входного сигнала или длительность импульса. Измерение же обеих этих величин дает представление о параметрах входного сигнала с широтно-импульсной модуляцией. Для таймера общего назначения в режиме захвата можно использовать довольно гибкие настройки этого режима, поскольку набор регистров и схема входного каскада таймера дают такую возможность.

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

Входной каскад

Самым первым модулем в схеме является цифровой входной фильтр. На входе TI1 фиксируется изменение уровня входного сигнала. На вход Fdts поступает сигнал опорной частоты, которая является исходной для задания параметров фильтра. Входной сигнал может иметь не совсем четкий фронт, с присутствием переходных процессов, наличием «дребезга» или затянутый по времени. При этом могут возникнуть ложные старты и остановы «захвата». Для определения достоверности изменения уровня на входе TI1 и предназначен цифровой входной фильтр. Также на входе могут присутствовать помехи, короткие по своей длительности, и цифровой фильтр позволяет отсеять их, чтобы исключить ложный «захват» содержимого счетчика таймера.

Как действует цифровой входной фильтр? По принципу обычного счетчика, работающего на уменьшение. При обнаружении изменения уровня на входе TI1 запускается счетчик цифрового входного фильтра. Начинается счет тактов внутреннего сигнала с частотой Fsampling. Изначально в счетчике фильтра записано число N, которое с каждым тактом Fsampling уменьшается. При обнулении счетчика, снова проверяется состояние входа TI1, так определяется «достоверность» изменения уровня на входе. Частоту выборок Fsampling и число выборок N задают в разрядах IC1F[3:0] регистра TIMx_CCMR1 (напомню, мы рассматриваем канал 1 таймера).

Digital Filter

Эти параметры определяют величину, которая в документации называется «длительность фильтра». Допустим, мы предполагаем, что входной сигнал может быть нестабилен в течение 5 периодов тактовой частоты, тогда «длительность фильтра» необходимо задать не менее этой величины, например, 8 периодов. При подтверждении достоверности изменения уровня сигнала на входе TI1, цифровой фильтр передает сигнал на выход – TI1F.

Далее идет детектор, который определяет какой перепад уровня сигнала обнаружен на входе, нарастающий или спадающий. И формирует сигнал на соответствующем выходе — TI1F_rising (нарастающий) или TI1F_falling (спадающий).

Эти сигналы поступают на мультиплексор и схему «ИЛИ». Схема «ИЛИ» формирует сигнал для «подчиненного» контроллера (Slave). В данный момент нам более важен мультиплексор, на который также поступают эти сигналы, поскольку он работает в схеме захвата для данного канала. С помощью этого мультиплексора как раз и выбирается активный перепад уровня сигнала (или полярность, другими словами), по которому произойдет «захват». Этот перепад уровня (полярность) задается в разряде CC1P регистра TIMx_CCER. В разных таймерах общего назначения для этого может быть предусмотрен не один разряд CC1P, а комбинация двух разрядов – CC1P и CC1NP. На выходе мультиплексора появляется сигнал TI1FP1, только в том случае, когда уровень сигнала на входе изменился в нужном направлении, то есть был переход из «0» в «1» или наоборот. Как задано в разряде CC1P.

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

  • 00: Канал CC1 сконфигурирован как выход
  • 01: Канал CC1 сконфигурирован как вход, IC1 подключен к TI1
  • 10: Канал CC1 сконфигурирован как вход, IC1 подключен к TI2
  • 11: Канал CC1 сконфигурирован как вход, IC1 подключен к TRC (внутренний сигнал). Этот режим работает только если вход внутреннего триггера выбран через бит TS в TIMx_SMCR.

Также, входной сигнал можно пропустить еще через делитель частоты. Коэффициент деления может быть равен 1, 2, 4 или 8, он задается в разрядах IC1PSC того же регистра TIMx_CCMR1. И, наконец, режим захвата для данного канала необходимо разрешить установкой бита CC1E в регистре TIMx_CCER.

 

Input Capture

На рисунке изображен пример, когда “захват” содержимого счетного регистра происходит по переднему фронту сигнала на входе TI1 в канале захвата/сравнения 1. В режиме захвата регистр захвата/сравнения TIMx_CCR1 используется для фиксации значения счетчика после обнаружения переднего фронта или заднего среза (зависит от настроек канала захвата) соответствующего сигнала IC1. Когда происходит захват, соответствующий флаг CC1IF регистра TIMx_SR устанавливается и может быть вызвано прерывание или запрос DMA, если они разрешены.

Если захват произошел в то время, когда флаг CC1IF уже был установлен, тогда устанавливается флаг “over-capture” CC1OF в регистре TIMx_SR. Флаг CC1IF может быть очищен программно, записью «0» в этот разряд, или чтением захваченных данных, хранящихся в регистре TIMx_CCR1. Флаг CC1OF очищается, когда вы запишете в него «0».

Для выполнения этого примера используется следующая процедура настройки канала захвата/сравнения:

  • Выбор активного входа. TIMx_CCR1 должен быть связан со входом TI1, поэтому в разряды CC1S регистра TIMx_CCMR1 записываем «01». Как только CC1S станет отличным от «00», канал конфигурируется как вход и регистр TIMx_CCR1станет доступным только для чтения.
  • Программирование времени действия входного фильтра. Эту операцию необходимо выполнить с учетом характеристик сигнала, поступающего на вход TI1. Время действия фильтра задается в разрядах IC1F регистра TIMx_CCMR1 (см. выше). Примем время действия фильтра (или «длительность» другими словами) равным 8 периодам тактовой частоты fDTS. Тогда в разряды IC1F регистра TIMx_CCMR1 необходимо записать число «0011».
  • Выбор активного перепада импульса. Выбор переднего фронта или заднего среза импульса на входе TI1 в качестве активного события для захвата («полярность») производится записью нужного значения в разряд CC1P регистра TIMx_CCER. В данном примере для активного переднего фронта необходимо записать «0» в этот разряд.
  • Программирование входного делителя. В этом примере необходимо выполнять захват на каждом действительном перепаде импульса, поэтому делитель должен быть деактивирован записью «00» в разряды IC1PS регистра TIMx_CCMR1. Другими словами, эта комбинация устанавливает коэффициент деления равным «1».
  • Разрешить захват. Устанавливаем бит CC1E в регистре TIMx_CCER.
  • При необходимости разрешить вызов прерывания или запрос DMA. Это биты CC1IE (прерывание) и CC1DE (запрос DMA) в регистре TIMx_DIER.

Когда произошел захват на входе, далее выполняются следующие действия:

  • Регистр TIMx_CCR1 получает значение счетчика на активном перепаде уровня сигнала.
  • Флаг CC1IF (прерывание) устанавливается. Флаг CC1OF также установится, если произошли по меньшей мере два захвата подряд, в то время пока флаг CC1IF не был сброшен.
  • Генерируется прерывание в зависимости от состояния бита CC1IE (разрешение прерывания).
  • Генерируется запрос DMA в зависимости от состояния бита CC1DE (разрешение запроса DMA).

Рекомендуется считывать данные до установки флага «over capture». Это позволит избежать потерь, которые могут произойти после чтения флага и перед чтением данных.

Прерывания и запросы DMA могут генерироваться программно, установкой соответствующего бита CCxG в регистре TIMx_EGR.

Перейдем к практической реализации данного примера для платы STM32VL-DISCOVERY (для платы STM32L-DISCOVERY пример будет рассмотрен чуть позже). В данном случае будут задействованы два таймера общего назначения, один для формирования сигнала на выходе, другой для измерения периода этого сигнала в режиме захвата. Как сформировать сигнала PWM на выходе с помощью таймера было рассказано в предыдущих статьях. В данном случае выходной сигнал будет формироваться таймером TIM2, а его период будет измеряться с помощью таймера TIM3, работающего в режиме захвата.

Взяв готовый проект из статьи

STM32. General-Purpose Timers. Часть 2. Формирование сигнала PWM для платы STM32VL-DISCOVERY

в котором таймер TIM2 формирует PWM сигнал на выходе микроконтроллера, остается добавить в этот проект код инициализации таймера TIM3, используемого для захвата входного сигнала.

Выход PA1 микроконтроллера используется для вывода сигнала, формируемого таймером TIM2. В примерах из предыдущих статей у таймера TIM2 был задействован канал 2 для формирования выходного сигнала, оставим этот код без изменений. Таймер TIM3, канал 1 которого будет работать в режиме захвата, должен анализировать состояние входа TI1, соединенного с выводом PA6 микроконтроллера. Соответственно, выводы PA1 и PA6 микроконтроллера необходимо замкнуть. В данном примере производится измерение периода сигнала, поступающего на внешний вход микроконтроллера, поэтому сигнал можно проконтролировать осциллографом.

Пример кода для платы STM32VL-DISCOVERY:

#include "stm32f10x.h"

/*Переменные:
 capture_1 - захваченное значение счетчика при первом импульсе
 capture_2 - захваченное значение счетчика при втором импульсе
 period - количество тактовых импульсов между двумя фронтами сигналов
 n - дополнительная переменная, состояние которой определяет первый 
или второй импульс (а точнее, четный или нечетный) поступил на вход. 
В зависимости от ее содержимого чтение регистра производится в 
переменную capture_1 или capture_2*/
uint16_t period, capture_1, capture_2;
uint8_t n = 0x00;

void TIM3_IRQHandler(void)
  {
    if(!n)
    {
      capture_1 = TIM3->CCR1;
      n = ~n;
    }
    else
    {
      capture_2 = TIM3->CCR1;
      period = capture_2 - capture_1;
      n = ~n;
    }
  }

int main()
{
  //Разрешение прерывания от таймера 3 и установка приоритета
  NVIC_SetPriority(TIM3_IRQn, 1);
  NVIC_EnableIRQ(TIM3_IRQn);
  
  /*Инициализация GPIOA.
  Вывод PA1 настраивается для работы с выходом TIM2_CH2*/
  RCC->APB2ENR |= (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN); //Тактирование порта GPIOA и альтернативных функций
  GPIOA->CRL |= GPIO_CRL_MODE1;//Максимальная скорость порта = 50 MHz
  GPIOA->CRL &= ~GPIO_CRL_CNF1;//Очистка бит CNF[1:0] для PA1 (поскольку после сброса мк задан режим "Input Floating")
  GPIOA->CRL |= GPIO_CRL_CNF1_1; //PA1 - выход Push-Pull в режиме альтернативной функции

  /*Инициализация таймера TIM2
  Для формирования сигнала ШИМ используется канал 2 (TIM2_CH2)*/
  RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;//Тактирование таймера TIM2
  TIM2->CR1 |= TIM_CR1_ARPE;//Включен режим предварительной записи регистра автоперезагрузки
  TIM2->CCMR1 |= TIM_CCMR1_OC2PE;//Включен режим предварительной загрузки регистра сравнения
  TIM2->CCMR1 |= (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1);//OC2M = 110 - PWM mode 1
  TIM2->ARR = 23999;//Период выходного сигнала T = 1mS
  TIM2->CCR2 = 19200;//Длительность импульса. В данном случае Duty cycle = 80%
  //TIM2->CCER |= TIM_CCER_CC2P;//Полярность выходного сигнала
  TIM2->CCER |= TIM_CCER_CC2E;//Выход канала захвата/сравнения включен
  TIM2->CR1 |= TIM_CR1_CEN;//Старт счета таймера
  
  /*Инициализация таймера TIM3.
  Для измерения периода входного сигнала используется канал 1 (TIM3_CH1)*/
  RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;//Включаем тактирование TIM3
  TIM3->CCMR1 |= TIM_CCMR1_CC1S_0;//Выбор активного входа. Записываем "01" в биты CC1S - связываем регистр TIM3_CCR1 со входом TI1 
  TIM3->CCMR1 |= (TIM_CCMR1_IC1F_0 | TIM_CCMR1_IC1F_1);//Выбор длительнотси действия фильтра - 8 тактов. IC1F = 0011.
  TIM3->CCER &= ~TIM_CCER_CC1P;//По переднему фронту - положительный перепад импульса
  TIM3->CCMR1 &= ~TIM_CCMR1_IC1PSC;//Предделитель отключен
  TIM3->CCER |= TIM_CCER_CC1E;//Разрешен захват значения счетчика в регистр TIM3_CCR1
  TIM3->DIER |= TIM_DIER_CC1IE;//Разрешена генерация прерывания при захвате
  TIM3->CR1 |= TIM_CR1_CEN;//Запускаем счет таймера

  while(1);
}

В данном случае производить инициализацию вывода PA6 отдельно не требуется. Напомню, что таймер TIM2 формирует выходной сигнал с периодом 1mS, что соответствует 24000 периодов тактовой частоты этого таймера, поскольку по умолчанию она равна 24MHz для этой платы. Значение переменной period = 24000 мы и должны увидеть в итоге, как разность между двумя последовательными захватами содержимого счетного регистра таймера – capture_1 и capture_2. Для этого необходимо установить точку останова (F9) в следующей строке обработчика прерывания

Breakpoint

 

Запускаем отладку, открываем панель переменных и прописываем в ней все необходимые переменные для наблюдения за ними. А далее, нажимая кнопку “Go” Go на панели отладки, с каждым нажатием наблюдаем за изменением захваченного содержимого счетчика в переменных capture_1 и capture_2. При этом разность этих двух переменных будет соответствовать числу импульсов, сосчитанных счетчиком между двумя событиями захвата, а соответственно периоду входных импульсов. Это число будет храниться в переменной period. Зная это количество импульсов и тактовую частоту на входе счетчика не составит труда посчитать, что период импульса будет равен 1mS. На рисунках ниже можно наблюдать, что при каждом новом нажатии кнопки “Go” и прерывании выполнения программы в точке останова, захваченное содержимое в переменных capture_1 и capture_2 каждый раз изменяется, так как эти значения соответствуют текущему значения счетного регистра таймера в момент захвата. Но разность между двумя последовательными значениями всегда остается неизменной (переменная period).

Период 1

Период 2

Период 3

Поскольку для инициализации таймера TIM2, формирующего выходной сигнал, взят готовый пример из другой статьи, кроме периода сигнала здесь задается и длительность импульса. В данный момент измеряется только период сигнала, длительность импульса пока не меряем. Хотя с помощью таймера можно измерить оба эти параметра, и период и длительность импульса. Для этого необходимо задействовать 2 канала одного таймера, настроенные на разную полярность (передний фронт и задний срез). Может быть я выложу такой пример, пока не знаю.

Для платы STM32L-DISCOVERY код будет несколько отличаться, по причине использования других выводов из-за разводки платы, другой частоты тактирования и настроек GPIO. Инициализация таймеров полностью идентична. Ниже приведен код программы для платы STM32L-DISCOVERY. Поскольку вывод PA6 на этой плате не выведен на внешние контакты (задействован для LCD) пришлось использовать в качестве входа TIM3_CH1 вывод PC6. Более широкие возможности настройки альтернативных функций выводов портов в семействе STM32L позволяют распределить периферийные функции по нескольким возможным выводам.

Соответственно, для работы с этим примером необходимо замкнуть выводы PA1 и PC6.

Пример программы для платы STM32L-DISCOVERY:

#include "stm32l1xx.h"

/*Переменные:
 capture_1 - захваченное значение счетчика при первом импульсе
 capture_2 - захваченное значение счетчика при втором импульсе
 period - количество тактовых импульсов между двумя фронтами сигналов
 n - дополнительная переменная, состояние которой определяет первый 
или второй импульс (а точнее, четный или нечетный) поступил на вход. 
В зависимости от ее содержимого чтение регистра производится в 
переменную capture_1 или capture_2*/
uint16_t period, capture_1, capture_2;
uint8_t n = 0x00;

void TIM3_IRQHandler(void)
  {
    if(!n)
    {
      capture_1 = TIM3->CCR1;
      n = ~n;
    }
    else
    {
      capture_2 = TIM3->CCR1;
      period = capture_2 - capture_1;
      n = ~n;
    }
  }

int main()
{
  
  //Разрешение прерывания от таймера 3 и установка приоритета
  NVIC_SetPriority(TIM3_IRQn, 1);
  NVIC_EnableIRQ(TIM3_IRQn);
    
  /*Инициализация GPIOA, GPIOC.
  Вывод PA1 настраивается для работы с выходом TIM2_CH2*/
  RCC->AHBENR |= (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOCEN);//Тактирование GPIOA, GPIOC
  GPIOA->MODER |= GPIO_MODER_MODER1_1;//PC6 - output AF
  GPIOA->OTYPER &= ~GPIO_OTYPER_OT_1;//PA1 - Push-Pull
  GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR1;//PA1 - Nopull
  GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR1;//PA1 - 40MHz
  GPIOA->AFR[0] = 0x00000010;//PA1 - AFIO1 (TIM2_CH2)
  
  //Вывод PC6 настраивается в качестве входа TIM3_CH2
  GPIOC->MODER |= GPIO_MODER_MODER6_1;//PC6 - AF
  GPIOC->AFR[0] |= 0x02000000;//PC6 - AFIO2 (TIM3_CH1)
  
  /*Инициализация таймера TIM2
  Для формирования ШИМ используется канал захвата/сравнения 2 (TIM2_CH2)*/
  RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;//Тактирование TIM2
  TIM2->CR1 |= TIM_CR1_ARPE;//Включен режим предварительной записи регистра автоперезагрузки
  TIM2->CCMR1 |= TIM_CCMR1_OC2PE;//Включен режим предварительной загрузки регистра сравнения
  TIM2->CCMR1 |= (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1);//OC2M = 110 - PWM mode 1
  TIM2->ARR = 2096;//Период выходного сигнала T = 1mS
  TIM2->CCR2 = 1677;//Длительность импульса (в данном случае Duty cycle = 80%)
  TIM2->CCER |= TIM_CCER_CC2E;//Выход канала захвата/сравнения включен
  TIM2->CR1 |= TIM_CR1_CEN;//Старт счета таймера
  
  /*Инициализация таймера TIM3.
  Для измерения периода входного сигнала используется канал 1 (TIM3_CH1)*/
  RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;//Включаем тактирование TIM3
  TIM3->CCMR1 |= TIM_CCMR1_CC1S_0;//Выбор активного входа. Записываем "01" в биты CC1S - связываем регистр TIM3_CCR1 со входом TI1 
  TIM3->CCMR1 |= (TIM_CCMR1_IC1F_0 | TIM_CCMR1_IC1F_1);//Выбор длительнотси действия фильтра - 8 тактов. IC1F = 0011.
  TIM3->CCER &= ~(TIM_CCER_CC1P | TIM_CCER_CC1NP);//По переднему фронту - положительный перепад импульса
  TIM3->CCMR1 &= ~TIM_CCMR1_IC1PSC;//Предделитель отключен
  TIM3->CCER |= TIM_CCER_CC1E;//Разрешен захват значения счетчика в регистр TIM3_CCR1
  TIM3->DIER |= TIM_DIER_CC1IE;//Разрешена генерация прерывания при захвате
  TIM3->CR1 |= TIM_CR1_CEN;//Запускаем счет таймера

  while(1);
}

При выполнении данного примера значение, сохраняемое в переменной period, будет равно 2097, поскольку для этой платы тактовая частота после сброса равна 2,097MHz.

По ссылке ниже можно скачать проекты в IAR для обеих плат.

Проект STM32F

Проект STM32L

Другие статьи:

  4 Responses to “STM32. General-Purpose Timers. Режим захвата.”

  1. Нарыл — (Особый случай
    Для облегчения измерения захвата ввода, счетчик таймера сбрасывается после каждого нарастающего фронта
    обнаруженого на входном канале таймера следующим путем:
    • выбрав TIxFPx в качестве входного триггера, установить биты TS в регистре SMCR
    • выбрать режим сброса как в подчиненном режиме, настроив SMS биты в SMCR регистре

    Используя эту конфигурацию, при обнаружении фронта, счетчик сбрасывается и период внешнего сигнала автоматически определяется значением в регистр CCRx. Этот метод используется только для канала 1 или канала 2.
    В этом случае захват входа предделителя (ICPSC) не считается в период вычисления.
    Период вычисляется следующим образом:
    Period = CCRx /(TIMx_CLK *(PSC+1)* polarity_index(1))
    polarity_index 1, если используется переднему или заднему фронту и 2, если используются оба фронта.)
    Что и пытаюсь сделать.

  2. Спасибо, отличная статья.

  3. Отличная статья. Но мне нужно определять параметры ШИМ сигнала. Период и длительность импульса. Из полученного вычислять коэффициент заполнения (1 / скважность). В ref.manual этому отведена страничка.

    • Я об этом в тексте упомянул. Настраиваем 2 канала захвата в одном таймере. Один срабатывает по нарастающему срезу сигнала, другой по спаду. В результате можно посчитать период и скважность.