この記事は?
AVR 等組み込みプログラムのコンパイル、及び書き込みは、PC 上で動作する C言語プログラムのコンパイルと異なり、必要なオプションや手順が多いです。
デバイス指定オプションなんかが必要ということもあり、コンパイルや書き込みのコマンド 1つも短くないので、毎回コマンドを打つのは手間で非効率的です。
ビルド関係は、やはりmakefile
にまとめておいてしまうのが効率的です。
この記事では、avr-gcc
とavrdude
で、C言語の AVR プログラムのコンパイル、及びマイコン書き込みができるmakefile
の作成を行います。
(makefile
の詳しい説明は、ここではそこまで深く扱っておりません。)
用意するもの
使うコマンドは以下の 4つ。
-
avr-gcc
- C言語を AVR 用実行ファイルへコンパイル -
avr-objcopy
- 実行ファイルから HEX ファイルの作成 -
avrdude
- HEX ファイルの AVR マイコンへの書き込み -
make
-makefile
に書かれたスクリプトの実行
avr-gcc でのコンパイルをするためには、AVR 用 Cライブラリavr-libc
も必要です。
ひとまず、以下のコマンドで全てインストールできます。(APT の場合)
sudo apt install gcc-avr avr-libc avrdude make
あとは、お好みのコードエディター等を任意で。
ビルド対象のソースコード
Lチカプログラムです。
ポートの初期化をAvrInit.c
で、LED の点灯切り替えをPBToggle.c
で行っています。
この程度の規模ならmain.c
にまとめても問題無いですが、複数ファイルのソースのビルドをするmakefile
を作るため、あえて分割しています。
#define F_CPU 1000000
#include <avr/io.h>
#include <util/delay.h>
#include "PBToggle.h"
#include "AvrInit.h"
int main() {
// Setup
AvrInit();
// Main loop
while(1) {
PBToggle();
_delay_ms(250);
}
return 0;
}
#ifndef AVRINIT_H
#define AVRINIT_H
#include <avr/io.h>
int AvrInit(void);
#endif /* AVRINIT_H */
#include "AvrInit.h"
int AvrInit(void) {
DDRB = DDRB | 0b11111111; // Set PBx to output
PORTB = 0b00000000; // Set PBx output to LOW
return 0;
}
#ifndef PBTOGGLE_H
#define PBTOGGLE_H
#include <avr/io.h>
int PBToggle(void);
#endif /* PBTOGGLE_H */
#include "PBToggle.h"
int PBToggle(void) {
PORTB = ~PORTB;
return 0;
}
make 無しでのコンパイル & 書き込み
とりあえず、make を使わない場合でのコンパイル、及び書き込みのコマンドを揃えます。
-
avr-gcc
で、ソースから実行ファイル生成 -
avr-objcopy
で、実行ファイルから HEX ファイル生成 -
avrdude
で、HEX ファイルを AVR マイコンへ書き込み
avr-gcc -Wall -Os -mmcu=atmega328p AvrInit.c PBToggle.c main.c -o Blink
## -Wall : 詳細な警告を全て表示するようにする
## -Os : バイナリサイズが小さくなるよう最適化をする
## -mmcu=<Name> : プログラムを動かすマイコンを指定
avr-objcopy -F ihex Blink Blink.hex
## -F : 出力形式指定 (Intel HEX に指定)
avrdude -c avrispmkII -P usb -p atmega328p -U flash:w:Blink.hex:i
## -c : 書き込み機器 (ライタ) の指定 (avrispmkII を使用)
## -P : 書き込み機器の接続ポートの指定 (USB ポートを使用)
## -p : 書き込み先のマイコンを指定 (atmega328p が書き込み先)
## -U <memtype>:r|w|v:<filename>[:format]
## : 書き込み先メモリタイプ:書き込みor読み込み:書き込むファイル:ファイルフォーマット を指定
avr-gcc
について、「全ての Cソースを指定して一発で実行ファイルまで」でも動かなくはないです。
ただ、せっかく make を使うので、オブジェクトファイル生成をさせるプロセスを挟んで、利口な方法にします。
avr-gcc -Wall -Os -mmcu=atmega328p -c AvrInit.c -o AvrInit.o
avr-gcc -Wall -Os -mmcu=atmega328p -c PBToggle.c -o PBToggle.o
avr-gcc -Wall -Os -mmcu=atmega328p -c main.c -o main.o
avr-gcc -Wall -Os -mmcu=atmega328p AvrInit.o PBToggle.o main.o -o Blink
makefile に書く
変数無しで (とりあえず上記ほぼコピペ)
以上のコマンドが動くように、makefile
を書いていきます。
以下のような動作をするように仕上げます。
-
make
と打つと、実行ファイル (Blink
) 及び HEX ファイル (Blinl.hex
) が作られる -
make write
と打つと、HEX ファイルがマイコンに書き込まれる (HEX ファイルが無ければ作る) -
make clean
と打つと、オブジェクトファイルと実行ファイルが削除される (オブジェクトファイル等が邪魔になる際に片付けできる)
# 実行ファイル及び HEX ファイル作成 (各 Cソースのオブジェクトファイルが必要)
Blink.hex: AvrInit.o PBToggle.o main.o
avr-gcc -Wall -Os -mmcu=atmega328p AvrInit.o PBToggle.o main.o -o Blink
avr-objcopy -F ihex Blink Blink.hex
# 各 Cソースのオブジェクトファイル作成
%.o: %.c AvrInit.h PBToggle.h
avr-gcc -Wall -Os -mmcu=atmega328p -c $< -o $@
# HEX ファイル書き込み (Blink.hex が必要)
write: Blink.hex
avrdude -c avrispmkII -P usb -p atmega328p -U flash:w:Blink.hex:i
# オブジェクトファイルと実行ファイル及び HEX ファイル削除
.PHONY: clean
clean:
-rm *.o
-rm Blink*
オブジェクトファイルの生成のルールは、%.o: %.c
で簡潔にします。
これにより、Cソースが増えても、ルールを新しく追加する手間が省けます。
%.o: %.c
を使わない方法で同様の動作を書くと、以下のような面倒な書き方になります。
AvrInit.o: AvrInit.c AvrInit.h PBToggle.h
avr-gcc -Wall -Os -mmcu=atmega328p -c AvrInit.c -o AvrInit.o
PBToggle.o: PBToggle.c AvrInit.h PBToggle.h
avr-gcc -Wall -Os -mmcu=atmega328p -c PBToggle.c -o PBToggle.o
ここで、オブジェクトファイル作成のルールについてですが、依存ファイルにヘッダを全て記してしまっています。
これでも動くには動きますが、AvrInit.o
はPBToggle.h
を使いません。
よって、この部分の依存関係の記述は正確とは言えません。
(より正確にできる方法もあるとは思いますが・・・makefile
マスターでない私にはこれが限界です・・・。)
変数を使ってスマートにする
さて、上記のmakefile
を変数を使って記述し、メンテナンス性を高めます。
ソースコードの構成が変わっても、変数を変えるだけで済むようにします。
# 変数宣言
## ソース情報
TARGET = Blink
HEDS = AvrInit.h PBToggle.h
SRCS = AvrInit.c PBToggle.c main.c
OBJS = $(SRCS:.c=.o) # AvrInit.o PBToggle.o main.o
## AVR デバイス情報
AVR_CHIP = atmega328p
AVR_WRIT = avrispmkII
AVR_PORT = usb
## コンパイラ情報
CC = avr-gcc
CFLAGS = -Wall -Os -mmcu=$(AVR_CHIP)
## 他
RM = -rm
# 実行ファイル及び HEX ファイル作成 (各 Cソースのオブジェクトファイルが必要)
$(TARGET).hex: $(OBJS)
$(CC) $(CFLAGS) $(OBJS) -o $(TARGET)
avr-objcopy -F ihex $(TARGET) $(TARGET).hex
# 各 Cソースのオブジェクトファイル作成
%.o: %.c $(HEDS)
$(CC) $(CFLAGS) -c $< -o $@
# HEX ファイル書き込み (Blink.hex が必要)
write: $(TARGET).hex
avrdude -c $(AVR_WRIT) -P $(AVR_PORT) -p $(AVR_CHIP) -U flash:w:$(TARGET).hex:i
# オブジェクトファイルと実行ファイル及び HEX ファイル削除
.PHONY: clean
clean:
$(RM) *.o
$(RM) $(TARGET) $(TARGET).hex
avr-objcopy
やavrdude
も変数にしても良いのですが、一度しか登場しない上、仮に変更することになったらどのみちコマンド引数を変えることになると思うので、ここは直書きにしました。
これでmakefile
の完成です。
ソースコードが増えたら、HEDS
とSRCS
を変えてやることですぐに対応できます。