modm API documentation
General Purpose I/O (GPIO)

Classes

struct  modm::platform::Gpio
 
struct  modm::platform::GpioConnector< peripheral, Signals >
 
class  modm::platform::GpioInverted< Pin >
 
class  modm::platform::GpioOpenDrain< Pin >
 
class  modm::platform::GpioSet< Gpios >
 
class  modm::platform::GpioStatic< GpioData >
 
class  modm::platform::GpioStatic< detail::DataUnused >
 
class  modm::platform::SoftwareGpioPort< Gpios >
 

Typedefs

using modm::platform::GpioUnused = GpioStatic< detail::DataUnused >
 
using modm::platform::GpioB0 = GpioStatic< detail::DataB0 >
 
using modm::platform::GpioOutputB0 = GpioB0
 
using modm::platform::GpioInputB0 = GpioB0
 
using modm::platform::GpioB1 = GpioStatic< detail::DataB1 >
 
using modm::platform::GpioOutputB1 = GpioB1
 
using modm::platform::GpioInputB1 = GpioB1
 
using modm::platform::GpioB2 = GpioStatic< detail::DataB2 >
 
using modm::platform::GpioOutputB2 = GpioB2
 
using modm::platform::GpioInputB2 = GpioB2
 
using modm::platform::GpioB3 = GpioStatic< detail::DataB3 >
 
using modm::platform::GpioOutputB3 = GpioB3
 
using modm::platform::GpioInputB3 = GpioB3
 
using modm::platform::GpioB4 = GpioStatic< detail::DataB4 >
 
using modm::platform::GpioOutputB4 = GpioB4
 
using modm::platform::GpioInputB4 = GpioB4
 
using modm::platform::GpioB5 = GpioStatic< detail::DataB5 >
 
using modm::platform::GpioOutputB5 = GpioB5
 
using modm::platform::GpioInputB5 = GpioB5
 

Detailed Description

lbuild module: modm:platform:gpio

This module provides register access to GPIO and connect their signals to the respective peripherals in a compile-time verified way.

Each GPIO is represented as its own class with only static methods, which implement the modm::GpioIO interface and provide additional platform-specific methods.

using namespace modm::platform;
using Button = GpioA0;
Button::setInput(Gpio::InputType::PullUp);
bool input = Button::read();
using Led = GpioInverted<GpioB3>; // inverts the IO logic of the pin
Led::setOutput();
Led::set(input);

You can also use an unordered set of GPIOs, which is useful when configuring a large number of pins, since the register accesses will be bundled and thus less code is generated.

using Set = GpioSet<GpioA0, GpioB1, GpioC2, GpioD3>;
Set::setInput();

To write and read a set of GPIOs, you need to use an ordered implementation, which defines the pins from MSB to LSB, left-to-right. You can also check the number of ports in case your use-case requires atomic reads/writes.

using Port = SoftwareGpioPort<GpioA7, GpioA2, GpioA6, GpioA0>;
static_assert(Port::number_of_ports == 1, "Read/write needs to be atomic");
Port::setInput(Gpio::InputType::PullUp);
uint8_t nibble = Port::read();
Port::setOutput();
Port::write(nibble);

For efficient access you can use a strictly-ordered implementation with a start pin and width. Note that you can reverse the data order with a negative width.

using Port = GpioPort<GpioA0, 8>;
Port::setOutput();
Port::write(data);
using ReversePort = GpioPort<GpioB7, -8>;
ReversePort::setInput();
uint8_t data = ReversePort::read();

Finally, you can use an empty GPIO implementation in cases where the API requires a GPIO, but you don't need one, for example, a bit-banged SPI without MISO pin:

Peripheral Signals

To make it easier to connect pins with peripherals, this module implements a compile-time map of (pin, signal, peripheral). Note that you must provide both peripherals and signals to be unambiguous.

GpioConnector<Peripheral::Uart0, GpioD0::Txd, GpioD1::Rxd>::connect();

However, it is recommended to wrap this functionality into a separate function Driver::connect<Signals...>(config), so that additional driver specific pin configuration can be done:

template< class... Signals >
void Uart0::connect()
{
Connector = GpioConnector<Peripheral::Uart0, Signals...>;
Connector::disconnect(); // reset to floating input
// extract pins from signals
using Rxd = Connector::GetSignal<Gpio::Signal::Rxd>;
using Txd = Connector::GetSignal<Gpio::Signal::Txd>;
// if not found, returns GpioUnused, you can check for this case
static_assert(not Connector::isValid<Txd>,
"This UART driver requires the Txd signal");
// configure both pins
Rxd::configure(Gpio::InputType::PullUp);
Txd::setOutput();
// connect both pins to alternate functions
// This will static assert if signals do not make sense
Connector::connect();
}
// Connect these pin signals to Uart0
Uart0::connect<GpioD0::Txd, GpioD1::Rxd>();

Note that you may pass a variable number of signals to this connect function, leaving out signals you don't need and adding signals that are not required.

// Connect only one signal
Uart1::connect<GpioD0::Txd>();
// Connect more signals than required
Uart1::connect<GpioD0::Txd, GpioD2::Xck>();

External Interrupts

You can also configure the external interrupts, however, you must provide the interrupt yourself.

GpioD2::setInputTrigger(Gpio::InputTrigger::RisingEdge);
GpioD2::enableExternalInterrupt();
MODM_ISR(INT0)
{
// your code
GpioD2::acknowledgeInterruptFlag();
}
GpioD2::disableExternalInterrupt();

When using multiple Pin-Change interrupts you need to first check the corresponding flag to determine which pin triggered the collective interrupt.

GpioB1::enablePcInterrupt();
GpioB2::enablePcInterrupt();
MODM_ISR(PCINT0)
{
if (GpioB1::getPcInterruptFlag()) {
bool state = GpioB2::read();
// your code
GpioB1::acknowledgePcInterruptFlag();
}
if (GpioB2::getPcInterruptFlag()) {
bool state = GpioB2::read();
// your code
GpioB2::acknowledgePcInterruptFlag();
}
}