Using AVRs GPIO pins with avr-gcc

24 Apr 2019 - tsp

GPIO code requires on to include the avr/io header

#include <avr/io.h>

I/O ports are controlled via the DDR, PORT and PIN registers for the respective banks (I/O bank A is controlled via DDRA, PORTA and PINA and vice versa).

Configuration of data direction

The DDR register is the data direction register which defined which direction data is sent or read from. Setting the bit corresponding to the given pin (Bit 0 in DDRA is PA0, etc.) to zero sets the pin to be an input, setting the pin to one sets it to be an input.

To configure all pins as input one could set

DDRA = 0x00;

If one just wants to set the PA0 to output but all other pins to input one would set

DDRA = 0x01;

To be more easy readable one could define constants for each Pin like

#define PIN0        0
#define PIN1        1
#define PIN2        2
#define PIN3        3
#define PIN4        4
#define PIN5        5
#define PIN6        6
#define PIN7        7

#define PIN0_OUT    0x01
#define PIN1_OUT    0x02
#define PIN2_OUT    0x04
#define PIN3_OUT    0x08
#define PIN4_OUT    0x10
#define PIN5_OUT    0x20
#define PIN6_OUT    0x40
#define PIN7_OUT    0x80

#define PIN0_IN     0x00
#define PIN1_IN     0x00
#define PIN2_IN     0x00
#define PIN3_IN     0x00
#define PIN4_IN     0x00
#define PIN5_IN     0x00
#define PIN6_IN     0x00
#define PIN7_IN     0x00

This would allow to define the directions as

DDRA = PIN0_OUT | PIN1_IN | PIN2_IN | PIN3_IN | PIN4_IN | PIN5_IN | PIN6_IN | PIN7_IN;

Setting pullup configuration or output values

Second one can use the PORT register to either write the current output state (for an output pin) or the pullup settings. To enable internal pullups one sets the respective PORT bit to 1, to disable pullups one writes a 0. One can use the same trick using defined to make code more readable:

#define PIN0_PULLUP    0x01
#define PIN1_PULLUP    0x02
#define PIN2_PULLUP    0x04
#define PIN3_PULLUP    0x08
#define PIN4_PULLUP    0x10
#define PIN5_PULLUP    0x20
#define PIN6_PULLUP    0x40
#define PIN7_PULLUP    0x80

#define PIN0_NOPULL    0x00
#define PIN1_NOPULL    0x00
#define PIN2_NOPULL    0x00
#define PIN3_NOPULL    0x00
#define PIN4_NOPULL    0x00
#define PIN5_NOPULL    0x00
#define PIN6_NOPULL    0x00
#define PIN7_NOPULL    0x00

This allows one to simply configure the pullups the same way as the I/O directions:

PORTA = PIN0_PULLUP | PIN1_PULLUP | PIN2_NOPULL | PIN3_NOPULL | PIN4_NOPULL | PIN5_NOPULL | PIN6_NOPULL | PIN7_NOPULL;

Of course one has to be careful to not overwrite current output values. To write output values the PORT register is used also. Writing a logical 0 pulls the output low, writing a logical 1 pulls the output high.

To change just a single value one can use logical OR or AND

PORTA = PORTA | (1 << PIN1); // Enable PA1

PORTA = PORTA & (~(1 << PIN2)); // Disable PA2

Reading input

To read inputs the PIN register is used. To check if a given pin is asserted:

if(PINA & (1 << PIN4)) {
   // Whatever we want to do if PA4 is set
}

An Arduino like set of functions


/*
    Note that every microcontroller has a
    different set of I/O banks. An ATMEGA328P
    would - for example - have no A bank and
    thus no PORTA/DDRA/PINA registers.
*/

#define PA          0
#define PB          1
#define PC          2
#define PD          3

#define PIN0        0
#define PIN1        1
#define PIN2        2
#define PIN3        3
#define PIN4        4
#define PIN5        5
#define PIN6        6
#define PIN7        7

#define MODE_INPUT  0
#define MODE_OUTPUT 1

#define PULLUP      1
#define NOPULL      0

void gpioSetPinMode(uint8_t port, uint8_t pin, uint8_t mode, uint8_t initialValue) {
    switch(port) {
        case PA:
            if(mode == MODE_INPUT) {
                DDRA = DDRA & (~(1 << pin));
            } else {
                DDRA = DDRA | (1 << pin);
            }
            if(initialValue) {
                PORTA = PORTA | (1 << pin);
            } else {
                PORTA = PORTA & (~(1 << pin));
            }
            return;
        case PB:
            if(mode == MODE_INPUT) {
                DDRB = DDRB & (~(1 << pin));
            } else {
                DDRB = DDRB | (1 << pin);
            }
            if(initialValue) {
                PORTB = PORTB | (1 << pin);
            } else {
                PORTB = PORTB & (~(1 << pin));
            }
            return;
        case PC:
            if(mode == MODE_INPUT) {
                DDRC = DDRC & (~(1 << pin));
            } else {
                DDRC = DDRC | (1 << pin);
            }
            if(initialValue) {
                PORTC = PORTC | (1 << pin);
            } else {
                PORTC = PORTC & (~(1 << pin));
            }
            return;
        case PD:
            if(mode == MODE_INPUT) {
                DDRD = DDRD & (~(1 << pin));
            } else {
                DDRD = DDRD | (1 << pin);
            }
            if(initialValue) {
                PORTD = PORTD | (1 << pin);
            } else {
                PORTD = PORTD & (~(1 << pin));
            }
            return;
    }
}

uint8_t gpioReadPin(uint8_t port, uint8_t pin) {
    switch(port) {
        case PA: return (PINA & (1 << pin));
        case PB: return (PINB & (1 << pin));
        case PC: return (PINC & (1 << pin));
        case PD: return (PIND & (1 << pin));
    }
}

void gpioSetPin(uint8_t port, uint8_t pin, uint8_t value) {
    switch(port) {
        case PA: PORTA = ((value != 0) ? (PORTA | (1 << pin)) : (PORTA & (~(1 << pin)))); return;
        case PB: PORTB = ((value != 0) ? (PORTB | (1 << pin)) : (PORTB & (~(1 << pin)))); return; 
        case PC: PORTC = ((value != 0) ? (PORTC | (1 << pin)) : (PORTC & (~(1 << pin)))); return;
        case PD: PORTD = ((value != 0) ? (PORTD | (1 << pin)) : (PORTD & (~(1 << pin)))); return;
    }
}

This article is tagged: AVR, Programming, Tutorial, Microcontroler


Dipl.-Ing. Thomas Spielauer, Wien (webcomplains389t48957@tspi.at)

This webpage is also available via TOR at http://jugujbrirx3irwyx.onion/

Valid HTML 4.01 Strict Powered by FreeBSD IPv6 support