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(入力)です。

プルアップ抵抗を有効にする際は、DDRの設定以外に、デジタルピンの出力を変更する必要があります。デジタルピンの値を決めるレジスタには、PORTBとPORTC、PORTDの3種類あります。それぞれのレジスタはArduino Unoのピンと以下のように対応しています。Axはアナログピン、Dxはデジタルピンです。初期値は0(LOW)です。

DDRB/PORTB
ビット76543210
ピン--D13D12D11D10D9D8
DDRC/PORTC
ビット76543210
ピン--A5A4A3A2A1A0
DDRD/PORTD
ビット76543210
ピンD7D6D5D4D3D2D1D0

Arduinoのピン番号からATmega328Pのレジスタに変換する大まかな手順は以下の通りです。

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

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

プログラムです。

プルアップ抵抗を有効にする場合は、上記に加えて、

  • ピンに対応するPORTレジスタの値を1にします。

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

ソースコード

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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に対応するビットとポートを求めます。

3
4
        uint8_t bit = digitalPinToBitMask(pin);
        uint8_t port = digitalPinToPort(pin);

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

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であれば、何もせずにこの関数は終了します。

7
        if (port == NOT_A_PIN) return;

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

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

10
11
        reg = portModeRegister(port);
        out = portOutputRegister(port);

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

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

modeがINPUTの場合
指定したピンに対応するDDRのビットを0にし、PORTのビットも0にします。
modeがINPUT_PULLUPの場合
指定したピンに対応するDDRのビットを0にし、PORTのビットを1にします。
modeがOUTPUTの場合(実際にはINPUTでもINPUT_PULLUPでもない場合)
指定したピンに対応するDDRのビットを1にします。
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
        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 AVR Boards 1.8.6

最終更新日

March 21, 2023

inserted by FC2 system