digitalWrite()

概要

digitalWrite()は、指定したピンにHIGHもしくはLOWを出力します。指定したピンのポートに対応するレジスタのビットの値を1にすれば、ピンがHIGHに、0にすれば、LOWになります。

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

PORTB
ビット76543210
ピン--D13D12D11D10D9D8
PORTC
ビット76543210
ピン--A5A4A3A2A1A0
PORTD
ビット76543210
ピンD7D6D5D4D3D2D1D0

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

digitalWrite()は、入力したピン番号が、どのポートのどのビットかを求め、そのビットを、出力する値がLOWのときには0に、HIGHのときには1に設定するプログラムです。

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

ソースコード

digitalWrite()は、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
void digitalWrite(uint8_t pin, uint8_t val)
{
        uint8_t timer = digitalPinToTimer(pin);
        uint8_t bit = digitalPinToBitMask(pin);
        uint8_t port = digitalPinToPort(pin);
        volatile uint8_t *out;

        if (port == NOT_A_PIN) return;

        // If the pin that support PWM output, we need to turn it off
        // before doing a digital write.
        if (timer != NOT_ON_TIMER) turnOffPWM(timer);

        out = portOutputRegister(port);

        uint8_t oldSREG = SREG;
        cli();

        if (val == LOW) {
                *out &= ~bit;
        } else {
                *out |= bit;
        }

        SREG = oldSREG;
}

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

まず、pinに対応するタイマを求めます。ATmega328Pのデジタルピンの中には、タイマに対応しているピンがあります。これは、PWM出力に利用されています。

3
        uint8_t timer = digitalPinToTimer(pin);

digitalPinToTimer()は、hardware/arduino/avr/cores/arduino/Arduino.h で定義しているマクロです。ピンがタイマに対応している場合はタイマ名が返ってきます。対応していない場合は、NOT_ON_TIMERが返ってきます。

次に、pinに対応するビットマスクを求めます。

4
        uint8_t bit = digitalPinToBitMask(pin);

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

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

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

8
        if (port == NOT_A_PIN) return;

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

ピンがタイマに対応している場合は、PWM出力を停止します。

10
11
12
        // If the pin that support PWM output, we need to turn it off
        // before getting a digital reading.
        if (timer != NOT_ON_TIMER) turnOffPWM(timer);

turnOffPWM()は、hardware/arduino/avr/cores/arduino/wiring_digital.cに定義されている関数で、指定したタイマを停止します。

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

14
        out = portOutputRegister(port);

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

最後に、valがLOWなら指定したピンに対応するポートのビットを0に、そうでなければ1に設定します。

16
17
18
19
20
21
22
23
24
25
        uint8_t oldSREG = SREG;
        cli();

        if (val == LOW) {
                *out &= ~bit;
        } else {
                *out |= 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