Add documentation for GpioMonitor and Gpio

This commit is contained in:
Simon Stürz 2019-09-13 15:32:18 +02:00
parent 237925b497
commit 60027f5580
8 changed files with 337 additions and 8 deletions

2
debian/changelog vendored
View File

@ -1,4 +1,4 @@
nymea-gpio (1.0.0) bionic; urgency=medium
nymea-gpio (1.0.0) UNRELEASED; urgency=medium
* Initial release.

View File

@ -1,3 +1,3 @@
usr/lib/@DEB_HOST_MULTIARCH@/libnymea-gpio.so
usr/include/libnymea-gpio/* usr/include/libnymea-gpio
usr/lib/@DEB_HOST_MULTIARCH@/pkgconfig/libnymea-gpio.pc
usr/lib/@DEB_HOST_MULTIARCH@/pkgconfig/nymea-gpio.pc
usr/include/nymea-gpio/* usr/include/nymea-gpio/

View File

@ -1,5 +1,89 @@
#include "gpio.h"
/*!
\class Gpio
\brief Represents a system GPIO in linux systems.
A "General Purpose Input/Output" (GPIO) is a flexible software-controlled
digital signal. They are provided from many kinds of chip, and are familiar
to Linux developers working with embedded and custom hardware. Each GPIO
represents a bit connected to a particular pin, or "ball" on Ball Grid Array
(BGA) packages. Board schematics show which external hardware connects to
which GPIOs. Drivers can be written generically, so that board setup code
passes such pin configuration data to drivers
(\l{https://www.kernel.org/doc/Documentation/gpio/gpio.txt}{source}).
General Purpose Input/Output (a.k.a. GPIO) is a generic pin on a chip whose
behavior (including whether it is an input or output pin) can be controlled
through this class. An object of of the Gpio class represents a pin.
\code
Gpio *gpioOut = new Gpio(23, this);
// Export Gpio
if (!gpioOut->exportGpio()) {
qWarning() << "Could not export Gpio" << gpioOut->gpioNumber();
gpioOut->deleteLater();
return;
}
// Configure Gpio direction
if (!gpioOut->setDirection(PiGpio::DirectionOutput)) {
qWarning() << "Could not set direction of Gpio" << gpioOut->gpioNumber();
gpioOut->deleteLater();
return;
}
gpioOut->setValue(Gpio::ValueHigh)
\endcode
\code
Gpio *gpioIn = new Gpio(24, this);
// Export Gpio
if (!gpioIn->exportGpio()) {
qWarning() << "Could not export Gpio" << gpioIn->gpioNumber();
gpioIn->deleteLater();
return;
}
// Configure Gpio direction
if (!gpioIn->setDirection(PiGpio::DirectionInput)) {
qWarning() << "Could not set direction of Gpio" << gpioIn->gpioNumber();
gpioIn->deleteLater();
return;
}
qDebug() << "Current value" << gpioIn->value();
\endcode
\sa GpioMonitor, GpioButton
*/
/*! \enum Gpio::Direction
This enum type specifies the dirction a \l{Gpio}.
\value DirectionInput
The \l{Gpio} is configured as \b input.
\value DirectionOutput
The \l{Gpio} is configured as \b output.
\value DirectionInvalid
The direction is not valid.
*/
/*! \enum Gpio::Value
This enum type specifies the value a \l{Gpio}.
\value ValueLow
The \l{Gpio} is low.
\value ValueHigh
The \l{Gpio} is high.
\value ValueInvalid
The value is not valid.
*/
/*! \enum Gpio::Edge
This enum type specifies the edge interrupt type of a \l{Gpio}.
\value EdgeFalling
The \l{Gpio} reacts on falling edge interrupt.
\value EdgeRising
The \l{Gpio} reacts on rising edge interrupt.
\value EdgeBoth
The \l{Gpio} reacts on both, rising and falling edge interrupt.
\value EdgeNone
The \l{Gpio} does not react on interrupts.
*/
Q_LOGGING_CATEGORY(dcGpio, "Gpio")
/*! Constructs a \l{Gpio} object to represent a GPIO with the given \a gpio number and \a parent. */

View File

@ -0,0 +1,131 @@
#include "gpiobutton.h"
GpioButton::GpioButton(int gpioNumber, QObject *parent) :
QObject(parent),
m_gpioNumber(gpioNumber)
{
}
int GpioButton::gpioNumber() const
{
return m_gpioNumber;
}
bool GpioButton::activeLow() const
{
return m_activeLow;
}
void GpioButton::setActiveLow(bool activeLow)
{
m_activeLow = activeLow;
}
bool GpioButton::repeateLongPressed() const
{
return m_repeateLongPressed;
}
void GpioButton::setRepeateLongPressed(bool repeateLongPressed)
{
m_repeateLongPressed = repeateLongPressed;
}
int GpioButton::longPressedTimeout() const
{
return m_longPressedTimeout;
}
void GpioButton::setLongPressedTimeout(int longPressedTimeout)
{
m_longPressedTimeout = longPressedTimeout;
}
QString GpioButton::name() const
{
return m_name;
}
void GpioButton::setName(const QString &name)
{
m_name = name;
}
void GpioButton::onTimeout()
{
qCDebug(dcGpio()) << this << "long pressed";
emit longPressed();
}
void GpioButton::onInterruptOccured(bool value)
{
if (value) {
// Pressed
qCDebug(dcGpio()) << this << "pressed";
emit pressed();
m_timer->setSingleShot(!m_repeateLongPressed);
m_timer->start(m_longPressedTimeout);
m_time.restart();
} else {
// Released
qCDebug(dcGpio()) << this << "released";
emit released();
m_timer->stop();
int duration = m_time.elapsed();
// Debounce and limit to 500 ms
if (duration >= 10 && duration <= 500) {
qCDebug(dcGpio()) << this << "clicked";
emit clicked();
}
}
}
bool GpioButton::enable()
{
// Make sure we have a clean start
disable();
m_monitor = new GpioMonitor(m_gpioNumber, this);
m_monitor->setEdge(Gpio::EdgeBoth);
m_monitor->setActiveLow(m_activeLow);
if (!m_monitor->enable()) {
qCWarning(dcGpio()) << "Could not enable GPIO monitor for" << this;
delete m_monitor;
m_monitor = nullptr;
return false;
}
connect(m_monitor, &GpioMonitor::interruptOccured, this, &GpioButton::onInterruptOccured, Qt::DirectConnection);
// Setup timer, if this timer reaches timeout, a long pressed happend
m_timer = new QTimer(this);
m_timer->setTimerType(Qt::PreciseTimer);
m_timer->setSingleShot(!m_repeateLongPressed);
m_timer->setInterval(m_longPressedTimeout);
connect(m_timer, &QTimer::timeout, this, &GpioButton::onTimeout, Qt::DirectConnection);
return true;
}
void GpioButton::disable()
{
if (m_monitor) {
delete m_monitor;
m_monitor = nullptr;
}
if (m_timer) {
delete m_timer;
m_timer = nullptr;
}
}
QDebug operator<<(QDebug debug, GpioButton *gpioButton)
{
debug.nospace() << "GpioButton(" << gpioButton->gpioNumber() << ", ";
debug.nospace() << "name: " << gpioButton->name() << ")";
return debug.space();
}

View File

@ -0,0 +1,61 @@
#ifndef GPIOBUTTON_H
#define GPIOBUTTON_H
#include <QTime>
#include <QTimer>
#include <QObject>
#include "gpiomonitor.h"
class GpioButton : public QObject
{
Q_OBJECT
public:
explicit GpioButton(int gpioNumber, QObject *parent = nullptr);
int gpioNumber() const;
bool activeLow() const;
void setActiveLow(bool activeLow);
bool repeateLongPressed() const;
void setRepeateLongPressed(bool repeateLongPressed);
int longPressedTimeout() const;
void setLongPressedTimeout(int longPressedTimeout);
QString name() const;
void setName(const QString &name);
private:
int m_gpioNumber;
bool m_activeLow = true;
bool m_repeateLongPressed = false;
int m_longPressedTimeout = 250;
QString m_name;
GpioMonitor *m_monitor = nullptr;
QTimer *m_timer = nullptr;
QTime m_time;
signals:
void clicked();
void pressed();
void released();
void longPressed();
private slots:
void onTimeout();
void onInterruptOccured(bool value);
public slots:
bool enable();
void disable();
};
QDebug operator<< (QDebug debug, GpioButton *gpioButton);
#endif // GPIOBUTTON_H

View File

@ -1,8 +1,42 @@
/*!
\class GpioMonitor
\brief Monitor for GPIO interrupts.
This class allows to monitor an input GPIO for the interrupts depending on the \l{Gpio:Edge} configuration.
\code
GpioMonitor *monitor = new GpioMonitor(112, this);
if (!monitor->enable()) {
qWarning() << "Could not enable GPIO monitor";
monitor->deleteLater();
return;
}
connect(monitor, &GpioMonitor::interruptOccured, this, [this, monitor](bool value){
qDebug() << "GPIO value changed" << value;
});
\endcode
*/
/*! \fn void GpioMonitor::interruptOccured(bool value);
This signal will be emitted, if an interrupt on the monitored Gpio occured with the new \a value. This event depends on the Gpio::Edge configuration of the Gpio.
\sa edge(), setEdge()
*/
/*! \fn void GpioMonitor::enabledChanged(bool enabled);
This signal will be emitted when the GpioMonitor \a enabled changed.
*/
#include "gpiomonitor.h"
#include <poll.h>
#include <QMutexLocker>
/*! Constructs a \l{GpioMonitor} object with the given \a gpio number and \a parent. */
GpioMonitor::GpioMonitor(int gpio, QObject *parent) :
QThread(parent),
m_gpioNumber(gpio)
@ -12,17 +46,28 @@ GpioMonitor::GpioMonitor(int gpio, QObject *parent) :
connect(this, &GpioMonitor::finished, this, &GpioMonitor::onThreadFinished, Qt::DirectConnection);
}
/*! Destroys and unexports the Gpio. */
GpioMonitor::~GpioMonitor()
{
disable();
wait(200);
}
/*! Returns the edge interrupt configuration for this GpioMonitor.
\sa Gpio::Edge
*/
Gpio::Edge GpioMonitor::edge() const
{
return m_edge;
}
/*! Sets the edge interrupt configuration for this GpioMonitor to the given \a edge.
\sa Gpio::Edge
*/
void GpioMonitor::setEdge(Gpio::Edge edge)
{
if (m_edge == edge)
@ -31,11 +76,13 @@ void GpioMonitor::setEdge(Gpio::Edge edge)
m_edge = edge;
}
/*! Returns true, if the monitor is configured as active low. If active low is true, the GPIO values and interrupt behavior will be inverted. */
bool GpioMonitor::activeLow() const
{
return m_activeLow;
}
/*! Sets the the monitor to \a activeLow. If active low is true, the GPIO values and interrupt behavior will be inverted. */
void GpioMonitor::setActiveLow(bool activeLow)
{
if (m_activeLow == activeLow)
@ -44,12 +91,14 @@ void GpioMonitor::setActiveLow(bool activeLow)
m_activeLow = activeLow;
}
/*! Returns the current value of the Gpio. */
Gpio::Value GpioMonitor::value()
{
QMutexLocker valueLocker(&m_valueMutex);
return m_value;
}
/*! Returns true if this GpioMonitor is enabled. */
bool GpioMonitor::enabled() const
{
return m_enabled;
@ -181,6 +230,7 @@ void GpioMonitor::onThreadFinished()
setEnabled(false);
}
/*! Returns true, if this GpioMonitor was enabled successfully. */
bool GpioMonitor::enable()
{
qCDebug(dcGpio()) << "Enable gpio monitor";
@ -203,6 +253,7 @@ bool GpioMonitor::enable()
return true;
}
/*! Disables this GpioMonitor. The \l{interruptOccured()} signal will not be emitted any more and the Gpio will be unexported. */
void GpioMonitor::disable()
{
qCDebug(dcGpio()) << "Disable gpio monitor";

View File

@ -32,7 +32,7 @@ private:
// Thread stuff
QMutex m_valueMutex;
Gpio::Value m_value = Gpio::ValueInvalid;
Gpio::Value m_value = Gpio::ValueInvalid;
QMutex m_stopMutex;
bool m_stop = false;

View File

@ -5,10 +5,12 @@ TEMPLATE = lib
HEADERS += \
gpio.h \
gpiobutton.h \
gpiomonitor.h
SOURCES += \
gpio.cpp \
gpiobutton.cpp \
gpiomonitor.cpp
target.path = $$[QT_INSTALL_LIBS]
@ -16,7 +18,7 @@ INSTALLS += target
# install header file with relative subdirectory
for(header, HEADERS) {
path = /usr/include/libnymea-gpio/$${dirname(header)}
path = $$[QT_INSTALL_PREFIX]/include/nymea-gpio/$${dirname(header)}
eval(headers_$${path}.files += $${header})
eval(headers_$${path}.path = $${path})
eval(INSTALLS *= headers_$${path})
@ -24,11 +26,11 @@ for(header, HEADERS) {
# Create pkgconfig file
CONFIG += create_pc create_prl no_install_prl
QMAKE_PKGCONFIG_NAME = libnymea-gpio
QMAKE_PKGCONFIG_NAME = nymea-gpio
QMAKE_PKGCONFIG_DESCRIPTION = nymea gpio development library
QMAKE_PKGCONFIG_PREFIX = $$[QT_INSTALL_PREFIX]
QMAKE_PKGCONFIG_INCDIR = $$[QT_INSTALL_PREFIX]/include/libnymea-gpio/
QMAKE_PKGCONFIG_INCDIR = $$[QT_INSTALL_PREFIX]/include/nymea-gpio/
QMAKE_PKGCONFIG_LIBDIR = $$target.path
QMAKE_PKGCONFIG_VERSION = $$VERSION_STRING
QMAKE_PKGCONFIG_FILE = libnymea-gpio
QMAKE_PKGCONFIG_FILE = nymea-gpio
QMAKE_PKGCONFIG_DESTDIR = pkgconfig