Существует множество способов, как заставить работать скетч из Arduino IDE в обычном микроконтроллере. Самый популярный и логически обоснованный – установка загрузчика (bootloader) Arduino желаемой версии на микроконтроллер и добавление внешнего кварца на 16 МГц.
Метод хорош тем, что дальнейшая работа с подобным микроконтроллером ничем не отличается от работы с обычной платой Arduino. Ещё бы – на нём же загрузчик!
Загрузчик, или в англоязычной литературе, bootloader, это небольшой фрагмент кода, располагающийся в специально отведенной области flash-памяти. Его задачей является организация загрузки и обновления основной программы. Загрузчик обычно запускается раньше основной программы и некоторое время мониторит последовательную шину - линии RX и TX шины UART. Если нет команд на загрузку новой программы, загрузчик переходит к запуску основной программы, находящейся во flash-памяти микроконтроллера.
Расположение во flash-памяти загрузчика Arduino Nano на микроконтроллере ATMega168. Обратите внимание на адрес, с которого начинается код. При подаче питания на МК именно этот фрагмент будет прочитан первым, а только потом фрагмент с адресом 0000.
Получается что-то наподобие программного программатора. В принципе, на этом и заканчивается работа загрузчика.
Перейдём от теории к практике. И первое что нам потребуется для записи загрузчика -программатор. На протяжении нескольких лет я использую для прошивки микроконтроллеров AVR недорогой программатор USBASP. Также нужен будет адаптер, для подключения микроконтроллера к программатору. Из программ понадобится пока только Arduino IDE.
Программатор USBASP c адаптером для микроконтроллеров с корпусами TQFP32.
Распиновка колодки программатора USBASP и расположение необходимых для прошивки пинов у ATMEGA 168/328 в корпусе TQFP 32.
Загрузчики для разных версий плат Arduino хранятся в папке с установленной Arduino IDE: Arduino/hardware/arduino/avr/bootloaders. Информацию о фьюз битах, частоте тактирования и какой загрузчик соответствует той или иной версии платы, можно узнать в файле boards.txt (Arduino/hardware/arduino/avr/boards.txt).
Небольшой совет: файл boards.txt удобно просматривать в Notepad++. В обычном Блокноте Windows файл трудночитаем.
Для примера, попробуем превратить ATMEGA168p в Arduino Nano.
Предупреждение: во время выполнения операций с микроконтроллером, будьте предельно внимательны – некорректно установленные фьюз биты могут заблокировать микроконтроллер и дальнейшая прошивка будет невозможна. Перед прошивкой не лишним будет проверить значения фьюз-битов в специальном калькуляторе. Ссылку на один из них оставлю под статьёй.
Чтобы прошить загрузчик, потребуется еще одна программа – программа для прошивки микроконтроллеров. Для этих целей можно воспользоваться тем, что уже есть - в комплект пакета Arduino IDE входит Avrdude. Ее можно найти в папке C:/Program Files/Arduino/hardware/tools/avr/bin. Приложение Avrdude консольное, поэтому работает только из Командной строки Windows. Для прошивки, нужно лишь запустить командную строку из папки и ввести заранее подготовленную команду.
Совет: для Windows 7 и выше – щелчок правой клавишей мыши на пустом месте с зажатой клавишей Shift -> Открыть окно команд. Или просто введите в Командной строке команду cd полный_путь_к_директории.
Строка ниже, и есть та самая команда, позволяющая прошить микроконтроллер:
avrdude -C ../etc/avrdude.conf -c usbasp -p atmega168p -U flash:w:ATmegaBOOT_168_diecimila.hex -U lfuse:w:0xff:m -U hfuse:w:0xdd:m -U efuse:w:0xf8:m
Немного расшифрую «что есть что» в этой команде:
flash:w:a328p_16MHz_ff_de_5.hex – путь до файла прошивки. Для удобства, можно скопировать в папку с Avrdude.
atmega168p – тип микроконтроллера, который нужно прошить. Можно посмотреть в файле boards.txt.
-U lfuse:w:0xff:m – значение Low fuse byte. Берётся из файла boards.txt
-U hfuse:w:0xde:m – значение High fuse byte. Берётся из файла boards.txt
-U efuse:w:0x5:m – значение Extended fuse byte. Берётся из файла boards.txt
../etc/avrdude.conf –c – путь до конфигурационного файла. Без этой строки, Avrdude выдает ошибку, и, ничего не прошивает. Не советую тут что-то менять.
-c usbasp – тип программатора, который используется для прошивки. Можно заменить на тот, который есть у вас. Предварительно рекомендую заглянуть в документацию программы Avrdude.
Теперь немного о безопасности. Порою возникает необходимость защитить flash-память микроконтроллера от дальнейшего считывания. Для таких случаев и существуют lock биты, грамотная установка которых не препятствует последующим обновлениям программы. Считать содержимое flash-памяти при этом можно, только вместо реального содержимого памяти, считается набор шестнадцатеричных значений от 0000 до ffff. Именно так работает защита от считывания.
Для прошивки lock-битов в Avrdude, добавьте к уже имеющейся команде небольшой фрагмент кода: -U lock:w:0x0F:m. Следует учесть, что значение 0х0F в данном случае так же следует брать из boards.txt в соответствии с выбранной версией загрузчика.
C:\Program Files\Arduino\hardware\tools\avr\bin>avrdude -C ..\etc\avrdude.conf -
c usbasp -p atmega168p -U flash:w:ATmegaBOOT_168_diecimila.hex -U lfuse:w:0xff:m
-U hfuse:w:0xdd:m -U efuse:w:0xf8:m -U lock:w:0x0F:m
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.06s
avrdude: Device signature = 0x1e940b (probably m168p)
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be perform
ed
To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "ATmegaBOOT_168_diecimila.hex"
avrdude: input file ATmegaBOOT_168_diecimila.hex auto detected as Intel Hex
avrdude: writing flash (16294 bytes):
Writing | ################################################## | 100% 0.13s
avrdude: 16294 bytes of flash written
avrdude: verifying flash memory against ATmegaBOOT_168_diecimila.hex:
avrdude: load data flash data from input file ATmegaBOOT_168_diecimila.hex:
avrdude: input file ATmegaBOOT_168_diecimila.hex auto detected as Intel Hex
avrdude: input file ATmegaBOOT_168_diecimila.hex contains 16294 bytes
avrdude: reading on-chip flash data:
Reading | ################################################## | 100% 0.07s
avrdude: verifying ...
avrdude: 16294 bytes of flash verified
avrdude: reading input file "0xff"
avrdude: writing lfuse (1 bytes):
Writing | ################################################## | 100% 0.03s
avrdude: 1 bytes of lfuse written
avrdude: verifying lfuse memory against 0xff:
avrdude: load data lfuse data from input file 0xff:
avrdude: input file 0xff contains 1 bytes
avrdude: reading on-chip lfuse data:
Reading | ################################################## | 100% 0.02s
avrdude: verifying ...
avrdude: 1 bytes of lfuse verified
avrdude: reading input file "0xdd"
avrdude: writing hfuse (1 bytes):
Writing | ################################################## | 100% 0.04s
avrdude: 1 bytes of hfuse written
avrdude: verifying hfuse memory against 0xdd:
avrdude: load data hfuse data from input file 0xdd:
avrdude: input file 0xdd contains 1 bytes
avrdude: reading on-chip hfuse data:
Reading | ################################################## | 100% 0.01s
avrdude: verifying ...
avrdude: 1 bytes of hfuse verified
avrdude: reading input file "0xf8"
avrdude: writing efuse (1 bytes):
Writing | ################################################## | 100% 0.03s
avrdude: 1 bytes of efuse written
avrdude: verifying efuse memory against 0xf8:
avrdude: load data efuse data from input file 0xf8:
avrdude: input file 0xf8 contains 1 bytes
avrdude: reading on-chip efuse data:
Reading | ################################################## | 100% 0.01s
avrdude: verifying ...
avrdude: 1 bytes of efuse verified
avrdude: reading input file "0x0F"
avrdude: writing lock (1 bytes):
Writing | ################################################## | 100% 0.03s
avrdude: 1 bytes of lock written
avrdude: verifying lock memory against 0x0F:
avrdude: load data lock data from input file 0x0F:
avrdude: input file 0x0F contains 1 bytes
avrdude: reading on-chip lock data:
Reading | ################################################## | 100% 0.01s
avrdude: verifying ...
avrdude: 1 bytes of lock verified
avrdude: safemode: Fuses OK (E:F8, H:DD, L:FF)
avrdude done. Thank you.
C:\Program Files\Arduino\hardware\tools\avr\bin>
Пример удачно завершенной прошивки микроконтроллера при помощи Avrdude.
Схема, представленная ниже, взята из документации на Arduino Nano. Это минимум деталей, при котором загрузчик и загружаемые в микроконтроллер скетчи будут работать так же, как и в случае с оригинальной Arduino Nano.
Для загрузки скетчей в память микроконтроллера потребуется USB-TTL (UART) конвертер – устройство, служащее посредником между средой Arduino IDE и микроконтроллером. Конвертер преобразовывает интерфейс USB в UART, благодаря которому данные поступают загрузчику, а последний в свою очередь записывает их во flash-память микроконтроллера.
Примечание: в случае, если у вашего конвертера уже имеется конденсатор в цепи DTR, конденсатор С4 можно исключить. Цепь VD1 R4 в принципе не является важной и её можно исключить.
Два конвертера: верхний на микросхеме PL2303, а нижний - на CH340G. Для загрузки скетча подходит любой.
Процесс загрузки скетча в точности такой же, как и в случае с оригинальной платой Arduino. Микроконтроллер и USB-TTL конвертер соединяются по вот такой не хитрой схеме:
Если у вашего USB-TTL конвертера есть выход DTR, то вам повезло – подключать его следует обязательно к выводу RESET микроконтроллера. Если же такого вывода нет – нужно будет сбрасывать микроконтроллер вручную, кратковременно коммутируя вывод RESET к минусу питания. Делать это необходимо сразу же, как только заметите в Arduino IDE сообщение «Загрузка…».
Обратите внимание на коричневый проводок. У моих конвертеров нет выхода DTR, поэтому при старте загрузки я просто размыкаю RESET и минус питания.
USB-TTL конвертер на микросхеме CH340G можно доработать: вывод 13 микросхемы и есть тот самый DTR, к которому можно подпаять проводок и использовать вывод по назначению. Всё же приятней когда всё автоматизировано, не так ли?..
Для тестов написал коротенький скетч и загрузил, сначала на Arduino Nano, а затем на микроконтроллер с загрузчиком Arduino. Особой разницы в работе схемы не замечено.
Есть чуть более легкий путь, чтобы добиться подобных результатов. И именно о нём я сейчас и расскажу.
Среда Arduino IDE позволяет выгружать готовый скетч в виде бинарного файла. Для этого нужно перейти в Скетч -> Экспорт бинарного файла.
В процессе выгрузки создаются 2 файла с расширением *.hex: с загрузчиком и без него. Внимательный читатель уже догадался, что файл без загрузчика, не подразумевает дальнейшего исправления загруженного скетча. Так оно и есть - вариант хорош в случае, когда исправлять уже больше ничего не нужно и вряд ли когда-нибудь понадобится.
Запись во flash-память одного из файлов в точности такая же, как и в случае с загрузчиком – пример команды для Avrdude у вас уже есть. Значения фьюз битов так же берутся из boards.txt и зависят от платы, выбранной в среде Arduino IDE. Единственная разница вместо файла загрузчика – бинарный *.hex файл, экспортированный из Arduino IDE.
Поменял сообщение на экране, чтобы не казалось что это результат от предыдущего эксперимента. Хотя разницы в работе опять же никакой.
Настало время поговорить о возможных проблемах. Первое с чем я столкнулся - программатор не видит микроконтроллер. В окне Avrdude можно видеть вот такое сообщение:
avrdude: error: program enable: target doesn’t answer.
avrdude: initialization failed, rc=-1
Double check connections and try again, or use -F to override
this check.
Самая распространённая причина – ошибка в подключении микроконтроллера. Следует проверить правильность соединения МК и адаптера. Более редкая причина – плохой контакт между самим микроконтроллером и адаптером. В таком случае, можно зачистить дорожки адаптера, подогнуть слегка ножки микроконтроллера.
Еще одна проблема - не загружается скетч. Причин может быть несколько: плохой контакт, неисправный чип, отсутствие питания или же перепутанные линии RX-TX. Последнее, к слову, самая частая беда.
Всё то, что я рассказал выше, может пригодиться, в случае если вам каким-то образом удалось умертвить микроконтроллер в плате Arduino. Либо, если возникнет потребность создать устройство на Ардуино с красивой и лаконичной топологией печатной платы, без каких-либо наслоений. Оба изложенных сценария вполне работоспособны и проверены на личном опыте, так как основной моей задачей было научиться. И научить вас, конечно же
На сегодня всё. Спасибо за внимание и удачи в конструировании!
P.S. У данной статьи обязательно будет продолжение в виде второй части, которую просто нельзя не опубликовать. Добавлю ниже, как рекомендацию.
Ссылки и прочее: