Для дальнейшего движения вперед, к практическому использованию микроконтроллеров, нам нужно познакомиться с их настройкой, которая выполняется еще до запуска вашей программы. Примером таких настроек является выбор типа тактового генератора и тактовая частота, защита кода программы от считывания, и тому подобное.
Как правило, такие настройки выполняются один раз при изготовлении устройства, но при необходимости могут быть изменены. Для подобных редко изменяемых настроек обычных устройств широко используют различные перемычки и переключатели. Это могут привычные съемные перемычки-джамперы, или проволочные перемычки устанавливаемые пайкой, или движковые переключатели.
Сегодня мы познакомимся с аналогами таких перемычек расположенных внутри микроконтроллера и переключаемых внешним программатором. А иногда и прикладной программой выполняемой микроконтроллером.
При дальнейшем изучении модулей микроконтроллеров мы будем постоянно сталкиваться с влиянием такого вот конфигурирования на их работу. А начнем мы с AVR
Fuses
Информация о конфигурации для AVR задается в виде Fuses, или "плавких перемычек". Fuses на самом деле это просто набор байт в специальной области ПЗУ для которой установлены жесткие ограничения доступа.
Фактически, такое название можно рассматривать как "дань истории", когда микросхемы ПЗУ действительно строились на основе разрушаемых (расплавляемых) перемычек. Примером таких ПЗУ являются отечественные 556РТ4 или 556РТ5.
Я не буду углубляться в историю, но отмечу один важный для понимания факт. Начальным состоянием перемычек в новой микросхеме ПЗУ было "перемычка целая". В процессе прошивки нужные перемычки меняли состояние на "перемычка отсутствует".
Если мы примем, что целая перемычка соответствует установленному состоянию соответствующего бита, а отсутствующая сброшенному, то станет понятна логика, по которой запрограммированному значению бита соответствует 0, а не 1.
Изменить состояние конфигурационных данных можно лишь внешним программатором. То есть, Fuses нельзя изменить ни из прикладной программы, ни из загрузчика. Более того, стирание памяти микроконтроллера не изменяет записанную конфигурацию.
Для изменение байтов Fuses в интерфейсе программирования есть специальные команды. Но даже они иногда бессильны, так как можно включить блокировку (снимается стиранием микроконтроллера).
При этом выполняемая микроконтроллером программа может считывать состояние конфигурационной информации с помощью команды LPM. Любительским программам это требуется редко, иногда может быть полезно.
Количество байт Fuse в разных микроконтроллерах может быть разным. Подробности необходимо искать в документации на используемый микроконтроллер. Давайте для примера посмотрим, как можно влиять на конфигурацию популярного микроконтроллера ATmega328.
Обратите внимание, что некоторые биты здесь уже имеют нулевое значение, что соответствует состоянию "запрограммировано".
Здесь 4 бита CKSEL определяют выбор источника тактового сигнала. По умолчанию выбран внутренний калиброванный RC-генератор. Два бита SUT определяют время старта микроконтроллера. По умолчанию выбрано максимальное время старта (для медленно нарастающего напряжения питания).
Бит CKOUT определяет, будет ли выводиться тактовая частота через вывод PORTB0. По умолчанию этот вывод не используется как вывод тактовой частоты.
Бит CKDIV8 определяет начальное состояние делителя генератора тактовых импульсов. По умолчанию (бит "запрограммирован") после старта делитель работает с коэффициентом деления 1:8.
Бит BOOTRST определяет адрес перехода после сброса. По умолчанию выполняется переход по вектору сброса. Если этот бит "запрограммировать", сбросить, после сброса будет запускаться загрузчик.
Биты BOOTSZ определяют размер и начальный адрес загрузчика. По умолчанию выбран максимальный размер загрузчика (2048 слов, 32 страницы для ATmega328).
Бит EESAVE лопределяет, будет ли EEPROM сохраняться при стирании микроконтроллера. Пор умолчанию стирается.
Бит WDTON определяет режим работы WDT. По умолчанию режим работы определяется регистром WDTCSR.
Бит SPIEN определяет, возможно ли использовать SPI для программирования микроконтроллера (прошивки). По умолчанию возможно. При этом данный бит невозможно изменить при подключении программатора через SPI. Если этот бит установлен, то возможна только работа программатора в режиме параллельного программирования.
Бит DWEN определяет возможность использования протокола внутрисхемной отладки. По умолчанию невозможно.
Бит RSTDISBL определяет возможность использования внешнего сброса. По умолчанию использование возможно.
Биты BODLEVEL определяют уровень напряжения срабатывания детектора аварии питания. По умолчанию детектор отключен.
"Убить" микроконтроллер некорректной установкой байтов конфигурации нельзя. Но если вы измените бит SPIEN на 1, то придется подключаться к микроконтроллеру в режиме параллельного программирования, что бы исправить ситуацию.
Изменение параметров конфигурации
Как я уже сказал, изменить байты Fuses может только внешний программатор. Однако, эти байты не имеют адресов, которые можно было бы задать в загружаемом в микроконтроллер файле программы. По этой причине байты конфигурации для AVR часто указывают отдельно от программного кода.
Между тем, avr-gcc поддерживает возможность задать значение бит конфигурации в исходном коде программы. Эта возможность обеспечивается файлом avr/fuse.h, который автоматически подключается файлом avr/io.h
Я не буду писать пример сам, я просто возьму его из avr-gcc
Здесь xFUSE_DEFAULT обозначает значение соответствующего байта по умолчанию. Это и другие значения определяются в файле описания соответствующего микроконтроллера. Для ATmega328, который я ранее уже использовал в примерах, это будет файл avr/iom328p.h
Компилятор размещает полученные значения в секции .fuse двоичного файла формата elf. Эту секцию, как и все остальные, можно перенести в hex файл с помощью avr-objcopy. Но особого смысла в этом нет, так строка hex файла будет выглядеть примерно так
:030000006291FF0B
Кстати, это строка именно для ATmega328 и конфигурации из приведенного выше примера. Здесь конфигурация занимает 3 байта, но вот начальный адрес размещения равен нулю. И нет никакого способа указать, что это не секция кода, а именно байты конфигурации.
Поэтому, что бы задание байтов конфигурации в исходном текста действительно имело смысл, необходимо что бы используемый программатор не только принимал elf файл на входе, но и мог разобраться в секции .fuse.
Популярный загрузчик avrdude (последняя версия 6.3) принимает формат elf, но легче от этого не становится, так как все равно требуется прошивать каждый байт Fuses отдельно.
Различные интегрированные среды разработки могут включать в себя средства облегчающие работу с байтами конфигурации. Но передавая свою программу другому человеку вы не можете быть уверены, что у него будут те же самые средства программирования.
Lock Bits
В дополнение к возможности настройки конфигурации имеется возможность ограничить доступность отдельных областей памяти. В случает ATmega328 выделяются три варианта доступа: доступ из прикладной программы ко всей области Flash и EEPROM, доступ из загрузчика к области Flash прикладной программы, доступ из загрузчика к области Flash загрузчика.
Для каждого варианта доступа выделяется два бита блокировки. Биты LB1 и LB0 определяют правила ограничения для прикладной программы. Биты BLB01 и BLB00 определяют правила ограничения для доступа загрузчика к памяти прикладной программы. Биты BLB11 и BLB10 определяют правила ограничения для доступа загрузчика к памяти самого загрузчика.
Не запрограммированное состояние каждой пары бит означает отсутствие ограничений. Я не буду сейчас описывать различные ограничения доступа соответствующие другим комбинациям бит. Эта информация есть в документации. Я не рекомендую начинающим как либо изменять состояние битов блокировки.
Но если вы все таки их изменили и потеряли возможность работы с микроконтроллером, то остается единственный выход - стирание памяти микроконтроллера. При этом биты блокировки, в отличие от Fuses, сбрасываются. Снять блокировку без стирания не получится.
Изменение Lock Bits
В отличии от Fuses программа выполняемая микроконтроллером может изменить состояние Lock Bits. Эта возможность обычно используется загрузчиком. Поскольку я уже не советовал изменять биты блокировки новичкам. И повторю это предупреждение.
Требуемое значение бит блокировки нужно загрузить в R0, после чего записать 0b00001001 в регистр SPMCSR (это установка бит BLBSET и SPMEN). Теперь у вас есть 4 машинных цикла для выполнения команды SPM, которая и изменит биты блокировки. Дополнительно рекомендуется записать в регистр Z значение 0х0001 (для последующей совместимости, как говорит документация).
Некоторую помощь в этом могут оказать макросы boot_lock_bits_set и boot_lock_bits_set_safe из avr/boot.h.
Чтение байт конфигурации и бит блокировки
В отличии от изменения, чтение информации о конфигурации безопасно. Но обычно такая потребность возникает лишь в загрузчике.
Чтение байт осуществляется зарузкой в регистр Z значения
- 0 для чтения Fuses Low byte
- 1 для чтения Lock Bits
- 2 для чтения Extended Fuse byte
- 3 для чтения Fuse High byte
После чего необходимо записать 0b00001001 в регистр SPMCSR (это установка бит BLBSET и SPMEN). Тепрь у вас есть три машинных цикла для выолнения команды LPM, которая и вернет состояние требуемого байта.
Некоторую помощь в этом может оказать макрос boot_lock_fuse_bits_get из уже упоминавшегося файла avr/boot.h. В качестве параметра можно задавать GET_LOW_FUSE_BITS, GET_LOCK_BITS, GET_EXTENDED_FUSE_BITS, GET_HIGH_FUSE_BITS.
Чтение типа микроконтроллера (сигнатуры) и калибровочного байта внутреннего тактового генератора
В AVR есть возможность узнать более точно тип микроконтроллера прочитав три байта сигнатуры. Дополнительно, так же возможно прочитать и байт калибровку внутреннего генератора.
Чтение выполняется почти так же, как и чтение байтов конфигураци. В регистр Z нужно записать адрес считываемого байта
- 0 для чтения первого байта сигнатуры
- 1 для чтения калибровочного байта
- 2 для чтения второго байта сигнатуры
- 3 для чтения третьего байта сигнатуры
Теперь в регистре SPMCSR нужно устанавливать биты SPMEN и SIGRD (вместо BLBSET). После этого есть три машинных цикла для выполнения команды LPM, которая и прочитает требуемый байт.
Некоторую помощь в этом может оказать макрос boot_signature_byte_get из уже упоминавшегося файла avr/boot.h. В качестве параметра нужно задавать адрес байта сигнатуры.
Заключение
Задание конфигурации микроконтроллера не так сложно, как может покзаться на первый взгляд. Но нужно быть внимательным, так как неосторожность может привести к некоторым проблемам. Это не так страшно, если вы работаете с микроконтроллером на макетной плате. Но если микроконтроллер уже установлен в устройство, то восстановление его работоспособности может потребовать его выпаиваивания.
Без крайней на то необходимости не изменяйте биты блокировки. Это необходимо для исключения возможности прочитать вашу программу кем то еще. В других случаях особого смысла не имеет.