Arduino / ATmega 328P: Настройки фьюзов

Часть программирования автономных чипов ATmega – это установка фьюз-байтов. Это специальные настройки, которые позволяют изменить работу чипов ATmega.

Вот что можно сделать, изменяя значения фьюзов:

  • Выбрать различные источники тактового сигнала и изменить скорость работы чипа.

  • Установить минимальное напряжение, необходимое для работы чипа.

  • Указать, используется ли загрузчик.

  • Установить, сколько памяти выделяется для загрузчика.

  • Отключить сброс (RESET).

  • Отключить последовательное программирование.

  • Предотвратить стирание данных EEPROM при загрузке нового скетча.

В интернете много статей, но я не смог найти единый источник, который объединял бы всю информацию и полностью объяснял, что на самом деле делают фьюзы.

Важно помнить, что некоторые фьюз-биты можно использовать для блокировки определённых аспектов чипа, и это потенциально может превратить его в «кирпич» (сделать непригодным к использованию). Однако при некоторой осторожности разобраться в настройках фьюзов и использовать их довольно просто.

Отказ от ответственности: я относительно новичок в программировании фьюзов, и это заметки, которые я написал, чтобы помнить основные моменты. Информация основана на техническом описании чипа ATmega, поисках в интернете и вопросах, которые я задавал на форумах (особенно на форуме Arduino).

Всего 3 байта:

  • Младший байт фьюзов (Low byte fuse)

  • Старший байт фьюзов (High byte fuse)

  • Расширенные фьюзы (Extended fuse)

Существует также четвёртый байт, который используется для программирования битов блокировки (lock bits). Биты блокировки не рассматриваются в этой статье.

Каждый байт – это 8 бит, и каждый бит – отдельная настройка или флаг. Когда мы говорим об установке/неустановке, запрограммированности/незапрограммированности фьюзов, мы фактически используем двоичную систему. 1 означает «не установлен/не запрограммирован», а 0 (ноль) означает «установлен/запрограммирован».

При программировании фьюзов можно использовать двоичную запись или (чаще) шестнадцатеричную. Для 8 бит (1 байт) можно использовать B11111111 или 0xFF. Оба значения одинаковы.

Примечание

Для всех фьюзов: значение 1 означает «не установлен», а значение 0 (ноль) – «установлен».

Распиновка ATmega 328P 28 PDIP

Распиновка ATmega 328P

Младший байт фьюзов (Low Byte Fuses)

Младший байт фьюзов отвечает за источник тактового сигнала, скорость работы чипа и время ожидания при запуске.

Младший байт фьюзов

Чипы ATmega могут работать на разных скоростях (частотах), и частота определяется используемым источником тактового сигнала. Источник тактового сигнала устанавливается с помощью фьюз-битов CKSEL.

CKSEL (Источники тактового сигнала / Выбор тактового сигнала)

Тактовый сигнал может поступать от внутреннего осциллятора, внешнего кварцевого резонатора или внешнего сигнала. Arduino обычно использует внешний кварц 16 МГц.

Кварцевый резонатор 16 МГц

Вот кварц 16 МГц, используемый на breadboard Arduino, подключённый к XTAL1 и XTAL2. Чипу ATmega нужно указать использовать внешний кварц, и это делается через биты CKSEL. Распространённая ошибка – правильно подключить кварц в схеме, но забыть сказать чипу его использовать.

Различные варианты CKSEL:

Варианты CKSEL

Arduino обычно используют низкопотребляющий кварцевый осциллятор.

ATmega имеет 2 встроенных осциллятора: RC-осциллятор на 128 кГц и калиброванный RC-осциллятор.

Опция внешнего тактового сигнала позволяет чипу использовать внешний прямоугольный сигнал. Это используется, когда у вас есть схема с собственным тактовым генератором, с которым нужно синхронизировать ATmega, или если вы хотите использовать отдельную микросхему тактового генератора. Внешний тактовый сигнал подключается к пину CLOCK-IN.

Варианты кварцевого осциллятора

Варианты настройки кварцевого осциллятора

CKSEL3=1 CKSEL2=1 CKSEL1=1 CKSEL0=1 выбирает кварц от 8 МГц до 16 МГц и является стандартной настройкой для Arduino.

Если вы хотите использовать более медленный кварц, например 6 МГц, то используйте CKSEL3=1 CKSEL2=1 CKSEL1=0 CKSEL0=1 (диапазон 3.0-8.0 МГц).

Для использования внутреннего RC-осциллятора на 8 МГц настройки: CKSEL3=0 CKSEL2=0 CKSEL1=1 CKSEL0=0.

SUT1/SUT0 (Время запуска)

Кварцы и осцилляторы требуют достаточного напряжения для корректной работы. При запуске чипа ATmega может потребоваться короткий период времени, чтобы напряжение достигло максимального значения. Пока напряжение растёт, источник тактового сигнала может работать на неправильной скорости или частоте. Для стабилизации тактового сигнала можно установить задержку запуска.

CKSEL0 используется вместе с SUT1 и SUT0 для установки времени задержки запуска.

Настройки времени запуска

BOD – это обнаружение пониженного напряжения (Brown Out Detection), обсуждается далее.

Arduino использует максимальную задержку запуска 14CK + 65 мс (CKSEL0=1, SUT1=1, SUT0=1), которые также являются настройками по умолчанию для новых чипов ATmega 328/328P.

CKDIV8 (Деление тактовой частоты)

Чипы ATmega 328/328P имеют встроенный RC-осциллятор с частотой 8.0 МГц. Новые чипы поставляются с этим источником тактового сигнала и активным фьюзом CKDIV8, что приводит к системной тактовой частоте 1.0 МГц. Время запуска установлено на максимум, период тайм-аута включён. (CKSEL = «0010», SUT = «10», CKDIV8 = «0»). Эта настройка используется для того, чтобы все пользователи могли выполнить желаемые настройки тактового сигнала с помощью любого доступного интерфейса программирования.

CKDIV8 следует использовать, если выбранный источник тактового сигнала имеет более высокую частоту, чем максимальная частота чипа ATmega.

Чипы ATmega можно использовать при очень низких напряжениях, однако чем ниже напряжение, тем медленнее они должны работать. CKDIV8 можно использовать для установки медленной тактовой частоты, соответствующей низкому напряжению.

CKOUT (Вывод тактового сигнала)

Тактовый сигнал может быть выведен на PB0. Это полезно, если тактовый сигнал нужен для управления другими схемами. Это работает для всех источников тактового сигнала, и настройка по умолчанию для новых чипов – CKOUT=1 (не установлен).

Старший байт фьюзов (High Byte Fuses)

Старший байт фьюзов содержит несколько различных настроек. Наиболее интересные (по крайней мере для меня как любителя) – это сторожевой таймер, сохранение/стирание EEPROM и настройки загрузчика.

Старший байт фьюзов

RSTDISBL (Отключение внешнего сброса)

PC6 – это пин сброса. Если подтянуть его к LOW (к земле), чип сбросится. Так работает кнопка на Arduino. Когда вы нажимаете кнопку сброса, она соединяет PC6 с землёй и сбрасывает чип.

PC6 также может использоваться как обычный пин ввода/вывода, но это означает отключение функции сброса, что, в свою очередь, означает невозможность программирования чипа в линию (inline-программирование требует сброса). Это одна из опций, которые лучше не менять.

Я полагаю, что RSTDISBL реализован из соображений безопасности, чтобы предотвратить перепрограммирование чипа или скачивание прошивки.

Когда RSTDISBL запрограммирован, время запуска (SUT1 SUT0) увеличивается до 14CK + 4.1 мс, чтобы обеспечить возможность входа в режим программирования.

По умолчанию RSTDISBL=1 (не установлен).

DWEN (Включение debugWIRE)

Чипы ATmega имеют встроенные инструменты отладки, которые по умолчанию отключены. Фьюз DWEN используется для их включения.

DWEN использует тот же пин, что и сброс (PC6), и когда DWEN включён (и биты блокировки не установлены), пин сброса становится коммуникационным пином, и обычный сброс больше не работает. Например, если включить DWEN на Arduino, кнопка сброса перестанет работать.

Для использования встроенной отладки нужен совместимый программатор, например AVR Dragon. Поскольку я никогда не использовал AVR Studio и не имею подходящего программатора, я никогда не использовал отладку и не знаю, как это делать.

Это один из фьюзов, с которым нужно быть осторожным. Если включить DWEN и также включить биты блокировки, чип больше нельзя будет программировать обычным способом.

Если вы только начинаете – оставьте DWEN в состоянии «не установлен». В конце концов, сброс очень полезен.

SPIEN (Включение последовательного программирования и загрузки данных)

Чипы ATmega могут программироваться с помощью последовательного программирования. Последовательное программирование используется для прошивки на плате через ISP (inline serial programmer) или UART (universal asynchronous receiver/transmitter).

Обычный метод программирования чипов ATmega – через интерфейс SPI с использованием пинов SCK (тактовый сигнал), MOSI (вход) и MISO (выход). Если отключить последовательное программирование, вы больше не сможете использовать SPI для программирования чипа. Последовательное программирование также можно отключить с помощью битов блокировки.

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

По умолчанию SPIEN включён, SPIEN=0.

Предупреждение

Фьюзы RSTDISBL, SPIEN и DWEN потенциально могут превратить чип ATmega в «кирпич» или, по крайней мере, сделать его очень сложным для повторного использования. Для обычного применения, и особенно если вы только начинаете работать с автономными чипами, не меняйте эти настройки фьюзов. Также дважды проверяйте, что вы не меняете их при перезаписи других настроек фьюзов.

WDTON (Постоянное включение сторожевого таймера)

Сторожевой таймер (watchdog) – это по сути таймер, который вызывает сброс чипа, если не получает сигнал OK в определённые моменты времени.

Это полезно при нестабильном коде или в системе, которая не должна навсегда зависнуть.

Когда сторожевой таймер включён, если скетч «упадёт» или зависнет, таймер сработает и сбросит чип, вызвав перезагрузку, аналогичную нажатию кнопки сброса на работающем Arduino.

Сторожевой таймер можно активировать программно, поэтому настройка фьюза не обязательно нужна. По умолчанию сторожевой таймер выключен, WDTON=1.

EESAVE (Сохранение памяти EEPROM)

При программировании чипа ATmega память стирается перед загрузкой нового кода. В обычных условиях память EEPROM стирается вместе с памятью программы. Фьюз EESAVE можно использовать, чтобы указать чипу не стирать EEPROM. Это полезно, когда нужно обновить код, но сохранить пользовательские настройки в EEPROM.

По умолчанию EESAVE=1 (не установлен), и память EEPROM стирается во время цикла стирания чипа при программировании.

Я привык устанавливать этот фьюз: EEPROM имеет ограниченное количество циклов записи, поэтому чем меньше записей – тем лучше. Стирание EEPROM не влияет на загрузку нового кода, и большинство моих проектов не используют EEPROM, так что его сохранение не является проблемой. dropController хранит настройки и время капель в EEPROM, поэтому я устанавливаю EESAVE для сохранения данных при загрузке новых версий кода.

BOOTSZ1 и BOOTSZ0 (Размер загрузчика)

Чипы ATmega имеют возможность использовать загрузчик (boot loader) – это небольшая программа, которая запускается при первом включении или сбросе чипа. Загрузчики обычно используются для инициализации устройства и проверки внешней связи. Arduino использует загрузчик для связи с подключённым компьютером, чтобы проверить, есть ли новая программа для загрузки. Именно поэтому Arduino сбрасывается при загрузке нового кода – сброс запускает загрузчик, загрузчик проверяет наличие нового кода.

Загрузчик хранится в памяти программы – той же памяти, что используется для пользовательского приложения. Поскольку загрузчик может быть разного размера, можно указать чипу ATmega, сколько места зарезервировать. Обычный (старый) загрузчик Arduino занимает 2 КБ, но новый Optiboot (используемый на UNO) занимает всего 0.5 КБ (512 байт). При использовании Optiboot и соответствующей настройке размера загрузчика вы получаете дополнительно 1.5 КБ для своей программы. 1.5 КБ может быть очень много – нехватка места для программы и стала причиной, по которой я начал программировать автономные чипы ATmega. Код для dropController стал слишком большим для обычного Arduino, поэтому я удалил загрузчик.

Распределение памяти программы

Загрузчик хранится в верхней части памяти программы.

Если у вас есть загрузчик, BOOTSZ нужно использовать совместно с BOOTRST. BOOTRST сообщает чипу ATmega адрес памяти, где начинается загрузчик. Если BOOTRST не установлен, пользовательское приложение может использовать всю память, независимо от настроек BOOTSZ.

Конфигурация размера загрузчика для 328/328P

Конфигурация размера загрузчика

Обратите внимание, что размеры указаны в словах (word = 2 байта), и 256 слов = 512 байт.

По умолчанию для новых чипов: BOOTSZ1=0 BOOTSZ0=0, что означает 4 КБ, зарезервированных для загрузчика.

BOOTRST (Выбор вектора сброса)

Чипы ATmega могут использовать загрузчик – небольшую программу, запускающуюся при сбросе чипа. Если у вас есть загрузчик, нужно сообщить об этом MCU, и это делается с помощью фьюза BOOTRST. Когда фьюз BOOTRST установлен, при сбросе устройство перейдёт к адресу загрузчика. Если не установлен – чип перейдёт к начальному адресу программы 0x0000.

По умолчанию BOOTRST=1 (не установлен).

Если BOOTRST не установлен (нет загрузчика), фьюзы BOOTSZ игнорируются.

Расширенные фьюз-биты (Extended Fuse Bits)

Расширенные фьюзы используются только для установки уровня обнаружения пониженного напряжения (BOD – Brown-Out Detection).

Чипы ATmega могут стать нестабильными или ненадёжными при использовании с недостаточным напряжением. Например, 328/328P может безопасно работать на 16 МГц при напряжении не менее 4V. Всё, что ниже 4V, означает вероятность некорректной работы чипа. Это было бы проблемой, если устройство используется с датчиками для измерений или зависит от точных временных интервалов.

Чтобы обеспечить достаточное входное напряжение, можно установить минимальный уровень напряжения. Если напряжение упадёт ниже этого уровня, чип сбросится. Это называется уровнем обнаружения пониженного напряжения (BOD level). По сути, если подаваемое напряжение ниже BOD, чип удерживает RESET в LOW.

Расширенные фьюзы Уровни BOD

Значения по умолчанию для новых чипов: BODLEVEL2=1 (не установлен), BODLEVEL1=1 (не установлен), BODLEVEL0=1 (не установлен).

Настройки фьюзов по умолчанию

Новый чип ATmega 328P

Новые чипы 328/328P обычно имеют следующие настройки фьюзов:

  • Low fuse = 0x62 (B01100010)

  • High fuse = 0xD9 (B11011001)

  • Extended fuse = 0xFF (B11111111)

Настройки фьюзов по умолчанию для нового ATmega328P

Arduino Duemilanove или Nano с ATmega328 – настройки фьюзов по умолчанию

  • Low fuse = 0xFF (B11111111)

  • High fuse = 0xDA (B11011110)

  • Extended fuse = 0x05 (B00000101)

Настройки фьюзов Arduino Duemilanove/Nano

Загрузчик Optiboot

Если загрузить загрузчик Optiboot, старший байт фьюзов должен быть установлен как 0xDE (B11011110). Единственное отличие – пространство, выделенное для загрузчика.

Настройки фьюзов для Optiboot

Полный список настроек фьюзов по умолчанию для различных Arduino можно найти на странице Coding with Cody’s Arduino Default Fuse Settings.

Биты блокировки (Lock Bits)

Биты блокировки могут использоваться для ограничения доступа на чтение/запись к памяти программы. Секция загрузчика и секция приложения имеют собственные биты блокировки, и доступ для каждой области можно контролировать отдельно.

Предупреждение

Вы можете превратить чип ATmega в «кирпич», если включите биты блокировки. Не меняйте их!

Фактическое программирование фьюзов

Для программирования автономных чипов ATmega я использовал Arduino Nano в качестве программатора (см. Arduino Nano как ISP-программатор) и обнаружил, что самый простой способ установить фьюзы – прошить загрузчик (фьюзы устанавливаются как часть процесса). Загрузчик затем может быть перезаписан при загрузке скетча. Редактируя файл boards.txt в папке установки Arduino, можно использовать любые значения фьюзов.

Вот запись, которую я добавил в boards.txt для использования с breadboard Arduino. Младший байт фьюзов – 0xFF, старший байт – 0xDF, расширенные фьюзы – 0x05, используется загрузчик Optiboot. Значения битов блокировки скопированы из записи Arduino Uno.

atmegasa16.name=ATmega328P Stand Alone (Arduino as ISP)
atmegasa16.upload.protocol=stk500
atmegasa16.upload.maximum_size=32768
atmegasa16.upload.speed=115200
atmegasa16.upload.using=arduino:arduinoisp
atmegasa16.bootloader.low_fuses=0xff
atmegasa16.bootloader.high_fuses=0xdf
atmegasa16.bootloader.extended_fuses=0x05
atmegasa16.bootloader.path=optiboot
atmegasa16.bootloader.file=optiboot_atmega328.hex
atmegasa16.bootloader.unlock_bits=0x3F
atmegasa16.bootloader.lock_bits=0x0F
atmegasa16.build.mcu=atmega328p
atmegasa16.build.f_cpu=16000000L
atmegasa16.build.core=arduino
atmegasa16.build.variant=arduino:standard

Полезные ссылки

На сайте Nick Gammon много отличной информации о сборке собственного Arduino и загрузке загрузчиков:

Моя статья Arduino на макетной плате объясняет, как настроить автономный чип ATmega, а Arduino Nano как ISP-программатор – как использовать Arduino в качестве ISP-программатора.

Техническое описание ATmega 328P можно скачать с сайта Microchip.

eleccelerator.com и engbedded.com имеют калькуляторы фьюзов, где вы выбираете нужные опции, и страница выдаёт настройки фьюзов. Лично я предпочитаю не использовать калькуляторы, так как считаю, что с ними легко допустить ошибку, но это лишь моё мнение.

Nick Gammon также имеет полезный скетч для определения типов чипов ATmega. Скетч можно найти на gammon.com.au. Скетч отображает настройки фьюзов, биты блокировки и информацию о загрузчике, если он присутствует. Я нашёл этот скетч очень полезным, когда впервые начал программировать чипы напрямую.