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

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

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

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

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

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

, 👍12

Обсуждение

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

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


2 ответа


Лучший ответ:

14

См. мой ответ здесь: Классы и объекты: сколько и какие типы файлов мне действительно нужны для их использования? — в частности: Как все организовано в 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


12

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

  • Вам не нужно переименовывать файл .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