Arduinoでデジタル信号の入力を行う標準関数として、digitalReadが用意されていますが、この関数よりもっと直接的で高速な動きをする関数がportInputRegisterです。この 関数は、Arduino環境におけるマクロで、特定のデジタルポートに関連付けられた入力レジスターへのアクセスを提供します。
portInputRegister()の使い方と関数仕様
portInputRegister()は、Arduinoや他のAVRベースのマイクロコントローラで使用されるマクロで、指定したポートのデータ入力レジスタ(PINレジスタ)のアドレスを返します。このマクロを使用すると、プログラムがポートの現在の入力値を直接読み取ることができます。このマクロは、特定のI/Oポートのデータ入力レジスタ(PINレジスタ)へのポインタを返します。これにより、そのポートのピンが現在HIGH(1)またはLOW(0)に設定されているかをビット単位で読み取ることが可能です。
仕様
- 説明: 指定されたポートの入力レジスタへのポインタを返します。
- 引数:
port
: ポートの識別子。通常、digitalPinToPort(pin)
関数を使用して、特定のデジタルピンに関連付けられたポートを取得します。
- 戻り値:
- 指定されたポートの入力レジスタへのポインタ。
サンプルプログラム
以下は、ArduinoでportInputRegister()
を使用してポートDの入力値を読み取る方法を示す例です。この例では、ポートDに接続されたすべてのピンの状態を読み取り、シリアルモニタに表示します。
void setup() {
Serial.begin(9600);
// ポートDのピンを入力として設定
DDRD = 0x00; // ポートDのすべてのピンを入力モードに設定
}
void loop() {
volatile uint8_t* pinD = portInputRegister(PORTD); // ポートDのPINレジスタへのポインタを取得
uint8_t pinValue = *pinD; // ポートDの現在の入力値を読み取る
Serial.println(pinValue, BIN); // バイナリ形式で値をシリアルモニタに表示
delay(1000); // 1秒待機
}
解説
DDRD = 0x00;
この行で、ポートDのすべてのピンを入力モードに設定しています。portInputRegister(PORTD)
はポートDの入力値を格納しているPINレジスタのアドレスを返します。*pinD
でそのアドレスの内容、つまりポートの全ピンの現在の状態を読み取ります。この値は、ポートの各ピンの状態を表すビットマスクです。
ビットマスク
ポートの各ピンの状態を表すビットマスクは、マイクロコントローラにおけるポートの個々のピンが現在HIGH(1)またはLOW(0)状態にあるかを示す値です。このビットマスクは、デジタル入力ピンの現在の電圧レベル(電圧が供給されているかどうか)を読み取るために使用されます。
ビットマスクの構造
ビットマスクは、通常、1バイト(8ビット)で表され、1つのポートに対して最大8つのピンの状態を表すことができます。各ビットは特定のピンに対応しており、そのビットの値(0または1)がそのピンの状態を示します。
例
たとえば、Arduino(ATmegaマイクロコントローラ)のポートDが以下の状態にあるとします:
- PD0(ピン0): LOW
- PD1(ピン1): HIGH
- PD2(ピン2): HIGH
- PD3(ピン3): LOW
- PD4(ピン4): LOW
- PD5(ピン5): HIGH
- PD6(ピン6): HIGH
- PD7(ピン7): LOW
DDR7 | DDR6 | DDR5 | DDR4 | DDR3 | DDR2 | DDR1 | DDR0 | |
---|---|---|---|---|---|---|---|---|
ピンモード | PD7 | PD6 | PD5 | PD4 | PD3 | PD2 | PD1 | PD0 |
この場合のビットマスクは 01100110
になります。ここで、最も右のビット(LSB:Least Significant Bit)がピン0に対応し、左に進むにつれてピンの番号が増えていきます。
ビットマスクの読み取り
Arduinoや他のマイクロコントローラで portInputRegister()
マクロを使用してポートの状態を読み取る場合、返されるのはこのビットマスクです。次のようにして特定のピンの状態をチェックすることができます。
volatile uint8_t* pinD = portInputRegister(PORTD);
uint8_t pinValue = *pinD;
bool isPin2High = pinValue & (1 << 2); // PD2の状態をチェック
ここで、(1 << 2)
はビットマスク 00000100
を生成し、これとポートの現在の状態をAND演算することで、ピン2の状態がHIGHかどうかを判定しています。
ATmega328PのポートとDDRについて
ATmega328Pは、以下の主要なI/Oポートを持っています:
- PORTB: ピン8から13(Arduino UNOのデジタルピン)
- PORTC: アナログピン0から5(Arduino UNOのアナログピン)
- PORTD: ピン0から7(Arduino UNOのデジタルピン)
注意点
portInputRegister()
は直接ハードウェアレジスタにアクセスするため、ピンの設定を間違えるとハードウェアに損傷を与える可能性があります。- プログラムのポータビリティが低下するため、異なるマイクロコントローラーや Arduino モデル間での移植を行う際は、ポートの設定を適宜変更する必要があります。
- 直接ポート操作を行う場合、バグの特定が難しくなることがあるため、注意が必要です。
- 複雑さの増加: 直接ポート操作を行うと、プログラムの理解と保守が難しくなる可能性があります。正しいポートとビットマスクの選択が必要で、間違えるとハードウェアの損傷や予期しない動作を引き起こすリスクがあります。
- ポータビリティの低下: プログラムの移植性が低くなります。異なるマイクロコントローラや Arduino モデル間でのコード移植を行う際には、ハードウェアの差異を考慮して適宜コードを修正する必要があります。
- デバッグの難易度: 直接レジスタを操作するコードはデバッグが難しくなることがあります。特に、問題の原因がハードウェアレベルの操作にある場合、問題の特定と修正には詳細なハードウェア知識が必要です。
ビットマスクを使用する際は、マイクロコントローラのデータシートを参照して、各ピンのビット位置を正確に理解しておく必要があります。また、ポートに未接続のピンがある場合は、ノイズの影響を受けやすいため、適切なプルアップまたはプルダウン抵抗を配置することが推奨されます。
メリット
1. 高速な読み取り
- 直接レジスタアクセス
portInputRegister()
によって得られるポインタを使うことで、直接ハードウェアの入力レジスタにアクセスできます。これにより、digitalRead()
のような標準関数を使用する場合に比べて読み取りプロセスが格段に速くなります。digitalRead()
関数ではピン番号からポートとビットを計算し、その情報を使ってピンの状態を読み取りますが、これには相対的に多くのCPUサイクルが必要です。
2. 効率の向上
- リソース消費の削減
直接レジスタを操作することで、関数呼び出しのオーバーヘッドがなくなり、実行時のメモリ使用量も削減されます。これは特にリソースが限られている組み込みシステムにおいて大きな利点となります。
3. 精密な制御
- ビットレベルの操作
portInputRegister()
を使用することで、ポートの個々のビットを直接操作できるため、より精密な制御が可能になります。これは、特定のアプリケーションで非常に高速な応答が求められる場合や、複数のピンの状態を同時に読み取りたい場合に特に有用です。
digitalReadとの比較
digitalRead()
関数と直接ポートからのビットマスクを読み取る方法との間には、いくつかの重要な違いがあります。これらの違いを理解することは、プロジェクトの要件に基づいて最適な読み取り方法を選択するのに役立ちます。
digitalRead()の基本
- 機能:
digitalRead(pin)
は、指定されたデジタルピンからHIGHまたはLOWの状態を読み取ります。 - 使いやすさ: Arduinoで最も基本的で使いやすい入力読み取り関数です。ピン番号を指定するだけで、そのピンの状態が簡単に読み取れます。
- オーバーヘッド:
digitalRead()
は関数呼び出しとピン番号から実際のポートとビットマスクへの変換が含まれるため、直接レジスタアクセスよりも処理に時間がかかります。 - 汎用性: どのArduinoボードでも動作し、ピンのセットアップが自動で行われるため、ユーザーは内部のハードウェア構成を気にする必要がありません。
ポートから直接ビットマスクを読み取る
- 機能: 特定のポートのデータ入力レジスタ(PINレジスタ)から直接ビットマスクを読み取ります。
- パフォーマンス:
digitalRead()
よりも高速です。レジスタからの直接読み取りは、関数呼び出しのオーバーヘッドがないため、非常に効率的です。 - 複雑さ: 使用するには、マイクロコントローラのポートとピンの配置を理解している必要があります。ピン番号ではなく、ビット操作を行うため、より低レベルのアプローチが必要です。
- 制限: 具体的なハードウェア依存の知識が必要であり、ポートとピンの割り当てを正確に把握していないと誤った動作を引き起こす可能性があります。
使用シナリオの比較
- 初心者向け:
digitalRead()
は簡単で、どんなユーザーでも容易に使用できます。教育目的や簡単なプロジェクトに適しています。 - 高性能が必要な場合: タイムクリティカルなアプリケーションや高速で多数のピンの状態を読み取る必要がある場合は、直接ポートアクセスが推奨されます。これにより、関数呼び出しの遅延を排除し、プログラムの実行速度を向上させることができます。