Будет ли .ino Arduino Sketch компилироваться непосредственно на GCC-AVR?

10

Хорошо, мы все видели эти вопросы по всей сети, такие как Arduino vs C ++ или другие подобные вопросы. И подавляющее большинство ответов даже не касаются различий в компиляции, кроме как посредством абстрактной информации.

Мой вопрос направлен на решение реальных различий (не предпочтений) в том, как файл .ino, переименованный в файл .cpp или другое подобное расширение файла для c ++, будет компилироваться с использованием GCC-AVR. Я знаю, что как минимум вы должны включить заголовочный файл Arduino, но помимо этого, что может вызвать ошибку компиляции, если компилировать указанный файл .ino в .cpp с использованием, например, GCC-AVR. Для простоты, давайте использовать классический пример мерцания, чтобы объяснить, в чем различия. Или, если у вас есть лучший фрагмент кода для использования, обязательно включите этот фрагмент в свой ответ и подробно объясните различия.

Пожалуйста, никаких мнений относительно того, какой способ лучше использовать.

FYI. Я использую Platformio для разработки и замечаю процесс преобразования, происходящий за кулисами во время компиляции. Я пытаюсь понять, что на самом деле там происходит, поэтому, когда я пишу код на Arduino, я понимаю и «чистую» версию C ++.

Спасибо за ваши вдумчивые ответы на мой вопрос заранее.

RedDogAlpha
источник
Вы спрашиваете конкретно о gccвашем рабочем столе или GCC для AVR компилятора avr-gcc? там гораздо большая разница, чем между файлом .inoи .cppфайлом.
BrettAM
@BrettAM GCC-AVR, как Arduino UNO, является целевой платой и использует чип Atmel AVR, как я уверен, вы знаете. Спасибо за призыв к двусмысленности в моем вопросе. И да, я знаю, что есть гораздо большая разница. Вот почему я задаю этот вопрос. Чтобы узнать, что это за различия!
RedDogAlpha

Ответы:

14

Смотрите мой ответ здесь: Классы и объекты: сколько и какие типы файлов мне действительно нужны, чтобы их использовать? - в частности: как IDE организует вещи .

Я знаю, что как минимум вы должны включить заголовочный файл Arduino

Да, вам нужно это сделать.

но помимо этого, что могло бы вызвать ошибку компиляции, если компилировать указанный .ino в файл .cpp с использованием, например, GCC-AVR.

IDE генерирует прототипы функций для вас. Код в файле .ino может нуждаться или не нуждаться в этом (вероятно, он понадобится, если автор недостаточно дисциплинирован, чтобы кодировать обычным способом C ++ и делать это самостоятельно).


Если «эскиз» содержит другие файлы (например, другие файлы .ino, .c или .cpp), то их необходимо будет включить в процесс компиляции, как я опишу в своем ответе, упомянутом выше.

Также вам нужно будет (скомпилировать и) связать любые библиотеки, используемые в эскизе.


Вы не спрашивали о связующей стороне вещей, но, естественно, различные файлы, скомпилированные, необходимо связать вместе, а затем превратить в файлы .elf и .hex для загрузки. См. ниже.


Пример makefile

На основе результатов IDE я сделал простой Makefile некоторое время назад :

#
# 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, с чего следует начать.
Ник Гэммон
10

Я просто хотел бы добавить несколько моментов к ответу Ника Гэммона:

  • Вам не нужно переименовывать файл .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где я сохранил скомпилированное ядро ​​Arduino. Я ненавижу перекомпилировать снова и снова одни и те же вещи.
  • -x noneнеобходимо, чтобы компилятор снова обращал внимание на расширения файлов. Без этого он предположил бы, что libcore.a - это файл C ++.

Я научился этим трюкам из Arduino-Makefile Судар Муту . Это очень общий Makefile, который работает со многими досками и библиотеками. Единственное, чего не хватает в Arduino IDE, это предварительные декларации.

Эдгар Бонет
источник
Очень мило, Эдгар! Мое решение в основном имитирует то, что делает IDE, а ваше решение решает реальную проблему намного проще. Конечно, вам придется сделать libcore.aфайл заранее. Я предполагаю, что строки в моем ответе о том, какую сборку core.aможно сделать заранее, чтобы они не были частью каждой сборки. Опыт показывает, что более сложные эскизы (например, с использованием Wire или SPI) требуют добавления большего количества файлов core.a.
Ник Гэммон
@NickGammon: Правильно, Makefile Muthu (и, я полагаю, IDE Arduino) имеет тенденцию помещать любые библиотеки, которые вы используете, в libcore.a. Мне не очень нравится этот подход, так как он делает предположительно «базовую библиотеку» зависимой от конкретной программы, которую вы компилируете. Для однофайловых библиотек, таких как Wire или SPI, я предпочитаю просто поместить библиотечный файл C ++ в ту же команду компиляции, что и основная программа. Эта командная строка легко становится довольно длинной, поэтому я использую Makefile.
Эдгар Бонет
1
Одна из вещей, которые мне нравятся в IDE, заключается в том, что вам не нужно возиться. В любом случае, для простых проектов это «просто работает».
Ник Гэммон