pw_digital_io_linux#

Digital I/O interface for Linux userspace

Unstable C++17

pw_digital_io_linux implements the pw_digital_io interface using the Linux userspace gpio-cdev interface.

Note

Currently only the v1 userspace ABI is supported (https://pwbug.dev/328268007).

API reference#

The following classes make up the public API:

LinuxDigitalIoChip#

Represents an open handle to a GPIO chip (e.g. /dev/gpiochip0).

This can be created using an existing file descriptor (LinuxDigitalIoChip(fd)) or by opening a given path (LinuxDigitalIoChip::Open(path)).

LinuxDigitalIn#

Represents a single input line and implements DigitalIn.

This is acquired by calling chip.GetInputLine() with an appropriate LinuxInputConfig.

LinuxDigitalInInterrupt#

Represents a single input line configured for interrupts and implements DigitalInInterrupt.

This is acquired by calling chip.GetInterruptLine() with an appropriate LinuxInputConfig.

LinuxDigitalOut#

Represents a single input line and implements DigitalOut.

This is acquired by calling chip.GetOutputLine() with an appropriate LinuxOutputConfig.

Warning

The Disable() implementation only closes the GPIO handle file descriptor, relying on the underlying GPIO driver / pinctrl to restore the state of the line. This may or may not be implemented depending on the GPIO driver.

Guides#

Example code to use GPIO pins:

Configure an input pin and get its state#

 1#include "pw_digital_io/polarity.h"
 2#include "pw_digital_io_linux/digital_io.h"
 3#include "pw_log/log.h"
 4#include "pw_status/try.h"
 5
 6using pw::digital_io::LinuxDigitalIoChip;
 7using pw::digital_io::LinuxInputConfig;
 8using pw::digital_io::Polarity;
 9using pw::digital_io::State;
10
11pw::Status InputExample() {
12  // Open handle to chip.
13  PW_TRY_ASSIGN(auto chip, LinuxDigitalIoChip::Open("/dev/gpiochip0"));
14
15  // Configure input line.
16  LinuxInputConfig config(
17      /* index= */ 5,
18      /* polarity= */ Polarity::kActiveHigh);
19  PW_TRY_ASSIGN(auto input, chip.GetInputLine(config));
20  PW_TRY(input.Enable());
21
22  // Get the input pin state.
23  PW_TRY_ASSIGN(State pin_state, input.GetState());
24  PW_LOG_DEBUG("Pin state: %s",
25               pin_state == State::kActive ? "active" : "inactive");
26
27  return pw::OkStatus();
28}

Configure an output pin and set its state#

 1#include "pw_digital_io/polarity.h"
 2#include "pw_digital_io_linux/digital_io.h"
 3#include "pw_status/try.h"
 4
 5using pw::digital_io::LinuxDigitalIoChip;
 6using pw::digital_io::LinuxOutputConfig;
 7using pw::digital_io::Polarity;
 8using pw::digital_io::State;
 9
10pw::Status OutputExample() {
11  // Open handle to chip.
12  PW_TRY_ASSIGN(auto chip, LinuxDigitalIoChip::Open("/dev/gpiochip0"));
13
14  // Configure output line.
15  // Set the polarity to active-low and default state to active.
16  LinuxOutputConfig config(
17      /* index= */ 4,
18      /* polarity= */ Polarity::kActiveLow,
19      /* default_state= */ State::kActive);
20  PW_TRY_ASSIGN(auto output, chip.GetOutputLine(config));
21
22  // Enable the output pin. This pulls the pin to ground since the
23  // polarity is kActiveLow and the default_state is kActive.
24  PW_TRY(output.Enable());
25
26  // Set the output pin to inactive.
27  // This pulls pin to Vdd since the polarity is kActiveLow.
28  PW_TRY(output.SetState(State::kInactive));
29
30  return pw::OkStatus();
31}

Configure an interrupt pin and handle events#

 1#include <chrono>
 2
 3#include "pw_chrono/system_clock.h"
 4#include "pw_digital_io_linux/digital_io.h"
 5#include "pw_log/log.h"
 6#include "pw_status/try.h"
 7#include "pw_thread/sleep.h"
 8#include "pw_thread/thread.h"
 9#include "pw_thread_stl/options.h"
10
11using namespace std::chrono_literals;
12
13using pw::digital_io::InterruptTrigger;
14using pw::digital_io::LinuxDigitalIoChip;
15using pw::digital_io::LinuxGpioNotifier;
16using pw::digital_io::LinuxInputConfig;
17using pw::digital_io::Polarity;
18using pw::digital_io::State;
19using pw::thread::Thread;
20
21pw::Status InterruptExample() {
22  // Open handle to chip.
23  PW_TRY_ASSIGN(auto chip, LinuxDigitalIoChip::Open("/dev/gpiochip0"));
24
25  // Create a notifier to deliver interrupts to the line.
26  PW_TRY_ASSIGN(auto notifier, LinuxGpioNotifier::Create());
27
28  // Configure input line.
29  LinuxInputConfig config(
30      /* index= */ 5,
31      /* polarity= */ Polarity::kActiveHigh);
32  PW_TRY_ASSIGN(auto input, chip.GetInterruptLine(config, notifier));
33
34  // Configure the interrupt handler.
35  auto handler = [](State state) {
36    PW_LOG_DEBUG("Interrupt handler fired with state=%s",
37                 state == State::kActive ? "active" : "inactive");
38  };
39  PW_TRY(input.SetInterruptHandler(InterruptTrigger::kActivatingEdge, handler));
40  PW_TRY(input.EnableInterruptHandler());
41  PW_TRY(input.Enable());
42
43  // There are several different ways to deal with events:
44
45  // Option A: Wait once for events.
46  PW_TRY(notifier->WaitForEvents(0));    // Non-blocking.
47  PW_TRY(notifier->WaitForEvents(500));  // Block for 500 milliseconds.
48  PW_TRY(notifier->WaitForEvents(-1));   // Block indefinitely.
49
50  // Option B: Handle events synchronously, blocking forever.
51  notifier->Run();
52
53  // Option C: Handle events in a separate thread.
54  Thread notifier_thread(pw::thread::stl::Options(), *notifier);
55  pw::this_thread::sleep_for(30s);
56  notifier->CancelWait();
57  notifier_thread.join();
58
59  return pw::OkStatus();
60}

Command-Line Interface#

This module also provides a tool also named pw_digital_io_linux which provides a basic command-line interface to the library. It provides the following sub-commands:

get#

Configure a GPIO line as an input and get its value.

Usage:

get [-i] CHIP LINE

Options:

  • -i: Invert; configure as active-low.

Arguments:

  • CHIP: gpiochip path (e.g. /dev/gpiochip0)

  • LINE: GPIO line number (e.g. 1)

set#

Configure a GPIO line as an output and set its value.

Warning

After this process exits, the GPIO line could immediately return to its hardware-controlled default state (depending on the GPIO driver).

Usage:

set [-i] CHIP LINE VALUE

Options:

  • -i: Invert; configure as active-low.

Arguments:

  • CHIP: gpiochip path (e.g. /dev/gpiochip0)

  • LINE: GPIO line number (e.g. 1)

  • VALUE: the value to set (0 = inactive or 1 = active)

watch#

Configure a GPIO line as an input and watch for interrupt events.

By default, both rising and falling edges will trigger an event. This can be changed via the -t option.

Usage:

watch [-i] [{-ta,-tb,-td}] CHIP LINE

Options:

  • -i: Invert; configure as active-low.

  • -t: Trigger for an interrupt:

    • -ta - activating edge

    • -tb - both edges (default)

    • -td - deactivating edge

Arguments:

  • CHIP: gpiochip path (e.g. /dev/gpiochip0)

  • LINE: GPIO line number (e.g. 1)