Загальна інформація
Таймер — це апаратний периферійний модуль у мікроконтролері, який збільшує значення лічильного регістру таймера на основі тактового джерела. Він використовується для вимірювання часу, генерації періодичних подій, створення точних затримок, а також генерації сигналів (хвильових форм) тощо.
Зазвичай в одному мікроконтролері доступно кілька таймерів, наприклад: Timer-1, Timer-2, Timer-3 тощо. Вони можуть бути 8-ми або 16-бітними. Деякі висококласні мікроконтролери мають 32-бітні або 64-бітні таймери. Відповідно до розрядності, значення лічильного регістру, до якого рахує таймер може бути 0-255 для 8-бітних, 0-65525 для 16-бітних тощо.
Таймери можуть працювати від системного тактового сигналу (наприклад, 16 МГц у ATmega328P), від попередньо поділеного тактового сигналу (prescaler — поділ системної частоти на 8, 64, 256 тощо), а також від зовнішнього тактового сигналу.
Prescaler — це апаратний дільник, який зменшує частоту системного тактового сигналу перед тим, як він надходить до таймера. Наприклад, якщо системна частота становить 16 МГц, а prescaler дорівнює 2, то частота сигналу на вході таймера буде 8 МГц (16 МГц / 2 = 8 МГц).
Частота таймера = частота тактового сигналу процесора / значення prescaler.
На діаграмі нижче зображено загальний цикл роботи таймера в мікроконтролері:

Режими та функції таймерів
| РЕЖИМ | ОПИС | ВИКОРИСТАННЯ |
| Режим таймера | Збільшення значення лічильного регістру таймера відбувається на основі тактового сигналу | Генерація точних затримок, тригери періодичних подій |
|
Режим лічильника |
Підрахунок зовнішніх імпульсів (подій) | Підрахунок імпульсів, вимірювання обертів (RPM) |
| Режим захоплення | Фіксація значення таймера у момент виникнення зовнішньої події | Вимірювання тривалості імпульсу |
| Режим порівняння | Генерує переривання, перемикає стан пінів або скидає таймер, коли значення таймера дорівнює заданому значенню | ШІМ (PWM), генерація хвильових сигналів та точного створення затримок |
| Режим CTC | Cкидання таймера при збігу з порівняним значенням | Генерація хвильових сигналів та точного створення затримок |
| Режим PWM | Генерація ШІМ-сигналів | Керування швидкістю моторів, регулювання яскравості світлодіодів |
| Режим One-Pulse | Генерація окремого імпульсу при спрацюванні тригера | Точний одноразовий імпульс (наприклад, для запуску зовнішніх пристроїв) |
Примітка: не всі мікроконтролери підтримують усі вищезгадані режими таймерів. Деякі мікроконтролери можуть пропонувати подібні функції під іншими назвами або через інші конфігурації.
Розглянемо детальніше кожен режим.
1. Режим таймера
У цьому режимі лічильник таймера збільшує своє значення на кожен тактовий імпульс. Коли він досягає максимального значення (наприклад, 255 для 8-бітного таймера), відбувається переповнення та скидання в 0. Після скидання встановлюється прапорець переповнення (overflow flag) і, якщо налаштовано, генерується переривання.

Встановлення власного початкового значення у регістрі лічильника таймера дозволяє створювати точні затримки.
Наприклад, якщо встановити початкове значення 100, він переповниться після 155 тактів (від 100 до 255).
2. Режим лічильника
Режим лічильника (Counter Mode) схожий на режим таймера, але використовує зовнішні імпульси замість внутрішнього тактового сигналу. Він ідеально підходить для підрахунку подій, таких як фронти сигналу, натискання кнопок або імпульси від датчиків (наприклад, датчик обертів двигуна).
3. Режим захоплення
Режим захоплення копіює поточне значення регістра-лічильника таймера в регістр захоплення (Input Capture Register) щоразу, коли на визначеному вхідному піні відбувається зовнішня подія (наприклад, фронт наростання). Це дозволяє вимірювати точний час зовнішніх сигналів, зберігаючи значення таймера саме в момент виникнення події.
4. Режим порівняння
У цьому режимі таймер збільшує своє значення з кожним тактовим імпульсом. Коли значення лічильника збігається зі значенням регістра порівняння, виконується певна дія (наприклад, перемикання, встановлення або скидання вихідного піна) або генерується переривання (якщо воно налаштоване).
Використовується для PWM, генерації сигналів та точних затримок.

5. Режим CTC (Clear Timer Compare Match)
Режим CTC скидає таймер до нуля, коли його значення збігається зі значенням у регістрі порівняння (Output Compare Register). Це дозволяє точно контролювати часові інтервали без переповнення лічильника. Такий режим зазвичай використовується для створення точних затримок, генерації сигналів або періодичних переривань.

6. Режим ШІМ (PWM)
Режим PWM використовує таймер для генерації сигналів ШІМ зі змінним коефіцієнтом заповнення, порівнюючи значення регістра таймера/лічильника зі значенням регістра порівняння.
Це корисно для таких задач, як керування двигунами, регулювання яскравості світлодіодів і імітація аналогового сигналу.
У режимі PWM таймера є 2 конфігурації:
-
Fast PWM (швидкий ШІМ)
-
Phase Correct PWM (ШІМ із корекцією фази)

Швидкий ШІМ: У цьому режимі таймер рахує від нижчого значення (0) до вищого (залежить від бітності лічильного регістру), після чого скидається в 0 і процес повторюється безперервно. В момент, коли значення в лічильному і порівняльному регістрах рівні, на вихідному піні встановлюється високий рівень (одиниця). Коли ж лічильник скидається в нуль, на вихідному піні напруга зникає (логічний нуль). Таким чином створюється високочастотний ШІМ-сигнал. Коефіцієнт заповнення (duty cycle) контролюється регістром порівняння. Оскільки оновлення відбувається на верхньому значенні, цей режим працює швидше, але є менш точним для задач, критичних до таймінгу. Ідеально підходить для керування швидкістю двигуна або регулювання яскравості світлодіодів.
ШІМ з корекцією фази: У цьому режимі таймер рахує вгору від найнижчого значення до найвищого, а потім назад (подвійний схил, dual-slope). Це створює симетричний ШІМ-сигнал, центрований відносно верхнього значення, що дає меншу частоту, але кращу фазову точність. Такий режим корисний у випадках, коли важлива симетрія сигналу, наприклад у силовій електроніці.
7. Режим одного імпульсу
Режим одного імпульсу дозволяє таймеру згенерувати один, точно визначений імпульс при спрацьовуванні. Таймер запускається від вхідної події, відлічує задану тривалість, а потім зупиняється — ідеально підходить для одноразової генерації сигналу або реакції на подію.
Таймери та регістри
Таймер — це важливий периферійний модуль, який присутній у кожному мікроконтролері, наприклад AVR, STM, 8051 тощо.
Візьмемо за приклад ATmega328P, який має 3 таймери: Timer0 (8-бітний), Timer1 (16-бітний) і Timer2 (8-бітний) з режимами Таймер, CTC, Порівняння та ШІМ (Швидкий та з корекцією фази).
Загалом у контролерах присутні такі регістри для роботи з таймерами (на прикладі AVR ATmega328P):
| Назва регістру | Опис |
| TCCRnA/B | Регістр керування таймером — встановлює режим роботи таймера, прескалер і поведінку PWM. |
| TCNTn | Лічильник таймера — зберігає поточне значення відліку таймера. |
| OCRnA/B | Регістр порівняння таймера — зберігає значення для порівняння. |
| TIMSKn | Регістр маски переривань — вмикає або вимикає базові переривання таймера. |
| TIFRn | Регістр прапорців переривань — показує, які прапорці переривань таймера встановлені. |
| ICRn | Регістр захоплення входу (лише для Timer1) — фіксує значення таймера під час фронту сигналу. |
*примітка: всі n значать номер таймера. Наприклад, регістр TCCR2A - регістр керування Timer2, OCR1A - регістр порівняння для Timer1
Функції Arduino, пов’язані з таймером
В Arduino, для різних задач часто потрібно налаштовувати регістри вручну, оскільки вбудовані функції дуже обмежені.
Доступні стандартні функції Arduino, які використовують теймери:
| Функція Arduino | Таймер | Опис |
delay(ms) |
Timer0 | Контролер очікує (зупиняється) протягом заданої затримки в мілісекундах. |
millis(), micros() |
Timer0 | Повертає кількість мілісекунд/мікросекунд, що пройшли з моменту запуску Arduino. |
analogWrite(pin, value) |
Timer0/1/2 |
Генерує PWM-сигнал на таких цифрових пінах:
|
tone(pin, frequency, duration) |
Timer2 | Генерує прямокутний сигнал (квадратну хвилю) вказаної частоти і з вказаною тривалістю |
noTone(pin) |
Timer2 | Зупиняє генерацію квадратної хвилі, запущеної за допомогою функції tone(). |
pulseIn(pin, value, timeout) |
Timer0 | Вимірює тривалість імпульсу на вказаному піні. |
Приклади використання
1. Затримка виконання роботи на вказаний час.
Таймер налаштований у звичайному режимі. У цьому режимі таймер рахує від попередньо завантаженого значення в регістрі лічильника таймера до свого максимального значення (255 для 8-бітного), а потім переповнюється.
Timer2 — це 8-бітний таймер з частотою 16 МГц і прескалером 1024. Розрахуємо початкове значення лічильника для затримки в 10 мілісекунд:
Частота таймера = 16 МГц / 1024 = 15,652 кГц
Час на один такт = 1 секунда / 15,652 кГц ≈ 64 µс (лічильник таймера збільшується кожні 64 µс).
Необхідна кількість тактів = 10 мілісекунд / 64 мікросекунди = 10 000 / 64 ≈ 156
Початкове значення лічильника = 256 − 156 = 100
Запишемо в регістр лічильника значення 100 і таймер переповниться після 150 тактів, таким чином згенерує затримку 10 мс.
void setup(){pinMode(7, OUTPUT);// Timer2 налаштовуємо на затримку 10 мсTCCR2A = 0; //всі біти регістра встановлено в 0 - нормальний режим роботи Timer2TCCR2B = 0; //регістр дільника частоти таймера (прескалер), обнуляємо...
//... і встановлюємо біти CS20, CS21, CS22 в 1
//ця комбінація встановить режим прескалера - 1024TCCR2B = (1 << CS22) | (1 << CS21) | (1 << CS20);}void loop(){
робимо_щось_корисне();
//Генеруємо затримку:TCNT2 = 100; //регістр лічильника - встановлюємо початкове значення
//зациклюємо програму до встановлення прапору переповнення Timer2 (біт TOV2, регістру TIFR2)while ((TIFR2 & (1 << TOV2)) == 0);TIFR2 |= (1 << TOV2); //для скидання прапорцю TOV2, записуємо в нього 1 (особливість AVR-мікроконтролерів)}
Подібним чином працює функція delay(ms) Ардуіно.
2. Генерація PWM-сигналу з робочим циклом 40 % у неінвертованому режимі за допомогою Timer0, з тактовою частотою CPU 16 МГц і прескалером 64.
У наведеному нижче коді Timer0 налаштований у режимі Швидкий ШІМ (неінвертований).
Генерація неінвертованого PWM на піні 6 (Arduino Uno) з використанням режиму Fast PWM (Timer0):
Значення OCR0A: 102 (≈40 % від 255)
Коефіцієнт заповнення (Duty Cycle): 40 %
Тут таймер налаштований у режимі Fast PWM з неінвертованим режимом. Це означає, що коли значення в регістрі OCR0 співпадає зі значенням таймера, вихід на піні 6 переходить у LOW, а при переповненні таймера вихід на піні 6 знову переходить у HIGH. Таким чином, змінюючи значення регістра OCR0A, ми можемо змінювати коефіцієнт заповнення (duty cycle) PWM.
У цьому прикладі значення OCR0A = 102, що становить приблизно 40 % від 255. Отже, згенерований PWM має коефіцієнт заповнення 40 %.
void setup() {noInterrupts(); //вимикаємо всі приховані переривання, щоб налаштування таймеру пройшло без збоївpinMode(6, OUTPUT); //пін 6 (OC0A) - спеціальний вихід таймера Timer0, таймер може напряму керувати нимTCCR0A = (1 << WGM01) | (1 << WGM00); //біти WGM00, WGM01 в встановлюємо в 1 - це режим швидкого ШІМTCCR0A |= (1 << COM0A1); //неінвертований режим для піна 6TCCR0B = (1 << CS01) | (1 << CS00); //прескалер = 64OCR0A = (255 * 0.4); //регістр порівняння = 102 (40% коефіцієнт заповнення)}void loop() {//нічого робити не треба, PWM працює автоматично на апаратному рівні.}
*Примітка: Timer0 використовується функціями Arduino — millis(), micros() і delay(), які залежать від переривань Timer0. Для точного таймінгу з Timer0 необхідно вимкнути ці переривання, щоб уникнути взаємного впливу.
3. Генерація прямокутного сигналу з конкретною частотою на піні 9 (пін OC1A) з використанням режиму CTC без прескалера.
Згенеруємо прямокутний сигнал частотою 400 Гц. Тактова частота CPU — 16 МГц.
Timer1 налаштований у режимі CTC (Clear Timer on Compare Match). У цьому режимі таймер рахує від початкового значення 0 до значення регістра порівняння (Compare Match Register). Коли відбувається співпадіння, на наступному такті таймер скидається в 0 і починає новий відлік.
Щоб згенерувати прямокутний сигнал, ми перемикаємо стан піна 9 при кожному співпадінні з регістром порівняння (Compare Match).
Timer1 — це 16-бітний таймер з 16 MHz і прескалером 1, тоді
Частота таймера = 16 MHz / 1 = 16 MHz
Часу на 1 тік = 1 / Частота таймеру = 1 / 16 MHz = 62.5 наносекунд (лічильник таймера збільшується кожні 62.5 нс)
Період прямокутного сигналу = 1 секунда / 400 Hz = 2.5 мсЩоб згенерувати прямокутний сигнал 400 Hz, пін 9 перемикаємо кожні 1.25 мс, оскільки повний цикл 2.5 мс (1.25 мс - HIGH, 1.25 мс - LOW).Необхідно тіків = 1.25 мс / 62.5 нс = 20 000
Значення регістру порівняння = 20 000 − 1 = 19 999 (віднімаємо одиницю, бо таймер рахує від 0)
Завантажте в регістр порівняння значення 19999. Таймер рахує від 0 і, досягнувши значення регістра порівняння (19999), скидається в 0 і перемикає стан піна.
void setup() {TCCR1A = (1 << COM1A0); //перемикати стан піна OC1A при співпадінні з регістром порівняння.TCCR1B = (1 << WGM12) | (1 << CS10); //режим CTC, прескалер = 1OCR1A = 19999; //Встановлюємо значення порівнянняDDRB |= (1 << PB1); // D9 (PB1) налаштовуємо на вихід}void loop() {}
4. Розрахунок частоти прямокутного сигналу з використанням режиму захоплення таймера Timer1 на Arduino Uno.
Примітка: тактова частота CPU — 16 МГц, прескалер не використовується.
У наведеному нижче прикладі ми хочемо розрахувати частоту прямокутного сигналу.
У режимі захоплення таймер Timer1 працює безперервно. Коли на певному піні з’являється фронт зовнішнього сигналу (зростаючий або спадаючий), поточне значення регістра лічильника негайно копіюється в регістр захоплення.
Timer1 — це 16-бітний таймер з тактовою частотою 16 МГц і прескалером 1, тоді
Частота таймера = 16MHz / 1 = 16 MHz
Час одного такту = 1 / Частота таймера = 1 / 16MHz = 62,5 наносекунд (лічильник збільшується кожні 62,5 наносекунд)
Частота = 1 / (Період одного циклу)
Щоб отримати період циклу, рахуємо такти між двома зростаючими фронтами. Зовнішній прямокутний сигнал подається на цифровий пін 8 (ICP1) на Arduino UNO. На першому зростаючому фронті значення TCNT1 копіюється в ICR1 і зберігається у змінній count. На другому зростаючому фронті TCNT1 знову копіюється в ICR1. Віднімаємо перше захоплене значення від другого, щоб отримати різницю в тактах.
Період одного циклу = Count * 62,5 наносекунд
uint16_t count = 0;void setup() {pinMode(8, INPUT);Serial.begin(9600);}void loop() {TCCR1A = 0;TCCR1B = 0x41; //Режим захоплення, шумопригнічувач вимкнено, тригер по зростаючому фронту, без прескалераTIFR1 = (1 << ICF1); //очистити прапор захопленняwhile ((TIFR1 & (1 << ICF1)) == 0); //зациклюємо до першого зростаючого фронтуcount = ICR1; //захопити значення регістра лічильника таймера на зростаючому фронті.TIFR1 = (1 << ICF1); //очистити прапор захопленняwhile ((TIFR1 & (1 << ICF1)) == 0); //зациклюємо до другого зростаючого фронтуcount = ICR1 - count;Serial.print("Частота сигналу:");Serial.print(((float)16000000/ count));Serial.println(" Hz");
delay(3000);}
Наведений код може детектувати сигнали приблизно від 250 Гц до 1 МГц.
