[recovery mode] Отладка кода Arduino (AVR). Часть 1. Виртуальная отладка
Предисловие
Как известно, среда Arduino (AVR) не содержит функции внутрисхемной отладки, что создаёт большие неудобства при поиске сложных ошибок и сопровождении проектов. Я хочу показать два способа, при помощи которых вы сможете отлаживать свои скетчи разного уровня сложности. Для первого способа вам понадобятся только программы, а для второго нужен недорогой (по сравнению с оригинальным отладчиком) адаптер, который вы можете либо собрать самостоятельно, либо купить готовый.
В общем, ничего нового для тех, кто пользуется отладчиками постоянно, но может быть полезным для пользователей Arduino (AVR).
Сборка при помощи Makefile
Прежде чем мы подойдём к описанию отладки в железе (во второй части) нам нужно немного потренироваться. Вероятно многие знают, что среда Arduino вызывает компилятор avr-gcc со всем окружением, автоматически настраивает параметры его вызова и запускает процесс сборки. Так вот, нам нужно повторить этот процесс явно. Делать это мы будем не вручную, хотя некоторые телодвижения всё-таки понадобятся. Нужно это для того, чтобы получить отладочный объектный файл в формате ELF. Среда Arduino подчищает за собой и у нас нет доступа ни к опциям компилятора, ни к результатам компиляции.
Это подводит нас к вопросу использования утилит для автоматической сборки avr-gcc проекта, а ещё точнее — скетча с библиотеками Arduino. Тут можно было бы погрустить, т.к. дело это не простое, но, как говорится, всё уже сделано до нас. Оказывается, на github уже есть всё необходимое именно для сборки скетчей с библиотеками Arduino. Проект называется Arduino-Makefile. Как не трудно догадаться из описания, этот проект содержит Makefile для автоматической сборки проектов на Arduino. Из перечисленных в описании особенностей мы обратим внимание только на некоторые.
Пакет Arduino-Makefile — это набор конфигурационных файлов, примеров и описаний. В нём нет утилит, которые всё это должны использовать. Нет также и среды разработки, т.е. сборка осуществляется через командную строку. Это означает, что вы можете прикрутить любую удобную для вас IDE, которая поддерживает сборку через Makefile. Я обычно использую Visual Studio.
Если внимательно посмотреть на содержимое, то можно заметить, что есть один глобальный универсальный Makefile (Arduino.mk), который включается в специфические местные файлы сборок. Вам нужно указать только частные (минимальные) настройки для конкретной цели сборки, а остальное будет выполнено в автоматическом режиме (настройка переменных, поиск зависимостей и прочее). Это очень удобно, как будет показано ниже.
Важно отметить, что поддерживаются файлы с различными расширениями (*.ino, *.pde, *.c и *.cpp). Дело в том, что одним из отладчиков у нас будет AVR Studio 4. При загрузке в неё отладочного файла исходники в дереве проектов будут расположены в разных папках: cpp-файлы в sources, а остальные в headers. Если мы оставим расширение для скетча (ino), то файл не попадёт в sources и на него не будет действовать подсветка синтаксиса. Смотреть на это невозможно. Поэтому возьмём на заметку, что расширение для скетчей при сборке для отладки в AVR Studio нужно поменять на стандартные (.c или .cpp).
Инструментарий
Мы выяснили, что можем собирать скетчи. Теперь посмотрим как будет организован сам процесс сборки и отладки. Я обещал показать два способа. У каждого из них есть свои плюсы и минусы. Сделаем их краткий обзор.
Способ 1. Создаём отладочный файл, загружаем его в Proteus, отлаживаем там же.
Достоинства:
— относительная простота отладки (с подключением монитора последовательного порта нужно будет только помучиться);
— доступ во внешний мир через последовательный порт;
— построение графиков (доступны виртуальные измерительные приборы);
— возможность моделировать схему в режиме реального времени;
— в отдельных окнах доступны регистры мк, области памяти, глобальные переменные и исходный код, конечно, в том числе его ассемблерный вариант.
Недостатки:
— платность Proteus;
— ограниченный набор периферии;
— сложные модели не могу учитывать всех особенностей оригинальных компонентов.
Способ 2. Создаём отладочный файл, загружаем его в AVR Studio 4, отлаживаем, используя специальный плагин Proteus VSM Viewer для AVR Studio 4.
По достоинствам и недостаткам почти то же, что и способе 1. Можно добавить, что AVR Studio показывает подробное дерево всех регистров моделируемого мк, вплоть до битов, что очень удобно. Из минусов стоит отметить, что отладка оптимизированного кода имеет свои особенности и не так просто понять как заставить отладчик останавливаться в нужных местах.
Способ 3. Создаём отладочный файл, загружаем его в AVR Studio 4, отлаживаем, используя программный эмулятор JTAG ICE mkII и специальный адаптер (HappyJTAG2).
Достоинства:
— это настоящая отладка в реальном «железе» с использованием JTAG ICE mkII отладчика (поддержка мк вплоть до ATmega2560);
— HappyJTAG 2.45 работает на Windows 7×64, нужно только пропустить одно окошко, где просят всё-таки купить драйвера.
Недостатки:
— замечена нестабильная работа HappyJTAG2 с небольшими по объёму исходниками;
— нестабильная работа AVR Studio при выполнении отладочных действий;
— автор HappyJTAG2 давно забросил своё детище (видимо с приходом AVR Studio 5 и Atmel Studio);
— некоторые особенности подключения (COM4 или один из первых 4-х последовательных портов должен быть свободен или отсутствовать, т.к. AVR Studio перебирает COM1-COM4 в поиске отладчика). Именно свободен или отсутствовать, т.к. HappyJTAG2 работает, так скажем, «изнутри».
Как вы видите я показал три способа, но в настоящее время практически у меня заработали только два из них. Первый способ будет описан в этой статье. Второй способ, к сожалению, мне не удалось повторить. Нужно найти «совместимую» комбинацию Proteus и AVR Studio. На картинке использована последняя студия AVR Studio 4.19.730 и Proteus 7.7 SP2. Когда-то давно я использовал этот способ, но при наличии железного отладчика пользоваться им не имеет особого смысла. Третий способ я оставил для второй части. Там нужен будет адаптер и описание его подключения к платам Arduino, поддерживающим отладку по JTAG.
И что же для этой всей кухни нам понадобится? Для начала нужно забыть про убогое Arduino IDE, его можно использовать только для контрольных проверок при сборке скетчей. Далее, нам понадобятся:
— Arduino-Makefile — набор конфигурационных файлов для ручной сборки проектов на Arduino;
— какой-нибудь редактор с подсветкой синтаксиса C++ и умением выполнять внешние команды (Notepad++, VS20xx, Far, …);
— Proteus 7.x — известный симулятор смешанных (аналого-цифровых) схем;
— терминальная программа (PuTTY, ZOC Pro), которая будет аналогом монитора;
— программа, создающая нуль-модемные виртуальные последовательные порты (VSPD, …);
— Arduino IDE 1.6.x (нам нужны библиотеки и компилятор avr-gcc);
— WinAVR (нам нужны binutils из его комплекта, это набор unix команд для Windows);
— avr gcc 4.9.2 (компилятор посвежее, если не хотите использовать тот, что идёт в комплекте с Arduino IDE);
— AVR Studio 4.19.730 — последняя версия среды разработки от Atmel перед переходом на Visual Studio (понадобится только для второй части);
— HappyJTAG 2.45 (понадобится только для второй части).
Будем считать, что вы знаете как спросить у Google где достать всё это хозяйство. На всякий случай я приведу полезные ссылки в конце статьи. Некоторые комментарии по поводу инструментария.
Как известно, Proteus платный, но это не самое печальное. К сожалению, его библиотеки не на столько близки к реальному миру, как хотелось бы. К примеру, Ethernet shield W5100 вы промоделировать в нём не сможете (по крайней мере в версии 7.x). Поэтому, уважаемые последователи Arduino, идите истинным путём. Только отладка и только в железе спасут ваши души от неправильно поставленных вопросов. Proteus мы будем использовать как инструмент обучения, а в полевых условиях — только JTAG.
Отладка скетчей Arduino (AVR) в Proteus 7.x
Хватит общих слов, теперь конкретики. Есть много вариантов запуска процесса сборки, их все не распишешь, поэтому я остановлюсь на конкретно одном и постараюсь описать его понятно. Увидев общую схему, вы сможете применить её на свой набор средств разработки. Для удобства я разобью описание всего процесса по шагам, некоторые можно будет пропускать. Надеюсь, что даже самые неопытные пользователи Arduino поймут о чём речь.
Шаг 1. Скачиваем и устанавливаем среду разработки Arduino. Для определённости допустим, что она будет из серии 1.6.x. Здесь сразу сделаю несколько замечаний. Вообще, от Arduino нам нужны только библиотеки. Если не считать всего остального, то сама идея упрощённого вида программы очень даже хороша (если сравнить C# и C++ или, не дай боже, C++/CLI, то это небо и земля). Отсутствие же нормальных отладочных средств привело, прямо скажем, к безграмотному программированию. Вместо того, чтобы осознанно превращать задуманный алгоритм в программный код, пользователи Arduino вынуждены делать комбинации из магических заклинаний, выцеживать инфу через Serial.print () и только некоторые пытаются статически прочитать исходники библиотек. Тяжко на это всё смотреть.
Я отвлёкся, а вы, наверное, успели поставить среду по стандартным путям. Желательно, чтобы папка Arduino находилась в корне раздела (C:\Arduino). Это связано с путями в makefile, которые не любят пробелы в «Program Files». Мы позже будем настраивать пути и для тех, у кого папка уже в «Program Files» придётся сделать одну сложную для пользователей Windows вещь — junction point на папку. Возможно, пробел можно экранировать, но я этого не пробовал.
Для определённости, допустим, что путь к среде такой: C:\Program Files\Arduino.
Шаг 2. Скачиваем и распаковываем Arduino-Makefile. Содержимое папки Arduino-Makefile-master распаковать в C:\Arduino-Makefile. Хочу сразу отметить, что внутри есть файл README.md, который лучше посмотреть на github’е, где много чего описано. Также следует взять на заметку файл arduino-mk-vars.md, который содержит описание используемых в пользовательском (проектном) Makefile переменных.
Для работы утилиты make нужен комплект gnu bin utils, который входил в состав WinAVR в своё время. Я не знаю есть ли официальный сайт сборки этих самых утилит под Windows, но можно поступить следующим образом. Вам нужно будет скачать старый добрый WinAVR последней версии и вытащить из него папку utils, где находятся командные утилиты. Можно установить, скопировать папку и деинсталлировать WinAVR (потому что в его комплект входит старый компилятор avr-gcc, который нам не нужен).
Далее, к примеру, создать папку c:\avr-gcc и скопировать utils в неё. После этого добавить в переменную PATH (через свойства Компьютера) путь C:\avr-gcc\utils\bin:
set PATH=C:\avr-gcc\utils\bin;%PATH%
Путь должен быть одним из первых в поиске. Не забывайте про это изменение, т.к. оно может повлиять на работу других программ, если вы используете другие подобные среды разработки.
Шаг 3. Сами знаете где берёте/покупаете Proteus [7.2 — 7.8]. Почему именно этой серии и такой интервал версий? Потому что я их пробовал и вроде бы на несложных проектах они вполне неплохи. Версии больше 7.8 не могли подгрузить объектный файл одного моего проекта в IAR, а ниже я не пробовал. Восьмёрка просто глючная пока, может быть потом кто-то напишет что-то и про неё. Здесь мы с вами возьмём конкретно Proteus 7.8 SP2.
Шаг 4. Используя статью, создаём junction point на папку с установленной средой Arduino, т.е. C:\Arduino должна ссылаться на C:\Program Files\Arduino. Это нужно, чтобы не мудрить с поддержкой пробелов в makefile’ах. Т.о., не копируя папку с Arduino, мы получили её копию в нужном нам месте. Кто пользуется Far’ом может использовать комбинацию Alt+F6 на папке.
Изменяем настройки среды Arduino. Путь к папке со скетчами: C:\Arduino-Makefile\examples. Если используете внешний редактор (Notepd++, …), то можете поставить галочку в настройках. При этом Arduino при активизации окна будет обновлять содержимое своего редактора автоматически. Выбираем плату Arduino Mega 2560 и процессор ATmega2560 (на самом деле не так важно что тут выбрать, главное определиться с используемым контроллером).
Пишем пример программы для тестирования сборки из среды Arduino, назовём её Example1 и сохраним в папке скетчей:
void setup()
{
DDRD |= ( 1 << DDD2 );
}
void loop ()
{
PIND |= (1 << PIND2 );
}
Компилируем и проверяем, что сборка проходит. У меня в Arduino 1.6.7 компоновщик объектных файлов (ld.exe) вылетел с ошибкой, я заменил его на другой (можно, к примеру, из этой сборки).
Шаг 5. Копируем файл C:\Arduino-Makefile\examples\WebServer\Makefile в папку с нашим скетчем: C:\Arduino-Makefile\examples\Example1. Исправляем его содержимое следующим образом:
# Arduino Make file. Refer to https://github.com/sudar/Arduino-Makefile
# Suppress printing of Arduino-Makefile configuration.
#ARDUINO_QUIET = 1
# Directory where the Arduino IDE and/or core files are stored. Usually can be auto-detected as `AUTO_ARDUINO_DIR`.
ARDUINO_DIR = ../../../Arduino
# Directory where tools such as `avrdude`, `avr-g++`, `avr-gcc`, etc. are stored in the `bin/` subdirectory.
AVR_TOOLS_DIR = ../../../Arduino/hardware/tools/avr
# Directory where the `*.mk` files are stored.
# Usually can be auto-detected as parent of `Arduino.mk`.
ARDMK_DIR = ../../../Arduino-Makefile
# Device type as listed in `boards.txt` or `make show_boards`.
BOARD_TAG = mega
# Microcontroller model.
# Usually can be auto-detected as `build.mcu` from `boards.txt`
MCU = atmega2560
#CPU speed in Hz
#Usually can be auto-detected as `build.f_cpu` from `boards.txt`, except in
#some 1.5+ cores like attiny where there is a clock submenu.
#F_CPU = 16000000L
# What name you would like for generated target files.
# Defaults to the name of your current working directory, but with underscores (_) instead of spaces.
#TARGET = project
# Baudrate of the serial monitor.
# Defaults to `9600` if it can't find it in the sketch `Serial.begin()`
MONITOR_BAUDRATE = 9600
DEBUG = 1
DEBUG_FLAGS = -O1 -gdwarf-2 -gstrict-dwarf
include ../../Arduino.mk
Вставляем вначале нашего исходника строчку, подключающую явно Arduino.h (это делать необязательно, если есть зависимости от библиотек, указываемые в переменной ARDUINO_LIBS):
#include "Arduino.h"
void setup ()
{
DDRD |= (1 << DDD2 );
}
void loop ()
{
PIND |= (1 << PIND2 );
}
Не забываем сохранить исходник и Makefile. Далее, находясь в папке Example1, вводим команду make (при помощи консоли или в Far’е, или другим удобным способом), должна появиться большая портянка, похожая на ту, что выводится в Arduino IDE при включении вывода полной информации о процессе сборки. Это если всё было сделано правильно, если же что-то не получилось, то попытайтесь сначала самостоятельно понять что не так, а потом уж пишите комменты к статье.
Поскольку мы в Makefile закомментировали строку ARDUINO_QUIET = 1, то перед информацией о сборке идёт шапка со значениями переменных самого Makefile. Часть из них задана, а другие вычисляются по ходу выполнения. Это помогает находить ошибки при правке Makefile проекта.
-------------------------
Arduino.mk Configuration:
- [AUTODETECTED] CURRENT_OS = WINDOWS
- [USER] ARDUINO_DIR = ../../../Arduino
Usage: egrep [OPTION]... PATTERN [FILE]...
Try `egrep --help' for more information.
- [USER] ARDMK_DIR = ../../../Arduino-Makefile
- [AUTODETECTED] ARDUINO_VERSION = 167
- [DEFAULT] ARCHITECTURE = avr
- [DEFAULT] ARDMK_VENDOR = arduino
- [DEFAULT] ARDUINO_SKETCHBOOK =
- [USER] AVR_TOOLS_DIR = ../../../Arduino/hardware/tools/avr
- [COMPUTED] ARDUINO_LIB_PATH = ../../../Arduino/libraries (from ARDUINO_DIR)
- [COMPUTED] ARDUINO_PLATFORM_LIB_PATH = ../../../Arduino/hardware/arduino/avr/libraries (from ARDUINO_DIR)
- [COMPUTED] ARDUINO_VAR_PATH = ../../../Arduino/hardware/arduino/avr/variants (from ARDUINO_DIR)
- [COMPUTED] BOARDS_TXT = ../../../Arduino/hardware/arduino/avr/boards.txt (from ARDUINO_DIR)
- [DEFAULT] USER_LIB_PATH = /libraries (in user sketchbook)
- [DEFAULT] PRE_BUILD_HOOK = pre-build-hook.sh
- [USER] BOARD_TAG = mega
- [COMPUTED] CORE = arduino (from build.core)
- [COMPUTED] VARIANT = mega (from build.variant)
- [COMPUTED] OBJDIR = build-mega (from BOARD_TAG)
- [COMPUTED] ARDUINO_CORE_PATH = ../../../Arduino/hardware/arduino/avr/cores/arduino (from ARDUINO_DIR, BOARD_TAG and boards.txt)
- [USER] MONITOR_BAUDRATE = 9600
- [DEFAULT] OPTIMIZATION_LEVEL = s
- [DEFAULT] MCU_FLAG_NAME = mmcu
- [DEFAULT] CFLAGS_STD =
- [DEFAULT] CXXFLAGS_STD =
- [AUTODETECTED] DEVICE_PATH =
- [DEFAULT] FORCE_MONITOR_PORT =
- [AUTODETECTED] Size utility: AVR-aware for enhanced output
- [COMPUTED] BOOTLOADER_PARENT = ../../../Arduino/hardware/arduino/avr/bootloaders (from ARDUINO_DIR)
- [COMPUTED] ARDMK_VERSION = 1.5
- [COMPUTED] CC_VERSION = 4.8.1 (avr-gcc)
-------------------------
mkdir -p build-mega
Будем считать, что всё прошло успешно, тогда у вас должна появиться папочка build-mega, в котором наш долгожданный Example1.elf — этот тот самый файл, ради которого всё действо затевалось. Этим файлом мы «прошьём мозги» виртуальному мк в Proteus и заживём… с ещё одной степенью свободы.
Шаг 6. Вернёмся к Proteus. Создаём новый проект (dsn-файл) в папке с исходником. Достанем из недр библиотек компонент — микроконтроллер ATmega2560 и вставим его куда он поместится, уж здоровый больно. Заполните свойства компонента по картинке. Устанавливать COMPIM пока не обязательно, он понадобиться для работы с монитором.
Затем входим в режим отладки Debug\Start/Restart Debugging. Получите картинку, похожую на такую.
Ну, а дальше, всё зависит от полёта вашей фантазии. В окошке исходников будет доступен не только Example1.ino, но и другие зависимые исходники. Можно раскрыть ассемблерный код, регистры процессора, память и что-то там вроде ещё. Читайте доку на Proteus.
Шаг 7. Нужно настроить монитор. Писать лень, надеюсь сделаете это уже самостоятельно. Смысл, вкратце, такой. Создаёте два виртуальных последовательных порта, соединённых нуль-модемом (лучше с номерами больше COM4). Один прописываете в компоненте COMPIM Proteus, а второй в терминальной программе (PuTTY). Не забудьте поправить скорость и кодировку вывода в терминальной программе, по идее она должна совпадать с кодировкой исходных файлов, если захотите делать вывод в монитор по-русски.
Шаг 8. Если хотите использовать avr gcc 4.9.2, то нужно положить содержимое архива в корень диска и исправить путь в переменной AVR_TOOLS_DIR. Только там у меня не заработал avr-size, кажется. Можно поменять его на тот, что идёт в комплекте WinAVR (или Arduino).
Кстати, чтобы размер выводился в нормальном виде нужно добавить опцию в вызов avr-size (файл Arduino.mk):
avr_size = $(SIZE) $(SIZEFLAGS) --format=avr $(1)
Полезные ссылки:
1. Arduino IDE 1.6.x.
2. Arduino-Makefile.
3. WinAVR.
4. avr gcc 4.9.2.
5. Hard links и пр. в Windows.
6. PuTTY.
7. Notepad++.