Прежде чем приступить к изучению таймера определимся с базовым понятием «частота». Простым языком, это количество повторений, в секунду. Это значит, что если вы за секунду хлопнете в ладошки 2 раза, то частота хлопков будет равна 2Гц. Если за 3 раза, значит 3Гц.
Каждый микроконтроллер работает на определенной частоте. Большинство инструкций выполняется за один такт, поэтому чем выше частота, тем быстрее работает микроконтроллер. Если нет источника тактирования, соответственно ничего работать не будет. На случай отсутствия внешнего источника тактирования, в большинстве микроконтроллеров имеется свой внутренний генератор. Обычно на него «с завода» настроены.
Частота внутреннего источника может изменяться («плавать») из за температуры и т.п., поэтому считается непригодным для серьезных проектов, а у нас ведь именно такие 🙂 Поэтому применяется стабильный источник внешней частоты — кварцевый резонатор (кварц). Один из вариантов исполнения кварцевого резонатора:
Теперь, кое что о таймере. Таймер работает на той же частоте, что и микроконтроллер. Иногда это может быть слишком быстро, поэтому используют предделитель который уменьшает количество тиков в 8/64/256/1024… раз. Включается это все программно.
Допустим, мы выбрали предделитель 1024, частота микроконтроллера 8 МГц, значит после предделителя частота таймера станет:
8 000 000 / 1024 = 7813 Гц — это частота, на которой работает наш таймер. По простому говоря, за одну секунду таймер тикнет 7813 раз.
К количеству тиков можно привязать выполнение кода. Эта фича есть не для всех таймеров, читайте документацию на свой камень. Допустим, нам нужно, чтобы раз в 0,5 секунды выполнялся наш код. За одну секунду 7813 тиков, за пол секунды в 2 раза меньше — 3906. Это значение вносится в регистр сравнения, и с каждым тиком проверяется достаточно ли оттикало или нет, как в будильнике, только очень быстро.
Но вот у нас совпали эти 2 значения и что дальше? Для этого существует такая полезная штука как прерывание по совпадению. Это значит, что при совпадении таймера и регистра сравнения, ваша текущая программа остановится. После этого выполнится участок кода, который абсолютно не связан с основной программой. Внутри этого участка вы можете писать что угодно и не беспокоиться о том, что он как то повлияет на программу, выполнится он только когда значение таймера совпадет с регистром сравнения.
После того как код внутри прерывания выполнится, программа продолжит работу с того места, где была остановлена. Таким образом, можно периодически сканировать кнопки, считать длительность нажатия кнопки, отмерять точные временные промежутки. Любимый вопрос начинающих, как мне делать мигать светодиодом и делать еще что то. Так вот, в этом вам помогут таймеры и прерывания.
Вот теперь мы готовы написать нашу программу. Поэтому создаем проект с помощью мастера проектов. Сразу прицепим LCD, мы же уже это умеем).
Переходим на вкладку Timers и тут остановимся поподробнее:
Выбираем частоту 7813 и устанавливаем галочку напротив пункта Interrupt on: Compare A Match. Таким образом мы указали, что при совпадении значения выполнять прерывание (то о чем было написано выше). Прерывание будем выполнять 1 раз в секунду, т.е. нам нужно тикнуть 7813 раз, поэтому переводим число 7813 в шестнадцатеричную систему и получим 1e85. Именно его и записываем в регистр сравнения Comp A. Регистр сравнения Comp A 16 битный, поэтому число больше 2^16=65536 мы записать не можем.
Генерим, сохраняем, вычищаем наш код. Появится новый непонятный кусок кода
// Timer 1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
}
Это то самое прерывание. Именно внутри этих скобок мы можем писать тот код, который мы хотели бы выполнять через определенные промежутки времени. У нас это одна секунда. Итак логично создать переменную, которую мы будем увеличивать 1 раз в секунду, т.е. 1 раз за прерывание. Поэтому проинициализируем переменную int s =0; а в прерывании будем ее увеличивать от 0 до 59. Значение переменной выведем на жк дисплей. Никаких хитростей, все очень просто.
Получившийся код.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #include <mega8.h> #asm .equ __lcd_port=0x18 ;PORTB #endasm #include <lcd.h> int s = 0; // переменная для хранения секунд // Обработка прерывания по совпадению interrupt [TIM1_COMPA] void timer1_compa_isr(void) { s++; // увеличиваем переменную каждую секунду if(s>59) // обнуляем секунды после 59 { s=0; } TCNT1=0; //обнуляем таймер } void main(void) { TCCR1A=0x00; //настройка таймера TCCR1B=0x05; TCNT1=0x00; //здесь увеличиваются тики OCR1A=0x1E85; //записываем число в регистр сравнения TIMSK=0x10; //запускаем таймер lcd_init(8); #asm("sei") while (1) { lcd_gotoxy(0,0); //вывод в 0 координате X и Y lcd_putchar(s/10+0x30); //вывод десятков секунд lcd_putchar(s%10+0x30); //вывод секунд }; } |
Прикрутив еще 2 переменные можно получить часы на микроконтроллере).
Файл прошивки и протеуса
если нужно точно, поставьте RTC и настройте будильник, если точность не нужна можно настроить таймер раз в секунду, внутри прерывания увеличивать счетчик и сравнивать по количеству прошедших секунд
Благодарю за помощь!
Здравствуйте, начал обучению на cvavr и вот по таймерам у меня не получается смена раз в секунду, на экран выводит +1 раз в 7 сек, симулятор протеус 8.4
Полезная статейка, спасибо.
Польза есть. Благодарю.
Уважаемый Админ! У меня такой вопрос: допустим я запустил все таймеры на микроконтроллере. Они будут «тикать» одновременно за один такт, или каждый таймер отдельно за такт «тикнет»?
Поправьте меня, если я не прав. Допустим, мы запустили проект с одним таймером, допустим, по совпадению. И тут нам понадобился второй таймер. Добавляем. Вопрос: не нужно ли теперь пересчитывать время первого таймера, т.к. «тики» контроллера будут тратиться и на подсчёт второго таймера?
И правильно ли я разумею: если нужны два или более таймера, то Timer0 должен быть с самым частым прерыванием в проекте, а Timer2 — с самым редким? Ведь по таблице прерываний приоритет Timer2 выше, чем Timer0, а иначе он будет «долго ждать очереди»… Так?
и ещё вопрос: как организована функция задержки времени delay_ms? За счёт какого-то таймера? Тогда, если использовать все таймеры микроконтроллера, нельзя будет пользоваться этой функцией?
1. таймеры тикают синхронно с источником, который для них выбран. почитайте статью про таймеры http://avr-start.ru/?p=4500
2. delay_ms сделан на базе цикла с счетчиком, который просто крутит впустую такты процессора.
Если я хочу чтобы прерывания срабатывали ~100 раз в секунду, как правильно записать число 78 в OCR1A: 0х4Е или 0х004Е?
И главное можно ли менять его внутри программы?
вы можете записать 78 или 0х4Е или 0х004Е, никакой разницы. можно.