¿Se compilará un .ino Arduino Sketch directamente en GCC-AVR?

10

De acuerdo, todos hemos visto esas preguntas en toda la web, como Arduino vs C ++ u otras preguntas similares. Y una gran mayoría de las respuestas ni siquiera tocan las diferencias de compilación que no sean a través de información resumida.

Mi pregunta tiene como objetivo resolver las diferencias reales (no las preferencias) en cómo un archivo .ino renombrado a un archivo .cpp u otra extensión de archivo similar para c ++ se compilaría usando GCC-AVR. Sé que, como mínimo, debe incluir el archivo de encabezado Arduino, pero más allá de eso, lo que causaría un error de compilación si compila dicho archivo .ino a .cpp usando, por ejemplo, GCC-AVR. Por simplicidad, usemos el clásico ejemplo de parpadeo para explicar cuáles son las diferencias. O si tiene un mejor fragmento de código para usar, por favor, incluya el fragmento en su respuesta y explique las diferencias a fondo.

Por favor, no hay opiniones sobre cuál es una mejor manera o herramienta para usar.

FYI. Utilizo Platformio para el desarrollo y noto un proceso de conversión que ocurre detrás de escena durante la compilación. Estoy tratando de entender lo que realmente está sucediendo allí, así que cuando codifico en Arduino, también entiendo la versión "pura" de C ++.

Gracias de antemano por sus reflexivas respuestas a mi pregunta.

RedDogAlpha
fuente
¿Está preguntando específicamente sobre gccsu escritorio o el compilador GCC para AVR avr-gcc? Hay una diferencia mucho mayor que la que existe entre a .inoy un .cpparchivo.
BrettAM
@BrettAM El kit de herramientas GCC-AVR como Arduino UNO es la placa de destino y utiliza un chip Atmel AVR, como estoy seguro de que sabe. Gracias por llamar a la ambigüedad en mi pregunta. Y sí, sé que hay una diferencia mucho mayor. Por eso estoy haciendo esta pregunta. ¡Aprender cuáles son esas diferencias!
RedDogAlpha

Respuestas:

14

Vea mi respuesta aquí: Clases y objetos: ¿cuántos y qué tipos de archivos necesito realmente para usarlos? - específicamente: cómo el IDE organiza las cosas .

Sé que, como mínimo, debe incluir el archivo de encabezado Arduino

Sí, necesitarías hacer eso.

pero más allá de eso, lo que causaría un error de compilación si compila dicho archivo .ino a .cpp usando, por ejemplo, GCC-AVR.

El IDE genera prototipos de funciones para usted. El código en un archivo .ino puede o no necesitar esto (probablemente lo hará a menos que el autor sea lo suficientemente disciplinado como para codificar en la forma habitual de C ++ y hacerlo ellos mismos).


Si el "boceto" contiene otros archivos (por ejemplo, otros archivos .ino, .c o .cpp), entonces estos deberán incorporarse en el proceso de compilación como describo en mi respuesta mencionada anteriormente.

También necesitaría (compilar y) vincular en cualquier biblioteca utilizada por el boceto.


No ha preguntado sobre el lado de vinculación de las cosas, pero, naturalmente, los diversos archivos, tal como se compilan, deben vincularse entre sí y luego convertirse en un archivo .elf y .hex para cargarlos. Vea abajo.


Ejemplo de archivo MAKE

Basado en la salida IDE, hice un simple archivo MAKE hace un tiempo :

#
# 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 

En ese caso particular, el archivo .ino se compiló sin ningún problema después de renombrarlo a Blink.cpp y agregar esta línea:

#include <Arduino.h>
Nick Gammon
fuente
Gracias Nick por tu respuesta concisa, no estoy ni cerca del nivel que estás, y ni siquiera pensé en un archivo make. Básicamente, la sintaxis es la misma, solo se trata de vincular objetos, ¿verdad? Gracias por compartir su archivo make para que lo diseccione. ¡Estoy seguro de que surgirán más preguntas! ¡Gracias de nuevo!
RedDogAlpha
Mi archivo anterior funcionó cuando lo publiqué, pero creo que puede necesitar ajustes para los IDE posteriores (porque se mueven o cambian el nombre de los archivos de la biblioteca). Aún así, hacer una compilación detallada muestra lo que el IDE está generando actualmente, lo que debería ayudarlo a comenzar.
Nick Gammon
10

Solo me gustaría agregar algunos puntos a la respuesta de Nick Gammon:

  • No necesita cambiar el nombre de un archivo .ino para compilarlo: si le dice explícitamente al compilador que es C ++ (opción -x c++), ignorará la extensión de archivo inusual y lo compilará como C ++.
  • No necesita agregar #include <Arduino.h>el archivo .ino: puede decirle al compilador que lo haga por usted ( -include Arduino.h).

Usando esos trucos, puedo compilar Blink.ino sin modificaciones , simplemente invocando avr-g ++ con las opciones de línea de comandos adecuadas:

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

Algunas notas sobre la línea de comandos anterior:

  • /usr/local/lib/arduino/uno/libcore.aes donde guardé el núcleo Arduino compilado. Odio recompilar una y otra vez las mismas cosas.
  • -x nonees necesario para decirle al compilador que tenga en cuenta las extensiones de archivo nuevamente. Sin él, supondría que libcore.a es un archivo C ++.

Aprendí esos trucos del Arduino-Makefile de Sudar Muthu . Este es un Makefile muy general que funciona con muchos tableros y bibliotecas. Lo único que falta en relación con el IDE de Arduino son las declaraciones directas.

Edgar Bonet
fuente
Muy bien, Edgar! Mi solución básicamente imita lo que hace el IDE, la suya resuelve el problema real de una manera mucho más ordenada. Por supuesto, tendría que hacer el libcore.aarchivo por adelantado. Supongo que las líneas en mi respuesta sobre qué compilación core.ase pueden hacer de antemano, por lo que no tienen que ser parte de cada compilación. La experiencia ha demostrado que los bocetos más complejos (por ejemplo, usando Wire o SPI) necesitan más archivos para agregarse core.a.
Nick Gammon
@NickGammon: Así es, el Makefile de Muthu (y, supongo, el IDE de Arduino) tiende a poner cualquier biblioteca que uses en libcore.a. Realmente no me gusta este enfoque, ya que hace que la supuestamente "biblioteca principal" dependa del programa particular que compiles. Para las bibliotecas de un solo archivo, como Wire o SPI, prefiero simplemente poner el archivo C ++ de la biblioteca en el mismo comando de compilación que el programa principal. Esa línea de comando se hace bastante larga, así que uso un Makefile.
Edgar Bonet
1
Una de las cosas que me gustan del IDE es que no tienes que perder el tiempo. Para proyectos simples de todos modos, "simplemente funciona".
Nick Gammon