[][src]Module avr_hal_generic::port

PORTx digital IO Implementations

Design Rationale

Each pin has a distinct type which allows pin-dependent HAL functionality to ensure at compile-time that the correct pins are used. For example, certain peripherals have the IO hardwired to some specific pins which can't be changed. For purposes where the exact pin does not matter, the distinct types can be 'downgraded' into a generic Pin<MODE> type. See the section about downgrading further down.

To instanciate the pin types, a port is .split() into its pins:

This example is not tested
let dp = atmega32u4::Peripherals::take().unwrap();

let mut portd = dp.PORTD.split();

let pd2 = portd.pd2.into_output(&mut portd.ddr);

Board crates usually provide a wrapper around that which makes access more convenient:

This example is not tested
let dp = arduino_leonardo::Peripherals::take().unwrap();

let mut pins = arduino_leonardo::Pins::new(

let mut led0 = pins.led_rx.into_output(&mut pins.ddr);
let mut led1 = pins.led_tx.into_output(&mut pins.ddr);
let mut led2 = pins.d13.into_output(&mut pins.ddr);


A pin's mode is modelled via the <MODE> generic parameter. Only when the pin is in the correct mode, relevant methods (e.g. set_high()) are available. Changing the mode is done via conversion methods that consume the pin:

This example is not tested
// By default, pins are floating inputs
let pd2: PD2<mode::Input<mode::Floating>> = portd.pd2;

// Convert into pull-up input
let pd2: PD2<mode::Input<mode::PullUp>> = pd2.into_pull_up_input(&mut portd.ddr);

// Convert into output
let pd2: PD2<mode::Output> = pd2.into_output(&mut portd.ddr);

// Convert into tri-state input and output.
let pd2: PD2<mode::TriState> = pd2.into_tri_state(&mut portd.ddr);

Digital Input

Digital Input pins (i.e. where MODE = mode::Input<_>) have the following methods available:

This example is not tested
// `true` if the pin is high, `false` if it is low

// `true if the pin is low, `false` if it is high

Digital Output

Digital Output pins (i.e. where MODE = mode::Output) can be used like this:

This example is not tested
// Set high or low

// Check what the pin was last set to

Digital Tri-State Output and Input

Digital I/O pins in tri-state mode (i.e. where MODE = mode::TriState), usually with an external pull-up, are useful for a one wire bus. They can be used as both output and input pins, like this:

This example is not tested
// Actively drive the pin low.
// Release the pin, allowing the external pull-up to pull the pin
// in the absence of another driver

// `true` if the pin is electrically high, `false` if it is low
// `true` if the pin is electrically low, driven by either the
// microcontroller or externally

Other Modes

Apart from input and output, certain pins can have other functionality associated with them. E.g. some pins can be used for PWM output, others as ADC inputs. For those pins, specific conversion methods exist:

This example is not tested
// Digital IO by default
let pd2 = portd.pd2;

// Make a pin an ADC channel
let pd2_analog = pd2.into_analog_input(&mut adc);

// Make a pin a PWM output
let pd2_pwm = pd2.into_output().into_pwm(&mut timer0);


As described above, usually each pin has its own distinct type. This is useful in a lot of cases but can be difficult to deal with when code does not care about the exact pin(s) it is working with. An easy example is trying to store a number of pins in an array; this is not possible when each pin has its own type.

For those usecases, a generic Pin type exists which can represent any pin. Specific pins are converted using the .downgrade() method:

This example is not tested
let pd2 = portd.pd2.into_output(&mut portd.ddr);
let pd3 = portd.pd3.into_output(&mut portd.ddr);

let pins: [Pin<mode::Output>; 2] = [pd2.downgrade(), pd3.downgrade()];



IO Modes