.ino Arduino Sketch是否可以直接在GCC-AVR上编译?


10

好的,我们都在网上看到了诸如Arduino vs C ++之类的问题,或者其他类似的问题。除通过抽象信息外,绝大多数答案甚至都不会涉及编译差异。

我的问题旨在解决在将重命名为.cpp文件或c ++的其他类似文件扩展名的.ino文件如何使用GCC-AVR进行编译时的实际差异(而非首选项)。我知道您至少必须包括Arduino头文件,但除此之外,如果使用例如GCC-AVR将.ino编译为.cpp文件,会导致编译错误。为简单起见,让我们使用经典的眨眼示例来说明差异。或者,如果您要使用更好的代码段,请务必在回答中包含该代码段,并彻底解释差异。

请不要就哪种是更好的使用方式或工具发表意见。

仅供参考。我使用Platformio进行开发,并且发现在编译过程中幕后发生了转换过程。我试图了解那里实际发生的情况,因此,当我在Arduino中编写代码时,我也理解“纯” C ++版本。

感谢您提前对我的问题进行周到的回答。


您是在专门询问gcc台式机还是GCC for AVR编译器avr-gcc?文件.ino.cpp文件之间的区别要大得多。
BrettAM '16

@BrettAM我确信您知道,作为Arduino UNO的GCC-AVR工具包是目标板,并使用Atmel AVR芯片。感谢您对我的问题的模棱两可的呼吁。是的,我知道差异更大。这就是为什么我问这个问题。要了解这些差异是什么!
RedDogAlpha

Answers:


14

请在此处查看我的答案:类和对象:使用它们实际上需要多少个文件类型?-特别是:IDE如何组织事物

我知道您至少必须包含Arduino标头文件

是的,您需要这样做。

但是除此之外,如果使用例如GCC-AVR将.ino编译为.cpp文件,将会导致编译错误。

IDE为您生成函数原型。.ino文件中的代码可能需要也可能不需要(除非作者受过足够的训练,可以使用常规的C ++方式编写代码并自行完成),否则可能需要这样做。


如果“草图”包含其他文件(例如,其他.ino,.c或.cpp文件),则需要将它们合并到编译过程中,如我在上面提到的答案中所述。

另外,您将需要(编译和)链接到草图使用的任何库中。


您没有询问过事物的链接方面,但是自然地,编译后的各种文件都需要链接在一起,然后变成用于上传目的的.elf和.hex文件。见下文。


示例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的简洁回答,我离您的水平还差得远,甚至都没有考虑过make文件。所以基本上语法是一样的,就是链接对象,对不对?感谢您分享我的制作文件供我剖析。我敢肯定会有更多的问题!再次感谢!
RedDogAlpha'3

我上面的文件在发布时可以正常工作,但我认为可能需要对以后的IDE进行调整(因为它们会移动或重命名库文件)。尽管如此,进行详细的编译仍会显示IDE当前正在生成的内容,这应该使您入门。
尼克·加蒙

10

我想对尼克·加蒙的答案补充几点:

  • 您无需重命名.ino文件即可对其进行编译:如果您明确告诉编译器它是C ++(选项-x c++),它将忽略不寻常的文件扩展名并将其编译为C ++。
  • 您无需添加#include <Arduino.h>.ino文件:您可以告诉编译器为您完成此操作(-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核心的地方。我讨厌一遍又一遍地重新编译相同的东西。
  • -x none需要告诉编译器再次注意文件扩展名。没有它,它将假定libcore.a是C ++文件。

我从Sudar Muthu的Arduino-Makefile学习了这些技巧。这是一个非常通用的Makefile,可与许多主板和库一起使用。相对于Arduino IDE唯一缺少的是正向声明。


很好,埃德加!我的解决方案基本上模仿了IDE的功能,您的解决方案以更加整洁的方式解决了实际问题。当然,您必须libcore.a提前制作文件。我想我的答案中的代码行core.a可以预先完成,因此它们不必一定要包含在每个版本中。经验表明,更复杂的草图(例如,使用Wire或SPI)需要将更多文件添加到中core.a
尼克·加蒙

@NickGammon:是的,Muthu的Makefile(我想是Arduino IDE)倾向于将您使用的任何库放到libcore.a中。我不太喜欢这种方法,因为它使所谓的“核心库”依赖于您编译的特定程序。对于单文件库,如Wire或SPI,我更喜欢将库C ++文件放在与主程序相同的编译命令中。该命令行很容易变得很长,所以我使用了一个Makefile。
Edgar Bonet

1
我喜欢IDE的一件事是,您不必搞乱。无论如何,对于简单的项目,它“都是可行的”。
尼克·加蒙
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.