Wird eine .ino Arduino-Skizze direkt auf GCC-AVR kompiliert?

10

Okay, wir haben alle diese Fragen im ganzen Web gesehen, wie Arduino vs C ++ oder andere ähnliche Fragen. Und die überwiegende Mehrheit der Antworten berührt nicht einmal Kompilierungsunterschiede außer durch abstrahierte Informationen.

Meine Frage zielt darauf ab, die tatsächlichen Unterschiede (nicht die Einstellungen) zu lösen, wie eine in eine CPP-Datei oder eine andere ähnliche Dateierweiterung für c ++ umbenannte .ino-Datei mit GCC-AVR kompiliert wird. Ich weiß, dass Sie mindestens die Arduino-Header-Datei einschließen müssen, aber darüber hinaus, was würde einen Kompilierungsfehler verursachen, wenn die .ino- in die .cpp-Datei kompiliert wird, beispielsweise mit GCC-AVR. Lassen Sie uns der Einfachheit halber das klassische Blinkbeispiel verwenden, um die Unterschiede zu erklären. Oder wenn Sie ein besseres Code-Snippet verwenden möchten, fügen Sie das Snippet auf jeden Fall in Ihre Antwort ein und erläutern Sie die Unterschiede gründlich.

Bitte keine Meinung darüber, welche Methode oder welches Werkzeug besser zu verwenden ist.

Zu Ihrer Information. Ich verwende Platformio für die Entwicklung und stelle fest, dass während der Kompilierung hinter den Kulissen ein Konvertierungsprozess stattfindet. Ich versuche zu verstehen, was dort tatsächlich passiert. Wenn ich also in Arduino codiere, verstehe ich auch die "reine" C ++ - Version.

Vielen Dank für Ihre nachdenklichen Antworten auf meine Frage im Voraus.

RedDogAlpha
quelle
Fragen Sie speziell nach gccauf Ihrem Desktop oder dem GCC for AVR-Compiler avr-gcc? Es gibt dort einen viel größeren Unterschied als zwischen einer .inound einer .cppDatei.
BrettAM
@BrettAM Das GCC-AVR-Toolkit als Arduino UNO ist die Zielplatine und verwendet, wie Sie sicher wissen, einen Atmel AVR-Chip. Vielen Dank für den Hinweis auf Mehrdeutigkeit in meiner Frage. Und ja, ich weiß, dass es einen viel größeren Unterschied gibt. Deshalb stelle ich diese Frage. Um zu erfahren, was diese Unterschiede sind!
RedDogAlpha

Antworten:

14

Meine Antwort finden Sie hier: Klassen und Objekte: Wie viele und welche Dateitypen benötige ich tatsächlich, um sie zu verwenden? - speziell: Wie die IDE Dinge organisiert .

Ich weiß, dass Sie mindestens die Arduino-Header-Datei einschließen müssen

Ja, das müssten Sie tun.

Aber darüber hinaus, was würde einen Kompilierungsfehler verursachen, wenn die .ino- in .cpp-Datei kompiliert wird, beispielsweise mit GCC-AVR.

Die IDE generiert Funktionsprototypen für Sie. Code in einer .ino-Datei benötigt dies möglicherweise nicht oder nicht (dies wird wahrscheinlich der Fall sein, es sei denn, der Autor ist diszipliniert genug, um auf die übliche C ++ - Weise zu codieren und diese selbst auszuführen).


Wenn die "Skizze" andere Dateien enthält (z. B. andere .ino-, .c- oder .cpp-Dateien), müssen diese in den Kompilierungsprozess einbezogen werden, wie ich in meiner oben erwähnten Antwort beschrieben habe.

Außerdem müssten Sie alle von der Skizze verwendeten Bibliotheken (kompilieren und) verknüpfen.


Sie haben nicht nach der Verknüpfungsseite der Dinge gefragt, aber natürlich müssen die verschiedenen Dateien, wie sie kompiliert wurden, miteinander verknüpft und dann zum Hochladen in eine .elf- und .hex-Datei umgewandelt werden. Siehe unten.


Beispiel Makefile

Basierend auf der IDE-Ausgabe habe ich vor einiger Zeit ein einfaches Makefile erstellt :

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

In diesem speziellen Fall wurde die .ino-Datei ohne Probleme kompiliert, nachdem sie in Blink.cpp umbenannt und diese Zeile hinzugefügt wurde:

#include <Arduino.h>
Nick Gammon
quelle
Vielen Dank, Nick, für deine prägnante Antwort. Ich bin nicht annähernd so gut wie du und habe nicht einmal an eine Make-Datei gedacht. Im Grunde ist die Syntax also dieselbe, es geht nur darum, Objekte zu verknüpfen, oder? Vielen Dank, dass Sie Ihre Make-Datei freigegeben haben, damit ich sie sezieren kann. Ich bin mir sicher, dass sich daraus weitere Fragen ergeben werden! Danke noch einmal!
RedDogAlpha
Meine obige Datei hat funktioniert, als ich sie veröffentlicht habe, aber ich glaube, dass für die späteren IDEs möglicherweise Anpassungen erforderlich sind (weil sie sich verschieben oder Bibliotheksdateien umbenennen). Eine ausführliche Kompilierung zeigt jedoch, was die IDE gerade generiert, und sollte Ihnen den Einstieg erleichtern.
Nick Gammon
10

Ich möchte nur ein paar Punkte zu Nick Gammons Antwort hinzufügen:

  • Sie müssen eine .ino-Datei nicht umbenennen, um sie zu kompilieren: Wenn Sie dem Compiler explizit mitteilen, dass es sich um C ++ handelt (Option -x c++), wird die ungewöhnliche Dateierweiterung ignoriert und als C ++ kompiliert.
  • Sie müssen #include <Arduino.h>die .ino-Datei nicht hinzufügen : Sie können den Compiler anweisen, dies für Sie zu tun ( -include Arduino.h).

Mit diesen Tricks kann ich Blink.ino ohne Änderung kompilieren, indem ich avr-g ++ mit den richtigen Befehlszeilenoptionen aufrufe :

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

Einige Hinweise zur obigen Befehlszeile:

  • /usr/local/lib/arduino/uno/libcore.aHier habe ich den kompilierten Arduino-Kern gespeichert. Ich hasse es, immer wieder das gleiche Zeug neu zu kompilieren.
  • -x nonewird benötigt, um den Compiler anzuweisen, die Dateierweiterungen erneut zu beachten. Ohne sie würde es annehmen, dass libcore.a eine C ++ - Datei ist.

Ich habe diese Tricks aus Sudar Muthus Arduino-Makefile gelernt . Dies ist ein sehr allgemeines Makefile, das mit vielen Boards und Bibliotheken funktioniert. Das einzige, was in Bezug auf die Arduino-IDE fehlt, sind die Vorwärtsdeklarationen.

Edgar Bonet
quelle
Sehr schön, Edgar! Meine Lösung ahmt im Grunde das nach, was die IDE tut. Ihre Lösung löst das eigentliche Problem auf eine viel übersichtlichere Art und Weise. Natürlich müssten Sie die libcore.aDatei im Voraus erstellen. Ich nehme an, dass die Zeilen in meiner Antwort, welcher Build erstellt wurde core.a, im Voraus erstellt werden könnten, sodass sie nicht Teil jedes Builds sein müssen. Die Erfahrung hat gezeigt, dass komplexere Skizzen (z. B. mit Wire oder SPI) mehr Dateien hinzufügen müssen core.a.
Nick Gammon
@NickGammon: Richtig, Muthus Makefile (und, ich nehme an, die Arduino IDE) tendiert dazu, alle Bibliotheken, die Sie verwenden, in libcore.a zu platzieren. Ich mag diesen Ansatz nicht wirklich, da er die vermeintliche „Kernbibliothek“ von dem jeweiligen Programm abhängig macht, das Sie kompilieren. Bei Einzeldateibibliotheken wie Wire oder SPI bevorzuge ich es, die C ++ - Datei der Bibliothek in denselben Kompilierungsbefehl wie das Hauptprogramm zu stellen. Diese Befehlszeile wird leicht ziemlich lang, deshalb benutze ich ein Makefile.
Edgar Bonet
1
Eines der Dinge, die ich an der IDE mag, ist, dass Sie nicht herumspielen müssen. Für einfache Projekte funktioniert es sowieso "einfach".
Nick Gammon