воскресенье, 19 августа 2018 г.

Что такое Фьюзы. Чтение и запись фьюзов Ардуино

Что такое фьюзы
Сегодня я хочу рассказать о том, что такое фьюзы, за что они отвечают и как их можно прочитать и записать в Ардуино. Рекомендую также заглянуть в предыдущую публикацию, т.к. работа с фьюзами возможна только через программатор.


Фьюзы (от английского Fuse bits) - это конфигурационные биты микроконтроллера, отвечающие за его предварительную настройку. Эти биты расположены в отдельном адресном пространстве, доступном только при программировании. Биты объединяются в конфигурационные байты и их состав зависит от конкретной модели микроконтроллера.

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

Фьюзы ATmega328 / ATmega328p

Микроконтроллеры ATmega328 и ATmega328p, на базе которых построено большинство плат семейства Ардуино, имеют 3 байта конфигурации: младший, старший и дополнительный. Их описание приведено ниже в таблицах.

Младший конфигурационный байт ATmega328/P

Номер бита Навание Описание Значение по умолчанию
7 CKDIV8 Divide clock by 8 0 (запрограммирован)
6 CKOUT Clock output 1 (не запрограммирован)
5 SUT1 Select start-up time 1 (не запрограммирован)
4 SUT0 Select start-up time 0 (запрограммирован)
3 CKSEL3 Select Clock source 0 (запрограммирован)
2 CKSEL2 Select Clock source 0 (запрограммирован)
1 CKSEL1 Select Clock source 1 (не запрограммирован)
0 CKSEL0 Select Clock source 0 (запрограммирован)


Старший конфигурационный байт ATmega328/P

Номер бита Навание Описание Значение по умолчанию
7 RSTDISBL External Reset Disable 1 (не запрограммирован)
6 DWEN debugWIRE Enable 1 (не запрограммирован)
5 SPIEN Enable Serial Program and Data Downloading 0 (запрограммирован)
4 WDTON Watchdog Timer Always On 1 (не запрограммирован)
3 EESAVE EEPROM memory is preserved through the Chip Erase 1 (не запрограммирован)
2 BOOTSZ1 Select Boot Size 0 (запрограммирован)
1 BOOTSZ0 Select Boot Size 0 (запрограммирован)
0 BOOTRST Select Reset Vector 0 (запрограммирован)


Дополнительный конфигурационный байт ATmega328/P

Номер бита Навание Описание Значение по умолчанию
7 - - 1
6 - - 1
5 - - 1
4 - - 1
3 - - 1
2 BODLEVEL2 Brown-out Detector trigger level 1 (не запрограммирован)
1 BODLEVEL1 Brown-out Detector trigger level 1 (не запрограммирован)
0 BODLEVEL0 Brown-out Detector trigger level 1 (не запрограммирован)


Таким образом, значения конфигурационных байтов в ATmega328/P по умолчанию: Low: 0x62, High: 0xD9, Extended: 0xFF. Обратите внимание, это значения именно для "чистого" микроконтроллера ATmega328/P , а не для Ардуино. Значения фьюзов для Ардуино можно найти в файле Arduino_dir\hardware\arduino\avr\Boards.txt, где Arduino_dir - это путь к IDE Arduino. Откройте файл в текстовом редакторе и найдите интересующую плату по названию, чуть ниже будет информация о фьюзах:

uno.name=Arduino/Genuino Uno
...
uno.bootloader.low_fuses=0xFF
uno.bootloader.high_fuses=0xDE
uno.bootloader.extended_fuses=0x05

Для расшифровки шестнадцатеричных значений конфигурационных байтов удобно использовать онлайн калькулятор Engbedded Atmel AVR® Fuse Calculator:

Engbedded Atmel AVR® Fuse Calculator

Выберите модель микроконтроллера и введите значения фьюзов в соответствующие поля внизу страницы в разделе Current settings - введенные значения будут моментально расшифрованы и представлены в удобном виде. И, наоборот, можно изменить конфигурацию битов в разделах Manual fuse bits configuration и Feature configuration и получить значения конфигурационных байтов для записи в микроконтроллер.

Рассмотрим назначение конфигурационных битов более подробно.

CKDIV8

Данный бит отвечает за деление тактовой частоты на 8. Он определяет начальное значение регистра делителя частоты: если бит не запрограммирован, то регистр делителя будет сброшен в "0000"; если бит CKDIV8 запрограммирован, то в регистре будет установлено значение "0011", что обеспечит деление тактовой частоты на 8. Однако вне зависимости от значения фьюза CKDIV8 значение делителя может быть изменено программой в любой момент.

CKOUT

ATmega328/p позволяет выводить тактовую частоту на цифровой вывод CLKO (digital pin 8 на Ардуино Уно). Для этого конфигурационный бит CKOUT должен быть запрограммирован. Данная функция может быть полезна при использовании таковой частоты микроконтроллера внешними устройствами. При использовании делителя системной частоты на вывод CLKO также будет поступать пониженная частота.

SUT

Конфигурационные биты SUT совместно с битами CKSEL определяют длительность задержки при старте микроконтроллера. Задержка необходима для того, чтобы источник тактовой частоты стабилизировался после подачи питания и вошел в свой рабочий режим. Величина задержки зависит от выбранного источника и составляет от 0 до 65мс. Конкретные значения можно найти в даташите.


CKSEL

Современные микроконтроллеры способны работать с различными источниками тактового сигнала. Выбор источника осуществляется установкой конфигурационных битов CKSEL. В таблице ниже приведены источники тактового сигнала, поддерживаемые микроконтроллерами ATmega328 / ATmega328P, и соответствующие им значения CKSEL.

Источник тактового сигнала Значение CKSEL3..0
Экономичный кварцевый генератор 1111-1000
Кварцевый генератор 0111-0110
Низкочастотный кварцевый генератор 0101-0100
Внутренний RC-генератор на 128кГц 0011
Внутренний калиброванный RC-генератор 0010
Внешний сигнал синхронизации 0000
Зарезервировано 0001


RSTDISBL

Фьюз RSTDISBL управляет работой цифрового вывода микроконтроллера, совмещенного с входом внешнего сброса. Если RSTDISBL запрограммирован,  то вывод может быть использован как обычный цифровой пин ввода/вывода. Если фьюз RSTDISBL не запрограммирован, то вывод используется для внешнего сигнала сброса: низкий уровень напряжения на нем приводит к генерации сигнала сброса микроконтроллера. Отключение внешнего сброса может быть оправдано при работе с микроконтроллерами, имеющими небольшое количество выводов, в других случаях лучше не трогать этот фьюз.

DWEN

Бит DWEN разрешает/запрещает работу отладочного интерфейса debugWire.

SPIEN

Данный бит разрешает/запрещает программирование по интерфейсу SPI. По умолчанию он запрограммирован и лучше не менять значение данного фьюза, как и значение RSTDISBL.


WDTON

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

EESAVE

Данный бит определяет влияние команды "Стирание кристалла" на EEPROM-память: если бит запрограммирован, то при выполнении команды Chip Erase будут очищены Flash, SRAM и Lock-биты, а содержимое EEPROM затронуто не будет. В противном случае содержимое EEPROM также будет стерто.

BOOTSZ

Конфигурационные биты BOOTSZ определяют размер FLASH памяти микроконтроллера, резервируемой для загрузчика. Значения битов BOOTSZ и соответствующие им размеры указаны в таблице ниже. По умолчанию резервируется максимальный размер - 2048 слов.

Начало секции загрузчика Размер секции (слов) Значение BOOTSZ1..0
0x3F00 256 11
0x3E00 512 10
0x3C00 1024 01
0x3800 2048 00

BOOTRST

Определяет положение вектора сброса. Если бит не запрограммирован, то вектор сброса располагается по адресу 0x0000, выполнение программы начнется именно с этого адреса. Если бит BOOTRST запрограммирован, то выполнение программы начнется с адреса начала секции загрузчика.

BODLEVEL

Конфигурационные биты BODLEVEL определяют порог срабатывания схемы BOD (Brown-Out Detection), которая отслеживает уровень напряжения источника питания. Если работа схемы BOD разрешена, то при снижении напряжения питания ниже установленного порога она переводит микроконтроллер в состояние сброса. Когда напряжение питания вновь увеличивается до порогового значения (данный порог чуть выше предыдущего за счет наличия гистерезиса), сигнал сброса снимается и происходит запуск микроконтроллера. Комбинации битов BODLEVEL и соответствующие им пороговые значения приведены в таблице:

Порог срабатывания схемы BOD (В) Значение BODLEVEL2..0
Схема BOD выключена 111
Vcc = 1.8 110
Vcc = 2.7 101
Vcc = 4.3 100


Теперь можно понять начальное состояние микроконтроллеров ATmega328/P, в каком они поставляются с завода: микроконтроллер сконфигурирован на работу с внутренним RC генератором на 8МГц, при этом прошитый бит CKDIV8 обеспечивает деление частоты генератора на 8; внешний сброс (по низкому уровню на выводе Reset) не запрещен; разрешено программирование по интерфейсу SPI; для загрузчика отведена область памяти 2048 слов; стартовый адрес программы 0x0000; схема контроля питания отключена.

В Ардуино Уно фьюзы микроконтроллера изменены таким образом, чтобы он был настроен на работу с экономичным кварцевым генератором без делителя частоты; для загрузчика отводится 256 слов; прошитый бит BOOTRST назначает стартовым адресом адрес начала секции загрузчика; порог срабатывания схемы BOD составляет 2.7В.

Разобравшись с назначением фьюзов можно перейти от теории к практике: попробуем прочитать и записать фьюзы Ардуино.

Чтение фьюзов Ардуино

В предыдущей публикации я рассказал о том, как превратить Ардуино в ISP программатор. И сейчас он нам пригодится для работы с фьюзами. Поэтому готовим программатор и подключаем к нему плату, из которой будем считывать фьюзы.

В среде разработки Ардуино нет инструментов для чтения/записи фьюзов. Разве что процедура записи загрузчика включает в себя установку фьюзов, значения которых берутся из упомянутого ранее файла Boards.txt. Но в состав IDE входит программа Avrdude, предназначенная для прошивки AVR микроконтроллеров, ей-то мы и воспользуемся. Avrdude - консольная программа, работать мы с ней будем через командную строку, поэтому запускаем проводник и переходим в папку с программой. У меня IDE Arduino расположена на диске D и путь к Avrdude выглядит следующим образом: D:\Arduino\arduino-1.6.12\hardware\tools\avr\bin.


Находясь в этой папке нужно поместить курсор в адресную строку, удалить из нее путь, ввести cmd и нажать Enter. Откроется окно интерпретатора командной строки:


После этого подключаем программатор к компьютеру и вводим в командной строке следующую команду:

avrdude -C ../etc/avrdude.conf -c arduino -p m328p -P com26 -b 19200 -U lfuse:r:-:h

Перед выполнением команды проверьте и скорректируйте ее параметры:
  • -c <programmer> - в качестве <programmer> указываем псевдоним используемого программатора, в нашем случае arduino.
  • -p <partno> - указывает тип микроконтроллера, m328p - это ATmega328p.
  • -P <port> - указывает используемый программатором порт.
  • -b <baudrate> - позволяет переопределить указанную для программатора в конфигурации программы скорость подключения по интерфейсу RS-232.
  • -U <memtype>:r|w|v:<filename>[:format] - комплексная опция для указания производимой с памятью операции (чтение. запись, проверка). -U lfuse:r:-:h означает, что мы хотим прочитать содержимое младшего байта конфигурации, считанное значение выводим на экран в шестнадцатеричном виде.
Более подробную информацию о параметрах запусках avrdude можно найти по ссылке https://ph0en1x.net/77-avrdude-full-howto-samples-options-gui-linux.html

Ниже приведен мой результат выполнения данной команды:

avrdude чтение фьюзов Ардуино

Как видно из скриншота, младший конфигурационный байт моей Arduino Uno содержит значение 0xFF. Кроме запрошенного значения avrdude показал также значения всех трех конфигурационных байт. Это произошло потому, что мы не отключили safemode, это можно сделать, добавив в команду параметр -u. Остальные два конфигурационных байта также соответствуют значениям, приведенным в файле Boards.txt. Если у кого-то возникнут сомнения по поводу соответствия дополнительного конфигурационного байта, то загляните в его описание выше - биты с 3 по 7 не используются и содержат "1", поэтому вместо 0x05 мы считываем значение 0xFD.

Запись фьюзов Ардуино

Полагаю, из приведенного выше описания фьюзов должно быть понятно, что при работе с ними нужно быть очень осторожным. В противном случае вы рискуете "залочить"  микроконтроллер, т.е. привести в такое состояние, когда его уже нельзя будет перепрограммировать (без дополнительного оборудования). Поэтому будьте внимательны, изменяя фьюзы микроконтроллера.

Давайте в качестве примера попробуем запретить работу схемы BOD в Ардуино Уно. Обратившись к приведенному выше описанию конфигурационных байтов или к онлайн калькулятору Engbedded Atmel AVR® Fuse Calculator, мы видим, что за работу схемы BOD в ATmega328/P отвечают фьюзы BODLEVEL и для ее запрета нужно записать в дополнительный конфигурационный байт значение 0xFF. Это можно сделать следующей командой avrdude:

avrdude -C ../etc/avrdude.conf -c arduino -p m328p -P com26 -b 19200 -U efuse:w:0xFF:m

avrdude запись фьюзов Ардуино

Из скриншота видно, что avrdude сначала выполняет запись байта efuse, после чего производит контрольное считывание и сообщает об успешном выполнении операции.

В принципе нет ничего сложного в работе с конфигурационными битами. Главное быть внимательным при их изменении, чтобы не залочить микроконтроллер.

12 комментариев:

  1. Перепутал BODLEVEL2-BODLEVEL0 находятся в Старшем конфигурационном байте, а с BOOTSZ1, BOOTSZ0, BOOTRST находятся в Дополнительном конфигурационном байте ATmega328/P.

    ОтветитьУдалить
    Ответы
    1. Нет, я не перепутал. Посмотрите даташит

      Удалить
    2. avrdude -C ../etc/avrdude.conf -c arduino -p m328p -P com26 -b 19200 -U lfuse:r:-:h
      С чем это связано

      Удалить
    3. Не понял вопроса. Что с чем связано? Это команда для чтения младшего байта конфигурации.

      Удалить
  2. Доброе время суток.
    как насчёт других настроек? АЦП,таймеры, прерывания и т. д. Например в CVAVR нужны предварительные настройки МК для дальнейшей работы....
    Заранее спасибо за помощь

    ОтветитьУдалить
    Ответы
    1. Добрый день!
      Публикации про прерывания у меня есть. АЦП в планах. Потом до таймеров доберусь

      Удалить
  3. Добрый день Владимир. я поменял контроллер в ардуино, но он не заработал, на сколько я понял, нужно настроить МК, изменить конфигурации.

    ОтветитьУдалить
    Ответы
    1. Добрый день!
      Вам нужно записать загрузчик. Посмотрите эту публикацию:
      http://tsibrov.blogspot.com/2018/07/arduino-as-isp.html

      Удалить
  4. Спасибо большое Владимир, на днях попробую. Ещё один вопрос, можно ли вместо Atmega328 использовать Atmega8, 8A, 8L-8PU, 8-16PU? В отличии от 328-меги, у меня, 8-меги достаточно.

    ОтветитьУдалить
    Ответы
    1. Должно получиться, распиновка у них, вроде, совпадает. Тогда и списке плат надо выбирать что-то соответствующее. Например, Arduino NG or older и указывать процессор atmega8. Но там загрузчик старый. Можно скомпилировать optiboot для atmega8 и добавить подменю выбора процессора для платы Arduino UNO

      Удалить
  5. Добрый день."Можно скомпилировать optiboot для atmega8 и добавить подменю выбора процессора для платы Arduino UNO" Вот здесь я не до понял.... Это как? Если не затруднит, можно подробнее.Спасибо

    ОтветитьУдалить
    Ответы
    1. В статье, ссылку на которую я дал ранее, описано что такое загрузчик и как его записать в Ардуино. Optiboot - это загрузчик, используемый в современных Ардуинках на ATmega328. Он входит в состав IDE Arduino и находится в папке arduino_dir\hardware\arduino\avr\bootloaders\optiboot\ Кстати, там уже есть скомпилированный файл для ATmega8, можно загрузить его в микроконтроллер.

      Что касается подменю, то речь идет о редактировании файла boards.txt (arduino_dir\hardware\arduino\avr\boards.txt). Я описывал как добавить подменю для выбора способа тактирования в статье https://tsibrov.blogspot.com/2018/12/arduino-internal-rc.html. Вам нужно сделать примерно так же, только конечные пункты меню будут отвечать не за тактирование, а за модель микроконтроллера, путь к загрузчику, фьюзы и объем памяти программ и данных. Может что-то еще, но я сходу не скажу.

      Возможно, вам будет проще установить в IDE какой-нибудь сторонний пакет для программирования atmega8

      Удалить