Программирование AVR Arduino: описание процесса сборки скетча Fri, March 29 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

Arduino: описание процесса сборки скетча Печать
Добавил(а) microsin   

Перед тем, как программа Arduino (скетч) попадет в память микроконтроллера платы и начнет работать, должно произойти несколько скрытых от глаз пользователя процессов. Сначала среда разработки Arduino IDE выполняет небольшие преобразования кода скетча, чтобы он стал текстом, полностью совместимым со стандартом языка C или C++ (это два наиболее известных языка программирования в мире микроконтроллеров). Затем полученный текст программы передается компилятору avr-gcc, который переводит человеко-читаемый код программы в объектные машинные коды, пригодные (после дальнейшего преобразования - линковки) для выполнения ядром используемого микроконтроллера (обычно это Atmel AVR ATmega328). Затем объектный машинный код скетча комбинируется (этот процесс называется линковкой) с кодом функций из стандартных библиотек Arduino (эти библиотеки предоставляют множество функций, таких как базовые digitalWrite() или Serial.print()). В результате получается один файл в формате Intel HEX [2], который должен быть записан в память микроконтроллера макетной платы Arduino. Обычно функцию программирования кода в память микроконтроллера берет на себя стандартный UART-загрузчик Arduino: код передается загрузчику через USB-соединение с компьютером (через специальную микросхему USB-UART, обычно компании FTDI). UART-загрузчик Arduino был изначально записан в память микроконтроллера платы специальным ISP-программатором (кстати говоря, многие дешевые китайские Ardiuno-совместимые платы, которые можно купить на ebay, страдают отсутствием прошитого загрузчика).

[Подготовка main sketch file]

Скетч может состоять из нескольких модулей кода - файлов, распределенных по отдельным вкладкам. Чтобы перемещаться по ним, сделайте клик мышью на нужную вкладку, или на обращенную вниз стрелку, находящуюся в правом верхнем углу окна редактора кода Arduino IDE. Эта стрелка позволяет также создавать новые вкладки и удалять их. Вкладки олицетворяют доступ к фалам с различными расширениями - без расширения, .c, .cpp или .h (если Вы дадите другое расширение при создании вкладки, то точка автоматически будет преобразована в символ подчеркивания). Когда скетч компилируется, то все вкладки без расширения будут объединены вместе в один main sketch file, "главный файл скетча". Вкладки с расширениями .c или .cpp компилируются отдельно. Чтобы использовать код вкладок с расширением .h, в код скетча нужно добавить директиву #include "имя_файла_вкладки" (указывать нужно именно двойные кавычки а не < угловые скобки >).

[Преобразования main sketch file]

Как уже упоминалось, Arduino IDE делает некоторые преобразования над main sketch file (это может быть как один файл на одной вкладке, таки и объединенные друг с другом файлы без расширений на нескольких закладках) перед передачей кода компилятору avr-gcc.

Сначала в начало main sketch file скрыто добавляется #include "Arduino.h", или для старых версий (меньше чем 1.0) #include "WProgram.h". Этот заголовочный файл (он находится в каталоге %ARDUINO%/hardware/cores/< CORE >/, где CORE название используемого ядра микроконтроллера) подключает все определения, которые нужны стандартному ядру Arduino.

Затем среда разработки ищет определения функций в main sketch file, и создает для них декларации (прототипы). Они вставляются после любых комментариев или директив препроцессора (#include и/или #define и т. п.), но перед любыми другими операторами, включая определения типа. Это означает, что если Вы хотите применить пользовательский тип в качестве аргумента у функции, то обязательно должны определить этот тип в отдельном заголовочном файле (и подключить его, как обычно, директивой #include в начале скетча). Кроме того, эта генерация несовершенна: она не создаст прототипы для функций, у которых есть значения параметров по умолчанию, или которые декларированы внутри пространства имен (namespace) или внутри класса. Чтобы обойти эту проблему, Вы можете предоставить свои собственные прототипы для таких функций; прототипы не будут генерироваться для функций, которые уже их имеют.

Препроцессор - это первый шаг в процессе обработки исходного кода (скетча), компилятором. Препроцессор компилятора ищет в коде специальные ключевые слова, начинающиеся на символ #, обозначающий начало директивы компилятора.

Вы наверное не раз встречались с ключевыми словами #include при использовании библиотек. Директива #include говорит препроцессору компилятора вставить в это место код из файла заголовка библиотеки.

Есть и другие директивы препроцессора (#define, #if, #else, #error, #pragma и т. д.), информацию по ним Вы можете найти в руководстве по компилятору [5]. Иногда препроцессор - единственная возможность решить задачу, однако синтаксис препроцессора отличается от языка C и C++, и неосторожное использование директив препроцессора может привести к трудно отлавливаемым ошибкам.

В файл добавляются различные константы (полученные из файла описания boards.txt и относящиеся к выбранной плате). Эти константы связаны с используемым типом микроконтроллера. И наконец, содержимое текущего целевого (target) файла main.cxx, добавляется в конец скетча. После всех этих преобразований файл компилируется компилятором AVR-GCC.

Не будет выполнено никаких преобразований с файлами .c, .cpp или .h, которые находятся в составе скетча. Кроме того, .h файлы скетча (например, которые открыты на вкладках) не будут автоматически подключаться к процессу компиляции, пока Вы их явно не подключите директивой #include в код скетча. В будущем, если Вы хотите вызвать функции модуля .c из файла .cpp, то Вам нужно обернуть объявления этих функция в блок 'extern "C" {}', который определяется только внутри файлов C++.

[Цели (targets)]

Под target-ом здесь подразумевается плата Arduino, для которой компилируется двоичный машинный код. Среда разработки Arduino поддерживает несколько плат с различными микроконтроллерами (в настоящее время это пока только различные микроконтроллеры одного семейства - AVR), тактовыми частотами (CPU speed) или загрузчиками. Все эти настройки платформ заданы в файле настроек (preferences.txt).

Некоторые настройки можно поменять в диалоге Настройки (Preferences) прямо из среды разработки Arduino. На платформах Windows или Linux это меню File (Файл), на платформе Mac это меню Arduino. Другие настройки должны быть изменены редактированием файла preferences.txt. Место расположения этого файла показано в диалоге Настройки. Это место должно быть:

Платформа Путь
Mac /Users/имя_пользователя/Library/Arduino/preferences.txt
Windows XP c:\Documents and Settings\имя_пользователя\Application Data\Arduino\preferences.txt
Windows Vista c:\Users\имя_пользователя\AppData\Roaming\Arduino\preferences.txt
Linux ~/.arduino/preferences.txt

Редактируйте этот файл только тогда, когда среда разработки Arduino не запущена, иначе все Ваши изменения будут перезаписаны и потеряны, когда Вы завершите работу с Arduino IDE.

Определения, которые задают содержимое меню Board, можно найти в файле boards.txt, находящемся в поддиректории hardware/ каталога приложения Arduino IDE. Определения для меню Burn Bootloader находятся в файле programmers.txt того же самого каталога. Чтобы создать новую плату или определение программатора, сделайте копию существующего, поменяйте префикс, используемый в ключах свойства (preference keys, например "diecimila." или "avrisp."), и поменяйте значения, чтобы они подходили к Вашей аппаратуре. Имейте в виду, что поддерживаются только определенные конфигурации плат.

Соответствующие переменные для настроек включают:

< BOARD >.name: имя платы, которое будет отображаться в меню Boards.
< BOARD >.build.mcu: микроконтроллер на плате ("atmega8" или "atmega168", чаще это "atmega328").
< BOARD >.f_cpu: тактовая частота (clock speed), на которой работает микроконтроллер (обычно "16000000L", или для случая, когда ATmega168 работает от своего внутреннего тактового генератора, то "8000000L").
< BOARD >.core: подкаталог каталога hardware/cores/ для линковки скетчей (обычно "arduino").

Полезна также следующая настройка в main preferences.txt:

build.verbose: нужно или нет выводить отладочные (подробные) сообщения, описывающие процесс компиляции скетча (например "false"). Если true, то будут выводиться полностью и командная строка для каждой выполняемой внешней команды как части процесса сборки.

Примечание: если false, то подробные сообщения о процессе компиляции выводиться не будут, однако Вы можете отменить это поведение для предстоящей процедуры компиляции или выгрузки, если будете удерживать клавишу Shift, когда делаете клик на команде Compile или Upload.

Примечание: в версиях Arduino 0004 и более поздних later, build.extension не используется - главный файл скетча всегда обрабатывается как файл .cpp.

[Процесс компиляции]

Скетчи компилируются компилятором avr-gcc и avr-g++ в соответствии с переменными файла boards.txt по выбранной платформе (выбранной плате Arduino).

Пути поиска для подключения заголовочных файлов (include path) включают каталог скетча, каталог target (%ARDUINO%/hardware/core/< CORE >/), каталог variant платы (подкаталог hardware/arduino/variants, указанный в файле board.txt) и каталог подключаемых файлов AVR (%ARDUINO%/hardware/tools/avr/avr/include/), а также все любые каталоги библиотек (в каталоге %ARDUINO%/libraries/), которые содержат заголовочные файлы, подключаемые главным файлом скетча.

Когда Вы проверяете (verify) или выгружаете (upload) скетч, он компилируется во временную системную директорию (например на Mac или Linux это каталог /tmp). Когда Вы делаете выгрузку (upload), сборка происходит в поддиректорию applet/ каталога скетча (в который Вы можете получить доступ, выбрав пункт "Show Sketch Folder", т. е. "Показать папку скетчей" меню "Sketch", "Скетч").

Целевые файлы .c и .cpp компилируются в выходные файлы с расширением .o, и попадают в эту папку, как и главный файл скетча, и любые другие файлы .c или .cpp в скетче или любые .c или .cpp файлы любой библиотеки, подключаемой директивой #include скетча.

Перед попыткой компиляции каждого .c или .cpp делается попытка повторного использования ранее скомпилированного файла .o - эта попытка ускоряет процесс сборки. Специальный файл .d (от слова dependency, зависимость) предоставляет список всех других файлов, подключаемых исходным файлом. Шаг компиляции пропускается, если файлы .o и .d существуют, и имеют метку времени новее, чем исходный файл и все файлы списка зависимостей. Если исходный файл или любой файл из списка зависимостей был изменен, или произошла любая ошибка при проверке файлов, то компилятор запускается как обычно, и записывает новые файлы .o и .d. После того, как в меню Tools была выбрана новая плата, все файлы .c и .cpp будут перекомпилированы заново при следующем запуске компиляции.

Затем эти файлы .o линкуются друг с другом в статическую библиотеку, которая затем линкуется с объектным файлом, полученным от основного файла скетча. Причем в выходной .hex файл попадет только код тех частей библиотек и объектных файлов, которые реально используются в скетче, чем уменьшается размер выходного файла.

Файл .hex является заключительным результатом компиляции, который выгружается в плату. Во время процедуры проверки "Verify" файл .hex записывается в каталог /tmp (на платформах Mac и Linux) или \Documents and Settings\имя_пользователя\Local Settings\Temp (на платформе Windows). Во время выгрузки HEX-файл записывается в подкаталог applet каталога скетча.

Утилиты, используемые для реализации процесса компиляции (в том числе и компилятор AVR-GCC), находятся в каталоге %ARDUINO%/hardware/tools/avr.

Для продвинутых программистов может оказаться полезной утилита avr-objdump, которая также находится в каталоге tools. Она позволяет посмотреть, как компилятор превратил код скетча в программу, которую выполняет микроконтроллер платы. Утилита avr-objdump генерирует листинг дизассемблера Вашего скетча, который покажет объектный код, перемешанный с соответствующими строками исходного кода. Листинг также покажет карту памяти (memory map) для всех переменных, используемых в Вашем скетче. Чтобы использовать avr-objdump, выполните сначала компиляцию скетча, и затем перейдите в папку каталога установки Arduino. Затем найдите папку, которая содержит все промежуточные файлы, используемые в процессе сборки (как было объяснено ранее). Промежуточный файл, используемый утилитой avr-objdump, имеет расширение .elf. Например, если Вы скомпилировали скетч Blink, то сможете увидеть вывод компиляции (машинные коды) путем запуска из командной строки следующей команды:

..\hardware\tools\avr\bin\avr-objdump.exe -S blink.cpp.elf

Иногда удобно просматривать результаты вывода в текстовом редакторе. Для этого выполните следующую команду:

..\hardware\tools\avr\bin\avr-objdump.exe -S blink.cpp.elf > blink.txt

Следующая версия команды добавит в вывод список секции заголовков (помогает для определения расхода памяти):

..\hardware\tools\avr\bin\avr-objdump.exe -S -h blink.cpp.elf > blink.txt

Вы также можете создать командный файл (файл с расширением .bat), который будет выводить листинг в файл. Добавьте путь до каталога инсталляции Arduino в следующую строку, и сохраните это как командный файл:

hardware\tools\avr\bin\avr-objdump.exe -S -h -Tdata %1 > %1%.txt

[Процесс выгрузки (Upload)]

Скомпилированный двоичный код скетча (представленный выходным файлом в формате Intel HEX) обычно попадает в память микроконтроллера платы с участием загрузчика и утилиты avrdude.

Процесс выгрузки также управляется переменными в настройках платы и основных настройках. Эти настройки включают:

< BOARD >.upload.protocol: протокол, который avrdude должен использовать для общения с загрузчиком платы (обычно это протокол "stk500").
< BOARD >.upload.speed: скорость (baud rate), которую avrdude должен использовать, когда выгружает скетчи (обычно "19200").
< BOARD >.upload.maximum_size: максимальный размер скетча для платы (зависит от модели чипа и размера кода загрузчика).

Также на процесс выгрузки влияет файл основных настроек (main preferences file):

upload.verbose: нужно или нет выводить отладочные (подробные) сообщения, описывающие процесс загрузки, т. е. передачи кода загрузчику платы (по умолчанию "false", т. е. подробные сообщения выводиться не будут).

Примечание: если false, то подробные сообщения о процессе выгрузки выводиться не будут, однако Вы можете отменить это поведение для предстоящей процедуры компиляции или выгрузки, если будете удерживать клавишу Shift, когда делаете клик на команде Compile или Upload.

[Ссылки]

1. Arduino Build Process site:code.google.com.
2. Intel HEX: описание формата файла.
3. Загрузчики (bootloader) для микроконтроллеров AVR.
4. Почему я не люблю Arduino.
5. The C Preprocessor site:gcc.gnu.org.

 

Добавить комментарий


Защитный код
Обновить

Top of Page