AVRでポート入出力

概要

マイコンの基本中の基本、ポートでの入出力をやってみます。
ポートの入出力はそれほど難しくないので、すぐにできると思います。

解説

ポートの入出力の前に少しだけポートについて解説しておきたいと思います。
もう理解しているという方はこの解説の項目は飛ばして下さい。

ポートとは?

ポートと言うのはマイコン内部と外部でデータをやり取りするインターフェースのことを言います。
この文章だけ読んでもイメージがつかめずさっぱりわからない方もいるかもしれません。
具体的に身近なものにポートを例えるならば、港や空港に当たります。
マイコンを日本という国に例えると、外国に行くため私たちは船や飛行機を利用します。この行き来する人がデータに相当します。
よって日本(マイコン)と外国(外部回路)の間で人々(データ)が行き来する玄関口といえるのが空港・港(ポート)という訳です。

通常、ポートではHighかLowを出力・入力することによってデータを入出力します。
(この後の説明では、Highのことを省略してH、Lowのことを省略してLと記述します。)
では、このHとLをマイコンではどのように表現しているのでしょう?

答えは、マイコンでは電圧の変化によってHかLを表現します。
この電圧ですが、基本的にLレベルはGND(0V)となります。
Hレベルは基本的にマイコンの電源電圧(VCC)となります。マイコンの種類によってはポート専用の電源ピンが存在するものもあります。
AVRではポート専用の電源ピンを持っているものはないので、必ず電源電圧となります。
このようにマイコンのポートではVCCと0Vの2つの電圧でデータを表現する訳です。
簡単でしたが、ポートについて理解していただけたでしょうか?
まだわからないという方は「ポート」などで検索するといろいろ詳しい解説が見つかると思います。

レジスタって何?

それでは、マイコンを動作させるのに必須なレジスタについてちょっと説明して行きましょう。
レジスタは、高速に使用できるデータの格納場所です。
機能に合わせてレジスタが用意されており、レジスタを書き換えたりすることにより、動作を変化させることができます。

レジスタで使用する単位はビットとバイトです。
コンピュータの基本もビットとバイトが基本単位になっています。
最近は「1テラバイトハードディスク」とか「64ビットOS」といったように一般の方でも聞くことが増えたと思います。
ビットは1か0の値を保存するもので、バイトはそのビットが8つ集まったものをいいます。
よって、1バイト=8ビットとなるわけです。
ビットはよく箱に例えられます。
箱にものが入っていれば「1」、箱にものが入っていなければ「0」となります。
この箱が8つ並んだものがバイトというわけです。
なぜ8つかといえば、コンピュータにとって8という数は区切りがいいからだそうです。

基本的にレジスタも8ビット(1バイト)で構成されています。
もちろん例外はあり、AVRでも種類によっては16ビットのタイマ用のレジスタが存在します。
このレジスタの意味を理解した上で、ポートの設定に使うレジスタを学んで行きましょう。

DDR(Data Direction Registe - データディレクションレジスタ)

データディレクションレジスタ(以下、略してDDRと記述)は、
直訳すると「ポート方向レジスタ」となるように、データの出入りする方向を決定するためのレジスタです。
この出入りをコンピュータ用語で入力・出力と言います。
わかりやすく言うならば、ポートを空港としたときに、
出国専用の空港にするか、入国専用の空港にするかを決めるような感じです。(実際にそんな空港はないですが。)

PORT(Port Data Register - ポート出力レジスタ)

ポート出力レジスタはデータの出力値を決定するレジスタです。
他のマイコンにあるポートデータレジスタは入力・出力の両方の値を扱うものもありますが、
AVRでは出力専用のレジスタとなっています。
また、AVRでは、ポート出力レジスタをポートと略しています。

PIN(Port Input Address - ポート入力レジスタ)

ポート入力レジスタはデータの入力値が代入されるレジスタです。
ポートに入った、HかLの電圧が1と0のデータとしてこのレジスタに格納されます。
AVRでは、ポート入力レジスタをピンと略しています。

方法

ポート制御の基礎

ポートはどのAVRを使用してもレジスタの名前は一緒です。
AVRの種類によって存在するポートの番・数が違います。

では、入出力するためにポートのレジスタを設定して行こうと思います。
ポートには、

  • DDR@ : ポート@方向レジスタ
  • PORT@ : ポート@出力レジスタ
  • PIN@ : ポート@入力レジスタ

が存在します。(注:@にはポートの番号が入ります。例、DDRB,PORTB,PINB)
それでは今回は試しにポートBでビット7〜4までを出力ポートに、3〜0までを入力ポートに設定。
更に、7・6ビットはHを出力、5・4ビットはLを出力、3・2ビットはプルアップを有効にしてみます。
プルアップがわからない方は、プルアップ・プルダウン抵抗 - センサ道場を参照してみて下さい。
では、それぞれのレジスタを設定して行きましょう。

DDRB
bit 7 6 5 4 3 2 1 0
DDRB DDB7 DDB6 DDB5 DDB4 DDB3 DDB2 DDB1 DDB0

DDRBの中はこのようになっています。
出力に設定したいビットを1に、入力に設定したいビットを0に設定します。
今回はビット7〜4を出力、ビット3〜0を入力として利用したいので、ビットの7〜4を1に、3〜0を0に設定します。

PORTB
bit 7 6 5 4 3 2 1 0
PORTB PORTB7 PORTB6 PORTB5 PORTB4 PORTB3 PORTB2 PORTB1 PORTB0

PORTBの中はこのようになっています。
DDRBで出力に設定されているビットに対応した番号のビットに1か0を書き込むことによって、ポートからHかLを出力します。
また、DDRBで入力に設定されているビットに対応した番号のビットは1を書き込むことによって、ポートの内臓プルアップを有効にできます。
今回はビット7・6をH、ビット5・4をL、ビット3・2をプルアップ有効したいので、ビット7・6を1、ビット5・4を0、ビット3・2を1に設定します。

PINB
bit 7 6 5 4 3 2 1 0
PINB PINB7 PINB6 PINB5 PINB4 PINB3 PINB2 PINB1 PINB0

PINBの中はこのようになっています。
基本的にDDRBで入力に設定したビットの状態がHなら1、Lなら0というように自動的にレジスタに格納されます。
なので、データを入力する際は変数などにこのレジスタからデータを読み込んで処理する形になります。
そこまで使いませんが付加機能として、DDRBで出力に設定されているビットに対応した番号のビットに1を書き込むと、対応した番号のPORTBのビットが反転します。
詳しくはGetting Started Notes - Ports - AVRWikiの「PINxnのsbi命令でのトグル動作の不思議」を参照してください。

以上で設定が終わりました。
上記で設定した内容をC言語で書きまとめると以下のようになります。

int a = 0;
DDRB = 0xF0; //1111 0000
PORTB = 0xCC; //1100 1100
a = PINB;

これでポートBから入出力ができました。
しかしこれでは1バイトごとしか値を扱うことができず、不便です。
なので、少し応用してビットごとに1か0を書き込む方法を試して見ましょう。

・ビットごとのレジスタへのアクセス

AVRでは、次の方法によってビットごとの入出力が可能になります。

  • PORTB |= _BV(3); //ポートBのビット3を1に
  • PORTB &= ~_BV(4); //ポートBのビット4を0に
  • bit_is_set(PINB,3) //ポートBのビット3がHかどうか
  • bit_is_clear(PINB,3) //ポートBのビット3がLかどうか

といったように使用することができます。
しかしこれではわかりにくいので、#defineを使用してわかりやすい書き方にまとめてみます。

#define sbi(PORT,BIT) PORT|=_BV(BIT)
#define cbi(PORT,BIT) PORT&=~_BV(BIT)
#define ibi(PORT,BIT) (bit_is_set(PORT,BIT)?1:0)

上記の3行をプログラムの最初の方に書いておきます。
ヘッダファイルにしてしまってもいいかもしれません。
使い方は下記の通りです。

sbi(PORT,BIT)

指定したポートのビットを1にします。sbiはset bitの略です。
(マイコンで使う「セット」は1にするという意味)

例 : sbi(PORTB,0); //ポートBのビット0を1にします。

cbi(PORT,BIT)

指定したポートのビットを0にします。cbiはclear bitの略です。
(マイコンで使う「クリア」は0にするという意味)

例 : cbi(PORTB,1); //ポートBのビット1を0にします。

ibi(PORT,BIT)

指定したポートのビットを0にします。ibiはinput bitの略です。

例 : a = ibi(PORTB,2); //変数aにポートBのビット2がHなら1、Lなら0を代入します。

この3つを上手に使えば指定したポートのビットそれぞれに値を書き込んだりすることができます。
便利なので使ってみてください。

参考

HERO'S and heavy friends様より、mega88.pdf(MEGA88シリーズ翻訳日本語版データシート)
AVRWikiより、Getting Started Notes - Ports

履歴

[08/10/27] : 解説にレジスタ・DDR・PORT・PINの説明を追加しました
[08/09/24] : 掲載開始

inserted by FC2 system