Pin Modes

Digital IO pins have a number of modes they can be driven or read in. The different modes are accessed via several port registers.

DIR defines if the port is input (0) or output (1). When set as an input, the pin is high impedance, which also behaves like the pin is tristated. When set as an output, the pin is driven low or high based on the OUT register.

For example, the following sets PC4 as an output, and drives it low:


An important detail about a pin being driven low is that is acts like the pin is directly connected to ground. This means it will sink current to ground. If we were to wire an LED to +3.3V with a current limiting resistor to a pin, the circuit is complete when the pin is driven low, and therefore the LED lights.

This situation is called active low and is very common in many circuits. However, since the circuit is complete when we write a zero to the OUT register, it is logically opposite to the common mindset that one means on. It would be useful to put a comment on the OUT clearing line above to make it obvious it’s really active low.

To help this, pins can be configured to invert the OUT register as it gets applied to the pin:

PORTC.PIN4CTRL |= INVEN_bm; /* invert OUT/IN */
PORTC.DIRSET = PIN4_bm; /* make output */
PORTC.OUTSET = PIN4_bm; /* actually will drive low, since inverted */

This can make it easier to wire the hardware a specific way, without having to remember the pin is actually active-low.

Tristate is where the pin is neither being driven high or low. This is commonly used on shared pins with other devices to keep off the pin. The common way to do this is set the direction to input, since an input pin is always high-impedance (and therefore, does not pull or drive a pin in any direction).

/* in this example, we're clearly a shared line */
PORTC.OUTCLR = PIN4_bm; /* active high in this case, so no longer assert */
PORTC.DIRCLR = PIN4_bm; /* make input, therefore tristated */

Switching back and forth between input and output modes can be distracting to the code flow, as input being tristated is almost a side-effect rather than explicitly stated.

Thankfully, we have output modes which allow us to choose between drive and tristate based purely on OUT instead. For example, this is an active-low interrupt line which when written 1 is tristated, and written 0 is driven low.

PORTC.PIN4CTRL |= PORT_OPC_WIREDAND_gc; /* tristate=1, low=0 */
PORTC.OUTSET = PIN4_bm; /* don't assert */
PORTC.OUTCLR = PIN4_bm; /* assert active-low shared line */

These can also be combined with inversion, so you can write 1 as active-low driven, and 0 as tristate.

There are other modes available on output pins as well, covering various cases of pull ups and pull downs that are built into the ports, and maintaining state on a line that is no longer being drive.

Leave a Reply

Tech and Electronics