Безопасное использование ножки RESET на Arduino

Картинка для привлечения внимания

Здравствуйте, уважаемые хабравчане!

В этой статье я опишу способ безопасного использования ножки 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].

«Atmega fusebit doctor» на беспаечной макетной плате
«Atmega fusebit doctor» на беспаечной макетной плате

Используя калькулятор фьюзов [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 на соответствующей ножке. Остальные фьюзы на ваше усмотрение (сверяемся с даташитом!). Этот момент настал, можно еще вернуться на светлую сторону к стандартной Ардуине, но мы не станем. После установки фьюзов данной командой стандартных способов поменять фьюзы или бутлоадер не будет! Но, если мы все сделали правильно, то основную программу мы можем шить как обычно при помощи заземления ноги RESET (или же просто зажимая кнопку 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]. Структура каталогов сохранена.

Замечания принимаются в личке или комментах.

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


  1. Мало выводов? Используем RESET
  2. optiboot – An optimised bootloader for Arduino platforms
  3. Arduino watchdog или автоматический RESET в случае зависания
  4. Русская документация к AVRDUDE
  5. Прошивка Arduino Pro Mini через Nano
  6. Fuse-биты — это не страшно
  7. Исправляем AVR фьюзы при помощи «Atmega fusebit doctor»
  8. Калькулятор фьюзов AVR
  9. ATmega328P datasheet
  10. Arduino Pro Mini schematic
  11. Архив со всеми затронутыми файлами для Arduino IDE 1.5.8
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 25

    +2
    После установки фьюзов данной командой стандартных способов поменять фьюзы или бутлоадер не будет!

    Единственный способ вернуться в прежнее состояние — использовать высоковольтный программатор. Он так называется потому, что подает на RESET 12 вольт.
      +1
      Может я чего-то не понимаю, зачем такой хардкор ради одной ноги?

      Ведь с 74HC595 и 74HC165 можно получить любое количество ног на ввод и вывод.
        +4
        Использование стандартной возможности микроконтроллера — это вовсе не хардкор. Получение лишней ноги из ресета часто практикуется, а в проектах на какой-нибудь ATTiny13 так вообще сплошь и рядом.
          +5
          Хардкор — это использовать стандартные возможности микроконтроллера, от которых неопытного пользователя огораживает arduino, но всё равно при этом использовать arduino.
            +5
            Особенно добило это:
            Гугление по данному вопросу поставит новичков в тупик — вроде бы везде пишут, что такое невозможно, но изредка упоминается, что есть способы обойти это ограничение
            (мой гнев относится не к автору, а вообще к ситуации)
            Чёрт возьми, это же в даташите описано! Кто там кого ограничивает? Вот так обыденные вещи становятся крутыми хаками…
              +4
              Кажется, у меня просветление.
              Я никогда не мог понять, зачем люди используют arduino, но теперь мне стало ясно. Ведь с ней никогда не надо смотреть в даташит! Зачем нужна документация, когда можно просто нагуглить готовые решения?
                +2
                К сожалению, идиократия является обратной стороной технического прогресса — технари прикладывают огромные умственные усилия, чтобы освободить от них своих последователей.
                  +1
                  Так вот что подразумевается под низким порогом вхождения :)
            0
            Я привел данное решение только в качестве вспомогательного, «софтового». Естественно, при проектировании надо сразу задуматься над требуемым количеством внешних выводов и, в случае надобности, включить в заказ поставки указанные микросхемы. Если поезд ушел, а сегодня нужно уже потестировать железку, то мое решение вполне сойдет.
              0
              Иногда в корпус не влезает регистр, а кнопка позарез нужна!
              +3
              Целая статья на хабре просто про то, как использовать бутлоадер в AVR и фьюзы? Серьёзно?
              И что за бред, что у ресета нет номера? Он чётко описан в даташите — C6.
                +3
                Имелось ввиду, что у него нету номера в ардуиновской библиотеке (вот жеж беда!)
                +2
                Аккуратней надо с такими статьями, а то не дай ТНБ граждане узнают, что ардуинку можно программить без Arduino IDE на нормальном ассемблере Си с полным использованием возможностей железа микроконтроллера. Тогда точно Земля налетит на небесную ось… истинно говорю!
                  +2
                  Да почти все это знают. В отличии от самого автора, которого ставит в тупик гугление вопроса, когда в документации чётко и ясно написано: «If the RSTDISBL Fuse is programmed, PC6 is used as an I/O pin». Уж если решили вылезти из пелёнок бутлоадера от arduino, то наверное можно было бы просто взглянуть в документацию.
                  Ну и всегда остаётся открытым вопрос — почему бы тогда изначально не использовать «нормальный» Си", и зачем нужно покупать arduino.
                    0
                    Что мешает использовать ардуино и «нормальный Си»?
                      0
                      Абсолютно ничего, собственно так и живем. Единственное условие — это чтобы ардуина была приобретена на каком нить алиекспресе по 10коп. за пучек… тогда полный феньшуй!
                        +2
                        Можно, но зачем? Или под ардуиной вы понимаете только вот эту стандартную платку, исключая весь софтверный треш который вокруг нее наверчен?
                          +1
                          В данном случае именно платку с припаянным контроллером и кварцем. Если нужно сляпать по-быстрому некое устройство, для которого микроконтроллер ардуины (как обычно) избыточен, то можно использовать и родную оболочку. Если же требуется критичный ко времени исполнения код или нестандартное (по «мнению» ардуины) использование железа, то вполне можно писать на нормальном Си.
                            0
                            И да, ардуину нужно покупать по 10коп. за пучок, а не по 1900р за платку типоразмера «мини», как в соседней статье. Иначе весь смысл теряется…
                              0
                              Зачем при этом покупать ардуину. Только ради платы? Если уж так нужна плата, это не единственное решение вроде.
                                0
                                Пустой спор. Кому что удобнее, то и используют.
                                  0
                                  Спором это было бы, если бы между вариантами была какая-то разница.
                                    0
                                    Разница в эстетике, цене и времени изготовления.
                                      0
                                      Мы наверное о разных вещах говорим. Ну и ладно, не будем комментарии засорять.
                            +1
                            Когда нужно просто отработать алгоритм например, а потом уже переносить его если идея сработала.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое