Apakah Sketsa .ino Arduino dapat dikompilasi langsung di GCC-AVR?

10

Oke, kita semua telah melihat pertanyaan-pertanyaan itu di seluruh web seperti Arduino vs C ++, atau pertanyaan serupa lainnya. Dan sebagian besar jawaban bahkan tidak menyentuh perbedaan kompilasi selain melalui informasi abstrak.

Pertanyaan saya bertujuan untuk menyelesaikan perbedaan yang sebenarnya (bukan preferensi) dalam bagaimana file .ino diubah namanya menjadi file .cpp atau ekstensi file serupa lainnya untuk c ++ akan dikompilasi menggunakan GCC-AVR. Saya tahu bahwa minimal Anda harus memasukkan file header Arduino, tetapi lebih dari itu, apa yang akan menyebabkan kesalahan kompilasi jika kompilasi kata .ino ke file .cpp menggunakan, katakanlah, misalnya GCC-AVR. Demi kesederhanaan, mari gunakan contoh blink klasik untuk menjelaskan apa perbedaannya. Atau jika Anda memiliki snipet kode yang lebih baik untuk digunakan, harap dengan segala cara, sertakan snippet dalam jawaban Anda dan jelaskan perbedaannya secara menyeluruh.

Harap tidak ada pendapat yang merupakan cara atau alat yang lebih baik untuk digunakan.

FYI. Saya menggunakan Platformio untuk pengembangan dan saya perhatikan proses konversi terjadi di belakang layar selama kompilasi. Saya mencoba memahami apa yang sebenarnya terjadi di sana, jadi ketika saya kode di Arduino, saya memahami versi C ++ yang "murni" juga.

Terima kasih atas jawaban Anda yang bijaksana atas pertanyaan saya sebelumnya.

RedDogAlpha
sumber
Apakah Anda bertanya secara spesifik tentang gccdi desktop Anda, atau GCC untuk kompiler AVR avr-gcc? ada perbedaan yang jauh lebih besar daripada ada di antara file .inodan a .cpp.
BrettAM
@ BrettAM Toolkit GCC-AVR sebagai Arduino UNO adalah papan target dan menggunakan chip Atmel AVR, seperti yang saya yakin Anda tahu. Terima kasih atas panggilan untuk ambiguitas dalam pertanyaan saya. Dan ya saya tahu bahwa ada perbedaan yang jauh lebih besar. Itu sebabnya saya menanyakan pertanyaan ini. Untuk mempelajari perbedaan itu!
RedDogAlpha

Jawaban:

14

Lihat jawaban saya di sini: Kelas dan objek: berapa banyak dan jenis file apa yang sebenarnya saya perlukan untuk menggunakannya? - khusus: Bagaimana IDE mengatur berbagai hal .

Saya tahu bahwa minimal Anda harus memasukkan file header Arduino

Ya, Anda harus melakukan itu.

tetapi lebih dari itu, apa yang akan menyebabkan kesalahan kompilasi jika kompilasi kata .ino ke file .cpp menggunakan, katakanlah, misalnya GCC-AVR.

IDE menghasilkan prototipe fungsi untuk Anda. Kode dalam file .ino mungkin atau mungkin tidak memerlukan ini (mungkin akan kecuali jika penulis cukup disiplin untuk kode dalam cara C ++ biasa dan melakukannya sendiri).


Jika "sketsa" berisi file lain (mis. File .ino, .c atau .cpp lainnya) maka ini harus dimasukkan dalam proses kompilasi seperti yang saya jelaskan dalam jawaban saya yang disebutkan di atas.

Anda juga perlu (kompilasi dan) tautan di perpustakaan apa pun yang digunakan oleh sketsa.


Anda belum bertanya tentang sisi tautan dari berbagai hal, tetapi secara alami berbagai file, seperti yang dikompilasi, perlu dihubungkan bersama, dan kemudian diubah menjadi file .elf dan .hex untuk keperluan pengunggahan. Lihat di bawah.


Contoh makefile

Berdasarkan output IDE saya membuat makefile sederhana beberapa waktu lalu :

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

Dalam kasus tertentu, file .ino dikompilasi tanpa masalah setelah mengubah nama menjadi Blink.cpp dan menambahkan baris ini:

#include <Arduino.h>
Nick Gammon
sumber
Terima kasih Nick atas jawaban singkat Anda, saya tidak berada di dekat level Anda, dan bahkan tidak memikirkan file make. Jadi pada dasarnya sintaksnya sama, itu semua hanya tentang menghubungkan benda, kan? Terima kasih telah berbagi file make Anda untuk saya bedah. Saya yakin akan ada lebih banyak pertanyaan yang muncul dari itu! Terima kasih lagi!
RedDogAlpha
File saya di atas berfungsi ketika saya mempostingnya, tapi saya percaya mungkin perlu mengubah untuk IDE selanjutnya (karena mereka bergerak atau mengganti nama file perpustakaan). Namun, melakukan kompilasi verbose menunjukkan apa yang dihasilkan oleh IDE, yang seharusnya Anda mulai.
Nick Gammon
10

Saya hanya ingin menambahkan beberapa poin pada jawaban Nick Gammon:

  • Anda tidak perlu mengganti nama file .ino untuk mengkompilasinya: jika Anda secara eksplisit memberi tahu kompiler itu C ++ (opsi -x c++), itu akan mengabaikan ekstensi file yang tidak biasa dan mengkompilasinya sebagai C ++.
  • Anda tidak perlu menambahkan #include <Arduino.h>file .ino: Anda dapat memberi tahu kompiler untuk melakukannya untuk Anda ( -include Arduino.h).

Dengan menggunakan trik itu, saya dapat mengkompilasi Blink.ino tanpa modifikasi , hanya dengan menggunakan avr-g ++ dengan opsi baris perintah yang tepat:

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

Beberapa catatan pada baris perintah di atas:

  • /usr/local/lib/arduino/uno/libcore.aadalah tempat saya menyimpan inti Arduino yang dikompilasi. Aku benci kompilasi berulang-ulang hal yang sama.
  • -x nonediperlukan untuk memberitahu kompiler untuk mengingat ekstensi file lagi. Tanpa itu, itu akan menganggap libcore.a adalah file C ++.

Saya belajar trik-trik itu dari Arduino-Makefile milik Sudar Muthu . Ini adalah Makefile yang sangat umum yang bekerja dengan banyak papan dan dengan perpustakaan. Satu-satunya hal yang hilang relatif terhadap Arduino IDE adalah deklarasi maju.

Edgar Bonet
sumber
Sangat bagus, Edgar! Solusi saya pada dasarnya meniru apa yang dilakukan IDE, milik Anda memecahkan masalah aktual dengan cara yang jauh lebih rapi. Tentu saja Anda harus membuat libcore.afile terlebih dahulu. Saya mengira bahwa garis-garis dalam jawaban saya yang membangun core.adapat dilakukan sebelumnya, sehingga mereka tidak harus menjadi bagian dari masing-masing membangun. Pengalaman menunjukkan bahwa sketsa yang lebih kompleks (mis. Menggunakan Wire atau SPI) membutuhkan lebih banyak file untuk ditambahkan core.a.
Nick Gammon
@NickGammon: Benar, Makefile Muthu (dan, saya asumsikan, IDE Arduino) cenderung untuk meletakkan perpustakaan apa pun yang Anda gunakan di libcore.a. Saya tidak terlalu suka pendekatan ini, karena itu membuat "perpustakaan inti" seharusnya bergantung pada program tertentu yang Anda kompilasi. Untuk pustaka file tunggal, seperti Wire atau SPI, saya lebih suka untuk hanya menempatkan pustaka C ++ file dalam perintah kompilasi yang sama dengan program utama. Baris perintah itu menjadi sangat mudah, jadi saya menggunakan Makefile.
Edgar Bonet
1
Salah satu hal yang saya sukai dari IDE adalah bahwa Anda tidak perlu bercanda. Untuk proyek-proyek sederhana, itu "hanya bekerja".
Nick Gammon