ATmega328PをArduinoなしで使ってみる
PICをやっていた感覚でArduinoで遊んでいると、いろいろ便利な仕組みが整備されているので、細かいことを考えなくてもいい反面、地に足がついてないというか、「これはマイコンなんだっけ」という根本的なところの理解があやふやな自分に気づく。
たとえば、Arduino Unoに載っているATmega328Pというマイコンは、原理的にはArduino Unoに載せずに単体で動かせるはずだけど、どうやったら動かせるのかよくわからない。
さらにいうと、Arduino IDEやArduino言語を使わずにプログラムを書けるはずだけど、どこから調べ始めればいいかよくわからない。
というわけで、ATmega328Pをスタンドアロンでやっていくメモ。
ATmega328PはAVRマイコンである
根本的なところからはじめると、ATmega328PはAVRマイコンなので、AVRの開発環境でプログラミングし、コンパイルしたバイナリをマイコンに書き込めばよい。だから、コンパイルしたhexファイルをマイコンにフラッシュする装置を用意し、それを動かすソフトウェアをパソコンにインストールすれば、ATmega328P上で動くプログラムを開発できる。なお、これらの装置とソフトは一般に「ROMライター」とか「ライター」などと呼ばれている。
ここで面白いのは、Arduinoでは、ライターを使わずにマイコン上で動くようにコンパイルしたプログラムをマイコンに書き込めるところだ。つまり、「コンパイルしたhexファイルをマイコンにフラッシュする装置」も、そのためのソフトウェアも必要なく、USBケーブルでパソコンとつなぐだけでAVRマイコン用のプログラムを転送できる。これがArduinoの気軽さであり、すごいところだといえる。
この気軽さは、Arduinoからマイコンを始めると、べつだん不思議なことではない。しかし、「マイコンとしてのATmega328PはArduino Unoなしでも動く」といった根本的な話は、むしろこの気軽さによってわかりにくくなっているようにも思う。もちろん、それがわからなくてもマイコンを使った何かを開発できるのがまさにArduinoのすごいところなので、これがわかりにくくなっていることは、当たり前といえば当たり前である。
Arduinoのブートローダー
というわけで、マイコンの勉強にとってArduinoがわかりにくいのは、その気軽さを支える「コンパイルしたhexファイルをマイコンにフラッシュする装置が必要ない」という特徴と表裏一体だ。なぜ、ArduinoをUSBケーブルでパソコンにつなぐだけで、AVRマイコン用のプログラムがマイコンに書き込めるのだろう?
これは、Arduinoに載っているAVRマイコンに、ブートローダーと呼ばれるプログラムがあらかじめ書き込まれているからだ。Arduinoのブートローダーは、「USB経由でパソコンから送られてくるAVRマイコン用のプログラムをマイコンに書き込み、さらにそのプログラムを先頭アドレスから実行する」、という機能を提供する。この機能のおかげで、ライターを使わずとも、パソコンからUSB経由でAVR用のプログラムを取り込んで気軽に実行できるようになっている。
Arduinoのブートローダーは、ちょっと特別な感じはあるけれど、やはりAVRマイコンに書き込んで使うふつうのプログラムである。ソースは以下で読める。いろいろな種類があるけど違いはよくわかっていない。
Arduino Unoを買ったときに付属してくるATmega328Pにも、Arduinoのブートローダーがあらかじめ書き込まれた状態になっている。が、素のATmega328PにはArduinoのブートローダーは書きこまれていないので、単独で手に入れたATmega328PをArduino Unoに指しても、USB経由でプログラムを取り込むことはできず、なにもできない。素のATmega328Pにプログラムを書きこむには、やはりArduinoとは別にライターが必要になる。
AVRマイコンにプログラムを書き込む2つの手段
ここまでの話を整理すると、AVRマイコンで動くプログラムは、以下のいずれかの方法でマイコンに書き込んで実行できるということがわかる。
- 専用のライターでプログラムを書き込む(マイコンとしてはふつうの方法)
- すでにブートローダーが書き込まれたマイコンに、USB経由でプログラムを転送する(Arduinoとかの方法)
ライターがあるなら、Arduinoのブートローダーは不要だといえる。ただし、ライターと一口にいってもいろいろな種類があるので、書き込みの方法はそれぞれ微妙に異なる。ふつうは購入したライターで指示された仕組みを使えばいいことだけど、ライターを自作する場合には、書き込みの実行に必要なソフトウェアも自分で書く必要があるはず。
逆に、ライターをもっていなくても、Arduinoさえあれば、その上のマイコンに組み込まれているブートローダーを利用することで、USBケーブル一本でプログラムをマイコンに書き込める。これがArduinoのすごいところなんだけど、ここまでの話で想像できるように、何が起こっているのかを理解しようと思うとややこしさは増える。あと、起動もブートローダー経由になるので、直接書き込むときよりちょっと立ち上がりが遅くなるかもしれない。
ATmega328Pの内部クロック
マイコンは、動作するためにクロックを必要とする。Arduino Unoの場合は、基板の上に発振子が実装されていて、これで16MHzのクロックを生成している。
だが、実はATmega328Pの内部にもクロックは実装されている。これを使えば、スタンドアローンで動かすときに発振子を回路上に載せる必要がなくなる。
どのクロックを使うかの切り替えは、lfuseビットというフラグをセットする。必要な設定は、マイコンを使う回路に合わせて、データシートの"13.2. Clock Sources"あたりを見て自分で考えるしかない。
内部クロックを使うときは、lfuseの値を0xF3
もしくは0x73
あたりにすればよさそう、とわかる。
回路とプログラム
以上の知見をLチカで試してみる。まずはプログラムから。
#include <avr/io.h>
#define F_CPU 1000000UL
#include <util/delay.h>
int main(void) {
DDRD = 0b00000100;
PORTD = 0b00000000;
while(1) {
PORTD = 0b00000100;
_delay_ms(100);
PORTD = 0b00000000;
_delay_ms(100);
}
}
このコードは以下のサイトで紹介されているものに周波数の設定(F_CPU
)を追加しただけ。
AVRマイコンのプログラムの基本は avr/io.h
の読み込みになる。DDRD
により、Dポートたちを出力として使えるようになる。PORTD
に3ビットめを立てた8ビットの値を代入することで、Dポートの3つめ、つまりATmega328Pであれば「PD2」がオンになる。これを while
により繰り返すことでPD2に接続したLEDをオンオフしようというプログラム。
これを led.c
として保存し、以下のコマンドでコンパイルする。
$ avr-gcc -mmcu=atmega328p -Os -Wall -g -o main.bin led.c
さらにマイコンへ書き込むためにhexに変換する。
$ avr-objcopy -j .text -j .data -O ihex main.bin main.hex
生成された main.hex
をATmega328Pに転送しすると、スタンドアローンで動かせるようになる。
転送は上記いずれの方法でもいいが、ライターを利用できる場合には前述のようにlfuseビットを 0xF3
にすると内部クロックで動かせる。Arduinoのブートローダーを使ってUSBで転送する場合は、ATmega328PのlfuseビットがArduinoのデフォルト設定である 0xFF
になっているので、回路に外部クロックを実装するしかないだろう。lfuseビットの書き込みにはライターが必要なので、あきらめるしかない。
以下は、ATmega328Pを電源につなぎ、PD2に抵抗を介してLEDを接続しただけの回路。ライターを使って転送し、lfuseビットを0xF3
にしたので、内部クロックで動いている。これでちゃんとLチカする。楽しい。
Discussion
F_CPU
の定義は#include
の前に書くべきです。今回は<util/delay.h>
で定義されているデフォルト値と同じ値なのでコンパイル&動作するかと思いますが、F_CPU
の値を変えると期待通りの動作をしなくなるかと思います。おお、ありがとうございます! 記事中も直しておきます。