Arduino Sketch จะรวบรวมโดยตรงกับ GCC-AVR หรือไม่


10

เอาล่ะเราทุกคนได้เห็นคำถามเหล่านั้นทั่วทั้งเว็บเช่น Arduino vs C ++ หรือคำถามที่คล้ายกันอื่น ๆ และคำตอบส่วนใหญ่ไม่ได้สัมผัสความแตกต่างในการรวบรวมนอกเหนือจากข้อมูลที่เป็นนามธรรม

คำถามของฉันมีจุดมุ่งหมายเพื่อแก้ไขความแตกต่างที่เกิดขึ้นจริง (ไม่ใช่การกำหนดค่าตามความชอบ) ในการเปลี่ยนชื่อไฟล์. ori เป็นไฟล์. cpp หรือนามสกุลไฟล์อื่น ๆ ที่คล้ายกันสำหรับ c ++ จะรวบรวมโดยใช้ GCC-AVR ฉันรู้ว่าอย่างน้อยคุณต้องรวมไฟล์ส่วนหัวของ Arduino แต่นอกเหนือจากนั้นสิ่งที่จะทำให้เกิดข้อผิดพลาดในการรวบรวมถ้ารวบรวมกล่าวว่า. ini ไปยังไฟล์. cpp โดยใช้พูดเช่น GCC-AVR เพื่อความเรียบง่ายลองใช้ตัวอย่างการกะพริบแบบคลาสสิคเพื่ออธิบายความแตกต่าง หรือหากคุณมีข้อมูลโค้ดที่ดีกว่าในการใช้โปรดรวมตัวอย่างไว้ในคำตอบและอธิบายความแตกต่างอย่างละเอียด

โปรดอย่าแสดงความคิดเห็นว่าวิธีใดเป็นวิธีที่ดีกว่าหรือเครื่องมือที่จะใช้

FYI ฉันใช้ Platformio เพื่อการพัฒนาและฉันสังเกตเห็นกระบวนการแปลงที่เกิดขึ้นเบื้องหลังในระหว่างการรวบรวม ฉันพยายามเข้าใจสิ่งที่เกิดขึ้นจริงที่นั่นดังนั้นเมื่อฉันรหัสใน Arduino ฉันเข้าใจรุ่น C ++ ที่ "บริสุทธิ์" เช่นกัน

ขอบคุณสำหรับคำตอบที่รอบคอบของคุณสำหรับคำถามของฉันล่วงหน้า


คุณถามเฉพาะเกี่ยวกับgccเดสก์ท็อปของคุณหรือคอมไพเลอร์ GCC สำหรับ AVR avr-gccหรือไม่? มีความแตกต่างที่ใหญ่กว่าที่มีอยู่ระหว่าง a .inoและ.cppไฟล์
BrettAM

@BrettAM ชุดเครื่องมือ GCC-AVR ในฐานะ Arduino UNO เป็นบอร์ดเป้าหมายและใช้ชิป Atmel AVR อย่างที่ฉันรู้ ขอบคุณสำหรับการโทรหาความกำกวมในคำถามของฉัน และใช่ฉันรู้ว่ามีความแตกต่างที่ใหญ่กว่ามาก นั่นคือเหตุผลที่ฉันถามคำถามนี้ เพื่อเรียนรู้ว่าความแตกต่างเหล่านั้นคืออะไร!
RedDogAlpha

คำตอบ:


14

ดูคำตอบของฉันที่นี่: คลาสและวัตถุ: ฉันต้องใช้ไฟล์ประเภทใดและจำนวนเท่าใด - โดยเฉพาะ: วิธีที่ IDE จัดการกับสิ่งต่าง

ฉันรู้ว่าอย่างน้อยคุณต้องรวมไฟล์ส่วนหัวของ Arduino

ใช่คุณจะต้องทำอย่างนั้น

แต่นอกเหนือจากนั้นสิ่งที่จะทำให้เกิดข้อผิดพลาดในการคอมไพล์หากการคอมไพล์กล่าวว่า. ini เป็นไฟล์. cpp โดยใช้ตัวอย่างเช่น GCC-AVR

IDE สร้างต้นแบบฟังก์ชันสำหรับคุณ โค้ดในไฟล์. ori อาจหรือไม่จำเป็นต้องใช้สิ่งนี้ (มันอาจจะยกเว้นกรณีที่ผู้เขียนได้รับการลงโทษทางวินัยมากพอที่จะใช้รหัสในวิธี C ++ ตามปกติและดำเนินการด้วยตนเอง)


หาก "ร่าง" มีไฟล์อื่น ๆ (เช่นไฟล์. ini, .c หรือ. cpp อื่น ๆ ) ไฟล์เหล่านี้จะต้องรวมอยู่ในกระบวนการรวบรวมตามที่ฉันอธิบายในคำตอบของฉันที่กล่าวถึงข้างต้น

นอกจากนี้คุณจะต้องลิงก์ (รวบรวมและ) ในห้องสมุดใด ๆ ที่ใช้โดยร่าง


คุณยังไม่ได้ถามเกี่ยวกับการเชื่อมโยงของสิ่งต่าง ๆ แต่โดยธรรมชาติแล้วไฟล์ต่าง ๆ ที่ได้ทำการคอมไพล์จำเป็นต้องทำการเชื่อมโยงเข้าด้วยกัน ดูด้านล่าง


ตัวอย่าง makefile

จากเอาต์พุต IDE ฉันสร้าง 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 

ในกรณีเฉพาะนั้นไฟล์. ino จะถูกคอมไพล์โดยไม่มีปัญหาหลังจากเปลี่ยนชื่อเป็น Blink.cpp และเพิ่มบรรทัดนี้:

#include <Arduino.h>

ขอบคุณ Nick สำหรับคำตอบที่กระชับของคุณฉันไม่อยู่ใกล้ระดับที่คุณเป็นและไม่แม้แต่คิดเกี่ยวกับไฟล์ทำ โดยพื้นฐานแล้วไวยากรณ์มีเหมือนกันมันเป็นเพียงแค่การเชื่อมโยงวัตถุเท่านั้นใช่ไหม ขอขอบคุณที่แบ่งปันไฟล์ทำของคุณให้ฉันแยกแยะ ฉันแน่ใจว่าจะมีคำถามเพิ่มเติมที่เกิดขึ้นจากที่! ขอบคุณอีกครั้ง!
RedDogAlpha

ไฟล์ของฉันทำงานด้านบนเมื่อฉันโพสต์ แต่ฉันเชื่อว่าอาจต้องปรับแต่งสำหรับ IDE ในภายหลัง (เพราะพวกเขาย้ายไปรอบ ๆ หรือเปลี่ยนชื่อไฟล์ไลบรารี) แต่ถึงกระนั้นการคอมไพล์ verbose แสดงสิ่งที่ IDE กำลังสร้างซึ่งควรให้คุณเริ่มต้น
Nick Gammon

10

ฉันอยากจะเพิ่มคะแนนให้กับคำตอบของ Nick Gammon:

  • คุณไม่จำเป็นต้องเปลี่ยนชื่อไฟล์. ini เพื่อรวบรวม: ถ้าคุณบอกคอมไพเลอร์อย่างชัดเจนว่าเป็น C ++ (ตัวเลือก-x c++) มันจะไม่สนใจนามสกุลไฟล์ที่ผิดปกติและคอมไพล์เป็น C ++
  • คุณไม่จำเป็นต้องเพิ่ม#include <Arduino.h>ในไฟล์. iino: คุณสามารถบอกคอมไพเลอร์ให้ทำเพื่อคุณ ( -include Arduino.h)

ด้วยการใช้เทคนิคเหล่านั้นฉันสามารถรวบรวม Blink.ino โดยไม่มีการดัดแปลงเพียงแค่เรียกใช้ avr-g ++ ด้วยตัวเลือกบรรทัดคำสั่งที่เหมาะสม:

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

หมายเหตุเล็กน้อยเกี่ยวกับบรรทัดคำสั่งด้านบน:

  • /usr/local/lib/arduino/uno/libcore.aเป็นที่ที่ฉันบันทึก Arduino core ที่คอมไพล์แล้ว ฉันเกลียดการคอมไพล์ซ้ำแล้วซ้ำอีกในสิ่งเดียวกัน
  • -x noneจำเป็นต้องบอกคอมไพเลอร์ให้คำนึงถึงนามสกุลไฟล์อีกครั้ง ก็ไม่มีมันจะถือว่า libcore.a เป็นไฟล์ C ++

ผมได้เรียนรู้เทคนิคจากSudar Muthu ของ Arduino-Makefile นี่เป็น Makefile ทั่วไปที่ทำงานกับบอร์ดจำนวนมากและกับไลบรารี สิ่งเดียวที่ขาดหายไปเมื่อเทียบกับ Arduino IDE คือการประกาศล่วงหน้า


ดีมากเอ็ดการ์! วิธีการแก้ปัญหาของฉันเลียนแบบสิ่งที่ IDE ทำคุณแก้ไขปัญหาที่เกิดขึ้นจริงเพื่อส่งมอบได้อย่างต่อเนื่อง แน่นอนคุณจะต้องทำlibcore.aไฟล์ล่วงหน้า ฉันคิดว่าบรรทัดในคำตอบของฉันซึ่งบิลด์core.aสามารถทำได้ล่วงหน้าดังนั้นพวกเขาจึงไม่จำเป็นต้องเป็นส่วนหนึ่งของแต่ละบิลด์ ประสบการณ์ได้แสดงให้เห็นว่าภาพวาดที่ซับซ้อนมากขึ้น (เช่น. ใช้ลวดหรือ SPI) ต้องไฟล์อื่น ๆ core.aอีกมากมายที่จะเพิ่ม
Nick Gammon

@NickGammon: ถูกต้อง Makefile ของ Muthu (และฉันถือว่า Arduino IDE) มีแนวโน้มที่จะนำไลบรารีใด ๆ ที่คุณใช้ใน libcore.a ฉันไม่ชอบวิธีนี้มากเพราะมันทำให้ "core library" ที่คาดคะเนขึ้นอยู่กับโปรแกรมที่คุณคอมไพล์ สำหรับไลบรารีไฟล์เดียวเช่น Wire หรือ SPI ฉันต้องการใส่ไฟล์ไลบรารี C ++ ในคำสั่งคอมไพล์เดียวกับโปรแกรมหลัก บรรทัดคำสั่งนั้นค่อนข้างยาวได้ง่ายดังนั้นฉันจึงใช้ Makefile
Edgar Bonet

1
สิ่งหนึ่งที่ฉันชอบเกี่ยวกับ IDE ก็คือคุณไม่ต้องทำสิ่งสกปรก สำหรับโครงการที่เรียบง่ายอยู่ดีมันใช้งานได้
Nick Gammon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.