Часто в програмах мікроконтролера потрібно створювати затримки, щоб скоординувати дії або чекати їх кінця. За ідеєю, найпростіший спосіб зробити паузу в роботі мікроконтролера - це перевантажити його процесор будь-яким іншим дією, наприклад, змусити його вважати великі числа. За частотою тактів процесора можна обчислити до скількох він должeн вважати числа, щоб створити певну часову затримку. Зчитування будь-якого числа від нуля до значення частоти такту процесора в герцах, теоретично створювало б затримку в одну секунду. На практиці, з деяких причин, це не так просто.
Якщо процесор мікроконтролера вважає за допомогою чисел, бінарна форма яких така ж широка як і внутрішня шина (в разі AVR - 8 біт), тоді одне аріфмітіческое дію, наприклад, додаток до числа одиниці, займає часу в 1 робочий такт процесора. Для того, щоб оперувати тисячами або мільйонами, число повинне бути 16- або 32-бітовим, і для їх обчислення 8 бітний процесор витрачає більше ніж 1 робочий такт. При великих числах потрібно знати внутрішній устрій процесора, a точніше систему його команд.
Так як в мовах програмування вищого рівня (наприклад, мова Сі) програма не пишеться безпосередньо на основі систем команд, слід, для створення програмної затримки, знати компілятор, який перетворює програму в машинний код. Саме від цього залежить скільки інструкцій (і в слідстві скільки тактів) потрібно для виконання арифметичної операції. Складності додає так само ту обставину, що компілятор може перетворювати програму в машинний код декількома способами - наприклад, роблячи машинний код якомога більш економним по пам'яті або швидко виконуваних. Такі можливості компілятора називають оптимізацією. При різних режимах оптимізації, машинний код програмної затримки і його тимчасова тривалість, різні.
Тут наведено той же відрізок програми на мові Сі тільки після компіляції. Два лівосторонніх шістнадцятирічних числа є машинним кодом і справа перебуває наказ на мові асемблер разом з операндом (операндами). Машинний код і мова асемблер пов'язані між собою, асемблер є просто для подання машинного коду в яку читає формі. При компіляції іспользованa оптимізація довжини програми (параметр компілятора -Os).
У компілювати формі видно, що насправді відбувається в циклі язикa Сі і на підставі цього, можна обчислити скільки тактів доводиться на заповнення одного періоду циклу. Інформація про дії інструкцій і робочий час знаходиться на сторінці даних системи команд AVR. В даному прикладі на один період циклу для заповнення 4 інструкцій припадає 4 такту, тому що всі інструкції запитують один робочий такт. У доповненні до цього до початку циклу витрачається 1 такт для інструкції по завантаженню та 1 такт для виходу з циклу. Якщо припустити, що робочий такт контролера 14,7456 MHz. то можна розрахувати виникла тимчасову затримку всього програмного відрізка:
(1 + 100 ⋅ 4 + 1) / 14745600 = 27,26 μs
Затримка, створена в прикладі, в мікросекундах і використовувана змінна - 8-бітна, тому й машинний код так само простий. Для того, щоб створити паузу в мілісекунди, потрібно зчитувати набагато більші числа і тоді машинний код стане довшим. Можна використовувати цикли, що працюють всередині один одного, але в разі цього методу вся затримка не перебуває у линеарной залежності від кількості циклів, так як з кожним рівнем циклу виникають невеликі додаткові затримки.
Метою даного завдання не є створення точної програмної затримки на рівні машинного коду, тому що це досить тонка робота і до того ж функції для створення затримки вже є в avr-libc і бібліотеці Домашньої Лабораторії. Вони також використовуються в наступних прикладах.
Про програмної затримки важливо знати, що всупереч своїй принциповій простоті - це виключно неефективний метод в плані витрати енергії. Всі ці такти витрачають енергію коли, мікроконтролер займається марною рахунком. Застосовуючи батареї, не радиться створювати довгих програмних затримок, а слід використовувати таймери, які працюють самостійно і пробуджують процесор від сну, якщо потрібно продовжити роботу.
Наступний програмний код відноситься до функції програмної затримки sw_delay_ms. що створює параметром count задану затримку в мілісекунди. Функція, в свою чергу, використовує наполовину написану на мові асемблер функцію _delay_ms бібліотеки avr-libc. Причина, по якій в завданнях відразу не використовується _delay_ms в тому, що з _delay_ms при довгих затримках можуть виникнути проблеми. Функція sw_delay_ms дає можливість без проблем створювати затримки до 65535 мс.
Для використання наведеної функції існує наступна програма, яка створює в нескінченному циклі дві затримки: 100 мс і 900 мс. Протягом короткої затримки LED горить і в перебігу довгої - гасне, і в результаті періодично блимає.
Незважаючи на те, що здається, що LED блимає через 1 секунду, дійсне час трохи довше, так як виклики функцій LED-а і затримки такти мікроконтролера займають деякий час.