Arduinoで遊ぶページ

Arduinoで遊んだ結果を残すページです。
garretlab
pinMode()

pinMode()

概要

Arduino(ATmega328P)のデジタル入出力は、各ピンに対応したポートと呼ばれる双方向(読み書き可能)のI/Oを通じて行います。

入力に設定したピンをHIGHにすることで内蔵のプルアップ抵抗の設定も行うことができます。Arduino 1.0まではpinMode()だけでは実現できず、pinMode()でピンを入力に設定した後、digitalWrite()でピンにHIGHを出力する必要がありました。Arduino 1.0.1からは、pinMode()だけで実現できるように修正されています。

pinMode()は、指定したピンの入出力モードを設定します。実際には指定したピンのポートに対応するDDR(Data Direction Register)のビットの値を設定することで実現します。ビットが0のとき入力モードで1のとき出力モードになります。

DDRには、DDRBとDDRC、DDRDの3種類があります。それぞれのレジスタはArduino Unoのピンと以下のように対応しています。Axはアナログピン、Dxはデジタルピンです。初期値は0(入力)です。

DDRB
ビット 7 6 5 4 3 2 1 0
ピン - - D13 D12 D11 D10 D9 D8
DDRC
ビット 7 6 5 4 3 2 1 0
ピン - - A5 A4 A3 A2 A1 A0
DDRD
ビット 7 6 5 2 4 3 1 0
ピン D7 D6 D5 D4 D3 D2 D1 D0

デジタルピンの値を決めるレジスタには、PORTBとPORTC、PORTDの3種類あります。それぞれのレジスタはArduino Unoのピンと以下のように対応しています。Axはアナログピン、Dxはデジタルピンです。初期値は0(LOW)です。

PORTB
ビット 7 6 5 4 3 2 1 0
ピン - - D13 D12 D11 D10 D9 D8
PORTC
ビット 7 6 5 4 3 2 1 0
ピン - - A5 A4 A3 A2 A1 A0
PORTD
ビット 7 6 5 2 4 3 1 0
ピン D7 D6 D5 D4 D3 D2 D1 D0

pinMode()は、入力したピン番号から、

  • DDRxのどのレジスタのどのビットかを求め、
  • そのビットを、入力のときは0、出力のときは1に設定する

プログラムです。

内蔵のプルアップ抵抗を有効にする場合は、DDRxで該当のピンを入力にした後、そのピンをHIGHに設定します(有効にしない場合はLOWに設定します)。

pinMode()のリファレンスはこちらをご覧ください。

ソースコード

pinMode()は、hardware/arduino/avr/cores/arduino/wiring_digital.c に定義されています。以下に全ソースコードを示します。

void pinMode(uint8_t pin, uint8_t mode)
{
	uint8_t bit = digitalPinToBitMask(pin);
	uint8_t port = digitalPinToPort(pin);
	volatile uint8_t *reg, *out;

	if (port == NOT_A_PIN) return;

	// JWS: can I let the optimizer do this?
	reg = portModeRegister(port);
	out = portOutputRegister(port);

	if (mode == INPUT) { 
		uint8_t oldSREG = SREG;
                cli();
		*reg &= ~bit;
		*out &= ~bit;
		SREG = oldSREG;
	} else if (mode == INPUT_PULLUP) {
		uint8_t oldSREG = SREG;
                cli();
		*reg &= ~bit;
		*out |= bit;
		SREG = oldSREG;
	} else {
		uint8_t oldSREG = SREG;
                cli();
		*reg |= bit;
		SREG = oldSREG;
	}
}

入力はpinとmodeで、それぞれ、uint8_t型の変数です。

まず、pinに対応するビットを求めます。

        uint8_t bit = digitalPinToBitMask(pin);

digaitalPinToBitMask()は、hardware/arduino/avr/cores/arduino/Arduino.h で定義しているマクロで、上のDDRの表で示した各ピンに対応するビットを1にした値が返ってきます。例えば、pinに13を指定すると、(DDRBの)D13に対応するビット5を1にした、0b00100000 という値が返ってきます。

次に、pinに対応するポートを求めます。

	uint8_t port = digitalPinToPort(pin);

digitalPinToPort()も、hardware/arduino/avr/cores/arduino/Arduino.h で定義しているマクロで、ピンに対応するポートを返却します。ピン番号が0から7のときはPD、8から13のときはPB、14(A0)から19(A5)のときはPCという値を返却します。PBとPC、PDは、hardware/arduino/variants/standard/pins_arduino.hで、それぞれ2と3、4と定義されています。pinが13のときはPBが返ってきます。

返ってきたportがNOT_A_PINであれば、何もせずにこの関数は終了します。

        if (port == NOT_A_PIN) return;

Arduino Unoでは、(入力値が正しい場合は)digitalPinToPort()はNOT_A_PINを返すことはありません。入力値が間違っている場合は、配列の範囲外を参照することになり、動作は未定義です。つまり、引数を間違えてはいけません。

その後、portをDDRxの対応するレジスタに変換します。

	reg = portModeRegister(port);

portModeRegister()は、hardware/arduino/avr/cores/arduino/Arduino.hで定義しているマクロで、portをDDRのアドレスに変換します。入力した値により、DDRB・DDRC・DDRDのいずれかが返ってきます。portがPBのときはDDRBが返ってきます。

次に、portをPORTxに対応するレジスタに変換します。

	out = portOutputRegister(port);

portOutputRegister()は、hardware/arduino/avr/cores/arduino/Arduino.hで定義しているマクロで、portをPORTのアドレスに変換します。入力した値により、PORTB・PORTC・PORTDのいずれかが返ってきます。portがPBの場合はPORTBが返ってきます。

最後に、modeに応じた処理を行います。

modeがINPUTの場合
指定したピンに対応するDDRのビットを0にし、PORTのビットも0にします。
modeがINPUT_PULLUPの場合
指定したピンに対応するDDRのビットを0にし、PORTのビットを1にします。
modeがOUTPUTの場合(実際にはINPUTでもINPUT_PULLUPでもない場合)
指定したピンに対応するDDRのビットを1にします。
	if (mode == INPUT) { 
		uint8_t oldSREG = SREG;
		cli();
		*reg &= ~bit;
		*out &= ~bit;
		SREG = oldSREG;
	} else if (mode == INPUT_PULLUP) {
		uint8_t oldSREG = SREG;
		cli();
		*reg &= ~bit;
		*out |= bit;
		SREG = oldSREG;
	} else {
		uint8_t oldSREG = SREG;
		cli();
		*reg |= bit;
		SREG = oldSREG;
	}

SREGは状態レジスタで割り込み許可やフラグやキャリーフラグなどを保持する8ビットのレジスタです。ピンのモード設定を変更する前にoldSREGに値を退避します。

その後、cli()で割り込みを禁止します。

次に、レジスタの値を変更します。

指定したビットを0にするには、bitを反転させて論理積をとります。例えば、デジタルの13番ピンの場合は、bitの値は0b00100000なので、~0b00100000を計算すると0b11011111となり、これと、もとのDDRの論理積をとると、他のビットは変更することなく、13番ピンに対応するビットだけを0にすることができます。

指定したビットを1にするには、bitと論理和をとります。

最後に退避した状態レジスタをもとに戻して終了です。禁止した割り込みを許可していないのは、禁止前の状態レジスタの値に戻すことで禁止前の状態に戻るためと思われます。

バージョン

Arduino 1.8.5



メニューを表示するためにJavaScriptを有効にしてください。

Arduinoで遊ぶページ
Copyright © 2016 garretlab all rights reserved.
inserted by FC2 system