Будет ли .ino-скетч ардуино компилироваться непосредственно на GCC-AVR?
Хорошо, мы все видели эти вопросы по всему Интернету, такие как Arduino против C++ или другие подобные вопросы. И подавляющее большинство ответов даже не касаются различий в компиляции, кроме абстрактной информации.
Мой вопрос направлен на устранение фактических различий (а не предпочтений) в том, как файл .ino, переименованный в файл .cpp или другое аналогичное расширение файла для С++, будет компилироваться с использованием GCC-AVR. Я знаю, что как минимум вы должны включить заголовочный файл Arduino, но помимо этого, что может вызвать ошибку компиляции при компиляции указанного файла .ino в .cpp с использованием, например, GCC-AVR. Для простоты давайте воспользуемся классическим примером моргания, чтобы объяснить, в чем заключаются различия. Или, если у вас есть лучший фрагмент кода для использования, обязательно включите его в свой ответ и подробно объясните различия.
Пожалуйста, не высказывайте мнений о том, какой способ или инструмент лучше использовать.
К вашему сведению. Я использую Platformio для разработки и заметил процесс преобразования, происходящий за кулисами во время компиляции. Я пытаюсь понять, что там на самом деле происходит, поэтому, когда я программирую на Arduino, я понимаю и «чистую» версию C++.
Заранее спасибо за вдумчивые ответы на мой вопрос.
@RedDogAlpha, 👍12
Обсуждение2 ответа
Лучший ответ:
См. мой ответ здесь: Классы и объекты: сколько и какие типы файлов мне действительно нужны для их использования? — в частности: Как все организовано в IDE.
Я знаю, что как минимум вы должны включить заголовочный файл Arduino
Да, вам нужно это сделать.
но помимо этого, что может вызвать ошибку компиляции при компиляции указанного файла .ino в .cpp с использованием, например, GCC-AVR.
Среда IDE создает для вас прототипы функций. Код в файле .ino может нуждаться в этом, а может и не нуждаться (вероятно, понадобится, если автор не достаточно дисциплинирован, чтобы кодировать обычным способом C++ и делать это самостоятельно).
Если «скетч» содержит другие файлы (например, другие файлы .ino, .c или .cpp), их необходимо включить в процесс компиляции, как я описал в своем ответе, упомянутом выше.
Также вам потребуется (скомпилировать и) связать любые библиотеки, используемые скетчем.
Вы не спрашивали о связывании вещей, но, естественно, различные файлы после компиляции необходимо связать вместе, а затем преобразовать в файлы .elf и .hex для целей загрузки. См. ниже.
Пример make-файла
На основе вывода IDE я сделал простой make-файл некоторое время назад:
#
# Simple Arduino Makefile
#
# Author: Nick Gammon
# Date: 18th March 2015
# where you installed the Arduino app
ARDUINO_DIR = C:/Documents and Settings/Nick/Desktop/arduino-1.0.6/
# various programs
CC = "$(ARDUINO_DIR)hardware/tools/avr/bin/avr-gcc"
CPP = "$(ARDUINO_DIR)hardware/tools/avr/bin/avr-g++"
AR = "$(ARDUINO_DIR)hardware/tools/avr/bin/avr-ar"
OBJ_COPY = "$(ARDUINO_DIR)hardware/tools/avr/bin/avr-objcopy"
MAIN_SKETCH = Blink.cpp
# compile flags for g++ and gcc
# may need to change these
F_CPU = 16000000
MCU = atmega328p
# compile flags
GENERAL_FLAGS = -c -g -Os -Wall -ffunction-sections -fdata-sections -mmcu=$(MCU) -DF_CPU=$(F_CPU)L -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=106
CPP_FLAGS = $(GENERAL_FLAGS) -fno-exceptions
CC_FLAGS = $(GENERAL_FLAGS)
# location of include files
INCLUDE_FILES = "-I$(ARDUINO_DIR)hardware/arduino/cores/arduino" "-I$(ARDUINO_DIR)hardware/arduino/variants/standard"
# library sources
LIBRARY_DIR = "$(ARDUINO_DIR)hardware/arduino/cores/arduino/"
build:
$(CPP) $(CPP_FLAGS) $(INCLUDE_FILES) $(MAIN_SKETCH) -o $(MAIN_SKETCH).o
$(CC) $(CC_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)avr-libc/malloc.c -o malloc.c.o
$(CC) $(CC_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)avr-libc/realloc.c -o realloc.c.o
$(CC) $(CC_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)WInterrupts.c -o WInterrupts.c.o
$(CC) $(CC_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)wiring.c -o wiring.c.o
$(CC) $(CC_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)wiring_analog.c -o wiring_analog.c.o
$(CC) $(CC_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)wiring_digital.c -o wiring_digital.c.o
$(CC) $(CC_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)wiring_pulse.c -o wiring_pulse.c.o
$(CC) $(CC_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)wiring_shift.c -o wiring_shift.c.o
$(CPP) $(CPP_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)CDC.cpp -o CDC.cpp.o
$(CPP) $(CPP_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)HardwareSerial.cpp -o HardwareSerial.cpp.o
$(CPP) $(CPP_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)HID.cpp -o HID.cpp.o
$(CPP) $(CPP_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)IPAddress.cpp -o IPAddress.cpp.o
$(CPP) $(CPP_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)main.cpp -o main.cpp.o
$(CPP) $(CPP_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)new.cpp -o new.cpp.o
$(CPP) $(CPP_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)Print.cpp -o Print.cpp.o
$(CPP) $(CPP_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)Stream.cpp -o Stream.cpp.o
$(CPP) $(CPP_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)Tone.cpp -o Tone.cpp.o
$(CPP) $(CPP_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)USBCore.cpp -o USBCore.cpp.o
$(CPP) $(CPP_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)WMath.cpp -o WMath.cpp.o
$(CPP) $(CPP_FLAGS) $(INCLUDE_FILES) $(LIBRARY_DIR)WString.cpp -o WString.cpp.o
rm core.a
$(AR) rcs core.a malloc.c.o
$(AR) rcs core.a realloc.c.o
$(AR) rcs core.a WInterrupts.c.o
$(AR) rcs core.a wiring.c.o
$(AR) rcs core.a wiring_analog.c.o
$(AR) rcs core.a wiring_digital.c.o
$(AR) rcs core.a wiring_pulse.c.o
$(AR) rcs core.a wiring_shift.c.o
$(AR) rcs core.a CDC.cpp.o
$(AR) rcs core.a HardwareSerial.cpp.o
$(AR) rcs core.a HID.cpp.o
$(AR) rcs core.a IPAddress.cpp.o
$(AR) rcs core.a main.cpp.o
$(AR) rcs core.a new.cpp.o
$(AR) rcs core.a Print.cpp.o
$(AR) rcs core.a Stream.cpp.o
$(AR) rcs core.a Tone.cpp.o
$(AR) rcs core.a USBCore.cpp.o
$(AR) rcs core.a WMath.cpp.o
$(AR) rcs core.a WString.cpp.o
$(CC) -Os -Wl,--gc-sections -mmcu=$(MCU) -o $(MAIN_SKETCH).elf $(MAIN_SKETCH).o core.a -lm
$(OBJ_COPY) -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 $(MAIN_SKETCH).elf $(MAIN_SKETCH).eep
$(OBJ_COPY) -O ihex -R .eeprom $(MAIN_SKETCH).elf $(MAIN_SKETCH).hex
В этом конкретном случае файл .ino скомпилировался без проблем после переименования его в Blink.cpp и добавления этой строки:
#include <Arduino.h>
Спасибо, Ник, за краткий ответ, я далеко не на вашем уровне и даже не подумал о make-файле. Итак, в основном синтаксис там тот же, это просто связывание объектов, верно? Спасибо, что поделились своим файлом make для анализа. Я уверен, что будут еще вопросы, связанные с этим! Спасибо еще раз!, @RedDogAlpha
Мой файл выше работал, когда я его опубликовал, но я считаю, что может потребоваться настройка для более поздних IDE (поскольку они перемещаются или переименовывают файлы библиотеки). Тем не менее, выполнение подробной компиляции показывает, что в настоящее время генерирует IDE, что должно помочь вам начать работу., @Nick Gammon
Я просто хотел бы добавить несколько моментов к ответу Ника Гэммона:
- Вам не нужно переименовывать файл .ino для его компиляции: если вы
явно указать компилятору, что это C++ (опция
-x c++
), он игнорировать необычное расширение файла и скомпилировать его как C++. - Вам не нужно добавлять
#include <Arduino.h>
в файл .ino: вы может сказать компилятору сделать это за вас (-include Arduino.h
).
Используя эти приемы, я могу скомпилировать Blink.ino без изменений, просто вызвав avr-g++ с соответствующими параметрами командной строки:
avr-g++ -mmcu=atmega328p -DARDUINO=105 -DF_CPU=16000000L \
-I/usr/share/arduino/hardware/arduino/cores/arduino \
-I/usr/share/arduino/hardware/arduino/variants/standard \
-Os -fno-exceptions -ffunction-sections -fdata-sections \
-Wl,--gc-sections -g -Wall -Wextra \
-x c++ -include Arduino.h \
/usr/share/arduino/examples/01.Basics/Blink/Blink.ino \
-x none /usr/local/lib/arduino/uno/libcore.a -lm \
-o Blink.elf
Несколько замечаний по приведенной выше командной строке:
/usr/local/lib/arduino/uno/libcore.a
— это место, где я сохранил скомпилированный Ядро Ардуино. Я ненавижу перекомпилировать один и тот же материал снова и снова.-x none
необходим, чтобы указать компилятору учитывать расширения файлов. опять таки. Без него libcore.a будет считаться файлом C++.
Я научился этим приемам у Судара Мутху. Arduino-Makefile. Это очень общий Makefile, который работает со многими платами и библиотеками. Единственное, чего не хватает по сравнению с Arduino IDE, — это прямой декларации.
Очень приятно, Эдгар! Мое решение в основном имитирует то, что делает IDE, ваше решает реальную проблему гораздо более аккуратно. Конечно, вам нужно было бы заранее создать файл libcore.a
. Я предполагаю, что строки в моем ответе, которые собирают core.a
, могут быть сделаны заранее, поэтому они не должны быть частью каждой сборки. Опыт показал, что более сложные скетчи (например, с использованием Wire или SPI) требуют добавления большего количества файлов в core.a
., @Nick Gammon
@NickGammon: Правильно, Makefile Muthu (и, я полагаю, Arduino IDE) имеет тенденцию помещать любые библиотеки, которые вы используете, в libcore.a. Мне не очень нравится такой подход, так как он делает якобы «основную библиотеку» зависимой от конкретной программы, которую вы компилируете. Для однофайловых библиотек, таких как Wire или SPI, я предпочитаю просто поместить файл библиотеки C++ в ту же команду компиляции, что и основная программа. Эта командная строка легко становится довольно длинной, поэтому я использую Makefile., @Edgar Bonet
Одна из вещей, которые мне нравятся в IDE, это то, что вам не нужно возиться. Во всяком случае, для простых проектов это «просто работает»., @Nick Gammon
Привет! Я пытаюсь понять, как сэкономить место в большом скетче Arduino, и хотел бы проверить сгенерированный объектный файл. Однако я не уверен, что понимаю все эти дела с предварительной компиляцией libcore.a
. Существует ли ELI5 для получения/проверки скомпилированного кода в форме, аналогичной той, которую сгенерирует Arduino IDE?, @MRule
@MRule: разберите финальный .elf с помощью avr-objdump
. Здесь есть несколько ответов, объясняющих, как это сделать., @Edgar Bonet
Великолепно! Это сработало; Похоже, сгенерированные инструкции в некоторых случаях довольно разочаровывают. Интересно, есть ли способ заставить компилятор использовать инструкции SBRC/SBRS (пропустить, если бит в регистре очистить/установить)., @MRule
- Как писать скетчи, совместимые с makefile?
- Преимущества глобальных переменных перед статическими членами класса?
- Какие накладные расходы и другие соображения существуют при использовании структуры по сравнению с классом?
- Что лучше использовать: #define или const int для констант?
- Функции со строковыми параметрами
- Как работать с аналоговыми контактами в цикле?
- Какие есть другие IDE для Arduino?
- Как использовать переменные и функции в нескольких файлах .ino
Вы спрашиваете конкретно о
gcc
на вашем рабочем столе или о компиляторе GCC для AVRavr-gcc
? там гораздо большая разница, чем между файлами .ino и .cpp., @BrettAM@BrettAM Набор инструментов GCC-AVR, такой как Arduino UNO, является целевой платой и использует чип Atmel AVR, как я уверен, вы знаете. Спасибо за призыв к двусмысленности в моем вопросе. И да, я знаю, что есть гораздо большая разница. Вот почему я задаю этот вопрос. Чтобы узнать, что это за различия!, @RedDogAlpha