Avr урок 10
Сьогодні ми дізнаємося, що таке таймери-лічильники в мікроконтролерах і для чого вони потрібні, а також що таке переривання і для чого вони теж потрібні.
Таймери-лічильники - це такі пристрої або модулі в мікроконтролері, які, як видно з назви, постійно щось вважають. Вважають вони або до певної величини, або до такої величини, скільки вони битности. Вважають вони постійно з однією швидкістю, зі швидкістю тактової частоти мікроконтролера, поправленої на подільники частоти, які ми будемо конфігурувати в певних регістрах.
І ось ці таймери-лічильники постійно вважають, якщо ми їх инициализируем.
Таймерів в МК Atmega8 три.
Два з них - це восьмібітних таймери, тобто такі, які можуть максимально дорахувати тільки до 255. Даною величини нам буде замало. Навіть якщо ми застосуємо максимальний дільник частоти, то ми не те що секунду не відлічимо, ми навіть півсекунди не зможемо порахувати. А у нас завдання саме така, щоб дораховувати до 1 секунди, щоб управляти нарощуванням рахунку світлодіодного індикатора. Можна звичайно застосувати ще нарощування змінної до певної величини, але хотілося б повністю апаратного рахунку.
Але є ще один таймер - це повноправний 16-бітний таймер. Він не тільки 16-бітний. але є в ньому ще певні принади, яких немає у інших таймерів. З даними опціями ми познайомимося пізніше.
Ось цей 16-бітний таймер ми і будемо сьогодні вивчати і використовувати. Також, познайомившись з даним таймером, вам нічого не коштуватиме самостійно вивчити роботу двох інших, так як вони значно простіше. Але тим не менше 8-бітові таймери надалі ми також будемо розглядати, так як для досягнення більш складних завдань нам одного таймера буде недостатньо.
Тепер коротко про переривання.
Переривання (Interrupts) - це такі механізми, які переривають код в залежності від певних умов або певної обстановки, які будуть диктувати деякі пристрої, модулі і шини, що знаходяться в мікроконтролері.
У нашому контролері Atmega8 існує 19 видів переривань. Ось вони всі знаходяться в таблиці в технічній документації на контролер
Якого типу можуть бути умови? У нашому випадку, наприклад, дорахував таймер до певної величини, або наприклад в якусь шину прийшов байт і інші умови.
Тепер давайте розглянемо наш 16-бітний таймер або TIMER1.
Ось його структурна схема
Ми бачимо там регістр TCNTn. в якому постійно змінюється число, тобто воно постійно нарощується. Практично це і є лічильник. Тобто даний регістр і зберігає число, до якого і дорахував таймер.
А в регістри OCRnA і OCRnB (літери n - це номер таймера, в нашому випадку буде 1) - це регістри, в які ми заносимо число, з яким буде порівнюватися чило в регістрі TCNTn.
Наприклад, занесли ми яке-небудь число в регістр OCRnA і як тільки дане число збіглося зі значенням в регістрі рахунку, то виникне переривання і ми його зможемо обробити. Таймери з перериваннями дуже схожі на звичайну затримку в коді, тільки коли ми перебуваємо в затримці, то ми в цей час не можемо виконувати ніякої код (ну знову ж образно "ми", насправді АЛУ). А коли вважає таймер, то весь код нашої програми в цей час спокійно виконується. Так що ми виграємо колосально, не даючи простоювати величезних ресурсів контролера по секунді або навіть по півсекунди. У цей час ми можемо обробляти натискання кнопок, які ми також можемо обробляти в таймері і багато іншого.
Є також регістр TCCR. Даний регістр - це регістр управління. Там налаштовуються певні біти, що відповідають за конфігурацію таймера.
Також у таймера існує кілька режимів, з якими ми також познайомимося трохи позденее.
Він складається з двох половинок, так як у нас конотроллер 8-бітний і в ньому не може бути 16-бітних регістрів. Тому в одній половинці регістра (а фізично в одному регістрі) зберігається старша частина регістру, а в іншому - молодша. Можна також назвати це реєстрової парою, що складається з двох окремих регістрів TCCR1A і TCCR1B. Цифра 1 означає те, що регістр належить саме таймером 1.
Даний регист TCCR відповідає за установку подільника, щоб таймер не так швидко вважав, також він відповідає (вірніше його певні біти) за установку певного режиму.
За установку режиму відповідають біти WGM
Ми бачимо тут дуже багато різновидів режимів.
Normal - це звичайний режим, таймер вважає до кінця.
PWM - це ШІМ тільки різні різновиди, тобто таймер може грати роль широтно-імпульсного модулятора. З цією технологією ми будемо знайомитися в більш пізніх заняттях.
CTC - це скидання за випадковим збігом, якраз те що нам буде потрібно. Тут то і сравнівются регістри TCNT і OCR. Таких режиму два, нам потрібен перший, другий працює з іншим регістром.
Всі різновиди режимів ми в даному занятті вивчати не будемо. Коли нам ці режими будуть потрібні, тоді і розберемося.
Ну давайте не будемо мучити себе документацією і нарешті спробуємо щось в якісь регістри занести.
Код, як завжди, був створений з минулого проекту. Для протеуса також код був скопійований і перейменований з минулого заняття, також у властивостях контролера був зазначений шлях до новій прошивці. Проекти ми назвемо Test07.
Спробуємо як завжди скомпілювати код і запустити його в протеус. Якщо все нормально працює, то починаємо додавати новий код.
Додамо ще одну функцію, благо додавати функції ми на минулому занятті навчилися. Код функції розмістимо після функції segchar і до функції main. Після через те, що ми будемо всередині нашої нової функції викликати функцію segchar.
Мало того, ми створимо не одну функцію, а цілих дві. В одну функцію ми розмістимо весь код ініціалізації нашого таймером, а інша функція буде оброблювачем переривання від таймера, а такі функції вони специфічні і викликати їх не потрібно. Коли виникне необхідність, вони викликаються самі в залежності від певних умов, які були обумовлені вище.
Тому першу функцію ми назвовём timer_ini
Ця функція, як ми бачимо не має ніяких аргументів - ні вхідних, чи не повертаються. Давайте відразу цю функцію викличемо в функції main ()
unsigned char butcount = 0, butstate = 0;
Тепер ми цю функцію почнемо потихеньку наповнювати кодом.
Почнемо з регістра управління таймером, наприклад з TCCR1B. Використовуючи нашу улюблену операцію "АБО", ми в певний біт регістра занесемо одиничку
void timer_ini (void)
TCCR1B | = (1< Також у таймера існує ще ось такий регістр - TIMSK. Даний регістр відповідає за маски переривань - Interrupt Mask. Доступний даний регістр для всіх таймерів, не тільки для першого, він загальний. В даному регістрі ми встановимо біт OCIE1A. який включить потрібний нам тип переривання TIMER1 COMPA TCCR1B | = (1< TIMSK | = (1< Тепер давайте пограємось з самими регістрами порівняння OCR1A (H і L). Для цього доведеться трохи порахувати. Регістр OCR1AH зберігає старшу частину числа для порівняння, а регістр OCR1AL - молодшу. Але перш ніж порахувати, давайте поки напишемо код з будь-якими значеннями даного регістра і потім поправимо, так як далі ми будемо ініціювати дільник і він теж буде брати участь в розрахунку необхідного часу рахунку. Без дільника таймер буде занадто швидко рахувати. TIMSK | = (1< OCR1AH = 0b10000000; // записуємо в регістр число для порівняння TCCR1B | = (); // встановимо дільник. Поки ніякої дільник не встановлює, тому що ми його ще не порахували. Давайте ми цим і займемося. Поки у нас в регістрі OCR1A знаходиться число 0b1000000000000000, що відповідає десятковому числу 32768. Мікроконтролер у нас працює, як ми домовилися, на частоті 8000000 Гц. Розділимо 8000000 на 32768, отримаємо приблизно 244,14. Ось з такою частотою в герцах і буде працювати наш таймер, якщо ми не застосуємо дільник. Тобто цифри наші будуть змінюватися 244 рази в секунду, тому ми їх навіть не побачимо. Тому потрібно буде застосувати дільник частоти таймера. Виберемо дільник на 256. Він нам якраз підійде, а рівно до 1 Гц ми скорегуємо потім числом порівняння. Ось які існують подільники для 1 таймера Я виділив в таблиці необхідний нам дільник. Ми бачимо, що нам потрібно встановити тільки біт CS12. Так як дільник частоти у нас 256, то на цей дільник ми поділимо 8000000, вийде 31250, ось таке ось ми і повинні занести число в TCNT. До такого числа і буде вважати наш таймер, щоб дорахувати до 1 секунди. Число 31250 - це в двійковому поданні 0b0111101000010010. Занесемо дане число в реєстрову пару, і також застосуємо дільник OCR1AH = 0b01111010; // записуємо в регістр число для порівняння З цією функцією все. Тепер наступна функція - обробник переривання від таймера за випадковим збігом. Пишеться вона ось так І тіло цієї функції буде виконуватися саме за фактом настання збігу чисел. Нам потрібна буде мінлива. Оголосимо її глобально, на початку файлу Відповідно, з коду у функції main () ми таку ж змінну приберемо unsigned char i; Тепер, власне, тіло функції-обробника. Тут ми будемо викликати функцію segchar. Потім будемо нарощувати на 1 змінну i. І щоб вона не пішла за межі однозначного числа, будемо її обнуляти при даному умови Тепер трохи виправимо код спочатку функції main (). Порт D. відповідає за стан сегментів, заб'ємо одиничками, щоб при включенні у нас не світився індикатор, так як він із загальним анодом. Потім ми тут занесемо число 0 в глобавльную змінну i, просто для порядку. Взагалі, як правило, при старті в неіціалізірованних змінних і так завжди нулі. Але ми все ж проініціалізіруем її. І, найголовніше, щоб переривання від таймера працювало, її недостатньо включити в ініціалізації таймера. Також взагалі для роботи всіх переривань необхідно вирішити глобальні переривання. Для цього існує спеціальна функція sei () - Set Interrupt. Тепер код буде ось таким Також ще ми зобов'язані підключити файл бібліотеки переривань спочатку файлу // unsigned char butcount = 0, butstate = 0; Зберемо наш код і перевіримо його працездатність спочатку в протеус. Якщо все нормально працює, то перевіримо також в живій схемою Все у нас працює. Відмінно! Ось такий от вийшов секундомір. Але так як у нас навіть немає кварцового резонатора, то даний секундомір не можна назвати точним. Проте сьогодні ми з вами багато чого навчилися. Ми дізналися про переривання, також навчилися їх обробляти, Навчилися працювати з таймерами, конфігурувати кілька нових регістрів мікроконтролера, до цього ми працювали тільки з регістрами портів. Також за рахунок всього цього ми значно розвантажили арифметичне-логічний пристрій нашого мікроконтролера. Купити програматор можна тут (продавець надійний) USBASP USBISP 2.0Схожі статті