Skip to main content
Engineering LibreTexts

24.1: Introduction

  • Page ID
  • The discussion that follows deals strictly with two-state high/low logic level sensing. For continuously variable analog signals see the Bits & Pieces entry covering analogRead(). Through the pinMode() function, or by directly accessing the appropriate data direction register bits (DDRx), the general purpose IO connections can be configured to read the state of external switches or logic levels. With the Arduino Uno, 5 volts represents a logic high while 0 volts represents a logic low. An added capacity of the ATmega 328P on the Uno is the ability to include an optional internal pull-up resistor at the input pin. This allows connection of a simple passive short-to-ground switch (i.e. the input pin floats high when the switch is open and goes low when the switch is engaged).

    To read individual pin inputs the Arduino system offers the digitalRead() function. Multiple pins can be read simultaneously by directly accessing the appropriate register, which we will examine afterward. Below is a copy of the online documentation for the digitalRead() function:

    Figure \(\PageIndex{1}\): digitalRead docs



    Reads the value from a specified digital pin, either HIGH or LOW.


    digitalRead( pin )


    pin: the number of the digital pin you want to read (int)


    HIGH or LOW

    int ledPin = 13;    // LED connected to digital pin 13
    int inPin = 7;      // pushbutton connected to digital pin 7
    int val = 0;        // variable to store the read value
    void setup()
        pinMode(ledPin, OUTPUT);      // sets the digital pin 13 as output
        pinMode(inPin, INPUT);        // sets the digital pin 7 as input
    void loop()
        digitalWrite(ledPin, val);    // read the input pin
        val = digitalRead(inPin);     // sets the LED to the button's value

    Sets pin 13 to the same value as the pin 7, which is an input.


    If the pin isn't connected to anything, digitalRead() can return either HIGH or LOW (and this can change randomly).

    The analog input pins can be used as digital pins, referred to as A0, A1, etc.

    See also

    A slightly cleaned-up version of the source code follows (found in the file wiring_digital.c):

    int digitalRead( uint8_t pin )
        uint8_t timer, bit, port;
        timer = digitalPinToTimer( pin );
        bit = digitalPinToBitMask( pin );
        port = digitalPinToPort( pin );
        if (port == NOT_A_PIN)                    return LOW;
        if (timer != NOT_ON_TIMER)                turnOffPWM(timer);
        if (*portInputRegister(port) & bit)       return HIGH;
        return LOW;
    static void turnOffPWM( uint8_t timer )
        switch ( timer )
            case TIMER0A:   cbi( TCCR0A, COM0A1 );    break;
            case TIMER0B:   cbi( TCCR0A, COM0B1 );    break;
            // and so forth for all available timers, not shown

    The first three lines convert the Arduino pin designator to the appropriate ATmega 328P port, bit number and timer. If the port is invalid, the function exits.

    timer = digitalPinToTimer( pin );
    bit = digitalPinToBitMask( pin );
    port = digitalPinToPort( pin );
    if (port == NOT_A_PIN) return LOW;

    The timer is important because the Arduino system preconfigures the Uno’s three on-board timers for use with the analogWrite() function through a pulse width modulation scheme. This affects six of the possible pins. For proper operation of the digital read, these timers need to be turned off. We saw this same bit of code inside the digitalWrite() function.

    if (timer != NOT_ON_TIMER) turnOffPWM(timer);

    At this point the contents of the input register are read (the direct name of the input register is PINx) and then ANDed with the requested bit. This removes all of the other bits so we can return either a simple high or low.

    if (*portInputRegister(port) & bit) return HIGH;
    return LOW;

    The function used to turn off the pulse width modulation timers is little more than a switch/case statement. If the specified Arduino pin is hooked up to a timer internally, that timer is found in the switch statement and a cbi() call is executed on the appropriate timer-counter control register. The cbi() function translates to a single assembly language instruction to clear the specified bit in the control register, thus turning off that timer.

    static void turnOffPWM( uint8_t timer )
        switch (timer)
            case TIMER0A:    cbi(TCCR0A, COM0A1);    break;
            case TIMER0B:    cbi(TCCR0A, COM0B1);    break;
            case TIMER1A:    cbi(TCCR1A, COM1A1);    break;
            case TIMER1B:    cbi(TCCR1A, COM1B1);    break;
            case TIMER2A:    cbi(TCCR2A, COM2A1);    break;
            case TIMER2B:    cbi(TCCR2A, COM2B1);    break;

    In some applications, several bits need to be read at once, for example when reading parallel data. This can be performed through a direct access of the appropriate PINx register. PINx is rather like the fraternal twin of the PORTx register. While PORTx is used to write digital data to an external connection, PINx is where you read digital data from an external connection. Just as there are four output port registers, A through D, there are four input pin registers, A through D. Not all microcontrollers are configured this way. Some of them use the same register for both reading and writing (the function being controlled by the associated data direction register).

    Here is how to directly access a single bit on a non-timer connected pin. First, clear the desired data direction register bit to activate input mode. Second, if desired, set the same bit in the associated port register to enable the optional pull-up resister. If you don’t want the pull-up, leave that bit clear. Finally, read the desired bit in the pin register and AND it with the bit mask to remove the other bits. For example, to read bit 4 (0x10 or 00010000 in binary) on port B:

    DDRB &= (~0x10);        // activate input mode
    PORTB |= 0x10;          // enable pull-up or use the bitSet macro
                            // bitSet( PORTB, 4 );
    value = PINB & (~0x10); // retrieve data

    It is only minor work to alter this for multiple bits. To read both bits 0 and 4 but without the pull-up resistors (bit pattern 00010001 or 0x11):

    DDRB &= (~0x11);            // activate input mode
    PORTB &= (~0x11);           // disable pull-up
    value = PINB & (~0x11);     // retrieve data bits

    or if you want the bits separately:

    value0 = PINB & (~0x01);    // retrieve data bit 0
    value4 = PINB & (~0x10);    // retrieve data bit 4