AVR Libc Home Page | ![]() |
AVR Libc Development Pages | ||
Main Page | FAQ | Library Reference | Additional Documentation | Example Projects |
マイクロコントローラを扱うとき、多くの仕事がデバイスに接続された周辺回路のコントロールや、コントローラを持っているサブシステム(コントローラと回路で結ばれ、通信している)の設定などに費やされます。
AVRシリーズマイクロコントローラは、この仕事に対して2つの異なった方式を提供しています。AVRは、一部 (コントローラによっては全て) のIOアドレス空間に対し、(in,out,sbi,cbiなど) 専用のIO命令でアクセスできる分離したIOアドレス空間を提供しています(これはいくつかのハイレベルCISC CPUに見られます)。
IOアドレス空間全体についてはメモリマップドIO手法によりアクセス可能です。これは通常のデータメモリと同様に読み書きできます。
IOレジスタ空間はデータメモリ空間の中で、0x20のオフセットをつけて割り付けられています。この空間より下位アドレスの部分(0x00-0x1F)はMCUの汎用レジスタ(R0-R31)に直接アクセスするための空間として予約されているからです。SRAMはIOレジスタ領域の後方に割り付けられており、0x0060または0x0100以降に割り付けられています。
訳注:これ以下の部分について、AVR-LibC 1.2.x以降では下記のoutb()マクロは既にサポートされていません。実質的に現在のバージョンでは PORTA = 0x33; 形式の書き方しかないと考えて下さい。こう書いても、最適化により可能なら短くて高速なout命令を使用するようになるのは同様です。
※PORTBに0x33を書き込むという動作に関して、以下の2通りが考えられます。(mega8の場合)
コンパイラはこれらの考えられるいくつかの方法から、その場に応じた適切な書き込み方法を選択します。
WinAVRでは単にPORTB=0x33;
と記載するだけ。コンパイラは以下より3番目の方法を選択します。
しかし、mega128のPORTFレジスタ(メモリアドレス0x62、IOレジスタアドレスなし)が相手だと、3番目の手段はとれません。
sts 0x0038,0x33 ;PORTBのメモリマップドアドレス0x0038に0x33を書き込む
ldi XL,0x38
ldi XH,0x00
st X,0x33 ;X=0x0038, [X}←0x33
out 0x13,0x33 ;PORTBのIOレジスタアドレス0x13に0x33を書き込む
※また、IOレジスタアドレス0x20以降とそれ以前のレジスタにも若干の差異があります。たとえばビット操作命令
SBIはIOレジスタアドレス0x1F以前に対してのみ使えます。
前者が選ばれますが、MCUCR(IOアドレス0x35)だと後者が、PORTFなどだとlds/ORI/sts命令が使われることになります。
PORTB |= 0x01;は、PORTBに関しては
sbi 0x13,0 ;PORTBのIOレジスタアドレス0x13のbit0をセットする
in r16,0x13 ;PORTBのIOレジスタアドレス0x13に0x33を書き込む
ori r16,0x01
out 0x13,r2 ;
※これより原文訳
AVR Libcは両方の手段をサポートします。デフォルトではメモリマップドIO手法を使いますが、これはプログラマからは隠されていますので、以下のマクロ outb()
のような方法でもアクセスできます。
#include <avr/io.h> outb(PORTA, 0x33);
もしくは、直接シンボリックアドレスに対し値を割り付けることもできます。
PORTA = 0x33;
コンパイラが実際のアクセスを行うときどちらの方法を用いるかについては、プログラマが上記2つどちらの方法でコードを書いたか、ということとは関係なく決定されます。そのため、下記のようにメモリマップド手法的に書いたときでさえ、
PORTA |= 0x40;
コンパイラはsbi命令を使うように最適化してしまいます。(もちろんIOアドレスがsbi命令が扱えるアドレスの範囲内にあり、右辺が定数又はコンパイル時決定できる変数である場合のみ)
C言語でのメモリマップドIO手法の利点は、プログラムを他のAVR用Cコンパイラに対しより移植性の良いものにできることです。読みやすいと感じる人もいることでしょう。たとえば、以下の2つの分は同等のものですが・・・・
outb(DDRD, inb(DDRD) & ~LCDBITS); DDRD &= ~LCDBITS;
生成されるコードは両者で全く同じです。最適化しない場合はコンパイラはメモリマップド手法でコードを生成しますが、最適化をONにするとより速くよりコードの小さい in/out命令を用いるようになります。
いくつかの16bitタイマ関連IOレジスタについては、割り込みの内部と外部で操作を行う場合には特別な注意が必要です。FAQ内の、なぜいくつかの16-bitタイマレジスタ値が壊れるの? を参照ください。
上で解説したように、AVRの単一ビットセット・クリア命令は標準のC言語ビット操作コマンドでも提供されます。sbi/cbiコマンドは直接はサポートされていませんが、従来の sbi (sfr, bit) は、sfr |= _BV(bit) の形式に置き換え可能です。
sbi(PORTB, PB1);→ PORTB |= _BV(PB1);
これは実際にはsbi命令を直接使用するよりもっと柔軟です。WinAVRの最適化機構は、利用可能ならハードウェアsbiを用い、そうでなければ read-or演算-writeによって実現します。あなたはどちらを選ぶべきかを調べる必要はありません。
これと同様に、cbi (sfr,bit) は、sfr &= ~(_BV(bit)); に置き換え可能です。
Modules | |
Additional notes from <avr/sfr_defs.h> | |
Bit manipulation | |
#define | _BV(bit) (1 << (bit)) |
IO register bit manipulation | |
#define | bit_is_set(sfr, bit) (_SFR_BYTE(sfr) & _BV(bit)) |
#define | bit_is_clear(sfr, bit) (!(_SFR_BYTE(sfr) & _BV(bit))) |
#define | loop_until_bit_is_set(sfr, bit) do { } while (bit_is_clear(sfr, bit)) |
#define | loop_until_bit_is_clear(sfr, bit) do { } while (bit_is_set(sfr, bit)) |
|
#include <avr/io.h>
ビット番号をバイト値に変換
|
|
#include <avr/io.h>
IOレジスタ sfr 上の ビット番号 |
|
#include <avr/io.h>
IOレジスタ sfr 上の ビット番号 |
|
#include <avr/io.h>
IOレジスタ sfr 内のビット番号 |
|
#include <avr/io.h>
IOレジスタ sfr 内のビット番号 |