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

Тактовая частота, фьюзы и F_CPU

Микроконтроллер не знает, на какой частоте работает, а компилятор не знает этого тем более. Частоту задают фьюзы, делитель CLKPR и кварц на плате, а программе её сообщают макросом F_CPU — и если они расходятся, все задержки и скорости UART становятся неправильными.

Обновлено Редакция GAW

AVR с нуляурок 4 из 830 мин

Цель урока: Расшифровать фьюзы ATmega328P, определить по ним тактовую частоту, правильно задать F_CPU и знать, какие фьюзы могут заблокировать кристалл.

До сих пор мы просто писали F_CPU=16000000UL и верили, что плата работает на 16 МГц. На Arduino Uno это так, но только потому, что в кристалл уже записаны нужные фьюзы. Чистый ATmega328P из магазина работает на другой частоте.

Откуда берётся тактовая частота

Источник тактового сигнала выбирают четыре бита фьюзов CKSEL3…0 (табл. 9-1 даташита):

CKSEL3…0Источник
1111–1000кварцевый генератор с малым потреблением (Low Power Crystal)
0111–0110кварцевый генератор с полным размахом (Full Swing Crystal)
0101–0100низкочастотный кварц
0011внутренний RC-генератор 128 кГц
0010калиброванный внутренний RC-генератор 8 МГц
0000внешний тактовый сигнал на XTAL1
0001зарезервировано

После источника стоит делитель системной частоты — регистр CLKPR (раздел 9.11). Фьюз CKDIV8 задаёт его начальное значение: если фьюз запрограммирован, после сброса частота делится на 8 (раздел 9.12.2).

С завода ATmega328P поставляется с внутренним RC-генератором 8 МГц и запрограммированным CKDIV8, то есть работает на 1 МГц (раздел 9.2.1). Точность заводской калибровки RC-генератора — ±10 % при 3 В и 25 °C (табл. 29-9): для мигания хватит, а для UART уже на грани. Поэтому на Arduino Uno стоит внешний источник 16 МГц, а кристалл настроен фьюзами на него. Наибольшая частота при питании 4,5–5,5 В — 20 МГц.

Фьюзы: три байта

Фьюзы — это биты энергонезависимой конфигурации, которые программа сама изменить не может. У ATmega328P три байта: младший, старший и расширенный (раздел 28.2). Главное правило: 0 — фьюз запрограммирован, 1 — нет. Стирание кристалла (Chip Erase) фьюзы не меняет.

БайтБиты 7 → 0С заводаНа Arduino Uno
Младший (low)CKDIV8, CKOUT, SUT1, SUT0, CKSEL3, CKSEL2, CKSEL1, CKSEL00x620xFF
Старший (high)RSTDISBL, DWEN, SPIEN, WDTON, EESAVE, BOOTSZ1, BOOTSZ0, BOOTRST0xD90xDE
Расширенный (extended)—, —, —, —, —, BODLEVEL2, BODLEVEL1, BODLEVEL00xFF0xFD

Заводские значения собраны нами из столбцов «Default Value» табл. 28-6, 28-8 и 28-9, значения Uno — из boards.txt ядра Arduino. Расшифруем фьюзы Uno:

  • Младший 0xFF = 1111 1111: CKDIV8 не запрограммирован — делителя нет; CKSEL3…1 = 111 — кварцевый генератор на 8–16 МГц (табл. 9-3).
  • Старший 0xDE = 1101 1110: SPIEN = 0 — программирование по SPI разрешено; BOOTSZ = 11 — загрузчик 256 слов с адреса 0x3F00 (табл. 27-13); BOOTRST = 0 — после сброса управление получает загрузчик.
  • Расширенный 0xFD: BODLEVEL = 101 — детектор пониженного питания сбрасывает кристалл при 2,5–2,9 В, типично 2,7 В (табл. 29-12).

Прочитать фьюзы можно программатором: avrdude -c usbasp -p m328p -U lfuse:r:-:h -U hfuse:r:-:h -U efuse:r:-:h. Загрузчик Optiboot фьюзы не читает и не пишет — для этого нужен ISP-программатор.

Опасные фьюзы

Неверно записанный фьюз может сделать кристалл недоступным для ISP-программатора. Перед записью всегда проверяйте значения в калькуляторе фьюзов AVR.

ФьюзЧто случитсяПочему
RSTDISBL = 0вывод PC6 становится обычным выводом порта, сброс внешним сигналом пропадаетISP-программирование начинается с удержания RESET на земле (разделы 14.3.2 и 28.8)
DWEN = 0вывод RESET занят отладчиком debugWIREвнешний сброс при debugWIRE не работает (раздел 25.5); выключить его может только отладчик
SPIEN = 1программирование по SPI запрещенов последовательном режиме бит SPIEN недоступен (примечание к табл. 28-8): ISP-программатором его не выключить, так что эта ловушка грозит только при работе с параллельным программатором — и вернуть бит может только он
CKSEL под кварц или внешний генератор, которого неткристалл не тактируетсядля последовательного программирования нужен рабочий тактовый сигнал (раздел 28.8)
CKSEL = 0011 (RC 128 кГц) или CKDIV8 при медленном источникекристалл работает, но программатор не связываетсянизкий и высокий уровни SCK должны длиться больше 2 тактов кристалла при частоте ниже 12 МГц и больше 3 тактов при 12 МГц и выше, то есть SCK ниже f/4 и f/6 соответственно (раздел 28.8)

Последний случай лечится просто — снижением скорости программатора ключом -B avrdude. Кристалл без тактов оживляют, подав на XTAL1 сигнал от другого микроконтроллера или генератора. После DWEN выключить debugWIRE может отладчик с его поддержкой, а после RSTDISBL и SPIEN поможет только высоковольтный параллельный программатор (раздел 28.7). Подробности — в разделе о фьюзах на странице ATmega328P.

F_CPU и задержки

Макрос F_CPU ничего не настраивает в кристалле — он только сообщает avr-libc, сколько тактов в секунде. Функция _delay_ms(t) превращается в цикл на F_CPU · t / 1000 тактов. Если F_CPU больше реальной частоты, задержки растягиваются, если меньше — сокращаются. Если F_CPU не задан вовсе, util/delay.h выдаёт предупреждение и считает частоту равной 1 МГц.

Ещё одно свойство задержек из документации avr-libc: на время _delay_ms прерывания не запрещаются, поэтому каждое сработавшее прерывание удлиняет задержку на время своего обработчика.

Делитель частоты можно поменять и из программы, не трогая фьюзы. Запись в CLKPR защищена: сначала пишется байт с единицей только в CLKPCE, а новое значение CLKPS3…0 — не позже чем через четыре такта (раздел 9.12.2):

#include <avr/interrupt.h>
#include <avr/io.h>
#include <stdint.h>

/* Remove the factory divide-by-8: 8 MHz RC -> 8 MHz system clock. */
static void clock_div1(void)
{
    uint8_t sreg = SREG;        /* remember the interrupt flag */
    cli();                      /* no interrupt between the two writes */
    CLKPR = _BV(CLKPCE);        /* enable the change for 4 cycles */
    CLKPR = 0;                  /* CLKPS3..0 = 0000: divide by 1 */
    SREG = sreg;                /* restore the interrupt flag */
}

int main(void)
{
    clock_div1();
    for (;;) {
    }
}

С оптимизацией -Os обе записи компилируются в две соседние команды и укладываются в четыре такта; без оптимизации такой код может не сработать. После вызова такой функции на чистом кристалле F_CPU надо задавать равной 8 МГц.

Задание

На плату без кварца поставили чистый ATmega328P, и программатором записали младший байт фьюзов 0xE2.

  1. На какой частоте теперь работает кристалл?
  2. Программа собрана с -DF_CPU=16000000UL. Сколько на самом деле длится _delay_ms(1000)?
Ответ и решение
  1. 0xE2 = 1110 0010: CKDIV8 = 1 (не запрограммирован, делителя нет), CKOUT = 1, SUT = 10, CKSEL = 0010 — калиброванный RC-генератор 8 МГц. Кристалл работает на 8 МГц с точностью заводской калибровки ±10 %.

  2. Задержка отсчитывает 16 000 000 тактов, а в секунду их проходит 8 000 000: 16 000 000 / 8 000 000 = 2 с (плюс-минус 10 % из-за RC-генератора). Нужно собирать программу с -DF_CPU=8000000UL.

Что дальше

В уроке 5 частота станет основой точного времени: таймер с предделителем и прерыванием отсчитает миллисекунды без пустых циклов. Все биты фьюзов, заводские значения и готовую команду avrdude показывает калькулятор фьюзов AVR, а фактическую длительность _delay_ms при разных частотах — калькулятор задержек AVR. Коротко о фьюзах и битах защиты — термины «Фьюзы AVR» и «Биты защиты»; о кварцевом резонаторе — термин «Кварцевый резонатор»; о сбросе по питанию — «Детектор пониженного питания». Регистры CLKPR и OSCCAL — на странице регистров CPU ATmega328P.

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

Почему _delay_ms(1000) длится 16 секунд?

Потому что F_CPU в программе равна 16 МГц, а кристалл работает на 1 МГц: так он поставляется с завода — внутренний RC-генератор 8 МГц и запрограммированный фьюз CKDIV8, делящий частоту на 8. Задержка считается в тактах по F_CPU, поэтому на в 16 раз меньшей частоте длится в 16 раз дольше.

Можно ли заблокировать ATmega328P фьюзами?

Да. Если запрограммировать RSTDISBL или DWEN, вывод RESET перестаёт работать как сброс, и программатор ISP уже не может войти в режим программирования. Если выбрать в CKSEL внешний генератор или кварц, которого на плате нет, кристалл останется без тактов. После RSTDISBL кристалл возвращают высоковольтным параллельным программатором, после DWEN — отладчиком debugWIRE, а без тактов — подав внешний тактовый сигнал на XTAL1.

Что значит «запрограммированный фьюз»?

У AVR запрограммированный бит фьюза читается как 0, а незапрограммированный — как 1. Поэтому фьюз CKDIV8 включён, когда бит 7 младшего байта равен нулю. Из-за этой инверсии фьюзы удобнее проверять в калькуляторе, а не в уме.

Источники

  1. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — табл. 9-1: источники тактирования по CKSEL3…0; разд. 9.2.1: с завода RC 8 МГц, CKDIV8 запрограммирован — 1 МГц, CKSEL = 0010, SUT = 10; табл. 9-3: кварц 8–16 МГц — CKSEL3…1 = 111; разд. 9.11 и 9.12.2: делитель CLKPR, бит CLKPCE сбрасывается через 4 такта; табл. 28-6, 28-8, 28-9: биты фьюзов, 1 — не запрограммирован; табл. 27-13: размер загрузчика по BOOTSZ; табл. 29-9: точность RC ±10 % с завода; табл. 29-12: BODLEVEL; разд. 28.2: Chip Erase не меняет фьюзы; разд. 28.8: нужен рабочий тактовый сигнал, низкий и высокий уровни SCK — больше 2 тактов кристалла при fck < 12 МГц и больше 3 тактов при fck ≥ 12 МГц; примечание к табл. 28-8: SPIEN недоступен в последовательном режиме; разд. 25.5 и 14.3.2: debugWIRE и RSTDISBL занимают вывод RESET
  2. AVR-LibC 2.3.2: util/delay.h (github.com/avrdudes/avr-libc) — F_CPU в герцах до подключения заголовка, иначе предупреждение и 1 МГц по умолчанию; задержка — константа, оптимизация включена; прерывания не запрещаются, и задержка удлиняется
  3. ArduinoCore-avr, boards.txt (github.com/arduino/ArduinoCore-avr) — Uno: low_fuses 0xFF, high_fuses 0xDE, extended_fuses 0xFD, f_cpu 16000000L

Нашли ошибку?