Учебный курс AVR. Таймер - счетчик Т0. Регистры. Ч1

14/08/2013 - 18:25 Павел Бобков

Введение

   Таймер-счетчик является одним из самых ходовых ресурсов AVR микроконтроллера. Его основное назначение - отсчитывать заданные временные интервалы. Кроме того, таймеры-счетчики могут выполнять ряд дополнительных функций, как то - формирование ШИМ сигналов, подсчет длительности и количества входящих импульсов. Для этого существуют специальные режимы работы таймера-счетчика. 

   В зависимости от модели микроконтроллера количество таймеров и набор их функций может отличаться. Например, у микроконтроллера Atmega16 три таймера-счетчика - два 8-ми разрядных таймера-счетчика Т0 и Т2, и один 16-ти разрядный - Т1. В этой статье, на примере ATmega16, мы разберем как использовать таймер-счетчик Т0.

Используемые выводы

   Таймер-счетчик Т0 использует два вывода микроконтроллера ATmega16. Вывод T0 (PB0) - это вход внешнего тактового сигнала. Он может применяться, например, для подсчета импульсов. Вывод OC0 (PB3) - это выход схемы сравнения таймера-счетчика. На этом выводе с помощью таймера может формировать меандр или ШИМ сигнал. Также он может просто менять свое состояние при срабатывании схемы сравнения, но об этом поговорим позже. 

   
   Выводы T0 и OC0 задействуются только при соответствующих настройках таймера, в обычном состоянии это выводы общего назначения. 

 

Регистры таймера-счетчика Т0

   Хоть это и скучно, но регистры - это то, без чего невозможно программировать микроконтроллеры, конечно, если вы не сидите плотно на Arduino. Так вот, таймер Т0 имеет в своем составе три регистра:


- счетный регистр TCNT0,
- регистр сравнения OCR0,
- конфигурационный регистр TCCR0.

Кроме того, есть еще три регистра, относящиеся ко всем трем таймерам ATmega16:

- конфигурационный регистр TIMSK,
- статусный регистр TIFR.
- регистр специальных функций SFIOR

Начнем с самого простого.

TCNT0

 

   Это 8-ми разрядный счетный регистр. Когда таймер работает, по каждому импульсу тактового сигнала значение TCNT0 изменяется на единицу. В зависимости от режима работы таймера, счетный регистр может или увеличиваться, или уменьшаться.
   Регистр TCNT0 можно как читать, так и записывать. Последнее используется когда требуется задать его начальное значение. Когда таймер работает, изменять его содержимое TCNT0 не рекомендуется, так как это блокирует схему сравнения на один такт.

OCR0  


   Это 8-ми разрядный регистр сравнения. Его значение постоянно сравнивается со счетным регистром TCNT0, и в случае совпадения таймер может выполнять какие-то действия - вызывать прерывание, менять состояние вывода OC0 и т.д. в зависимости от режима работы.

   Значение OCR0 можно как читать, так и записывать.

TCCR0 (Timer/Counter Control Register)



   Это конфигурационный регистр таймера-счетчика Т0, он определяет источник тактирования таймера, коэффициент предделителя, режим работы таймера-счетчика Т0 и поведение вывода OC0. По сути, самый важный регистр. 


   Биты CS02, CS01, CS00 (Clock Select) - определяют источник тактовой частоты для таймера Т0 и задают коэффициент предделителя. Все возможные состояния описаны в таблице ниже.

   
   Как видите, таймер-счетчик может быть остановлен, может тактироваться от внутренней частоты и также может тактироваться от сигнала на выводе Т0. 


   Биты WGM10, WGM00 (Wave Generator Mode) - определяют режим работы таймера-счетчика Т0. Всего их может быть четыре - нормальный режим (normal), сброс таймера при совпадении (CTC), и два режима широтно-импульсной модуляции (FastPWM и Phase Correct PWM). Все возможные значения описаны в таблице ниже.



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

   Биты COM01, COM00 (Compare Match Output Mode) - определяют поведение вывода OC0. Если хоть один из этих битов установлен в 1, то вывод OC0 перестает функционировать как обычный вывод общего назначения и подключается к схеме сравнения таймера счетчика Т0. Однако при этом он должен быть еще настроен как выход.
   Поведение вывода OC0 зависит от режима работы таймера-счетчика Т0. В режимах normal и СTC вывод OC0 ведет себя одинаково, а вот в режимах широтно-импульсной модуляции его поведение отличается. Не будем сейчас забивать себе голову всеми этими вариантами и разбирать таблицы для каждого режима, оставим это на практическую часть.

   И последний бит регистра TCCR0 - это бит FOC0 (Force Output Compare). Этот бит предназначен для принудительного изменения состояния вывода OC0. Он работает только для режимов Normal и CTC. При установки бита FOC0 в единицу состояние вывода меняется соответственно значениям битов COM01, COM00. FOC0 бит не вызывает прерывания и не сбрасывает таймер в CTC режиме.

TIMSK (Timer/Counter Interrupt Mask Register)


   Общий регистр для всех трех таймеров ATmega16, он содержит флаги разрешения прерываний. Таймер Т0 может вызывать прерывания при переполнении счетного регистра TCNT0 и при совпадении счетного регистра с регистром сравнения OCR0. Соответственно для таймера Т0 в регистре TIMSK зарезервированы два бита - это TOIE0 и OCIE0. Остальные биты относятся к другим таймерам.

TOIE0 - 0-е значение бита запрещает прерывание по событию переполнение, 1 - разрешает. 
OCIE0 - 0-е значение запрещает прерывания по событию совпадение, а 1 разрешает.

   Естественно прерывания будут вызываться, только если установлен бит глобального разрешения прерываний - бит I регистра SREG.

TIFR (Timer/Counter0 Interrupt Flag Register)



   Общий для всех трех таймеров-счетчиков регистр. Содержит статусные флаги, которые устанавливаются при возникновении событий. Для таймера Т0 - это переполнение счетного регистра TCNT0 и совпадение счетного регистра с регистром сравнения OCR0. 

Если в эти моменты в регистре TIMSK разрешены прерывания и установлен бит I, то микроконтроллер вызовет соответствующий обработчик.
   Флаги автоматически очищаются при запуске обработчика прерывания. Также это можно сделать программно, записав 1 в соответствующий флаг.

TOV0 - устанавливается в 1 при переполнении счетного регистра.
OCF0 - устанавливается в 1 при совпадении счетного регистра с регистром сравнения

SFIOR (Special Function IO Register)


   
    Начинающему про этот регистр в принципе можно и не знать, один из его разрядов сбросывает 10-ти разрядный двоичный счетчик, который делит входную частоту для таймера Т0 и таймера Т1. 

   Сброс осуществляется при установке бита PSR10 (Prescaler Reset Timer/Counter1 и Timer/Counter0) в единицу.

Заключение

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

Comments   

# foxit 2013-08-14 20:24
Ждем продолжение!
# Pashgan 2013-08-16 18:54
Ученые бьются над этим.
# D.mas 2014-09-21 11:04
Здравствуйте, спецы :-) Я не волшебник, а только учусь, пытаюсь самостоятельно запилить хронограф для пневматики. Видел в интернете и схемы и решения, но хочется свое. С датчиками еще не определен, разбираюсь с по. Поэтому хотел бы у вас спросить.. Какое количество тактов уходит на обработку прерывания по совпадению с OCR0? Понимаю, что зависит от длины кода в обработчике. И вообще, если установить CLK 8МГц,prescaler= 1; OCR0=0x01; и в теле прерывания, допустим, глобальная переменная i будет i++, будет ли исправно отсчитываться по 1/8К секунд?
# D.mas 2014-09-21 11:06
сорри, по 1/8М сек
# alex6441161 2014-12-02 14:40
подскажите а обязательно значение в регистр OCR1A записывать в 16ричном формате? и обязательно ли записывать значение сначала в старший а потом младший, т.е. вот так:

OCR1AH=0x03;//записываем в регистр OCR1A 1000
OCR1AL=0xE8;
или можно просто

OCR1A=0x03E8;
# _Артём_ 2014-12-03 08:09
Quoting alex6441161:
подскажите а обязательно значение в регистр OCR1A записывать в 16ричном формате?

Нет, не обязательно. Можете хоть в десятичном, хоть в символьном.

Quoting alex6441161:
и обязательно ли записывать значение сначала в старший а потом младший, т.е. вот так:

OCR1AH=0x03;//записываем в регистр OCR1A 1000
OCR1AL=0xE8;

У АВР шина 8-битная, поэтому сначала надо писать в Н, потом в L. При чтении наоборот.

Quoting alex6441161:

OCR1A=0x03E8;

Так тоже можно, если компилятор поддерживает (IAR и GCC позволяют так обращатся к регистру, насчёт других не знаю).
# Zaprom 2014-12-04 20:05
Здравствуйте!! По-моему есть ошибка в строке " Биты WGM10, WGM00 (Wave Generator Mode) - определяют режим работы таймера-счетчик а Т0." Там должен быть бит WGM01.
# Ваха 2015-01-23 13:13
Quote:
Биты WGM10, WGM00 (Wave Generator Mode) - определяют режим работы таймера-счетчика Т0.
думаю тут ошибка,наверное подразумевалось WGM01 судя по таблице которая приводится следом
# goodspeedmen 2016-12-09 18:24
Code:ISR(TIMER2_OVF_vect) //программа обработки прерывания по переполнению таймера
{
PORTB = (0);
TIMSK0 = (0b00000000);
}

int main(void) //основная программа
{
TCCR0A = (0b00000000);
TCCR0B |= (0b00000101);
DDRB |= (0b10000001);
DDRD |= (0b10000000);
sei(); //разрешаем глобальные прерывания

while(1)
{
IF(PIND.0 == 1)
{
TIMSK0 = (0b00000001);
TCNT0 = (0b00000000);
PORTB.0 = 1;
}
}
}
# goodspeedmen 2016-12-09 18:28
Quoting goodspeedmen:
Code:ISR(TIMER2_OVF_vect) //программа обработки прерывания по переполнению таймера
{
PORTB = (0);
TIMSK0 = (0b00000000);
}


int main(void) //основная программа
{
TCCR0A = (0b00000000);
TCCR0B |= (0b00000101);
DDRB |= (0b10000001);
DDRD |= (0b10000000);
sei(); //разрешаем глобальные прерывания

while(1)
{
IF(PIND.0 == 1)
{
TIMSK0 = (0b00000001);
TCNT0 = (0b00000000);
PORTB.0 = 1;
}
}
}

Можно ли использовать нечто такое?
# ujin 2017-01-15 18:23
ну разве что для посмотреть в протэусе,в реальной железке недееспособный код

У вас недостаточно прав для комментирования.