GPIOの入出力
通常ArduinoでGPIOの読み込みや書き込みを行う場合、digitalReadやdigitalWriteを使います。
しかしdigitalReadやdigitalWriteは関数呼び出しとなるため、便利な反面遅いというデメリットがあります。
そこで処理を高速化するために、GPIOのレジスタから直接操作を行いました。
ピンの入出力やPullUpの設定といった初期化もレジスタで行うことはできますが、
入出力と比較してレジスタを直接操作するメリットが余りないので、本稿で扱いません。
GPIOの入力(digitalReadの代用)
PORTx.IN のnビット目の状態が、PIN_PXnの状態となります。
例えばPIN_PB3のピンの状態を取得したい場合、PORTB.INの3bit目を確認します。
そこで下記のようにすればピンの状態を取得することが出来ます。
_BV(n)は組み込みのマクロで、n bit目だけがセット(1になる)された値を返します。
uint8_t val;
val = PORTB.IN & _BV(3);
GPIOの出力(digitalWriteの代用)
出力させる場合も入力の時とほとんど同じレジスタを扱います。
PORTx.IN のnビット目の状態が、PIN_PXnの状態となります。
例えばPIN_PB3のピンにHIGHを出力したい場合、PORTB.INの3bit目に1をセットします。
そこで下記のようにすればピンの状態を取得することが出来ます。
outputは出力させたいデータとします。
uint8_t outval = PORTB.OUT;
outval = outval & ~_BV(n);
outval = outval | ((output & 0x01) << n);
PORTB.OUT = outval;
少しコードの説明をします。
入力より少しややこしい理由として、出力するものがHIGHかLOWかによって処理が変わるためです。
HIGHの場合、n bit目に1をセットしたいのでPORTB.OUTと_BV(n)のORで設定します。
LOWの場合、n bit目に0をセットしたいのでPORTB.OUTと_BV(n)を反転させた~_BV(n)をANDで設定します。
ただし遅くならないように、条件分岐を使わず単純な計算だけで設定しました。
最初は該当bitにANDを使い0をセットしました。次はoutputの値をORを使って設定しました。
1行で出力
下記のように1行で記述することも可能です。私はこれを関数マクロにして利用しています。
(val は出力させたい値、bitはピンのビット位置)
PORTB.OUT = (PORTB.OUT & ~_BV(bit)) | (((uint8_t) ((bool) val)) << bit);
複数を同時出力
また同じポート限定となりますが、同時に複数のピンへの出力も可能です。
(val1をPIN_PB3へ、val2をPIN_PB1へ出力させたい場合)
PORTA.OUT = (PORTA.OUT & ~(_BV(3) | _BV(1))) | ((val1 & 0x01) << 3) | ((val1 & 0x01) << 1);
最後に
次はCCLあたりを投稿する予定です。
気が向いたら、本稿の延長として割り込み設定なども調べて書いていきたいです。