.ino Arduino Sketch가 GCC-AVR에서 직접 컴파일됩니까?


10

자, 우리 모두 Arduino vs C ++와 같은 웹상의 모든 질문이나 다른 유사한 질문을 보았습니다. 그리고 대부분의 답변은 추상적 정보를 통한 것 외에는 컴파일 차이를 건드리지 않습니다.

내 질문은 .ino 파일의 이름을 .cpp 파일 또는 c ++의 다른 유사한 파일 확장자로 변경하여 GCC-AVR을 사용하여 컴파일하는 방법의 실제 차이점 (환경 설정이 아닌)을 해결하는 것입니다. 최소한 Arduino 헤더 파일을 포함해야하지만 그 이상으로 예를 들어 GCC-AVR을 사용하여 .ino를 .cpp 파일로 컴파일하면 컴파일 오류가 발생합니다. 간단히하기 위해 클래식 깜박임 예제를 사용하여 차이점을 설명하겠습니다. 또는 더 나은 코드 스 니펫을 사용하는 경우 반드시 스 니펫을 답변에 포함시키고 차이점을 철저히 설명하십시오.

어느 것이 더 나은 방법이나 도구인지에 대해서는 의견이 없습니다.

참고로 개발을 위해 Platformio를 사용하고 컴파일 과정에서 변환 과정이 진행되는 것을 알 수 있습니다. 나는 실제로 무슨 일이 일어나고 있는지 이해하려고 노력하고 있으므로 Arduino로 코딩 할 때 "순수한"C ++ 버전도 이해합니다.

내 질문에 미리 답변 해 주셔서 감사합니다.


gcc데스크톱 또는 GCC for AVR 컴파일러에 대해 특별히 묻고 avr-gcc있습니까? a .ino.cpp파일 사이에있는 것보다 훨씬 큰 차이가 있습니다.
BrettAM

@BrettAM Arduino UNO와 같은 GCC-AVR 툴킷은 대상 보드이며 Atmel AVR 칩을 사용합니다. 내 질문에 모호함을 불러 주셔서 감사합니다. 그리고 네, 훨씬 더 큰 차이가 있다는 것을 알고 있습니다. 이것이 제가이 질문을하는 이유입니다. 그 차이점이 무엇인지 배우십시오!
RedDogAlpha

답변:


14

여기에 내 대답을 참조하십시오 클래스와 객체 : 나는 실제로 사용하는 방법을 많은 및 파일 형식을 필요합니까? -구체적으로 : IDE가 구성하는 방법 .

최소한 Arduino 헤더 파일을 포함해야한다는 것을 알고 있습니다.

예, 그렇게해야합니다.

그러나 그 이상으로 예를 들어 GCC-AVR을 사용하여 .ino를 .cpp 파일로 컴파일하면 컴파일 오류가 발생합니다.

IDE는 함수 프로토 타입을 생성합니다. .ino 파일의 코드는 이것을 필요로 할 수도 있고 필요하지 않을 수도 있습니다 (아마도 일반적인 C ++ 방식으로 코딩하고 스스로 수행 할 수 있도록 충분히 훈련되지 않은 경우).


"sketch"에 다른 파일 (예 : 다른 .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 파일에 대해 생각조차하지 않았습니다. 기본적으로 구문은 동일합니다. 객체를 연결하는 것뿐입니다. 내가 분석 할 수 있도록 make 파일을 공유해 주셔서 감사합니다. 그로부터 더 많은 질문이있을 것입니다. 다시 감사합니다!
RedDogAlpha

위의 파일은 게시 할 때 작동했지만 나중에 IDE를 조정해야 할 수도 있습니다 (라이브러리 파일을 이동하거나 이름을 변경하기 때문에). 여전히 자세한 컴파일을 수행하면 IDE에서 현재 생성중인 내용이 표시되어 시작해야합니다.
Nick Gammon

10

Nick Gammon의 답변에 몇 가지 요점을 추가하고 싶습니다.

  • 컴파일하기 위해 .ino 파일의 이름을 바꿀 필요는 없습니다. 명시 적으로 컴파일러에게 C ++ (옵션 -x c++) 라고 지시 하면 비정상적인 파일 확장자를 무시하고 C ++로 컴파일합니다.
  • #include <Arduino.h>.ino 파일 을 추가 할 필요는 없습니다 . 컴파일러에게이를 수행하도록 지시 할 수 있습니다 ( -include Arduino.h).

이러한 트릭을 사용 하여 적절한 명령 행 옵션으로 avr-g ++를 호출하면 수정없이 Blink.ino를 컴파일 할 수 있습니다.

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미리 수행 할 수 있다고 가정 하므로 각 빌드의 일부일 필요는 없습니다. 경험에 따르면보다 복잡한 스케치 (예 : 와이어 또는 SPI 사용)에 추가 할 파일이 더 필요합니다 core.a.
Nick Gammon

@NickGammon : 맞습니다. Muthu의 Makefile (그리고 Arduino IDE)은 libcore.a에서 사용하는 라이브러리를 넣는 경향이 있습니다. 필자가“핵심 라이브러리”를 컴파일하는 특정 프로그램에 의존하게하므로이 접근 방식이 마음에 들지 않습니다. Wire 또는 SPI와 같은 단일 파일 라이브러리의 경우 라이브러리 C ++ 파일을 기본 프로그램과 동일한 컴파일 명령에 넣는 것을 선호합니다. 그 명령 행은 매우 길어지기 때문에 Makefile을 사용합니다.
Edgar Bonet

1
IDE에 대해 내가 좋아하는 것 중 하나는 주위를 둘러 볼 필요가 없다는 것입니다. 어쨌든 간단한 프로젝트의 경우 "그냥 작동"합니다.
Nick Gammon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.