Для самых маленьких познавателей микроконтроллеров.
Поговорим про таймеры-счетчики микроконтроллеров AVR и конкретно ATTiny13A.
Начинающим очень сложно порой понять, как работают таймеры, в даташите столько написано про таймеры и так непонятно, что жуть. Да ещё и по-английски.
Обычно описание работы начинают как в даташите сверху вниз, а я начну снизу вверх, так более понятно начинающим.
Работа таймера-счетчика очень похожа на работу механического колесика одометра в панели приборов автомобиля. Вот и с ним будем сравнивать.

Долее по даташиту:
TCNT0 Timer/Counter Register. Это регистр таймера счетчика. Это колесико одометра. Если этому колесику придать вращение от какого нибудь сигнала, то он начнет крутиться. У колесика 255 положений, а не от нуля до девяти, как у одометра авто. Крутится он по кругу, то есть 0,1,2,3…254,255,0,1,2,3… Этот регистр открыт для чтения и записи, то есть можно считывать его значение и писать свое. простой пример: колесико должно крутится до 127, а потом сразу сбрасываться в ноль и считать дальше:
if(TCNT0==127) {TCNT0=0};
По-русски:
Если значение счетчика равно 127 if(TCNT0==127)
То сбрасываем этот же счетчик в ноль (присваиваем регистру нулевое значение ) {TCNT0=0}
В этом примере есть и чтение регистра TCNT0 и запись в этот же регистр.
Теперь выше по даташиту: как и чем и отчего заставить крутится это колесико одометра?
Для этого есть два способа:
1.- От тактового генератора микроконтроллера.
2. — От состояние входа( ножки) Т0
От тактового генератора:
Счетчик крутится от тактового генератора, "скорость" вращения колесика можно выбирать делителем. Отвечает за делитель регистр TCCR0B
Пример расчета "скорости": тактовый генератор лопатит на 1,2МГц:
TCCR0B=0x00 счетчик выключен.Можно использовать это значение как выключалку таймера-счетчика
TCCR0B=0x01 счетчик лопатит на 1,2МГц: деление на единицу типа)
TCCR0B=0x02 счетчик лопатит на 150кГц: деление на 8
TCCR0B=0x03 счетчик лопатит на 18,75кГц: деление на 64
TCCR0B=0x04 счетчик лопатит на 4,688кГц: деление на 256
TCCR0B=0x05 счетчик лопатит на 1,172кГц: деление на 1024
От состоянии на входной ножке Т0 (это 7 ножка порт РВ2)
TCCR0B=0x06 по спаду сигнала
TCCR0B=0x07 по фронту сигнала
Например: на входе Т0 логический ноль. В регистре TCCR0B=0x06 стоит по спаду сигнала. Счетчик стоит. Появилась единица на входе. Счетчик почуял это и приготовился, ибо по спаду. Пропала единица — стал ноль на входе Т0 — регистр таймера счетчика TCNT0 прибавил себе единичку и опять следит за состоянием по входу Т0
Колесико то крутится, но в холостую. Надо теперь сделать так, чтоб оно делало полезную работу. А вот тут уже много возможностей.
Есть несколько основных выводов полезной работы счетчика-таймера.
Делать работу, можно если значение счетчика (колесико TCNT0) сравняется с регистром сравнения-совпадения OCR0х. Это ещё составная часть таймера счетчика, состоящая из двух равнозначных регистра OCR0A и OCR0B.
OCR0A и OCR0B — это как колесики на кодовом навесном замке. Если уж совсем так сравнивать, то это два отдельных кодовых навесных замка с одним колесиком:

То есть можно выставлять свое значение колесика замка OCR0A или OCR0B, затем крутить колесико таймера одометра TCNT0, при совпадении значений замок открывается и происходит какое либо событие. Описание этого события зависит от того, в каком режиме работы находится таймер-счетчик. От этого режима зависит алгоритм работы и взаимодействия этих регистров. Режимы можно посмотреть в мастере CodeVisionAVR:

Далее простым детским языком уже достаточно сложно объяснять, поэтому резко взрослеем, и начинаем понимать, что такое прерывание, что такое Широтно Импульсная Модуляция и что такое прерывание по переполнению таймера и как эти слова можно использовать к нашему таймеру счетчику, чтоб получить полезное действие.
Теперь примеры, созданные для среды разработки CodeVisionAVR:
значение фьюзов по умолчанию, поэтому тактовая частота равна 1.2МГц. Значения настройки портов и т.п. не указано, только мясо:
TCCR0B=0x03; //Запускаем таймер в обычном счетном режиме на 18,750 kHz
OCR0A=0xF0; // Указываем значение регистра сравнения равное 0хF0
TIMSK0=0x04;//Разрешаем выполнение прерываний по совпадению в OCR0A
#asm("sei") // Разрешаем сами глобальные прерывания
interrupt [TIM0_COMPA] void timer0_compa_isr(void)
{
// Здесь малюем код
TCNT0=0x00;
}
Скриншот мастера:

Описание:
Таймер-счетчик прибавляет единичку 18750 раз в секунду (то самое колесико одометра TCNT0), значение OCR0A=0xF0; как и так понятно равно F0, в десятичной системе это равно числу 240. Если колесико одометра насчитает 240 "тиков", то сработает прерывание: основной цикл программы останавливается, и начинается выполнения кода "здесь малюем код":
interrupt [TIM0_COMPA] void timer0_compa_isr(void)
{
// Здесь малюем код
TCNT0=0x00;
}
Внутри фигурных скобок есть сброс колесика одометра в ноль: TCNT0=0x00;, то есть после выполнения кода счетчик сбрасывается и начинается счет с нуля. И так циклично.
Этот код применяется, если необходимо производить какое либо действо через определенный точно заданный промежуток времени: делать опрос какого либо устройства, мерять температуру, сканировать состояние энкодера да еще много что можно. Ну или например, если настроить вызов прерывания ровно раз в секунду, то можно сделать секундомер. Конкретно данный пример срабатывает 18750/240=78,125 раза в секунду.
Теперь интереснее: запускаем ШИМ:
TCCR0A=0x81;//запускаем ШИМ с фазовой коррекцией и назначаем выход ножки PB0(OC0A) на выход импульсов ШИМа
TCCR0B=0x02;//частота работы таймера 150 кГц
TIMSK0=0x02;//разрешаем прерывания по переполнению таймера
#asm("sei")// Разрешаем сами глобальные прерывания
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
// Малюем код
}
Ну а здесь описание совсем короткое: на выходе PB0 микроконтроллера (ножка 5) будет присутствовать ШИМ сигнал (если будет правильно сконфигурирован этот порт на выход), с заполнением пропорционально значению регистра OCR0A
Примеры:
OCR0A=0; — ШИМа небудет, ноль на выходе
OCR0A=127; — на выходе ровный меандр с заполнением 50/50
OCR0A=255; — на выходе единица, ШИМа нет
Так как включено прерывание по переполнению таймера, то когда счетчик насчитает максимальное значение, то будет срабатывать прерывание (выполняться код "Малюем код")
Скриншот мастера:

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


Комментарии 24
Степан, подскажите, не могу вкурить. Я могу одновременно на Attiny 13a использовать и ШИМ и просто 8битный счетчик? По даташиту, насколько я понял, что нет
Можете, если это разные таймеры. Хотя можно сделать и на одном таймере и шим и выполнение чего либо по переполнению таймера шима к примеру
Я тут еще покопался, поизучал)) Выходит для двух таймеров один счетчик TCNT0, я и также могу хоть в цикле ждать его совпадения или же, как вы предложили, выполнять что-то по прерыванию переполнения счетчика, или прерывание по совпадению. Спасибо большое, чет затупил.
Извиняюсь, конечно, но как по мне, то лучше использовать вместо if(TCNT0==127) {TCNT0=0}; вариант if(TCNT0>=127) {TCNT0=0}; Такая подстраховка многим, особенно новичкам, позволит избежать непонятных ситуаций, когда по какой то причине счетчик перескочит.Особенно в любых других функциях, не только таймере.
В таймере он не может так перескочить. Прямое сравнение более наглядно для новичков.
Как не настраиваю делитель или сравнение по прерыванию всё равно считает с интервалом где то около 2сек(((
Спасибо! Всё наглядно и понятно, понял что делал вроде как правильно, но почему то прерывания у меня работают не коректно((( Ни как не могу понять что не так…
С кнопками разобрался, с прерываниями разобрался, с входами разобрался — там всё достаточно наглядно…
Как счётчик начинает работать от прерывания тоже понятно — помогло сравнение генерируемого файла Wizard с настройками.
С регистрами сравнения куда ни шло — постараюсь разобраться, благо есть пару прог, где это можно посмотреть и почитать в книжках. Единственно вопрос — обязательно ли сравнивать с числом в шестнадцатеричной системе? или можно с десятичным?
Но вот это место для меня равносильно что рояль из кустов:
" TCCR0A=0x81;//запускаем ШИМ с фазовой коррекцией и назначаем выход ножки PB0(OC0A) на выход импульсов ШИМа"
Не было, не было, а потом — бац! и на тебе — появился выход ножки на PB0.
Если бы был какой проектик, который можно проследить как работает, благо Proteus немного освоил…
Ну и дальше про ШИМ тоже тёмный лес если честно…
ужасно хочется применить
Когда пыталась Atmega16A заставить музыку играть, тоже начала разбираться с таймерами-счетчиками… Друзья мне букварь нарисовали такой)))) Для чайников. piccy.info/view3/5556145/…2555f13b2253c7e55baa5a0d/
И после его применения, музыка заиграла)))) Ужасно интересная тема =)
Спасибо большое, как раз вовремя :)
Хорошая годная статья!
дельно, полезно, осуществимо. СПАСИБО!
И снова браво! Огромное спасибо!
Для новичков статья самое оно, хотя я категорически против использования новичками мастеров от кодевижена.
Да и на сообщество эзи выкладывать явно не стоит, это так дружеский совет.
А я категорически за использование мастера в кодевижн новичками. Да и выкладывать больше никуда не собираюсь.
Все же подумай ))
Твои статьи очень не помешают на we.easyelectronics.ru/ будут очень полезны и оценены по достоивству. Мне нравится расписано очень хорошо.
Там для более взрослых. Я сам там бывает ищу ответы на свои вопросы.
спасибо за очень хорошо написанаю статью немног с таким сталкивался но иза малого опыта и отсутствия инфы пришлось койкаие планы отложить, теперь буду знать к кому обратится.
Спасибо! Здесь освещенно много вопросов которые я ранее не понимал, хоть и много где читал. Спасибо ещё раз)))
Степа у меня в глазах двоится?
Ты же по моему про таймеры уже писал?
Это полная версия. Просили сообщить.
мозг взорвал., но практического применения для себя пока не увидел. а так жму нравится