Using Nt GPIO
Naming a GPIO line, reading a push-to-talk switch, driving an LED, and getting permission on Linux.
Link Nt::Gpio (or Nt6::Gpio from an installed package) and include <QGpioLine>. On Zephyr, also set CONFIG_NT_GPIO in prj.conf.
find_package(Nt6 6 REQUIRED)
add_executable(app main.cpp)
target_link_libraries(app PRIVATE Nt6::Gpio)
Nt GPIO is not part of Qt. A program that must also build with Qt keeps its use of QGpioLine behind an interface of its own.
Naming a Line
A QGpioLine is made from the name of one line:
Name | Platform | Meaning |
|---|---|---|
| Linux | The line with this name, from |
| Linux | Line 17 of |
| Zephyr | Pin 17 of the GPIO device with this name, the name that |
gpioinfo, from the libgpiod tools, lists the chips and lines of a Linux machine.
Reading a Switch
A new line is an input. Set whether it is active low, its bias and its debounce interval before open(). For a push-to-talk switch that pulls the line to ground:
QGpioLine ptt(parser.positionalArguments().constFirst());
ptt.setActiveLow(true); // pressed pulls the line low
ptt.setBias(QGpioLine::PullUp); // released floats high
ptt.setDebounceInterval(20); // ignore contact bounce
if (!ptt.open()) {
std::fprintf(stderr, "ptt: %s\n", qPrintable(ptt.errorString()));
return 1;
}
value() is then the logical value, true while the switch is held, and valueChanged() is emitted each time it changes:
QObject::connect(&ptt, &QGpioLine::valueChanged, [&](bool pressed) {
std::printf("TX %s\n", pressed ? "on" : "off");
std::fflush(stdout);
if (led.isOpen())
led.setValue(pressed);
});
The line is watched for edges where the hardware can, and polled every pollInterval() milliseconds where it cannot; isEventDriven() tells which. With a debounce interval, a change is reported once the line has been steady for that long, so a switch reports one press however much its contacts bounce.
Driving a Line
An output line is opened with the value set by setValue() beforehand, false unless changed, and changed with setValue():
QGpioLine led(parser.value(ledOption));
led.setDirection(QGpioLine::Output);
if (parser.isSet(ledOption) && !led.open()) {
std::fprintf(stderr, "ptt: %s\n", qPrintable(led.errorString()));
return 1;
}
Handling Errors
open() returns false and sets error() and errorString() when the line cannot be had. BusyError means that another program, or a kernel driver, holds the line. A ResourceError while the line is open means it went away, for example because a USB GPIO adapter was unplugged; the line is closed, and must be opened again:
QObject::connect(&ptt, &QGpioLine::errorOccurred, [&](QGpioLine::GpioError error) {
if (error == QGpioLine::ResourceError) { // the chip went away
std::fprintf(stderr, "ptt: %s\n", qPrintable(ptt.errorString()));
app.exit(1);
}
});
Permission on Linux
/dev/gpiochip* belongs to root, or to the group gpio on some distributions. To let a user in the group plugdev open them, add a udev rule such as this as /etc/udev/rules.d/60-gpio.rules:
SUBSYSTEM=="gpio", KERNEL=="gpiochip*", GROUP="plugdev", MODE="0660"
Zephyr
The line is configured with gpio_pin_configure() and watched with an interrupt on both edges. The interrupt only wakes the thread that owns the QGpioLine, so its slots run in that thread as usual. A pin whose controller cannot interrupt is polled. Names of lines from the devicetree are not looked up; use the device name and pin number.