Перейти к содержанию

Задержки AVR: _delay_ms, _delay_us и циклы avr-libc

Задайте частоту процессора и задержку — калькулятор посчитает, сколько тактов на самом деле выполнят функции avr-libc, какой будет фактическая задержка и ошибка, где кончается допустимый диапазон, и выдаст код с нужными макросами.

Исходные данные

МГц
мс
Реализация

Результат

Фактическая задержка
1 мс
+0 % к заданной
Тактов
16000__builtin_avr_delay_cycles — точный счёт тактов
Предел _delay_ms и _delay_us
268,435 с0xFFFFFF00 тактов __builtin_avr_delay_cycles
Предел _delay_loop_1 / _delay_loop_2
48 мкс / 16,384 мс256 проходов по 3 такта, 65 536 — по 4

Код на C (avr-libc)

#define F_CPU 16000000UL
#include <util/delay.h>

_delay_ms(1);   /* 16000 cycles, 1 ms */

Расчёт выполнен по формулам и данным из источников, приведённых на этой странице, и не заменяет проверку: параметры компонентов у разных производителей и даже партий различаются. Перед применением сверяйте результат с документацией на конкретные детали.

Функции задержки avr-libc — самый простой способ подождать на AVR: ядро крутит цикл заданное число тактов. Но сколько тактов получится на самом деле, зависит от частоты F_CPU, от реализации (встроенная функция компилятора или старые циклы), от округления и от того, не превышен ли предел. Калькулятор повторяет расчёт из исходников util/delay.h и util/delay_basic.h.

Как считаются задержки

Если компилятор поддерживает встроенную функцию __builtin_avr_delay_cycles и включена оптимизация, _delay_ms(t) и _delay_us(t) переводят время в такты:

N=⌈FCPU⋅t⌉,N = \lceil F_\text{CPU} \cdot t \rceil,

и компилятор вставляет цикл ровно на N тактов. По умолчанию такты округляются вверх, чтобы задержка была не короче заданной; макросы __DELAY_ROUND_DOWN__ и __DELAY_ROUND_CLOSEST__ меняют округление на вниз и к ближайшему. Наибольшее значение — 0xFFFFFF00 тактов, то есть 4294967,04 / f_МГц мс: около 71 минуты при 1 МГц.

Без встроенной функции — или если определён __DELAY_BACKWARD_COMPATIBLE__ — работает старая реализация на двух циклах из util/delay_basic.h:

ФункцияЦиклТактов на проходНаибольшая задержка
_delay_loop_1(n)8-битный счётчик, DEC и BRNE3768 / f_МГц мкс (n = 256)
_delay_loop_2(n)16-битный счётчик, SBIW и BRNE4262,14 / f_МГц мс (n = 65 536)

Старый _delay_ms делит время на проходы _delay_loop_2 с отбрасыванием дробной части, а если их больше 65 535, переходит на шаги по 0,1 мс — до 6,5535 с независимо от частоты. Старый _delay_us использует _delay_loop_1, для больших значений — _delay_loop_2, а дальше вызывает _delay_ms. Пересчёт идёт с отбрасыванием дробной части, поэтому задержка получается чуть короче заданной: 10 мкс при 16 МГц — это 53 прохода по 3 такта, 9,94 мкс.

Условия, без которых задержки врут

  • Оптимизация включена, задержка — константа. avr-libc прямо требует обоих условий: иначе время пересчитывается в такты при выполнении, в числах с плавающей точкой, задержка становится намного длиннее и непредсказуемой, а к программе подключается библиотека плавающей точки.
  • F_CPU совпадает с реальной частотой. Макрос задаётся до подключения util/delay.h или ключом -D F_CPU=.... Для Arduino Uno — 16 МГц, для ATmega328P с заводскими фьюзами — 1 МГц (внутренний генератор 8 МГц с делителем на 8); частоту, которую задают фьюзы, показывает калькулятор фьюзов AVR.
  • Прерывания удлиняют задержку. Функции не запрещают прерывания, и время обработчиков прибавляется к циклу. Для точного отсчёта и для задержек, во время которых процессор должен делать что-то ещё, используйте таймер — расчёт в калькуляторе таймера.

Такты команд циклов — DEC и BRNE, SBIW и BRNE — описаны в статье о системе команд AVR.

Частые вопросы

Какая максимальная задержка у _delay_ms?

Если компилятор поддерживает __builtin_avr_delay_cycles (avr-gcc с оптимизацией), — 4294967,04 / f_МГц миллисекунд, около 71 минуты при 1 МГц и 4,5 минуты при 16 МГц; больше задержка обрезается. В старой реализации — 262,14 / f_МГц мс с полной точностью (16,4 мс при 16 МГц), а дальше до 6,5535 с с шагом 0,1 мс.

Почему _delay_ms работает медленнее, чем задано?

Три частые причины. Компиляция без оптимизации (-O0): avr-libc требует включённой оптимизации и константы на этапе компиляции, иначе задержка непредсказуемо растёт, а в программу подключается библиотека плавающей точки. Неверное значение F_CPU — например, 16 МГц в коде при работе от внутреннего генератора 8 МГц. И прерывания: функции не запрещают их, и время обработчиков прибавляется к задержке.

Можно ли передать в _delay_ms переменную?

Нельзя: задержка должна быть константой, известной при компиляции, иначе пересчёт в такты выполняется во время работы в числах с плавающей точкой — задержка выходит намного длиннее и раздувается код. Для переменной задержки вызывают в цикле _delay_ms(1) или используют таймер.

Источники

  1. avr-libc. util/delay.h и util/delay_basic.h (репозиторий avrdudes/avr-libc) — _delay_ms и _delay_us: __builtin_avr_delay_cycles с округлением вверх, __DELAY_ROUND_DOWN__ и __DELAY_ROUND_CLOSEST__, насыщение 0xFFFFFF00 тактов — 4294967,04 / f_МГц мс; старая реализация: 262,14 / f_МГц мс, затем шаг 0,1 мс до 6,5535 с, 768 / f_МГц мкс; _delay_loop_1 — 3 такта, _delay_loop_2 — 4 такта на проход (эталон тестов)
  2. avr-libc. Руководство пользователя: <util/delay.h> — Convenience functions for busy-wait delay loops — те же пределы; требование оптимизации и константной задержки; прерывания удлиняют задержку
  3. Microchip. AVR Instruction Set Manual (DS40002198C) — DEC — 1 такт, SBIW — 2, BRNE — 2 при переходе: 3 и 4 такта на проход циклов (второй источник тестов)
  4. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — 9.2.1 — поставляется с внутренним генератором 8 МГц и запрограммированным CKDIV8: системная частота 1 МГц
Встроить калькулятор на свой сайт

Скопируйте код и вставьте его в HTML страницы, статьи или вики. Виджет бесплатный, без рекламы, счётчиков и cookie. Условие одно — ссылка на GAW под рамкой остаётся на месте. Подробнее о виджетах

<iframe src="https://gaw.ru/embed/avr-delay/" title="Задержки AVR: _delay_ms, _delay_us и циклы avr-libc" width="100%" height="640" style="border:1px solid #e3e5e9;border-radius:8px" loading="lazy" allow="clipboard-write"></iframe>
<p style="margin:6px 0 0;font-size:14px">Калькулятор: <a href="https://gaw.ru/tools/avr-delay/">Задержки AVR: _delay_ms, _delay_us и циклы avr-libc</a> — GAW.ru</p>
Автоподстройка высоты рамки

Если сайт разрешает скрипты, добавьте этот код один раз в любом месте страницы: рамка будет менять высоту вместе с результатом расчёта.

<script>window.addEventListener("message",function(e){if(e.origin!=="https://gaw.ru"||!e.data||e.data.type!=="gaw:embed-height")return;var f=document.querySelectorAll('iframe[src^="https://gaw.ru/embed/"]');for(var i=0;i<f.length;i++)if(f[i].contentWindow===e.source)f[i].style.height=e.data.height+"px"})</script>

Обновлено .

Нашли ошибку в расчёте?