Sean Sean - 18 days ago 8
C Question

Implementing pin numbers in AVR microcontroller similar to Arduino

I'd like to implement something similar to Arduino's pin numbers for an Atmel ATMega32U4. I've looked at Arduino's

digitalWrite
command and associated source files to see how they do it, but I think this is slightly convoluted so I wanted to implement a more basic version.

The idea is to have integers 1 to n that represent each of the I/O pins on the AVR chip. I started with an array of pointers to the addresses of the DDR/PORT registers, where the index would represent the pin:

volatile uint8_t *pin_port_dir_regs[] = {
0, // NOT USED
0, // NOT USED
0, // NOT USED
0, // NOT USED
0, // NOT USED
0, // NOT USED
0, // NOT USED
&DDRE, // PIN_7
&DDRB, // PIN_8
0, // NOT USED
0, // NOT USED
0, // NOT USED
0, // NOT USED
0, // NOT USED
0, // NOT USED
&DDRB, // PIN_15
&DDRB // PIN_16
};

volatile uint8_t *pin_port_out_regs[] = {
0, // NOT USED
0, // NOT USED
0, // NOT USED
0, // NOT USED
0, // NOT USED
0, // NOT USED
0, // NOT USED
&PORTE, // PIN_7
&PORTB, // PIN_8
0, // NOT USED
0, // NOT USED
0, // NOT USED
0, // NOT USED
0, // NOT USED
0, // NOT USED
&PORTB, // PIN_15
&PORTB // PIN_16
};


I also need to know the bit numbers in each of the DDRx/PORTx registers, so I created another array:

const uint8_t pin_bits[] = {
_BV(0), // NOT USED
_BV(0), // NOT USED
_BV(0), // NOT USED
_BV(0), // NOT USED
_BV(0), // NOT USED
_BV(0), // NOT USED
_BV(0), // NOT USED
_BV(6), // PIN_7
_BV(4), // PIN_8
_BV(0), // NOT USED
_BV(0), // NOT USED
_BV(0), // NOT USED
_BV(0), // NOT USED
_BV(0), // NOT USED
_BV(0), // PIN_14
_BV(1), // PIN_15
_BV(3) // PIN_16
};


To set the pin mode and write to the pins, I created the following functions:

void pin_mode(uint8_t pin, uint8_t direction) {
// defeference the pointer to the direction register
uint8_t port_dir_register = *(pin_port_dir_regs[pin]);

// get pin mask
uint8_t mask = pin_bits[pin];

// set its mode
if (direction == INPUT) {
port_dir_register &= ~mask;
} else {
port_dir_register |= mask;
}
}

void pin_write(uint8_t pin, uint8_t level) {
// defeference the pointer to the output register
uint8_t port_out_register = *(pin_port_out_regs[pin]);

// get pin mask
uint8_t mask = pin_bits[pin];

// set output
if (level == LOW) {
port_out_register &= ~mask;
} else {
port_out_register |= mask;
}
}


What's supposed to happen is that you would call e.g.
pin_mode(7, OUTPUT)
to set pin 7 as an output, and then
pin_write(7, HIGH)
to set the output to 1 (where OUTPUT and HIGH are predefined macros). The code compiles and uploads to the AVR successfully, but when I test the output it doesn't respond. I guess I must be writing to some memory location, but not the one corresponding to the intended registers. Does anyone see a problem with the way I am trying to do this?

Answer

The reason to why your code does not work is this step:

uint8_t port_out_register = *(pin_port_out_regs[pin]);

You want to write to a hardware register located at a specific address. To do this you need to:

*(pin_port_out_regs[pin]) = some_value;

Otherwise you will just modify stack memory to no gain.

Try to change to this instead:

void pin_write(uint8_t pin, uint8_t level)
{
    uint8_t *port_out_register = pin_port_out_regs[pin];
    uint8_t mask = pin_bits[pin];

    if (level == LOW) {
        *port_out_register &= ~mask;
    } else {
        *port_out_register |= mask;
    }
}
Comments