謎's キッチン

謎のひとりごと。Amazon欲しい物リストはこちら: https://www.amazon.co.jp/hz/wishlist/ls/CCPOV7C6JTD2

avr-gccでArduino IDE用の*.inoファイルを一発コンパイルする

Arduinoを組み込んだ自作仮想アナログ時計 (gsclock) のインストールをより簡単にしようと思い、今回はArduino IDE無しでArduino IDE向けソースをコンパイルする方法を探してみました。(なお同様の目的のものとしてarduino-mk、arduino-builder、arduino-cliなどもあるようなので普通の人はそっちを試した方が良いかもしれません。)

まずはAVR向けのクロスコンパイラ、標準Cライブラリ、Arduinoコアライブラリを入れます。

sudo apt install gcc-avr avr-libc arduino-core-avr

ここで注意ですが、Arduinoコアライブラリは未コンパイルの状態のようです。そのため本当は事前コンパイルをしてライブラリを作るのが正攻法だと思うのですが、今回は横着して自作プログラムと同時にライブラリもコンパイルすることにします。

問題はArduinoコアライブラリにC言語とC++言語が混在しているためコマンドラインでオプションを一括指定ができないことです。これも普通ならMakefileを使ってどうこうするところなのですが、他に良い方法は無いかなと探したところGCCにはcc1 (gcc相当)とcc1plus (g++相当)に渡すパラメータを個別に変更できるSpecsファイルというものがあるようです。今回はそれを使ってみます。

まずはAVR向けgccの標準Specsファイルをダンプします。

avr-gcc -dumpspecs > avr-gcc-arduino.specs

次にavr-gcc-arduino.specsのcc1とcc1plusのパラメータを変更します。

*cc1:
-%(cc1_n_flash) %(cc1_errata_skip) %(cc1_rmw) %(cc1_non_bit_addressable_registers_mask) %(cc1_absdata)
+%(cc1_n_flash) %(cc1_errata_skip) %(cc1_rmw) %(cc1_non_bit_addressable_registers_mask) %(cc1_absdata) -flto -fuse-linker-plugin
*cc1plus:
-%(cc1) %{!frtti:-fno-rtti} %{!fenforce-eh-specs:-fno-enforce-eh-specs} %{!fexceptions:-fno-exceptions}
+%(cc1) %{!frtti:-fno-rtti} %{!fenforce-eh-specs:-fno-enforce-eh-specs} %{!fexceptions:-fno-exceptions} -std=c++14 -fno-threadsafe-statics -fno-exceptions

まずはhello worldを試すことにします。以下をhello.inoとして保存します。

#include <Arduino.h> /*これは今の所外せません*/

void setup() {
  Serial.begin(9600);
}

void loop() {
   Serial.println("Hello, World, from avr-gcc!");
   delay(500);
}

そんで作成したSpecsファイルを使ってそれをコンパイルしてみます。今回はMPUがatmega328p 16MHzのArduino Uno R3向けです。

avr-gcc -specs=avr-gcc-arduino.specs -Os -Wall -mmcu=atmega328p -ffunction-sections -fdata-sections -DF_CPU=16000000L -DARDUINO=10807 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -I/usr/share/arduino/hardware/arduino/avr/cores/arduino -I/usr/share/arduino/hardware/arduino/avr/variants/standard  /usr/share/arduino/hardware/arduino/avr/cores/arduino/*.cpp  /usr/share/arduino/hardware/arduino/avr/cores/arduino/*.c -x c++ hello.ino -o hello.elf

デバイスにアップロードできるようにhex形式に変換します。

avr-objcopy -O ihex hello.elf hello.hex

デバイスにアップロードするためのavrdudeをインストールします。

sudo apt install avrdude

avrdudeでアップロードします。今回のポートは /dev/ttyACM0 でした。

$ avrdude -c arduino -p atmega328p -b 115200 -P /dev/ttyACM0 -U flash:w:hello.hex

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "hello.hex"
avrdude: input file hello.hex auto detected as Intel Hex
avrdude: writing flash (6502 bytes):

Writing | ################################################## | 100% 1.04s

avrdude: 6502 bytes of flash written
avrdude: verifying flash memory against hello.hex:
avrdude: load data flash data from input file hello.hex:
avrdude: input file hello.hex auto detected as Intel Hex
avrdude: input file hello.hex contains 6502 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 0.84s

avrdude: verifying ...
avrdude: 6502 bytes of flash verified

avrdude: safemode: Fuses OK (E:00, H:00, L:00)

avrdude done.  Thank you.

ためしにシリアルコンソールに繋いでみます:

$ tail -f /dev/ttyACM0
Hel, World, from avr-gcc!
Hello, World, from avr-gcc!
Hello, World, from avr-gcc!
Hello, World, from avr-gcc!
Hello, World, from avr-gcc!
Hello, World, from avr-gcc!
Hello, World, from avr-gcc!
Hello, World, from avr-gcc!

ちゃんと動いてますね。ただ機種固定しちゃって柔軟性が下がってたり、ヒューマンエラーが起きやすくなってたりするのであんまりオススメはしません。今後どうするかは迷い中です。