itsukichang

フロントエンドが得意なエンジニア.ダーツと旅行とギターが好き

AVR入門 -I/O出力編

前回まででAVRを使ったプログラミングをするための準備ができました.
ので,今回は実際に回路を組んでプログラムを書いて,実践的にAVRを学んでいきましょう.

LED点滅

マイコンでのHelloWorldといえば,LEDの点滅,通称Lチカですね.
点灯→delay→消灯→delay→点灯…を繰り返すシンプルなアルゴリズムです.
これをC言語で書いてAVR上で実現してみましょう.

回路

まず回路を組みましょう.
回路はAVRマイコンにライタとLED,抵抗を繋げるだけです.
マイコンとライタを正しく繋ぎ,PD0のピンにLEDと抵抗(220ohm程度)を繋いでGNDに持っていきましょう.

プログラム1

とりあえずコードを載せます.解説は後ほど.

#include <avr/io.h>
#include <util/delay.h>

int main() {
	
	DDRD = 0b00000001; //ポートの設定
	
	while(1) { 				//無限loop
		PORTD = 0b00000001; //点灯
		_delay_ms(1000);    //1秒待つ
		PORTD = 0b00000000; //消灯
		_delay_ms(1000);    //1秒待つ
	}
	
	return 0;
}

これをmain.cに書き,ビルドしてAVRマイコンに書き込みましょう.
やり方は前回までの記事を参考してください.
LEDが1秒毎に点滅しはじめましたか?

重要ワード

ここで今回のLED点滅に重要な部分を見てみましょう.

  • DDRD

なにやら大文字のワードに01の数字が代入されていますね.
まず簡単そうな右辺から見てみましょう.0bというのはC言語で2進数を表記するときの接頭語です.
ほかにも0xをつけると16進数,0をつけると8進数を表す事が出来ます.基本ですね.
つまりこの0b00000001というものは2進数8桁,つまり8bit=1byte文のデータであり,1ビット目が1になっています.

さて,左辺のDDRDというのは,AVRのDポート(PD0,PD1,PD2….PD7)の入出力設定を行う部分です.
AVRではひとつのアルファベット(A,B,C,Dのいずれか)に8つのポート(例:PA0~PA7の8つ) が用意されており,各ポートの設定はDDRDのビットに対応します.1ビット目が0, 2ビット目が1, 3ビット目が2…といった具合です.
(例:ポートDだと, PD0の入出力設定がDDRDの1ビット目,PD1の入出力設定がDDRDの2ビット目…)

つまりここで行っている,

DDRD = 0b00000001; 

という文はポートDの0番,PD0を1に設定し,その他(PD1~PD7)を0に設定しているということなのです.
この設定の数値(0 or 1)は0ならばそのポートが入力モード,1ならばそのポートが出力モードに設定されます.
arduinoで言う

pinMode(0,INPUT);
pinMode(1,OUTPUT);

と同じことをしています.

まとめると,DDRDはポートDの入出力設定を行うもの(レジスタといいます)で,今回はPD0を出力設定にしている.という訳です.これは他のポートも同じで,DDRA,DDRB,DDRCなどを設定すると,それぞれ対応するポート(PA@,PB@,PC@ @は0~7)の入出力設定ができます.

  • PORTD

無限ループの中にPORTDというキーワードがありますね.
これは各ポートの出力値を決めるものです.考え方は先ほどのDDRDと同じで,各ビットが各ポートに対応しています.
この値が1ならばHigh,0ならばLowの出力を行います.
ここで行っている

PORTD = 0b00000001;

というものはポートDの0番(PD0)の出力をHighにするということなんですね.
(ちなみにポートが入力モードであった場合,1に設定すると内部プルアップモードになります.詳しくはまた後日.)
すると下の全部0の部分ももう分かりますよね.PD1~7はもちろん,PD0もLowになります.

arduinoで言う

digitalWrite(0, HIGH);
digitalWrite(0, LOW);

と同じなんですね.

  • _delay_ms

これは関数名から推測できると思いますが,指定したミリ秒delayが生じる関数です.
2行目で読み込んでいる,に定義されています.

arduinoで言う

delay(1000);

と同じですね.

まとめ

一見ちょっと小難しそうに見えますが,分かってしまえばarduinoと同じことをしているのが分かりましたね.
基本的にデジタルにIOを使う時は,ポートの設定・入出力の管理を行うだけなので本質は同じです.
さて,このコードでもちゃんとLEDが1秒毎に点滅するのですが,もうちょっとマイコンっぽく書いてみましょう.

プログラム2

AVRのようなマイコン使ってプログラムを書く時はPCと違ってメモリに気を使わなくてはなりません(大きさが全然違いますから…)
またCPUもPCに比べると非常に低速なので,処理はできるだけスマートに書く必要があります.
つまり省メモリ・高速が基本な訳です.
今回のような簡単なプログラムではそこまで気を使う必要はありませんが,今後大規模な開発にあたることを想定して,ちょっと書き方を覚えておきましょう.

  • ビット演算

省メモリ・高速化の基本は,まずビット演算を使用することです.
ビット演算にはAND OR XOR シフト 反転などがあります.
詳しく書くと切りがないので,このビット演算の細かいところはググってください.わかりやすいサイトがたくさんあります.

さて,この使うといい事があるらしいビット演算を使って,先ほどのプログラムをちょっと変えてみましょう.

#include <avr/io.h>
#include <util/delay.h>

int main() {
	
	DDRD = 0b00000001; //ポートの設定
	
	while(1) { 				//無限loop
		PORTD ^= 0b00000001; //点滅
		_delay_ms(1000);    //1秒待つ
	}
	
	return 0;
}

ループの中身が変わっていますね.見ていきましょう.

PORTDのイコールの前に^という記号がついています.
これはXOR演算子です.(XORは計算の対象となる両方のビットが異なるときに1を返す.両方同じなら0を返す)
はじめPORTDは0b00000000となっています.ここに0b00000001をXOR演算すると,1ビット目が異なっているので1ビット目は1になります(点灯).
次にもう一度回って来たとき,PORTDは0b00000001となっており,ここに0b00000001をXOR演算すると,1ビット目が同じになっているので1ビット目は0になります(消灯).
つまりループする度にPORTDの1ビット目が変化するということです.
これでコードを2行減らすことができましたね.

まとめ

今回はAVRのIOポートを使った基本的な出力を扱いました.
ポートの設定,HIGH, LOWの指定.といった非常に基本的なことですが,これでとりあえず7セグLEDなどを制御することができますよね.
次回は入力の扱い方を見ていきましょう.