
Здравствуйте, уважаемые хабравчане!
В этой статье я опишу способ безопасного использования ножки RESET на плате Ардуино для собственных нужд.
Собственно, на поиски решения данного вопроса меня сподвиг некоторый недостаток ножек в разрабатываемой мной системе (которую я надеюсь описать на Хабре, как закончу реализацию) на основе Arduino Pro Mini.
Итак, задача ясна, а требования таковы:
- Возможность загружать скетчи обычными для Ардуины способами
- Возможность использовать в своей программе ножку RESET как digital I/O pin
Внимание: в статье приводятся команды, неаккуратное использование которых может привести Ардуину в неработоспособное состояние.
Гугление по данному вопросу поставит новичков в тупик — вроде бы везде пишут, что такое невозможно, но изредка упоминается, что есть способы обойти это ограничение, как, например, здесь [1].
Итак, чтобы получить желаемый результат, нам надо изменить код бутлоадера, прошить его в Ардуину и изменить фьюзы! Именно в такой последовательности! Это очень важно! Вы готовы?
Шаг первый
Тогда начнем с бутлоадера. В сети можно найти множество бутлоадеров для Ардуины с различными возможностями. Я взял самый маленький – optiboot [2]. Он не только освобождает для кода основной программы дополнительные 1,5 килобайта, но и позволяет использовать Watchdog [3]. Скачиваем с сайта последнюю версию с исходниками (архив), распаковываем и копируем папку «\optiboot-9e0c0b9db6fe\optiboot\bootloaders\optiboot» в папку «pathToArduino\hardware\arduino\avr\bootloaders\». Я использую версию Arduino IDE 1.5.8. Если у вас версия Arduino IDE 1.0.5, то найдите папку bootloaders самостоятельно.
Открываем файл «pathToArduino\hardware\arduino\avr\bootloaders\optiboot\optiboot.c» в текстовом редакторе или в имеющейся среде программирования (я использую Atmel Studio 6.2). Следующие строчки надо закомментировать:
ch = MCUSR;
MCUSR = 0;
if (ch & (_BV(WDRF) | _BV(BORF) | _BV(PORF)))
appStart(ch);
А вслед за ними вставить такой код:
//ch = MCUSR;
//MCUSR = 0;
//if (ch & (_BV(WDRF) | _BV(BORF) | _BV(PORF)))
//appStart(ch);
#define PIN_BOOT PCINT14
DDRC &= ~_BV(PIN_BOOT);
PORTC |= _BV(PIN_BOOT);
_delay_us(4);
if(_BV(PIN_BOOT)&PINC) {
PORTC &= ~_BV(PIN_BOOT);
appStart(MCUSR);
}
Также в шапке программы найдите #include и после всех инклюдов вставьте:
#include <util/delay.h>
Для проверки правильности всех действий с бутлоадером лучше всего создать тестовую прошивку, заменив в коде прошивки указатель на ножку. Допустим, так (PCINT10 – это ножка A2):
#define PIN_BOOT PCINT10
Тогда на тестовой прошивке, не затрагивая фьюзов, можно убедиться, что мы все делаем правильно.Объясню, что тут происходит. Мы убрали переход на основную программу, который происходит после программного сброса и сброса по низкому питанию. PCINT14 – это адрес нашей ножки RESET в регистре PORTC. С помощью прямого обращения к регистру DDRC мы установили для этой ножки pinMode INPUT и подали туда 5 вольт через внутреннее сопротивление (регистр PORTC, так называемое pinMode INPUT_PULLUP). Немного подождали, пока через резистор не перетечет достаточное количество электрончиков, и посмотрели сигнал на ножке через регистр PINC. Если на ноге нет сигнала, значит она заземлена, – приступаем к перепрошивке основной программы. В ином случае, переводим ножку в дефолтный режим INPUT и запускаем основную программу.
Теперь надо это все откомпилировать. В файле omake.bat надо заменить путь к make.exe. В моем случае (версия Arduino IDE 1.5.8) – это «..\..\..\sam\system\CMSIS\Examples\cmsis_example\gcc_atmel\make.exe». Я добавил еще пару опций, так что строчка запуска компиляции выглядит так:
..\..\..\sam\system\CMSIS\Examples\cmsis_example\gcc_atmel\make.exe OS=windows ENV=arduino LED_DATA_FLASH=1 BAUD_RATE=57600 %*
Сначала запускаем omake.exe c параметром clean, затем с параметром atmega328 и внимательно читаем ответ. Если там не упоминаются ошибки «error:», то в папке должна появиться наша прошивка optiboot_atmega328.hex. Замечательно!
Шаг второй
Приступим к перепрошивке бутлоадера. Использовать функцию перепрошивки в среде Arduino IDE в данном случае не самый лучший вариант – это, скорее всего, приведет к неработоспособности Ардуины. Для прошивки бутлоадера будем использовать avrdude.exe. Это консольная программа находится в папке «pathToArduino\hardware\tools\avr\bin\». Прочитать про используемые параметры можно здесь [4]. Для ее использования в папке с прошивкой создадим один командный скрипт flash.bat следующего содержания:
..\..\..\..\tools\avr\bin\avrdude.exe -C "..\..\..\..\tools\avr\etc\avrdude.conf" -c stk500v1 -p m328p -b 19200 -P COM5 %*
И второй – flash_boot.bat:
flash.bat -e -U flash:w:optiboot_atmega328.hex:i
Пути к файлам подставьте свои. COM-порт укажите тот, который используется вашим SPI-программатором или Ардуиной, заменяющей программатор. Я же использовал еще одну Pro Mini через COM-программатор PL2303HXA. В нее я закачал стандартный скетч ArduinoISP, который эмулирует работу SPI-программатора по протоколу STK500 v1. Примерная схема подключения показана в этой стате [5].
После того, как все подключено, можно проверить связь с программируемой Ардуиной с помощью запуска flash.bat: он покажет сигнатуру микроконтроллера и другую информацию. Теперь можно запустить второй скрипт flash_boot.bat. Если скажет «столько-то bytes of flash verified», значит все ОК, бутлоадер на месте.
На этом шаге можно проверить работу тестовой прошивки: например, загрузить стандартный Blink и, во время запуска Ардуины, заземлять тестовую ножку (A2 в данном случае). Должно наблюдаться беспорядочное мигание встроенного светодиода – это Watchdog рестартует микроконтроллер на этапе неудачных попыток бутлоадера прочесть из Serial-порта команду для перезаписи основной программы.
Шаг третий
Наконец, самая ответственная стадия – это установка фьюзов. Перед тем, как с ними работать, о них надо в обязательном порядке прочитать, например, тут [6].
Хочу заметить, что неправильные фьюзы можно вылечить с помощью «Atmega fusebit doctor» [7].

Используя калькулятор фьюзов [8] и даташит [9], создадим командный скрипт flash_fuse.bat для установки правильных фьюзов в наш многострадальный чип:
flash.bat -u -U lfuse:w:0xFF:m -U hfuse:w:0x56:m -U efuse:w:0x05:m
Цифра 5 в hfuse отвечает за отключение функции RESET на соответствующей ножке. Остальные фьюзы на ваше усмотрение (сверяемся с даташитом!). Этот момент настал, можно еще вернуться
Шаг четвертый
Преодолев множество трудностей, мы скомпилировали правильный бутлоадер, прошили его в микроконтроллер и установили правильные фьюзы. Теперь надо исправить кое-что в файле «pathToArduino\hardware\arduino\avr\boards.txt», чтобы Arduino IDE смогла общаться с нашей платой.
Добавим свои настройки в конец файла (для версии Arduino IDE 1.0.5 они немного другие):
##############################################################
pro328o16.name=[Optiboot] Arduino Pro Mini (5V, 16MHz) w/ ATmega328p
pro328o16.upload.tool=avrdude
pro328o16.upload.protocol=arduino
pro328o16.upload.maximum_size=32256
pro328o16.upload.speed=57600
pro328o16.bootloader.tool=avrdude
pro328o16.bootloader.low_fuses=0xff
pro328o16.bootloader.high_fuses=0x56
pro328o16.bootloader.extended_fuses=0x04
pro328o16.bootloader.file=optiboot/optiboot_atmega328.hex
pro328o16.bootloader.unlock_bits=0x3F
pro328o16.bootloader.lock_bits=0x0F
pro328o16.build.mcu=atmega328p
pro328o16.build.f_cpu=16000000L
pro328o16.build.board=AVR_PRO
pro328o16.build.core=arduino:arduino
pro328o16.build.variant=arduino:eightanaloginputs
Название платы «[Optiboot] Arduino Pro Mini (5V, 16MHz) w/ ATmega328p» появится в меню «Tools/Board» в самом конце списка.
Ограничения
Как вы помните, кнопка RESET на плате Ардуино замыкает ножку RESET на землю [10]. С помощью нее мы вводим микроконтроллер в режим перепрошивки основной программы. Именно поэтому я не рекомендую использовать эту ножку в режиме вывода пяти вольт. Если надо управлять «силовой» нагрузкой, то лучше вывести на ножку землю, а на вторую линию питания нагрузки завести пять вольт. Отключение питания происходит переводом ножки в режим ввода. Так мы избежим возможного короткого замыкания. Остальные режимы можно использовать без проблем.
Второе замечание касается сторонних библиотек: если мы передаем ножку как параметр, то нам неизвестно в каком режиме она будет работать. Тут уж ничего не поделать. Разве, что использовать ножку только на очень простой логике, которую сами пишем. И еще рекомендую ставить паузу в начале инициализации программы, чтобы было понятно по светодиоду, что кнопку RESET лучше отпустить немедленно, если кнопка вдруг не сработала, как ожидалось.
Третье замечание касается стандартной нумерации ножек. Да, у ножки RESET нету номера! Есть только регистры микроконтроллера, которыми она управляется. Эта проблема легко решается несколькими правками файла «pathToArduino\hardware\arduino\avr\variants\standard\pins_arduino.h»:
static const uint8_t RST = 20;
const uint8_t PROGMEM digital_pin_to_port_PGM[] = {
…
PC, /* 20 */
};
const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[] = {
…
_BV(6), /* 20, port C */
};
const uint8_t PROGMEM digital_pin_to_timer_PGM[] = {
…
NOT_ON_TIMER, /* 20 - port C */
};
После этого все стандартные функции, кроме analogRead(), будут работать с ножкой RESET под номером 20 (RST), как и с любой другой. И даже если у вас имеется дополнительные аналоговые ножки A6 и A7, то они работают отдельно по своему принципу. Можете сами убедиться в этом, написав такой скетч:
void setup() {
Serial.begin(9600);
pinMode(RST, INPUT_PULLUP); // Устанавливаем на ножке RST уровень HIGH в режиме INPUT
}
void loop() {
Serial.println(digitalRead(RST)); // Читаем с ножки RST число 1, если кнопка не нажата
Serial.println(analogRead(A6)); // Читаем с ножки A6 "случайное" число, зависящее от прикосновения
delay(500);
}
Заключение
Надеюсь, что моя статья не будет для вас единственно возможным выходом из сложной ситуации в ваших проектах на Ардуино. Но буду весьма рад, если кого-нибудь она подтолкнет к тому, чтобы копать еще глубже и находить интересные решения.
Архив со всеми затронутыми файлами для Arduino IDE 1.5.8 вы можете скачать по ссылке [11]. Структура каталогов сохранена.
Замечания принимаются в личке или комментах.
Полезные ссылки:
- Мало выводов? Используем RESET
- optiboot – An optimised bootloader for Arduino platforms
- Arduino watchdog или автоматический RESET в случае зависания
- Русская документация к AVRDUDE
- Прошивка Arduino Pro Mini через Nano
- Fuse-биты — это не страшно
- Исправляем AVR фьюзы при помощи «Atmega fusebit doctor»
- Калькулятор фьюзов AVR
- ATmega328P datasheet
- Arduino Pro Mini schematic
- Архив со всеми затронутыми файлами для Arduino IDE 1.5.8