Информационный портал технической поддержки Центра проектирования интегральных микросхем |
В данной статье рассматривается общая информация по таймерам (так называемый обобщенный таймер), которая поможет освоить соответствующий раздел спецификации. Необходимо помнить, что таймеры в различных микроконтроллерах несколько различаются.
В каждом микроконтроллере присутствует несколько таймеров общего назначения. Например, в К1986ВЕ92QI реализовано три 16-ти битных таймера, а в К1986ВЕ1QI четыре 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, а не где-то посередине.
Основная задача счетчика - это считать число входных импульсов. Количество импульсов считается в регистре CNT. Для того чтобы ограничить верхнюю границу счета используется регистр ARR (Auto-Reload Register). При достижении регистром CNT значения, заданного пользователем в ARR, генерируется прерывание, и счет начинается заново. Таким образом, регистр ARR является своего рода периодом, поскольку его можно использовать для генерации прерываний с заданным периодом времени.
Рисунок 1 - Шестнадцати битный счетчик таймера
Счет в регистре CNT может происходить не только в сторону увеличения. Доступны режимы:
Прерывания могут генерироваться, когда (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 - Источники "событий счета"
Передний фронт сигнала будем называть просто "фронт", а задний фронт называть "спад". Регистры и относящиеся к ним биты помечены одинаковыми цветами.
"Событием счета" - может выступать:
Выбор события счета и направления счета задаются в регистре 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.
Регистр 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.
Событие CNT==ARR в таймерах используется для объединения нескольких таймеров в общий таймер-счетчик с большей разрядностью.
Когда прямой GPIO-вывод канала настроен в режим захвата, то сигнал на этом входе может служить источником событий для счета. Необходимо лишь выбрать фронт или спад внешнего сигнала как источник для изменения регистра CNT. Выбор осуществляется в поле CHxSel[4..5] регистра CHx_CNTRL. Такой режим полезен для подсчета количества каких-то внешних событий. Например, при подключении детектора фотонов, можно считать число зарегистрированных частиц с момента запуска измерения.
Внешний сигнал от данного входа может быть отфильтрован, подробнее далее.
Каждый таймер имеет отдельный внешний вывод ETR, который всегда используется как вход. Фронт или спад (в зависимости от выставленного бита ETR_INV регистра BRKETR_CNTRL - по умолчанию ETR_INV = 0 (счёт CNT по фронту)) сигнала на данном выводе могут также быть источником изменения CNT. Вход ETR необходим, чтобы оставить каналам таймера их основную функциональность - работу в режимах ШИМ (Широтно-Импульсной Модуляции) и захвата, при этом счет будет происходить по сигналу с ETR.
Существует два основных способа задать сигнал "тактирования счета" (изменения CNT) - внутренний и внешний. Внутренний - это использование импульсов TIM_CLK, внешний - это использование внешних импульсов со входа ETR. При этом "тактировании счета" каналы таймера работают в режимах захвата и ШИМ, выполняя заложенную в них пользователем функцию.
Внешний сигнал от данного входа также может быть отфильтрован.
Когда таймер считает импульсы с внешнего входа, может возникнуть необходимость отфильтровать помехи и исключить ложные срабатывания при колебаниях сигнала на входе. Для этого внешние сигналы проходят через фильтр, который пропускает только те сигналы, которые соответствуют ожидаемой длительности импульса.
Поскольку внешние импульсы могут быть заведены через входы каналов и вывод 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 показывает, что:
В случаем с использованием сигнала счета с ETR рисунок будет аналогичным, только выбор частоты сэмплирования и значения фильтра происходит в регистре BRKETR_CNTRL. Также для сигнала ETR есть дополнительный инвертор и предделитель внешней частоты. Предделитель используется тогда, когда используется внешний стабильный сигнал тактирования счета, и нет необходимости в фильтрации коротких импульсов.
Каждый канал таймера может работать в режиме захвата. В этом режиме канал "детектирует" фронты и спады сигнала на прямом внешнем выводе. Происходит это следующим образом:
Рисунок 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. Подробнее в описании данного поля в разделе ШИМ.
Каждый канал таймера может работать в режиме ШИМ (широтно-импульстной модуляции), то есть генерировать импульсы заданной длины при заданном периоде. Для этого также используются регистры 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
Как уже упоминалось в начале статьи, мертвая зона нужна для того, чтобы исключить протекание сквозного тока при одновременном переключении фронтов на прямом и инверсном выводах. Для того чтобы на выводы подавался не сигнал 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ВЕ92QI).
Рисунок 8 - Сигналы Ref и DTG на выводах микроконтроллера К1986ВЕ92QI при скважности 30%. (Смещение DTG в данных графиках гипертрофировано для наглядности)
По рисунку 8 видно, что на выходе прямого и инверсного выхода каналов не случается ситуации, когда логическая "1" присутствует на обоих выводах.
Рисунок 9 - Сигналы Ref и DTG на выводах микроконтроллера К1986ВЕ92QI, включена инверсия выводов. (Происходит перекрытие зон логической "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
Для одновременного запуска нескольких таймеров необходимо их сначала инициализировать и запустить, а затем подать частоту счета, записав в регистр 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);
В каждом таймере есть 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ВЕ1QI - Отдельная настройка запросов DMA для каждого канала.
// В DMA_RE - настройка для все каналов сразу.
__IO uint32_t DMA_REChx[4];
} MDR_TIMER_TypeDef;
Есть отдельные DMA каналы, работающие по запросам от самого таймера и от каналов таймера. На примере Timer1:
Соответственно 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ВЕ92QI дополнительных каналов DMA TIMх_DMA_REQх нет, поэтому нет и регистров DMA_RE1,2,3,4.
В регистре 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, и только после этого делать новую запись.
Может возникнуть ситуация, когда при синхронном перезапуске нескольких таймеров они стартуют не синхронно, если внести изменения в направление счета между стартами. Например, один из трех таймеров может начать работать в противофазе. Этим таймером окажется тот таймер, в котором было изменено направление.
Вспомним, что возможность синхронного старта таймеров реализуется одновременной подачей им частоты TIM_CLOCK. Соответственно, для реализации синхронного старта таймеров частота должна быть выключена, а таймеры включены. При подаче TIM_CLOCK начинается синхронный счет. Поэтому необходимо:
При включении таймера сразу же выставляются биты 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), если не требуется их обработка.
Событие захвата происходит по фронту тактовой частоты таймера 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. При выставлении этого бита в "1", выставление флагов и генерация сигналов событий происходит синхронно с обновлением регистров CCR. То есть, если стоит флаг события захвата, то можно считывать регистр CCR, значение в нем заведомо обновлено.
В микроконтроллерах К1986ВЕ1QI, К1986ВЕ92QI - бит EV_DELAY есть, помогает обойти проблемуСайт: | https://support.milandr.ru |
E-mail: | support@milandr.ru |
Телефон: | +7 495 221-13-55 |