Czy .ino Arduino Sketch będzie się kompilować bezpośrednio na GCC-AVR?

10

Okej, wszyscy widzieliśmy te pytania w Internecie, takie jak Arduino kontra C ++ lub inne podobne pytania. Zdecydowana większość odpowiedzi nawet nie dotyka różnic w kompilacji inaczej niż poprzez abstrakcyjne informacje.

Moje pytanie ma na celu rozwiązanie faktycznych różnic (a nie preferencji) w sposobie kompilacji pliku .ino na plik .cpp lub inne podobne rozszerzenie dla c ++ przy użyciu GCC-AVR. Wiem, że co najmniej musisz dołączyć plik nagłówkowy Arduino, ale poza tym, co spowodowałoby błąd kompilacji, gdyby kompilacja wspomnianego pliku .ino do pliku .cpp przy użyciu, powiedzmy, na przykład GCC-AVR. Dla uproszczenia, skorzystajmy z klasycznego przykładu mrugnięcia, aby wyjaśnić różnice. Lub jeśli masz lepszy fragment kodu do użycia, należy w jakikolwiek sposób dołączyć ten fragment do swojej odpowiedzi i dokładnie wyjaśnić różnice.

Proszę nie opiniować, który jest lepszym sposobem lub narzędziem do użycia.

Do Twojej wiadomości Używam Platformio do programowania i zauważam, że proces kompilacji zachodzi za kulisami podczas kompilacji. Próbuję zrozumieć, co się tam właściwie dzieje, więc kiedy piszę w Arduino, rozumiem również „czystą” wersję C ++.

Dziękuję za z góry przemyślane odpowiedzi na moje pytanie.

RedDogAlpha
źródło
Czy pytasz konkretnie o gcckomputer, czy kompilator GCC for AVR avr-gcc? istnieje o wiele większa różnica niż pomiędzy plikiem .inoa .cppplikiem.
BrettAM,
@BrettAM Zestaw narzędzi GCC-AVR jako Arduino UNO jest tablicą docelową i wykorzystuje układ Atmel AVR, jak jestem pewien. Dziękuję za wezwanie do dwuznaczności w moim pytaniu. I tak, wiem, że istnieje znacznie większa różnica. Dlatego zadaję to pytanie. Aby dowiedzieć się, jakie są te różnice!
RedDogAlpha

Odpowiedzi:

14

Zobacz moją odpowiedź tutaj: Klasy i obiekty: ile i jakich typów plików faktycznie muszę ich używać? - w szczególności: jak IDE organizuje rzeczy .

Wiem, że przynajmniej musisz dołączyć plik nagłówka Arduino

Tak, musisz to zrobić.

ale poza tym, co spowodowałoby błąd kompilacji, jeśli kompilacja powiedziałaby .ino do pliku .cpp przy użyciu, powiedzmy, na przykład GCC-AVR.

IDE generuje dla Ciebie prototypy funkcji. Kod w pliku .ino może, ale nie musi tego potrzebować (prawdopodobnie będzie to konieczne, chyba że autor jest wystarczająco zdyscyplinowany, aby kodować w zwykły sposób w C ++ i robić je sami).


Jeśli „szkic” zawiera inne pliki (np. Inne pliki .ino, .c lub .cpp), należy je włączyć do procesu kompilacji, jak to opisałem w mojej odpowiedzi wspomnianej powyżej.

Musisz także (skompilować i) link w dowolnych bibliotekach używanych w szkicu.


Nie pytałeś o linkowanie rzeczy, ale oczywiście różne skompilowane pliki muszą być połączone razem, a następnie przekształcone w plik .elf i .hex do celów przesyłania. Patrz poniżej.


Przykładowy plik makefile

Na podstawie danych wyjściowych IDE zrobiłem jakiś prosty plik 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 

W tym konkretnym przypadku plik .ino skompilowano bez żadnych problemów po zmianie nazwy pliku na Blink.cpp i dodaniu tej linii:

#include <Arduino.h>
Nick Gammon
źródło
Dziękuję Nick za zwięzłą odpowiedź. Nie jestem w pobliżu poziomu, na którym jesteś, i nawet nie pomyślałem o pliku make. Więc w zasadzie składnia jest taka sama, chodzi tylko o łączenie obiektów, prawda? Dziękujemy za udostępnienie mi pliku make do analizy. Jestem pewien, że z tego wyniknie więcej pytań! Dzięki jeszcze raz!
RedDogAlpha
Mój plik powyżej działał, kiedy go opublikowałem, ale uważam, że może być konieczne dostosowanie późniejszych IDE (ponieważ przenoszą się lub zmieniają nazwy plików bibliotek). Mimo to, pełna kompilacja pokazuje, co IDE aktualnie generuje, co powinno zacząć.
Nick Gammon
10

Chciałbym tylko dodać kilka punktów do odpowiedzi Nicka Gammona:

  • Nie trzeba zmieniać nazwy pliku .ino, aby go skompilować: jeśli wyraźnie powiesz kompilatorowi, że jest to C ++ (opcja -x c++), zignoruje nietypowe rozszerzenie pliku i skompiluje go jako C ++.
  • Nie musisz dodawać #include <Arduino.h>pliku .ino: możesz powiedzieć kompilatorowi, aby zrobił to za Ciebie ( -include Arduino.h).

Używając tych sztuczek, mogę skompilować Blink.ino bez modyfikacji , po prostu wywołując avr-g ++ z odpowiednimi opcjami wiersza poleceń:

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

Kilka uwag na temat powyższego wiersza poleceń:

  • /usr/local/lib/arduino/uno/libcore.ato gdzie zapisałem skompilowany rdzeń Arduino. Nienawidzę kompilowania w kółko tych samych rzeczy.
  • -x nonejest potrzebne, aby poinformować kompilator o tym, żeby pamiętał o rozszerzeniach plików. Bez tego zakładałby, że libcore.a jest plikiem C ++.

Nauczyłem się tych sztuczek z Arduino-Makefile Sudar Muthu . Jest to bardzo ogólny plik Makefile, który działa z wieloma tablicami i bibliotekami. Jedyne, czego brakuje w stosunku do Arduino IDE, to deklaracje forward.

Edgar Bonet
źródło
Bardzo miło, Edgarze! Moje rozwiązanie w zasadzie naśladuje to, co robi IDE, twoje rozwiązuje rzeczywisty problem w znacznie bardziej zgrabny sposób. Oczywiście musisz wcześniej zrobić libcore.aplik. Przypuszczam, że wiersze w mojej odpowiedzi, która kompilacja core.amoże być wykonana z wyprzedzeniem, więc nie muszą być częścią każdej kompilacji. Doświadczenie pokazuje, że bardziej złożone szkice (np. Przy użyciu Wire lub SPI) wymagają dodania większej liczby plików core.a.
Nick Gammon
@NickGammon: Zgadza się, Makefile Muthu (i, jak zakładam, Arduino IDE) ma tendencję do umieszczania dowolnych bibliotek w libcore.a. Nie podoba mi się to podejście, ponieważ uzależnia rzekomo „bibliotekę podstawową” od konkretnego kompilowanego programu. W przypadku bibliotek z jednym plikiem, takich jak Wire lub SPI, wolę po prostu umieścić plik C ++ biblioteki w tym samym poleceniu kompilacji, co program główny. Ta linia poleceń staje się dość długa, więc używam Makefile.
Edgar Bonet
1
Jedną z rzeczy, które lubię w IDE, jest to, że nie musisz się pieprzyć. Zresztą w przypadku prostych projektów „po prostu działa”.
Nick Gammon