24260

Таймеры общего назначения

Дата последнего изменения: 13.07.2021 15:31:02

В данной статье рассматривается общая информация по таймерам (так называемый обобщенный таймер), которая поможет освоить соответствующий раздел  спецификации. Необходимо помнить, что таймеры в различных микроконтроллерах несколько различаются. 

Реализованные возможности блока таймеров для каждого микроконтроллера описаны в соответствующем разделе спецификации!

1. Общая информация

В каждом микроконтроллере присутствует несколько таймеров общего назначения. Например, в 1986ВЕ9х реализовано три 16-ти битных таймера, а в 1986ВЕ1Т четыре 32-битных таймера. Каждый таймер обеспечивает свой счет и генерацию прерываний согласно настройкам.

В каждом таймере существует по четыре канала, каждый из которых может независимо работать как в режиме ШИМ (генерация импульсов), так и в режиме захвата. Для реализации этих режимов используются выводы GPIO, настроенные как выход для ШИМ и как вход для захвата внешнего сигнала.

В режиме ШИМ используется от одного до двух выводов GPIO, настроенных как выход. На один вывод, называемый "прямым", подается генерируемый таймером сигнал заданной скважности. На второй вывод, называемый "инверсным", выводится сигнал, инверсный относительно прямого вывода. Инверсный вывод используется не всегда, в основном он необходим для моторов, требующих такого управления. При использовании обоих выводов иногда требуется разнести во времени фронты переключения прямого и инверсного сигналов, чтобы исключить протекание сквозного тока в момент общего переключения. Для этого используется контроллер "мертвой зоны", который задает необходимую задержку между фронтами. Когда инверсный вывод не используется, то вывод GPIO для него можно не занимать.

При управлении моторами также бывает необходима функция аварийной остановки, когда таймер перестает выводить ШИМ, переключение выводов прекращается. Для этого в таймере есть внешние входы BRK и ETR, они так же должны быть назначены на GPIO в случае надобности. BRK - это аварийный вход, а ETR (External Timer Reference) помимо аварийного использования служит для задания внешней частоты счета. Подробнее про частоту счета в "Режим счетчика". Необходимо отметить, что BRK останавливает ШИМ асинхронно, а ETR - синхронно с частотой тактирования таймера.

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

Необходимо отметить, что каждый таймер работает на своей тактовой частоте TIM_CLK, которая формируется из частоты ядра с помощью делителя (от 1 до 128, по 2n), задаваемого отдельно для каждого таймера в регистре TIM_CLOCK. Там же таймер включается. Вся работа таймера, захват и ШИМ режимы выполняются по переднему фронту сигнала TIM_CLK. Таким образом, при задании TIM_CLK равной частоте ядра достигается максимальная точность захвата фронта внешнего сигнала. То есть если внешний сигнал поменяет уровень между фронтами TIM_CLK, то захват этого события случится на ближайшем фронте TIM_CLK, а не где-то посередине.

2. Режим счетчика

Основная задача счетчика - это считать число входных импульсов. Количество импульсов считается в регистре CNT. Для того чтобы ограничить верхнюю границу счета используется регистр ARR (Auto-Reload Register). При достижении регистром CNT значения, заданного пользователем в ARR, генерируется прерывание, и счет начинается заново. Таким образом, регистр ARR является своего рода периодом, поскольку его можно использовать для генерации прерываний с заданным периодом времени.

Рисунок 1 - Шестнадцати битный счетчик таймера

Счет в регистре CNT может происходить не только в сторону увеличения. Доступны режимы:

  • Инкремент (СNT++): Счет от 0 до ARR, сброс в 0 и далее по циклу.
  • Декремент (СNT–): Счет от ARR до 0, сброс в ARR и далее по циклу.
  • Двунаправленный (СNT+-): Счет от 0 до ARR, затем от от ARR до 0 и далее по циклу.

Важно обратить внимание на то, что значение ARR является основанием счёта, при этом период будет составлять ARR+1 такт. Поэтому для формирования таймера с периодом N необходимо задать основание счёта ARR равным N-1

Прерывания могут генерироваться, когда (CNT == ARR) и когда (CNT == 0), это задается флагами в регистре IE. Событие совпадения CNT с регистрами CCR и CCR1 используется в режиме ШИМ и описано в соответствующем разделе ниже.

Совпадения CNT с 0 и ARR, могут являться "событиями счета" для других таймеров, подключенных каскадно. Например, TIMER1 может отсчитывать 16 младших бит от общего количества входных импульсов, а TIMER2 старшие 16 бит. При этом каждое событие CNT==ARR в TIMER1, увеличивает на 1 регистр CNT в TIMER2. Таким образом, реализуется составной 32-битный таймер на основе двух 16-ти битных.

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

На самом деле значение CNT изменяется не просто по входному импульсу, а по фронту или спаду этого сигнала. Поэтому событие, по которому происходит изменение CNT будем называть "событием счета".

Источников входных импульсов ("событий счета") может быть несколько, все они приведены на рисунке 2.

Рисунок 2 - Источники "событий счета"

Передний фронт сигнала будем называть просто "фронт", а задний фронт называть "спад". Регистры и относящиеся к ним биты помечены одинаковыми цветами.

"Событием счета" - может выступать:

  • Фронт тактового сигнала TIM_CLK после делителя PSG.
  • Событие CNT==ARR в других таймерах.
  • Фронт или спад сигнала на прямых выводах каналов таймера, настроенных как вход.
  • Фронт или спад сигнала на входе ETR таймера.

Выбор события счета и направления счета задаются в регистре CNTR в битовых полях, представленных в таблице 1.

Таблица 1

Событие счета EventSel[8..11] CntMode[6..7] Dir[3] Режим счета
Фронт TIM_CLK b0000 b00 b0 CNT++
b1 CNT–
b01 bx CNT+-
CNT==ARR
в таймерах
b000-b0011
b1010
b1x b0 CNT++
b1 CNT–
Фронт или спад
на каналах таймера
b01хх bxx b0 CNT++
b1 CNT–
Фронт или спад
на входе ETR
b1000
b1010
b1x b0 CNT++
b1 CNT–

Символом "х" помечены биты, значения которых не учитываются в заданном режиме.

Как видно из таблицы, направление счета, как правило, задается битом DIR. Исключением является случай счета вперед-назад, который доступен только по фронту внутреннего тактирования TIM_CLOCK.

2.1. Счет по фронту TIM_CLK

Регистр CNT изменяется на каждом фронте сигнала TIM_CLK, прошедшего через делитель, который определяется регистром PSG. То есть частота счета получается CNT_CLOCK = TIM_CLK / ( PSG + 1).

В спецификации для значений CNT_MODE, равных b00 и b01, стоит уточнение "при PSG = 0". Это не относится к значению регистра PSG. Значение PSG может быть больше 0! Здесь имеется ввиду, что значение PSG делит тактовую частоту TIM_CLOCK. Событие счета возникает при обнулении внутреннего счетчика (назовем его PSG_cnt), который пропускает для инкремента регистра CNT только каждый PSG-й импульс TIM_CLOCK. То есть регистр CNT изменяется при PSG_cnt = 0, после запускается новый цикл делителя PSG_cnt = PSG.

2.2. Счет по CNT==ARR в таймерах

Событие CNT==ARR в таймерах используется для объединения нескольких таймеров в общий таймер-счетчик с большей разрядностью.

2.3. Счет по фронту или спаду в канале таймера

Когда прямой GPIO-вывод канала настроен в режим захвата, то сигнал на этом входе может служить источником событий для счета. Необходимо лишь выбрать фронт или спад внешнего сигнала как источник для изменения регистра CNT. Выбор осуществляется в поле CHxSel[4..5] регистра CHx_CNTRL. Такой режим полезен для подсчета количества каких-то внешних событий. Например, при подключении детектора фотонов, можно считать число зарегистрированных частиц с момента запуска измерения.

Внешний сигнал от данного входа может быть отфильтрован, подробнее далее.

2.4. Счет от внешнего вывода ETR

Каждый таймер имеет отдельный внешний вывод ETR, который всегда используется как вход. Фронт или спад (в зависимости от выставленного бита ETR_INV регистра BRKETR_CNTRL - по умолчанию ETR_INV = 0 (счёт CNT по фронту))  сигнала на данном выводе могут также быть источником изменения CNT. Вход ETR необходим, чтобы оставить каналам таймера их основную функциональность - работу в режимах ШИМ (Широтно-Импульсной Модуляции) и захвата, при этом счет будет происходить по сигналу с ETR.

Существует два основных способа задать сигнал "тактирования счета" (изменения CNT) - внутренний и внешний. Внутренний - это использование импульсов TIM_CLK, внешний - это использование внешних импульсов со входа ETR. При этом "тактировании счета" каналы таймера работают в режимах захвата и ШИМ, выполняя заложенную в них пользователем функцию.

Внешний сигнал от данного входа также может быть отфильтрован.

3. Фильтрация входного сигнала

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

Поскольку внешние импульсы могут быть заведены через входы каналов и вывод ETR, то у каждого из этих входов есть свои фильтры. Для канала таймера значение фильтра задается в регистре CHy_CNTRL битами CHFLTR[0..3]. А для ETR в регистре BRKETR_CNTRL битами FILTER[4..7]. Значение этих полей определяет, сколько тактов "частоты сэмплировния" должен продолжаться импульс на входе, чтобы считаться достоверным, а не помехой.

Под сэмплированием подразумевается производное понятие от SampleRate, скорости оцифровки сигнала. То есть частота сэмплирования - это частота измерения или взятия отсчетов.

Поскольку частота TIM_CLK часто настраивается в максимум для достижения наименьшей погрешности определения фронтов, то использовать ее для проверки длительности входного сигнала неудобно. Потребуется счетчик большой разрядности, чтобы замерять в тактах TIM_CLK относительно длинные входные импульсы. Для упрощения счета ввели производную от TIM_CLK частоту - FDTS, которая наравне с TIM_CLK используется для задания фильтрации (рисунок 3).

Рисунок 3 - Частота FDTS

Частота FDTS в периодах TIM_CLK задается в регистре CNTRL в битах DTS[4..5].  Максимальное значение DTS[4..5] = 3 формирует частоту FDTS = TIM_CLK / 4. (В спецификации описанные биты называются FDTS[4..5]. В данной статье, чтобы не возникла путаница, частота называется FDTS, а управляющие биты - DTS.)

Рисунок 4 показывает счет события по переднему фронту входного сигнала с фильтрацией.

Рисунок 4 - Счет события по переднему фронту входного сигнала с фильтрацией

ВАЖНО! В данном случае на рисунке 4 представлен гипотетический вариант, когда сэмплирование происходит в 2-х триггерах на частоте FDTS / 2. Ближайшее реально возможное значение CHFLTR[0..3] = b0100 задает режим сэмплирования в 6 триггерах на частоте FDTS / 2, но этот вариант сложно отобразить на рисунке - он получится слишком длинным. Вариант же сэмплирования от TIM_CLK представлен в спецификации.

Рисунок 4 показывает, что:

  1. Вся работа в таймере происходит синхронно с фронтом сигнала TIM_CLK - красные линии.
  2. Источник частоты сэмплирования и фильтрация входного сигнала выбирается в битах CHFLTR[0..3] регистра CHy_CNTRL. В данном случае используется сэмплирование от частоты FDTS.
  3. Частота сэмплирования FDTS задается в DTS[4..5] регистра CNTRL.
  4. Событие счета выбирается в битах CHSEL[4..5] регистра CHy_CNTRL. В примере выбран фронт сигнала, также может быть выбран спад.
  5. Захваченное событие, в данном случае это фронт, должно сохраняться заданное число триггеров фильтра, определяемого вместе с выбором частоты сэмплирования в п.2. Импульсы меньшей длительности никак не влияют на счет счетчика.

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

4. Режим захвата

Каждый канал таймера может работать в режиме захвата. В этом режиме канал "детектирует" фронты и спады сигнала на прямом внешнем выводе. Происходит это следующим образом:

  • Таймер считает свои отсчеты в регистре CNT. Частота счета CNT_Clock задана одним из способов описанных выше.
  • На частоте сэмплирования (TIM_CLK или FDTS) проверяется уровень внешнего сигнала. Если уровень изменился, например был "0", а в следующем такте сэмплирования на входе появилась "1", то генерируется событие.
  • По этому событию текущее значение CNT сохраняется в одном из регистров CCR или CCR1.
  • На каждое событие может быть разрешено генерирование прерываний.

Рисунок 5 - Работа таймера в режиме захвата

В регистре CHy_CNTRL в поле CHSEL[4..5] задается по какому событию CNT будет сохраняться в CCR, по фронту или по спаду. Аналогичный выбора события для CCR1 осуществляется в регистре CHy_CNTRL2 поле CHSEL1[0..1].

Делитель позволяет захватывать не каждое событие, а лишь каждое второе, четвертое или восьмое. Настройка делителя происходит в поле CHPSC[6..7] регистра CHy_CNTRL.

Таким образом, в двух регистрах CCR и CCR1 "захватывается" значение CNT в тот или иной момент. Настроив CCR на фронт, а CCR1 на спад, можно узнать длительность импульса в отсчетах времени регистра CNT. Отследив изменение CCR, несложно также получить и период сигнала.

Регистр CCR1 используется не всегда - он подключается опционально. Для использования CCR1 его необходимо включить в регистре CHy_CNTRL2 битом CCR1_EN[2].

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

Инверсный вывод GPIO канала таймера в данном режиме не используется. Прямой вывод должен быть настроен как вход, в регистре CHy_CNTRL1 поле SELOE[0..1] = 0. Подробнее в описании данного поля в разделе ШИМ.

5. Режим ШИМ

Каждый канал таймера может работать в режиме ШИМ (широтно-импульстной модуляции), то есть генерировать импульсы заданной длины при заданном периоде. Для этого также используются регистры CCR и CCR1, которые разбивают диапазон значений CNT от 0 до ARR на необходимые интервалы. При совпадении значения CNT с одним из регистров CCR и CCR1 меняется уровень выходного сигнала Ref. Как именно изменяется сигнал Ref при совпадении регистров, настраивается в CHy_CNTRL полем OCCM[9..11].

Регистр CCR1 используется не всегда - он подключается опционально. Для использования CCR1 его необходимо включить в регистре CHy_CNTRL2 битом CCR1_EN[2].

Рисунок 6 - Структурная схема блока формирования ШИМ

Сигнал Ref - это внутренний сигнал, он может непосредственно подаваться на выводы канала таймера - прямой и инверсный - либо пропускаться сначала через блок DTG, а затем подаваться на выводы. При стандартных настройках на инверсный вывод автоматически подается сигнал, инвертированный относительно прямого вывода.

За подачу сигналов на прямой и инверсный выводы отвечает регистр CHy_CNTRL1, здесь для каждого из выводов своя настройка. Поле SELO[3...2] определяет, какой сигнал подается на выход.

Таблица 2 

SELO[2..3] выход GPIO
00 "0"
01 "1"
10 Ref
11 RefDTG

RefDTG - это сигнал Ref, прошедший через блок DTG. Помимо сигналов ШИМ может выводиться постоянный уровень "0" или "1".

Бит INV[4] позволяет инвертировать выходной сигнал.

В блоке управления портами GPIO есть регистр OE (Output Enable), где каждый бит регистра задает режим работы порта - на вход или на выход (подробнее в статье Схемотехника портов GPIO). При настройке вывода в функцию Port (Func = 0) регистр OE задается программно. Если же функция назначает вывод для использования какому-то блоку периферии, например, UART, то значением OE управляет сам периферийный блок. В таком случае блок UART сам переключит вывод UART_TX в режим OUT, а UART_RX оставит с выключенным выходным драйвером.

При назначении вывода микроконтроллера на каналы таймера, состояние ОЕ данного вывода необходимо задать в регистре SELOE[1...0]. То есть у каждого канала есть внутренний сигнал разрешения CHy_oe (nCHy_oe). Этот сигнал уходит в блок GPIO и задает, будет ли вывод канала являться входом или выходом. Если на CHy_oe подана "1", то канал работает как выход и выводит сигнал, определенный полем SELO[3...2]. Если CHy_oe = 0, то вывод является входом. В случае работы в режиме захвата с этого вывода захватывается сигнал. В терминах спецификации это обозначено как - "канал на выход не работает".

Значение сигнала разрешения задается в поле SELOE[1...0]. Кроме ручного выставления "0" и "1", доступен вариант, когда для сигнала разрешения используется сигнал Ref или RefDTG. В этом случае текущий уровень выбранного сигнала определяет, работает ли канал на выход или находится в Z состоянии.

Таблица 3

SELOE[0..1] Ref / RefDTG выход GPIO
00 - Z
01 - SELO[2..3]
10 Ref: 0 / 1 Z / SELO[2..3]
11 RefDTG: 0 / 1 Z / SELO[2..3]

Для инверсного вывода доступны такие же поля с префиксом n - nSELO[10..11], nINV[12], nSELOE[8..9].

Назначение входов ETR и BRK было описано в начале статьи - они служат для аварийной остановки ШИМ. Рисунок 7 показывает активные уровни ETR и BRK, при которых сбрасывается сигнал ШИМ. Необходимо отметить, что по умолчанию эти уровни инверсны друг относительно друга. Инверсию можно настроить в регистре BRKETR_CNTRL. Работу вывода ETR необходимо разрешить битом CHx_CNTRL.OCCE=1, для BRK дополнительных настроек не требуется.

Рисунок 7 - Уровни сигналов ETR и BRK

Отдельно стоит отметить логику работы прерываний по выводу BRK. При поступлении на него активного уровня (по умолчанию - это низкий логический уровень)  происходит остановка ШИМ и при этом генерации прерываний не происходит, а при снятии с данного вывода активного уровня происходит генерация прерываний и запуск ШИМ. Разрешение работы прерываний осуществляется флагом BRK_EVENT_IE.

Рисунок 7.1 - Логика работы прерываний по событию BRK_EVENT.

6. Контроллер мертвой зоны, DTG

Как уже упоминалось в начале статьи, мертвая зона нужна для того, чтобы исключить протекание сквозного тока при одновременном переключении фронтов на прямом и инверсном выводах. Для того чтобы на выводы подавался не сигнал Ref, а сигнал с DTG, необходимо в регистре CHy_CNTRL1 задать поля SELO[3...2] = nSELO[11...10] = 3, при этом поля SELOE[1...0] = nSELOE[9...8] = 1.

Если смотреть осциллографом вывод канала таймера, то можно заметить, что при переключении с Ref на DTG сигнал, наблюдаемый на осциллографе, инвертировался. То есть, например, если при выводе Ref длительность логической "1" составляла 30%, то при выводе сигнала с DTG эти 30% занимает логический "0". Если при этом включить инвертирование в том же регистре CHy_CNTRL1 битами INV[4] = nINV [12] = 1, то сигнал на выходе вновь станет таким же, каким был только что при выводе Ref. Но необходимо учитывать, что перекрытие фронтов теперь получается совершенно иное. По этой причине для управления скважностью необходимо перестроить регистр CCR, а не использовать инверсии. Нагляднее эта ситуация представлена на рисунках 8 - 11(осциллограммы, полученные на 1986ВЕ92У).

Рисунок 8 - Сигналы Ref и DTG на выводах микроконтроллера 1986ВЕ92 при скважности 30%. (Смещение DTG в данных графиках гипертрофировано для наглядности)

По рисунку 8 видно, что на выходе прямого и инверсного выхода каналов не случается ситуации, когда логическая "1" присутствует на обоих выводах.

Рисунок 9 - Сигналы Ref и DTG на выводах микроконтроллера 1986ВЕ92, включена инверсия выводов. (Происходит перекрытие зон логической "1")

Для задания фазы сигнала DTG по сравнению с сигналом Ref необходимо выбрать частоту, в периодах которой будет задаваться сдвиг. Эта частота задается полем CHx_DTG.EDTS[4] -  можно выбрать периоды частоты TIM_CLOCK или FDTS. Количество периодов задается полями регистра CHx_DTG.DTGx[15..8], далее это количество умножается множителем в поле CHx_DTG.DTG[3..0]. 

На  рисунке 10 показан случай, когда в TIM_Clock задается и частота выходного сигнала задается, и сдвиг для DTG. Поэтому, если задать сдвиг, равный значению в регистре CCR2, то выходной сигнал пропадает. Это объясняется тем, что сдвиг становится равен длительности высокого уровня выходного сигнала. На рисунке 10: желтый сигнал - это сигнал Ref с канала 1 таймера CH1, а зеленый сигнал - это сигнал с DTG с выхода nCH2. Здесь учтено, что сигнал Ref, проходя блок DTG, инвертируется. А поскольку удобнее сравнивать сигналы одной полярности, то выбрана именно эта пара (качество сигнала не являлось критичным, поэтому расхождение задних фронтов не стоит принимать во внимание).

Рисунок 10 - Сигналы Ref и DTG в случае, когда частота выходного сигнала и сдвиг для DTG задаются в TIM_Clock

Рисунок 11 показывает, как влияют параметры CHx_DTG.DTGx[15..8] и CHx_DTG.DTG[3..0] на фазу сигнала DTG.

Рисунок 11 - Влияние параметров CHx_DTG.DTGx[15..8] и CHx_DTG.DTG[3..0] на фазу сигнала DTG

7. Одновременный запуск таймеров

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

// Тактирование для управления регистрами
RST_CLK_PCLKcmd(RST_CLK_PCLK_TIMER1 | RST_CLK_PCLK_TIMER2, ENABLE);

// Настройки таймеров
TMR1_HW_Init(); TMR2_HW_Init();

// Запуск таймеров
MDR_TIMER1->CNTRL |= 0x0001;
MDR_TIMER2->CNTRL |= 0x0001;

// Подача частоты счета - реальный синхронный запуск!
MDR_RST_CLK->TIM_CLOCK = (3<<24);

8. Регистры таймера и каналы

В каждом таймере есть 4 канала, каждый из которых может независимо работать либо в режиме ШИМ, либо в режиме захвата. Но при этом счетчик у всех каналов один. Это показано в регистрах таймера. Рассмотрим структуру, описывающую регистры таймера подробнее:

typedef struct {
// Счетчик - один на все каналы.
  __IO uint32_t CNT;
  __IO uint32_t PSG;
  __IO uint32_t ARR;
  __IO uint32_t CNTRL;

// Регистры события 1. СВОЙ регистр для каждого канала! События:
// ШИМ: - переключение фронта/спада на выводах канала при равенстве (CNT == CCRy).
// Захват: - обнаружение фронта/спада на прямом выводе канала, присвоение (CCRy = CNT).

  __IO uint32_t CCR1;
  __IO uint32_t CCR2;
  __IO uint32_t CCR3;
  __IO uint32_t CCR4;
  __IO uint32_t CH1_CNTRL;
  __IO uint32_t CH2_CNTRL;
  __IO uint32_t CH3_CNTRL;
  __IO uint32_t CH4_CNTRL;

// Регистры настройки ШИМ и "мертвой зоны" (DTG).
  __IO uint32_t CH1_CNTRL1;
  __IO uint32_t CH2_CNTRL1;
  __IO uint32_t CH3_CNTRL1;
  __IO uint32_t CH4_CNTRL1;
  __IO uint32_t CH1_DTG;
  __IO uint32_t CH2_DTG;
  __IO uint32_t CH3_DTG;
  __IO uint32_t CH4_DTG;
  __IO uint32_t BRKETR_CNTRL;

// Регистры статуса, настройки прерываний и DMA
  __IO uint32_t STATUS;
  __IO uint32_t IE;
  __IO uint32_t DMA_RE;

// Регистры события 2. СВОЙ регистр для каждого канала! События:
// ШИМ: - переключение фронта/спада на выводах канала при равенстве (CNT == CCRy1).
// Захват: - детектирование фронта/спада на прямом выводе канала, присвоение (CCRy1 = CNT).
  __IO uint32_t CH1_CNTRL2;
  __IO uint32_t CH2_CNTRL2;
  __IO uint32_t CH3_CNTRL2;
  __IO uint32_t CH4_CNTRL2;
// Регистры CCRy1
  __IO uint32_t CCR11;
  __IO uint32_t CCR21;
  __IO uint32_t CCR31;
  __IO uint32_t CCR41;

 // ТОЛЬКО для 1986ВЕ1Т - Отдельная настройка запросов DMA для каждого канала.
 // В DMA_RE - настройка для все каналов сразу.
 __IO uint32_t DMA_REChx[4];

} MDR_TIMER_TypeDef;

8.1. Регистры DMA_RE и DMA_REx

Есть отдельные DMA каналы, работающие по запросам от самого таймера и от каналов таймера. На примере Timer1:

  • DMA_RE разрешает вызовы к DMA каналу TIM1_DMA_REQ
  • DMA_RE1 отвечает за TIM1_DMA_REQ1
  • DMA_RE2 отвечает за TIM1_DMA_REQ2
  • и т.д.

Соответственно DMA канал TIM1_DMA_REQ настраивается через регистр DMA_RE, и все разрешенные события в DMA_RE вызывают запрос к DMA по одному каналу TIM1_DMA_REQ.

Чтобы как-то разделить события на разных каналах добавили аналогичные регистры DMA_REх(1,2,3,4), и теперь можно получить отдельный запрос к DMA от выбранного канала таймера. Т.е. события разрешенные в DMA_RE1 будут вызывать запросы к каналу TIM1_DMA_REQ1.

В 1986ВЕ9х дополнительных каналов DMA TIMх_DMA_REQх нет, поэтому нет и регистров DMA_RE1,2,3,4.

9. Особенности программирования

9.1. Биты WR_CMPL не сбрасываются, если не подана частота TIM_CLOCK

В регистре CNTRL есть бит WR_CMPL, который при значении "0" разрешает запись в регистры CNT, ARR, PSG. Значение "1" означает, что идет запись. Аналогичный бит есть в регистре CHy_CNTRL.WR_CMPL, который отражает завершенность записи в регистры CCR и CCR1. Биты WR_CMPL не сбрасываются, если не подана частота TIM_CLOCK на блок таймера. Частота TIM_CLOCK подается через разрешающий бит регистра TIM_CLOCK.TIMx_CLK_EN в блоке тактовых частот.

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

9.2. Проблема с синхронным стартом

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

Вспомним, что возможность синхронного старта таймеров реализуется одновременной подачей им частоты TIM_CLOCK. Соответственно, для реализации синхронного старта таймеров частота должна быть выключена, а таймеры включены. При подаче TIM_CLOCK начинается синхронный счет. Поэтому необходимо:

  • настроить таймеры при поданной частоте TIM_CLOCK;
  • отключить TIM_CLOCK;
  • включить необходимые таймеры битом EN;
  • Записью в регистр MDR_CLOCK→TIM_CLOCK подать TIM_CLOCK одновременно на все выбранные таймеры.

9.3. При выставлении CNTRL.CNT_EN в 1 выставляются события статуса

При включении таймера сразу же выставляются биты CNT_ZERO и CNT_ARR, регистр STATUS равен 0x3. С регистром CNT это ожидаемо, поскольку обычно он инициализируется в 0 перед стартом счета. Но CNT не равен ARR, поэтому выставление бита CNT_ARR выглядит неожиданно. Кроме этого, даже если, например, выставить CNT=1, то все равно при включении таймера CNT_EN=1 выставляются биты CNT_ZERO и CNT_ARR.

Поэтому необходимо учитывать, что если на момент включения таймера будут разрешены прерывания от событий CNT_ZERO и CNT_ARR в регистре IE и в NVIC, то сразу же сработает обработчик прерывания. Поэтому прерывания лучше настраивать после включения таймера, по крайней мере в NVIC. А активные флаги в статусе сразу же стереть (STATUS = 0), если не требуется их обработка.

9.4. Бит CAP_FIX

Событие захвата происходит по фронту тактовой частоты таймера TIM_CLOCK. Если на некотором фронте сигнал на входе канала поменял значение, например, был логический "0", а стал "1", то генерируется сигнал возникновения события, который уходит на NVIC, DMA и т.д. На следующем такте TIM_CLOCK значение CNT сохраняется в регистры CCR и/или CCR1 и служит значением, при котором было зафиксировано событие.

Но частота TIM_CLOCK может быть много меньше, чем частота ядра. Если NVIC получит сигнал на прерывание от события, то ядру Cortex-M3 потребуется 12 тактов на то, чтобы войти в прерывание и начать исполнять код. Если этот код считывает значение CCR, то это значение может оказаться старым, еще не обновленным, потому что такт TIM_CLOCK еще не прошел.

Этот вариант событий наблюдается в проекте "Timer_CapSyncChannels" (доступен для скачивания в конце статьи). В этом проекте один канал захватывает фронт внешнего сигнала, а второй канал захватывает само событие захвата на первом канале. В прерывании считываются значения CCR обоих каналов и, теоретически, они должны отличаться не больше, чем на единицу. Но если сделать частоту TIM_CLOCK небольшой, то возникают события, когда CCR одного из каналов имеет еще старое, не обновленное значение. Получается так, что прерывание возникает от первого события по захвату фронта сигнала, а через такт TIM_CLOCK будет захвачено "событие захвата в первом канале". Но обработчик прерывания, сработавший по первому событию, уже может считать флаги событий в обоих каналах, хотя CCR во втором канале еще не обновился. То есть флаги показывают, что значения захвата можно считывать, но CCR еще не обновилось. Если же поднять частоту TIM_CLOCK, то такого поведения не возникает - обработчик не успеет добраться до регистров CCR раньше, чем отработает обновление в таймерах.

Чтобы избежать подобного сценария, в регистры CHx_CTRL2 таймеров добавлен 4-й бит, который называется EV_DELAY в 1986ВЕ8Т. При выставлении этого бита в "1", выставление флагов и генерация сигналов событий происходит синхронно с обновлением регистров CCR. То есть, если стоит флаг события захвата, то можно считывать регистр CCR, значение в нем заведомо обновлено.

В микроконтроллерах:

  • 1986ВЕ1Т, 1986ВЕ3Т и 1986ВЕ9х - бит EV_DELAY есть, помогает обойти проблему
  • 1986ВЕ3Т - проблема не наблюдается.
  • 1901ВЦ1, 1986ВК234 - выставление бита не помогает обойти проблему, бита нет.
При работе с таймерами, в частности, в режиме захвата, если ведётся какая-либо обработка данных в прерывании, при отладке проекта необходимо учитывать, что точки останова в обработчике прерываний необходимо ставить с особой осторожностью. Таймер продолжит вести счёт после остановки, что может повлечь за собой нежелательное изменение флагов регистра STATUS. По этим флагам зачастую выполняются различные условия для подсчёта данных, поэтому важно отслеживать ситуацию по подсчитанному значению в обработчике на примере массива: пускай микроконтроллер произведет счёт без внешнего вмешательства, после этого по полученному массиву можно отслеживать действительно корректные данные, в противном случае с точками останова могут возникать некорректные значения.

Сохранить статью в PDF

Файлы для скачивания

Документация

Теги

Была ли статья полезной?