Прежде чем приступить к изучению таймера определимся с базовым понятием «частота». Простым языком, это количество повторений, в секунду. Это значит, что если вы за секунду хлопнете в ладошки 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 переменные можно получить часы на микроконтроллере).
Файл прошивки и протеуса
Евгений, можно разним способом ето реализова. первым это таймером. вторим цикл for. Интересно а зачеи вам такие условия?
Добрый день.
Прошу помочь разобраться с макросом «interrupt», не могу найти описания, не понимаю как им пользоваться.
interrupt это не макрос. это функция куда прыгнет микроконтроллер, когда произойдет условие прерывания. описание в даташите.
Уважаемый админ, как можно переменную (s) реализовать в микросекундах? Например мне нужно увеличивать её или уменьшать для управления симистором путем нажатия кнопок + и -. Как вообще сообщить микроконтроллеру о том, на сколько должна увеличиваться переменная(s++), например на микросекунды или секунды? Читал ваш урок про управление симистором, но как управлять значением OCR при помощи кнопок пока не дошло до меня… 😐
нажали кнопку1 OCR++, нажали кнопку2 OCR—
Добрый вечер, админ, подскажите пожалуйста, как реализовать частоту импульсов 150 Гц с длительностью импульса 150 мкс?
настройте таймер не на секунду а на 150мкс, и инвертируйте там ножку
так это будет только длительность 150 мкс, а частота 150 Гц?
Или это и будет импульс с частотой 150 Гц? Можете объяснить, пожалуйста?
посчитайте период для 150Гц.
Админ, проверьте, я правильно реализовал задачу, частота импульсов 150 Гц и длинна импульса 150 мкс?
МК мега8, настроил на частоту 8 МГц, взял первый таймер и записал в Compare A число 53333 (D055), если поделить 8 МГц на 53333, то будет 150, соответственно частота МК будет делится числом которое в сравнении, и в итоге прерывания будут 150 раз в одну секунду, то есть частота импульсов 150 Гц. А что бы сделать длину импульса 150 мкс, я сделал так:
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
PORTC.0=0;
delay_us(150);
PORTC.0=1;
delay_us(150);
}
Если что то не так, скажите.
Можете ответить?
могу, главное до компа добраться) по факту, работать должно, один из делеев можно смело убрать, хотя всеравно делеи внутри прерывания это не есть хорошо.
А как тогда лучше? я понял что нужно дергать ножкой, как тогда без делеев?
настройте прерывание таймера, например на 10 мкс, заходим в первый раз в прерывание включаем ногу, ставим флаг что сейчас считается время включения, увеличивайте переменную, когда переменная досчитает до 15 это будет 150мкс. сбросим переменную, выключим ногу, поставим флаг, что теперь считается время выключения, до конца периода (1/150Гц).
Админ, вы конечно извините, но я что то не могу настроить таймер на прерывание в 10 мкс. У Вас когда то был пример в уроке о кнопках, но Вы удалили, и там был очень хороший пример. Подкажите пожалуйста как сделать? я понимаю задачу, но не могу реализовать. Не судите строго
очень интересно, на 150Гц вы смогли посчитать таймер, а для 10мкс не можете. читайте эту статью и про студентам о таймерах
Админ, считайте меня тупым, но я реально не могу понять , как настроить таймер именно на 10 мкс. Давайте я напишу как я понимаю, а вы прокомментируйте. Я прочитал статью, это все понятно, но вот реально ступор, не могу понять, аж зло берет, что такая простая задача, а справиться не могу.
Итак, мк мега8, беру первый таймер, частоту беру минимальную, 7813, то есть за одну секунду таймер тикнет 7813 раз, например, я хочу что бы переменная в прерывании по совпадению, увеличивалась каждую секунду на 1 , для этого нужно число 7813 (1E85) записать в регистр сравнения и в обработчик прерывания написать соответствующий код. Это я понял и знаю. Дальше, 7813 это 1 секунда, 3907 это 0.5 сек…Переведем 10 мкс в секунды, это 0,00001 сек. Итак, что бы отсчитывать промежутки по 10 мкс, нам нужно 7813 поделить на 0,00001, полученное число записать в регистр сравнения, 7813/0,00001 = 781300000 в хексе это будет 2E91B120, и как его записать в регистр сравнения? Не злитесь если что, просто реально не могу сообразить
возьмите за основу не 7,813кГц а 8МГц. Вообще я рассуждаю всегда не так. Прикидываю максимальную частоту. Ищем ее период. 1/8000 000 = 0,000000125 сек. Это длительность одного тика. Смотрю сколько в нужном мне отрезке таких тиков 10мкс = 0.00001 сек, 0.00001/0,000000125 = 80 тиков на макс частоте в 10мкс. Это нормально для регистра сравнения. Если было бы больше 255 или 65535 (в зависимости от таймера), то можно было бы уже смотреть следующий предделитель.
здравствуйте,как можно получить задержку времени в секундах -delay_s(10) например. Ругается при компиляции и приходится использовать ms,us….
таймером или через ms
Добрый день
Можно сделать так, чтоб таймер отсчитал
(x) тиков за 1 сикунду умножил на 60 секунд ивывел результат на сигмент. И запускать цикл и обнулять результ отдельными кнопками? И как это сделть ?
Зарание спасибо
Хорошая статья, НО!…. Это все так замечательно ТОЛЬКО в CodeWizardAVR. Для работы таймера нужно поставить галочку «хачу таймер» и будет вам «щастье….». Это все равно, что сходить к соседу и сказать: «хачу вот такое устройство!!!» и если у вас есть такой сосед — он вам сделает (при этом вы как не знали каким образом он это делает, так и не узнаете), а если соседа нет, ну тогда вы так ничего и не сделаете… Правильно было бы назвать Урок «Использование таймера AVR микроконтроллеров в CodeWizardAVR»… Кроме того, на самом деле не сказано как программно, через регистры!!!, настроить таймер (это раз), о том что таймеры могут быть разные, например 8 и 16 битные тогда максимум времени у 8 битных это 1024*256 тиков (32,768 мс для 8 MHz) а у 16 бит 1024*65536=67108864 тиков (8,388608 с для 8 MHz) (это два), как сделать при этом задержку скажем в 18 сек (оптимально с помощью счетчика 3 раза по 6 сек, а не 18 раз по 1 сек)… ни слова про прерывания по совпадению или по переполнению счетчика… Ни слова про сброс при совпадении… Все эти темы очень полезны для осознания особенно у новичков, даже в случае демонстрации их в CodeWizardAVR. Сказав об этом, народ понимал бы что у таймера гораздо больше возможностей, чем просто сделать паузу в несколько мс. Ну а подробно расписав конкретную задачу с мерцаниями, например по 5 мкс с частотой 100 Гц, вопросов типа «а как мерцать по 15 мкс с частотой 150 Гц» было бы гораздо меньше…
ДЛЯ !!! Viktor on !!!
МОЖНО!!! МОЖНО отсчитывать ЛЮБЫЕ временные промежутки любым таймером!!!!!!!!!!! И точность можно подобрать!!!! Элементарно предделитель в 1 и по 250 тиков, при этом считать кол-во прерываний. При частоте в 1 MHz 1 000 000 тиков это 4000 обращений к таймеру. В таймере ставите счетчик, досчитал до 4000, значит 1 сек прошла, сбрасываем счетчик и считаем следующую! Да, не самый айсовский вариант. при этом видим, 4000 можно поделить на 4. Ставим предделитель 4 и уже 1 сек всего 1000 обращений к таймеру. Хотелось бы сказать «Хачу секунду», но автор сам понимает, у нас ограничения: таймер всего 8 бит, если точность критична, то предделитель маленький, а если +- микросекунды, то предделитель можно увеличить, уменьшив количество обращ. к таймеру. в идеале взять кварц с частотой кратной 1024, тогда предделитель 1024 не будет давать погрешности…
Здрасти) Хочу сделать выдержку на таймере приблизительно на 5 минут , чтобы считалось в прерывании ,а в основном цикле пока включены были порты, по проишествии этих 5 мин хотелось бы отключить таймер и включить спящий режим , до следующего включения питания). Это реально? я так понимаю нужно записать в управляющий регистр TCCR1 =0x00; Чтобы его остановить. А как потом перевести контроллер в спящий режим?)
Можно, нужно выставить биты в регистре MCUCR, какие именно зависит от конкретной ситуации.
Уважаемый Админ! По Вашему уроку сделал «показометр» для блока питания (напряжение, ток, мощность). Теперь хочется сделать счетчик ампер-часов. Там тоже таймер используется? Или как это реализовать, чтобы амперы умножались на время и выводилось на дисплей. Благодарен заранее!!!
вам нужно постоянно усреднять потребление тока, таким образом увидите сколько прибор скушал в среднем, далее умножаете на время.
Я очень извиняюсь! Можно чуточку поподробнее. Текущий ток я вывожу так
// Текущий ток
lcd_gotoxy(9,0);
result=((5.00*adc_data[2])*3/1023.00); //пересчитываем значение АЦП в вольты
sprintf(lcd_buffer,»%.1f A «,result); //помещаем во временную переменную результат
lcd_puts(lcd_buffer); //выводим на экран
Как это умножить на время?
Давно читаю сайт, по нему по сути и пытаюсь что-то воспроизвести вживую. Вот возник вопрос: в статье рассмотрен внутренний задатчик 8МГц, а как настроить для этого же кода допустим, внешний кварц на 4 МГц (только фьюзами?)? Как выбрать предделитель не 1024, а 256?
посмотрите список доступных предделителей в кодвизарде. про фьюзы есть статья, читайте.
С этим всем разобрался, но возникла немного другая проблема (может даже немного не к теме урока). В коде устанавливаю на PINC.2=1, после чего выполняю код, делаю задержку Х, и далее на эту же ножку устанавливаю PINC.2=1. Так с 3, 4 и 5 пинами аналогично. Но в протеусе они (2, 3, 4 и 5 пины) последовательно становятся =1, после чего последовательно гаснут. В чем у меня тут косяк, подскажите.
Скриншот вот тут https://imgur.com/a/2ok6W
Исправляюсь: и далее на эту же ножку устанавливаю PINC.2=0
зачем вы устанавливаете в PINC? не путаете с PORTC
Спасибо, исправил и все заработало. И еще вопросик по теме: Когда таймер срабатывает по совпадению, срабатывает прерывание, и счетчик не начинает считать по-новой до тех пор, пока не выполнится код? Или он параллельно начнет? Ведь в 1-м случае со временем будет появляться небольшая ошибка, равная времени выполнения кода? Я это к тому, что в живую часы у меня тикали, но спустя 4 часа они немного ушли вперед (~30 секунд). Списываю на ошибку внутреннего генератора, но мало ли).
оффтоп….
скажите пожалуйста, я как вы вставляете текст (код программы) на сайт что он у вас такой получается…как его так же оформить..что для этого нужно!?
Помогите пожалуйста!!!
geshi
админ, подскажи пожалуйста, у меня мк мега328, работает от внешнего кварца на 16 МГц, я хочу сделать задержку не делеями а таймером на 200мс, все понимаю, написать условие, пока таймер не досчитал до нужного значения, с цикла не выходим. Но значение для 200мс получается большое, даже 16 разрядный таймер не справится, даже с предделителем. Как выйти с ситуации?
увеличивайте счетчик внутри прерыавания на 1мс, когда будет 200 тогда выполняйте условие
Админ, подскажи пожалуйста! Мне надо натсроить таймер так, чтобы он подавал напряжение в течении 10 часов, затем 10 часов не работал. Как можно это реализовать?