В микроконтроллерах avr есть такой периферийный модуль как таймер счетчик. В соответствии со своим названием он считает время. У таймер счетчика есть регистр TCNT из которого можно прочитать сколько времени прошло с момента запуска таймера. Значение в этом регистре не в минутах или секундах, а в ‘попугаях’ — ‘тиках’ таймера. Чему равен один тик — зависит от тактовой частоты, на которой работает микроконтроллер avr, и от настроек таймера.
Так же у таймера счетчика есть настраиваемый делитель частоты, который определяет на сколько будет поделена тактовая частота микроконтроллера перед тем как будет подана на таймер счетчик. Длительность тика таймера является обратной величиной от частоты, полученной в результате деления.
Например:
Тактовая частота = 11059200 Гц ( 11.0592 МГц )
Делитель = 1024
Частота таймера счетчика = Тактовая частота/Делитель = 11059200/1024 = 10800 Гц
Длительность периода( тика ) = 1/10800 = 92.593*10^-6 секунды = 92.593 мкс
Для настройки делителя используются биты CS регистра TCCR.
Например для таймера 1 в atmega8 используются регистр TCCR1B и биты CS10, CS11, CS12.
Зависимость делителя от состояния бит CS для таймера 1 в atmega8
CS12 | CS11 | CS10 | Делитель |
0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 |
0 | 1 | 0 | 8 |
0 | 1 | 1 | 64 |
1 | 0 | 0 | 256 |
1 | 0 | 1 | 1024 |
Пример кода настройки делителя для таймера 1 avr atmega8:
TCCR1B = (1<<CS12)|(0<<CS11)|(1<<CS10); //xtall/1024 |
В avr таймеры могут быть 8 или 16 разрядными. Разрядность определяет максимальное количество тиков которые может сосчитать таймер. Для 8 разрядного это 256 тиков, для 16 это 65536.
В нашем случае один тик равен 92.593 мкс, соответственно максимальное значение, которое может измерить 16 битный таймер, это 65536*92.593*10^-6 = 6.068 секунды, 8-ми битный — 0.0237 секунды.
После того как таймер досчитает до максимального значения, он переполняется, т.е. начинает считать с нуля. Эту ситуацию можно обрабатывать при помощи прерываний. Для этого надо разрешить прерывание по переполнению таймера и выставить бит общего разрешения прерываний.
Пример кода для разрешения прерываний таймера 1 avr atmega8:
TIMSK |= (1<<TOIE1); // разрешить прерывание по переполнению таймера счетчика sei(); // выставить бит общего разрешения прерываний |
Так же надо определить обработчик данного прерывания — функцию которая будет вызвана при переполнении таймера.
Добавим в эту функцию код изменяющий состояние ножки PB0, к которой подключен светодиод.
Пример кода функции обработчика прерывания для таймера 1 avr atmega8:
ISR( TIMER1_OVF_vect ) { if( PINB & ( 1 << PB0 ) ) { PORTB &= ~( 1 << PB0 ); } else { PORTB |= ( 1 << PB0 ); } } |
Светодиод будет изменять свое состояние через каждые 6 секунд.
Чтобы светодиод менял свое состояние с частотой 10Гц, надо чтобы таймер отсчитывал не 65536 тиков а меньше. Рассчитаем необходимое число тиков:
10800/10 = 1080
Чтобы таймер считал не с нуля а с некоторого значения, при инициализации и при каждом срабатывании прерывания в регистр TCNT1 будем записывать число (65536 — 1080) = 64456.
Листинг итоговой программы для таймера 1 avr atmega8:
#include <avr/io.h> #include <avr/interrupt.h> ISR( TIMER1_OVF_vect ) { TCNT1 = 64456; //выставляем начальное значение TCNT1 if( PINB & ( 1 << PB0 ) ) { PORTB &= ~( 1 << PB0 ); } else { PORTB |= ( 1 << PB0 ); } } int main() { DDRB = ( 1 << PB0 ); // настраиваем PB0 на выход TCCR1B = (1<<CS12)|(0<<CS11)|(1<<CS10); // настраиваем делитель TIMSK |= (1<<TOIE1); // разрешаем прерывание по переполнению таймера TCNT1 = 64456; // выставляем начальное значение TCNT1 sei(); // выставляем бит общего разрешения прерываний while(1); // вечный цикл return 0; } |
Так же вы можете прочитать другие статьи из рубрики «Микроконтроллеры avr».
Архив с исходниками под avr-gcc (WinAvr) можно скачать тут.
P.S. Если Вам понравилась эта статья, или же наоборот, Вы считаете ее бесполезной, если у Вас есть какие-то вопросы или пожелания, пожалуйста, напишите комментарий.
Норма, все зрозуміло.
Андрій, радий, що допоміг =)
Все понятно, полезная статья. Самое главное все работает сразу.
Тагир, пожалуйста!
Полезная статья. Спасибо
Serge, спасибо за отзыв!
Есть неточность в настройке таймера счетчика, нужно еще настроить биты WGM в одном из регистров, плохо что тут это не расписано. Ищу информ-ию по этим битам. =\
биты WGM в основном используются для настройки ШИМ (PWM), а если надо таймер настроить на срабатывание через заданный промежуток времени, то эти биты не нужны, они по умолчанию выставлены в 0, что обеспечивает требуемый режим работы таймера.
Очень хорошо описано!
Большое спасибо — стало понятнее.
Игорь, пожалуйста! :)
Неплохая статья, но можно было бы (и скорее всего, необходимо) добавить про тактирование таймера, что он может тактироваться не только от микроконтроллера, но и из вне. Со всеми вытекающими обстоятельствами.
То есть предложение:
«Так же у таймера счетчика есть настраиваемый делитель частоты, который определяет на сколько будет поделена тактовая частота микроконтроллера перед тем как будет подана на таймер счетчик. » рассматривает только лишь частный случай работы таймера.
Спасибо за отзыв. Да Вы правы, надо будет дополнить статью, или может отдельную заметку написать.
Спасибо. Толково. Есть вопрос. Таймер стартует при включении МК. А как можно запускать и останавливать его вручную(при помощи кнопки например).
Пожалуйста :)
Таймер стартует когда делитель частоты не равен нулю, соответственно, что бы остановить таймер надо выставить делитель частоты равным нулю, например, для таймера 1 atmega8 надо обнулить биты CS12, CS11, CS10
здравствуйте. как получить сигнал от ИК приемника(38 КГц) его data подключен в PB0 у.
не знаю как настроит таймеры .
помогите пожалуйста.
за ранние спасибо.
Доброго времени суток! А какой у Вас микроконтроллер?
Помогите пожалуйста . Сейчас разбираюсь с UART на avr studio4 (mega8)
Хочу сделать программу , в которой через терминал мы вводим число , и светодиод мигает , ну например написал 10, значит мигать будет 10 раз в сек и тд.
Помогите советом пожалуйста
Заранее благодарен
Вот мой код
День добрый, попробую помочь.
Но сейчас я немного занят — работаю :) вечером посмотрю.
И у меня к Вам просьба, пожалуйста, причешите немного свою программу — уберите неиспользуемый код, добавьте комментариев, поправьте форматирование.
Может кто-нибудь на ассемблере выложить настройку таймера и мигалку на светодиоде?
Добрый день!
Нужна ваша помощь! Я не программист, начинаю с простого — с Ардуино (знаю, плохое начало, но так сложилось). Вот код
Ардуина не считает время меньше 4 мкс, надо работать с таймерами на прямую. Но я не знаю как и что!!! Помогите, пожалуйста!
Назар, день добрый!
Извините, что не сразу отвечаю, лето, отпуск… отдыхаю от компьютера :)
Ардуино — нормальное начало, пока его возможностей хватает, почему-бы и не использовать, тем более что это проще чем писать на голом Си.
Могу подсказать, как решить вашу задачу без библиотек от arduino, используя только ардуиновску плату.
В качестве компилятора будет avr-gcc или WinAvr.
Если Вам интересно — пишите, продолжим общение :)
Кстати, а что это за устройство, которое Вы разрабатываете?
Здравствуйте, Артем! Спасибо за ответ!
Я свою проблему решил, прибор — это измеритель емкости от 1пФ+-0,25 пФ до 1,5 мкФ+-2,5нФ. Все работает, но интересно было бы сделать все не на Arduino IDE.
Вот мой код :
Еще вопрос: как сделать что-бы значение таймера при переполнении продолжало увеличиваться, а не обнулялось и начиналось сначала? То есть, я хочу сделать таймер 32-х битным. Это для того, что-бы не делать отдельные пределы измерений, а просто увеличить время заряда конденсатора.
Заранее спасибо!
П.С. могу скинуть схемку, если интересно.
Доброго времени суток, Назар!
Таймер в avr 16 битный, что бы получить 32 разряда можно настроить прерывание по переполнению, и в этом прерывании увеличивать программный счетчик, назовём его cnt.
Тогда измеренное значение будет (cnt << 16) + TCNT1;
Если есть желание работать без Arduino IDE, поставьте WinAvr. И двигайтесь по пути от простого к сложному, сначала помигайте светодиодом, потом подключите LCD, http://mainloop.ru/avr-atmega/lcd.html.
затем освойте работу с компаратором.
Будут вопросы — обращайтесь, постараюсь помочь.
А схема конечно интересна, пришлите, пожалуйста!
PS кстати, мне кажется, в этой строке ошибка:
В этом цикле TCNT1 приравнивается 14001.
А должно быть, скорее всего, сравнение (TCNT1==14001)
Здравствуйте, очень интересная и полезная статься, а можете подсказать как это сделать в microPascal for AVR?
Пытаюсь играться с таймером в меге8. Обнаружил что строчка TCNT0=128; в обработчике прерывания влияет на частоту, замедляет ее и она не соответствует расчетной. Без этой строчки фактическая частота замеренная тестером соответствует расчетной а со строчкой уже нет, даже если прописано TCNT0=0;. Собственно нужно чтобы тикало меньше чем 256 раз. В чем нюанс?
Дело может быть в том, что у вас делитель = 1, т.е. таймер работает с частотой 8МГц. С этой же частотой выполняются и команды микроконтроллера (1 — 3 такта на команду). Получается, что если код обработчика прерываний выполняется более чем за 256 тактов, то частота будет уменьшаться. Попробуйте дизасемблировать прошивку и посмотреть какой код генерируется для обработчика прерывания.
Чтобы получить частоту больше чем 8МГц/256 можно попробовать переписать обработчик на ассемблере или использовать таймер1 в режиме CTC (Clear Timer on Compare).
подскажите а обязательно значение в регистр OCR1A записывать в 16ричном формате? и обязательно ли записывать значение сначала в старший а потом младший, т.е. вот так:
или можно просто
Александр,
Значение можно записывать в любом формате.
Если записывать значения отдельно в старший и младший байт, то сначала обязательно надо записать старший байт.
Но можно записывать и сразу два байта: OCR1A=0x03E8, в этом случае компилятор сам произведет запись в нужной последовательности.
как настроить три таймера?
с таемером 2 всё как то неработает
Роман, здравствуйте.
A что значит не работает?
P.S.
строку
лучше заменить на
подскажите пожалуйста. В ATtiny2313a в настройках 16 битного таймера в регистре TCCR1A есть такие биты: COMnA1:0 , COMnB1:0, COMnC1:0. Из даташита вычитал, что при совпадении с регистром сравнения (в соответствии с записанными значениями в эти регистры) будут меняться уровень напряжения на противоположный, подтягиваться к+5 или 0. Но вот не могу понять, при активации данной функции можно ли менять значения этих ножек в программе?
Александр, здравствуйте.
Выставляя биты COMnA1:0 , COMnB1:0, COMnC1:0 Вы включаете альтернативную функцию вывода, и запрещаете его работу как обычного порта ввода-вывода (т.е. выставить значение из программы не получиться). При этом надо не забыть настроить соответствующий вывод на выход.
Попытался прошить программу под AtMega 128 с внешним кварцем на 16МГц, программа почему-то не заработала. Наверно надо исправить значения битов?
Доброго времени суток, Александр.
Может попробовать сначала простую программу?
Если будет мигать, то дело в настройках таймера, если нет, то не работает светодиод.
добрый вечер! помогите пожалуйста как записать программу на аттини 2313 чтобы
одно лампа сразу загоралась а вторая через 5 сек
заранее спасибо!!!!!
Нурик, доброго времени суток :)
Попробую помочь, но мне не известна схема вашего устройства.
По этому есть вопросы, микроконтроллер работает на какой частоте? Какие выводы используете для управления «лампами»? А «лампы» — это что?
Артём, спасибо за статью. Но я никак не могу разобраться с расчетом задержки. Правильно ли я понимаю: attiny2313, выставлено во фьюзах 8МГц (8000000Гц), делитель 1024. Частота таймера: 8000000 / 1024 = 7812,5. Мигание светодиода с частотой 10Гц: 7812,5 / 10 = 781,25, 65536 — 781 = 64754
Но светодиод мигает раз в 2, сек, а 10Гц это 10 раз в секунду.
Может я, что-то не так понял?
Доброго времени суток, Олег.
Вы все рассчитываете правильно. Cветодиод должен менять своё состояние с частотой 10Гц, но мигать он будет с частотой 5 Гц.
А 0.5 Гц получается скорее всего из за того, что выставлен fuse — CKDIV8.
Артём, большое спасибо. Так и есть, CKDIV8 был выставлен.
Спасибо за статью и примеры.
Я ардуину изучаю и наткнулся на пример инициализации и чтения таймера для
CodeVisionAVR и там TCNT1H и TCNT1L регистры отдельно надо грузить и я так и сделал, и компилятор ардуины это проглотил но TCNT1H при раьоте программы всегда был ноль что бы я в него не грузил. А исправил на TCNT1 = xxxxx; и всё стало правильно работать
А нет ли кода как в статье, только на ассемблере?
Вот спасибо большое! Все доходчиво, хоть пользуюст CVAVR, а не тем на чем вы пишете.
Пожалуйста, заходите ещё =)
Помогите начинающему!
Не запускается таймер на Attiny44
/////
#define F_CPU 8000000UL
#include
void init(void)
{
TCCR0B |= (1<<CS02); //настройка делителя нулевого таймера 256
TIMSK0 |= (1<<TOIE0); //разрешение прерывания по переполнению
}
ISR (TIM0_OVF_vect) //прерывание
{
}
int main(void)
{
init(); //инициализация
sei(); //разрешение всех прерываний
while(1)
{
asm("nop");
}
}
/////////////////////////
В данном случае таймер даже не начинает считать!
Подскажите в чём проблема??
Заранее благодарен!!!
Объясните, пож.
1. Из IDE загружаю скетч в ардуино Леонардо, где информация по USB каждую секунду направляется на выход. В консоли вижу ежесекундное обновление.
2. Подключаю вместо консоли TeraTerm\ttermpro.exe». В окне вижу ежесекундное обновление.
3. Все закрываю, отключаю ардуино от USB.
4. Подключаю ардуино к USB. Подключаю TeraTerm\ttermpro.exe». В окне вижу обновление через 8 секуунд.
Почему?
А не проще в счётный регистр занести сразу ноль TCNT1 = 0;
А в регистр сравнения столько тактов, сколько надо сосчитать, например 500 OCR1A = 500;
И настроить таймер на прерывание по совпадению. Вот весь код настройки:
TCCR1A |= (0 << WGM11) | (0 << WGM10);
TCCR1 |= (0 << WGM13) | (1 << WGM12); // Режим 4, CTC "Clear Timer on Compare" значение счётного регистра TCNT1 будет сравниваться со значением в регистре OCR1A
TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10); // Настраиваем предделитель = 8
TIMSK |= (1 << OCIE1A); // Устанавливаем прерывания по совпадению
OCR1A = 461; // Число для сравнения (когда TCNT1 = OCR1A наступит прерывание по совпадению)
TCNT1 = 0; // Обнуляем значение счётного регистра таймера/счётчика1
Ах да и вот собственно обработка этого прерывания:
ISR(TIMER1_COMPA_vect)
{
// Тут код обработчик прерывания
}
Я скопировал данных код в atmel studio 4. И записал код в atmega32a.
У меня диод на ноге PB0 в макетной плате стоит, он так и не замигал
Здравствуйте!
Пишу код в Atmel Studio 4 все работает, но записываю в контроллер (макетная плата на atmega32a) диод на PB0 не мигает. Что-то не могу разобраться с таймерами…