Un .ino Arduino Sketch se compilera-t-il directement sur GCC-AVR?

10

D'accord, nous avons tous vu ces questions partout sur le Web, comme Arduino vs C ++, ou d'autres questions similaires. Et une grande majorité des réponses ne touchent même pas aux différences de compilation autrement que par le biais d'informations abstraites.

Ma question vise à résoudre les différences réelles (et non les préférences) dans la façon dont un fichier .ino renommé en fichier .cpp ou autre extension de fichier similaire pour c ++ se compilerait à l'aide de GCC-AVR. Je sais qu'au minimum, vous devez inclure le fichier d'en-tête Arduino, mais au-delà de cela, ce qui provoquerait une erreur de compilation si vous compiliez ledit fichier .ino en fichier .cpp en utilisant, par exemple, GCC-AVR. Par souci de simplicité, utilisons l'exemple de clignotement classique pour expliquer les différences. Ou si vous avez un meilleur extrait de code à utiliser, veuillez par tous les moyens, inclure l'extrait de code dans votre réponse et expliquer les différences de manière approfondie.

S'il vous plaît, aucune opinion sur la meilleure façon ou un meilleur outil à utiliser.

Pour info. J'utilise Platformio pour le développement et je remarque un processus de conversion se déroulant dans les coulisses lors de la compilation. Je cherche à comprendre ce qui se passe réellement là-bas, donc quand je code en Arduino, je comprends aussi la version "pure" C ++.

Merci pour vos réponses réfléchies à ma question à l'avance.

RedDogAlpha
la source
gccDemandez -vous spécifiquement sur votre bureau ou le compilateur GCC pour AVR avr-gcc? il y a là une différence beaucoup plus grande qu'entre un .inoet un .cppfichier.
BrettAM
@BrettAM La boîte à outils GCC-AVR comme Arduino UNO est la carte cible et utilise une puce Atmel AVR, comme je suis sûr que vous le savez. Merci pour l'appel à l'ambiguïté dans ma question. Et oui, je sais qu'il y a une différence beaucoup plus grande. C'est pourquoi je pose cette question. Pour savoir quelles sont ces différences!
RedDogAlpha

Réponses:

14

Voir ma réponse ici: Classes et objets: combien et de quels types de fichiers ai-je réellement besoin pour les utiliser? - plus précisément: comment l'IDE organise les choses .

Je sais qu'au minimum, vous devez inclure le fichier d'en-tête Arduino

Oui, vous auriez besoin de le faire.

mais au-delà de cela, ce qui provoquerait une erreur de compilation si la compilation dudit fichier .ino en fichier .cpp en utilisant, par exemple, GCC-AVR.

L'IDE génère pour vous des prototypes de fonctions. Le code dans un fichier .ino peut ou non en avoir besoin (il le sera probablement à moins que l'auteur ne soit suffisamment discipliné pour coder de la manière habituelle C ++ et le faire lui-même).


Si l'esquisse contient d'autres fichiers (par exemple, d'autres fichiers .ino, .c ou .cpp), ceux-ci devront être incorporés dans le processus de compilation comme je le décris dans ma réponse mentionnée ci-dessus.

Vous devrez également (compiler et) lier dans toutes les bibliothèques utilisées par l'esquisse.


Vous n'avez pas posé de questions sur le côté de la liaison, mais naturellement les différents fichiers, tels qu'ils sont compilés, doivent être liés ensemble, puis transformés en fichiers .elf et .hex à des fins de téléchargement. Voir ci-dessous.


Exemple de makefile

Sur la base de la sortie IDE, j'ai fait un simple makefile il y a quelque temps :

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

Dans ce cas particulier, le fichier .ino compilé sans aucun problème après l'avoir renommé en Blink.cpp et ajouté cette ligne:

#include <Arduino.h>
Nick Gammon
la source
Merci Nick pour ta réponse concise, je suis loin du niveau que tu es, et je n'ai même pas pensé à un fichier make. Donc, fondamentalement, la syntaxe est la même, il s'agit simplement de lier des objets, non? Merci de partager votre fichier make pour que je dissèque. Je suis sûr que cela posera plus de questions! Merci encore!
RedDogAlpha
Mon fichier ci-dessus a fonctionné lorsque je l'ai publié, mais je pense qu'il peut être nécessaire de le modifier pour les IDE ultérieurs (car ils se déplacent ou renomment les fichiers de bibliothèque). Pourtant, faire une compilation détaillée montre ce que l'IDE génère actuellement, ce qui devrait vous aider à démarrer.
Nick Gammon
10

Je voudrais juste ajouter quelques points à la réponse de Nick Gammon:

  • Vous n'avez pas besoin de renommer un fichier .ino pour le compiler: si vous dites explicitement au compilateur qu'il s'agit de C ++ (option -x c++), il ignorera l'extension de fichier inhabituelle et le compilera en C ++.
  • Vous n'avez pas besoin d'ajouter #include <Arduino.h>le fichier .ino: vous pouvez dire au compilateur de le faire pour vous ( -include Arduino.h).

En utilisant ces astuces, je peux compiler Blink.ino sans aucune modification , simplement en appelant avr-g ++ avec les options de ligne de commande appropriées:

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

Quelques notes sur la ligne de commande ci-dessus:

  • /usr/local/lib/arduino/uno/libcore.aest l'endroit où j'ai enregistré le noyau Arduino compilé. Je déteste recompiler encore et encore les mêmes trucs.
  • -x noneest nécessaire pour indiquer au compilateur de garder à l'esprit les extensions de fichier. Sans cela, il supposerait que libcore.a est un fichier C ++.

J'ai appris ces astuces avec Arduino-Makefile de Sudar Muthu . Il s'agit d'un Makefile très général qui fonctionne avec de nombreuses cartes et bibliothèques. La seule chose qui manque par rapport à l'IDE Arduino est les déclarations avancées.

Edgar Bonet
la source
Très bien, Edgar! Ma solution imite essentiellement ce que fait l'IDE, le vôtre résout le problème réel de manière beaucoup plus nette. Bien sûr, vous devrez faire le libcore.adossier à l'avance. Je suppose que les lignes de ma réponse qui core.apeuvent être construites à l'avance, donc elles n'ont pas à faire partie de chaque build. L'expérience a montré que des esquisses plus complexes (par exemple, en utilisant Wire ou SPI) ont besoin d'ajouter plus de fichiers core.a.
Nick Gammon
@ NickGammon: C'est vrai, le Makefile de Muthu (et, je suppose, l'IDE Arduino) a tendance à mettre toutes les bibliothèques que vous utilisez dans libcore.a. Je n'aime pas vraiment cette approche, car elle rend la soi-disant «bibliothèque de base» dépendante du programme particulier que vous compilez. Pour les bibliothèques à fichier unique, comme Wire ou SPI, je préfère simplement mettre le fichier C ++ de la bibliothèque dans la même commande de compilation que le programme principal. Cette ligne de commande devient facilement assez longue, donc j'utilise un Makefile.
Edgar Bonet
1
L'une des choses que j'aime à propos de l'IDE, c'est que vous n'avez pas besoin de bouger. Pour les projets simples de toute façon, cela "fonctionne tout simplement".
Nick Gammon