Um esboço .ino do Arduino será compilado diretamente no GCC-AVR?

10

Ok, todos nós já vimos essas perguntas em toda a Web, como Arduino vs C ++, ou outras perguntas semelhantes. E uma grande maioria das respostas nem sequer toca em diferenças de compilação, a não ser através de informações abstratas.

Minha pergunta tem como objetivo resolver as diferenças reais (não as preferências) em como um arquivo .ino renomeado para arquivo .cpp ou outra extensão de arquivo semelhante para c ++ seria compilado usando o GCC-AVR. Sei que, no mínimo, você precisa incluir o arquivo de cabeçalho do Arduino, mas além disso, o que causaria um erro de compilação se compilasse o arquivo .ino para .cpp usando, por exemplo, o GCC-AVR. Por uma questão de simplicidade, vamos usar o exemplo clássico de piscada para explicar quais são as diferenças. Ou, se você tem um snippet de código melhor para usar, inclua o snippet em sua resposta e explique as diferenças completamente.

Não tenha opiniões sobre qual é a melhor maneira ou ferramenta para usar.

PARA SUA INFORMAÇÃO. Uso Platformio para desenvolvimento e noto um processo de conversão acontecendo nos bastidores durante a compilação. Estou tentando entender o que realmente está acontecendo lá, então, quando codifico no Arduino, também entendo a versão C ++ "pura".

Agradecemos suas respostas atenciosas à minha pergunta com antecedência.

RedDogAlpha
fonte
Você está perguntando especificamente sobre gccsua área de trabalho ou o compilador GCC for AVR avr-gcc? existe uma diferença muito maior do que entre .inoum .cpparquivo e um .
BrettAM
@BrettAM O kit de ferramentas GCC-AVR, como o Arduino UNO, é o quadro de destino e usa um chip Atmel AVR, como eu tenho certeza que você sabe. Obrigado pela chamada à ambiguidade na minha pergunta. E sim, eu sei que há uma diferença muito maior. É por isso que estou fazendo essa pergunta. Para aprender quais são essas diferenças!
RedDogAlpha

Respostas:

14

Veja minha resposta aqui: Classes e objetos: quantos e quais tipos de arquivo eu realmente preciso para usá-los? - especificamente: como o IDE organiza as coisas .

Eu sei que, no mínimo, você precisa incluir o arquivo de cabeçalho do Arduino

Sim, você precisaria fazer isso.

além disso, o que causaria um erro de compilação ao compilar o arquivo .ino para .cpp usando, por exemplo, o GCC-AVR.

O IDE gera protótipos de função para você. O código em um arquivo .ino pode ou não precisar disso (provavelmente será necessário, a menos que o autor seja disciplinado o suficiente para codificar da maneira C ++ usual e executá-los eles mesmos).


Se o "esboço" contiver outros arquivos (por exemplo, outros arquivos .ino, .c ou .cpp), eles precisarão ser incorporados no processo de compilação, conforme descrito na minha resposta mencionada acima.

Além disso, você precisaria (compilar e) vincular todas as bibliotecas usadas pelo esboço.


Você não perguntou sobre o lado da vinculação, mas naturalmente os vários arquivos, conforme compilados, precisam ser vinculados e depois transformados em um arquivo .elf e .hex para fins de upload. Ver abaixo.


Makefile de exemplo

Baseado na saída do IDE, criei um makefile simples há algum tempo :

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

Nesse caso específico, o arquivo .ino foi compilado sem problemas após renomeá-lo para Blink.cpp e adicionar esta linha:

#include <Arduino.h>
Nick Gammon
fonte
Obrigado Nick por sua resposta concisa, não estou nem perto do nível que você está e nem sequer pensou em um arquivo make. Então, basicamente, a sintaxe é a mesma, trata-se apenas de vincular objetos, certo? Obrigado por compartilhar seu arquivo make para eu dissecar. Estou certo de que haverá mais perguntas decorrentes disso! Obrigado novamente!
RedDogAlpha
Meu arquivo acima funcionou quando o publiquei, mas acredito que possa precisar de ajustes para os IDEs posteriores (porque eles se movem ou renomeiam os arquivos da biblioteca). Ainda assim, fazer uma compilação detalhada mostra o que o IDE está atualmente gerando, o que deve ajudá-lo a começar.
Nick Gammon
10

Gostaria apenas de acrescentar alguns pontos à resposta de Nick Gammon:

  • Você não precisa renomear um arquivo .ino para compilá-lo: se você disser explicitamente ao compilador que é C ++ (opção -x c++), ele ignorará a extensão incomum do arquivo e o compilará como C ++.
  • Você não precisa adicionar #include <Arduino.h>o arquivo .ino: pode instruir o compilador a fazer isso por você ( -include Arduino.h).

Usando esses truques, posso compilar o Blink.ino sem nenhuma modificação , apenas invocando o avr-g ++ com as opções de linha de comando apropriadas:

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

Algumas notas na linha de comando acima:

  • /usr/local/lib/arduino/uno/libcore.afoi onde salvei o núcleo compilado do Arduino. Eu odeio recompilar repetidamente as mesmas coisas.
  • -x noneé necessário dizer ao compilador para cuidar das extensões de arquivo novamente. Sem ele, seria assumido que libcore.a é um arquivo C ++.

Eu aprendi esses truques com o Arduino-Makefile de Sudar Muthu . Este é um Makefile muito geral que funciona com muitas placas e com bibliotecas. A única coisa que falta em relação ao IDE do Arduino são as declarações de encaminhamento.

Edgar Bonet
fonte
Muito bem, Edgar! Minha solução basicamente imita o que o IDE faz, o seu resolve o problema real de uma maneira muito mais organizada. Claro que você teria que fazer o libcore.aarquivo com antecedência. Suponho que as linhas da minha resposta, que compilam, core.apossam ser feitas com antecedência, para que não precisem fazer parte de cada compilação. A experiência mostrou que esboços mais complexos (por exemplo, usando Wire ou SPI) precisam de mais arquivos para serem adicionados core.a.
Nick Gammon
@NickGammon: Isso mesmo, o Makefile de Muthu (e, presumo, o IDE do Arduino) tende a colocar as bibliotecas que você usa no libcore.a. Eu realmente não gosto dessa abordagem, pois torna a supostamente "biblioteca principal" dependente do programa específico que você compila. Para bibliotecas de arquivo único, como Wire ou SPI, prefiro apenas colocar o arquivo C ++ da biblioteca no mesmo comando de compilação que o programa principal. Essa linha de comando fica facilmente muito longa, então eu uso um Makefile.
Edgar Bonet
11
Uma das coisas que eu mais gosto no IDE é que você não precisa se mexer. De qualquer maneira, para projetos simples, "simplesmente funciona".
Nick Gammon