diff --git a/drivers/avr/pio.h b/drivers/avr/pio.h index dd3772a..e84355b 100644 --- a/drivers/avr/pio.h +++ b/drivers/avr/pio.h @@ -36,6 +36,47 @@ #include #endif +/** AVR microcontrollers use two flip-flops per PIO stored in two + separate registers: the data direction register (DDRx) and the + port register (PORTx). The DDRx register controls the whether the + pin is an input or an output; the PORTx register determines the + output state when the pin is an output but also doubles as an + enable for a pull-up resistor when the pin is an input. + + Mode: DDRx PORTx + Input, no pull-up 0 0 + Input, pull-up 0 1 + Output, low 1 0 + Output, high 1 1 + + These two registers cannot be changed at the same time so a + transient intermediate state can occur if both these registers + need to be changed. This is only a concern for pins that need to + switched between being an input and an output; say for a + bidirectional communication link. + + Let's consider the scenario where we change DDRx before PORTx. + + If we want to reconfigure a PIO from an input with no pull-up to a + high output it will be momentarily a low output. + + If we want to reconfigure a PIO from an input with pull-up to a + low output it will be momentarily a high output. + + Let's consider the alternative scenario where we change DDRx after PORTx. + + If we want to reconfigure a PIO from an input with no pull-up to a + high output it will be momentarily an input with a pull-up. This + is not a bad option since a high-impedance load will not notice. + + If we want to reconfigure a PIO from an input with pull-up to a + low output it will be momentarily an input with no-pullup. + + p.s. I am sure this subtle quirk has caused much grief to some + embedded systems engineers. + */ + + /** Define port names; note not all the ports are available on some AVRs. */ @@ -58,7 +99,7 @@ typedef enum pio_config_enum #ifdef DEBUG -/* Define a PIO as a structure in terms of a port and a bit. */ +/* Define PIO as a unique integer. */ #define PIO_DEFINE(PORT, PORTBIT) ((PORT) * 8 + (PORTBIT)) @@ -164,13 +205,13 @@ bool pio_config_set (pio_t pio, pio_config_t config) switch (config) { case PIO_OUTPUT_LOW: - PIO_DATA_ (pio) &= ~PIO_BITMASK_ (pio); PIO_DDR_ (pio) |= PIO_BITMASK_ (pio); + PIO_DATA_ (pio) &= ~PIO_BITMASK_ (pio); return 1; case PIO_OUTPUT_HIGH: - PIO_DATA_ (pio) |= PIO_BITMASK_ (pio); PIO_DDR_ (pio) |= PIO_BITMASK_ (pio); + PIO_DATA_ (pio) |= PIO_BITMASK_ (pio); return 1; case PIO_INPUT: @@ -265,7 +306,7 @@ bool pio_output_get (pio_t pio) /** Set pio to desired state. @param pio - @param state value to write pio */ + @param state value to write to pio */ static inline void pio_output_set (pio_t pio, bool state) {