From 60027f558024a5f97b4fb9bb61a01fd22e8aaac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 13 Sep 2019 15:32:18 +0200 Subject: [PATCH] Add documentation for GpioMonitor and Gpio --- debian/changelog | 2 +- debian/libnymea-gpio-dev.install.in | 4 +- libnymea-gpio/gpio.cpp | 84 ++++++++++++++++++ libnymea-gpio/gpiobutton.cpp | 131 ++++++++++++++++++++++++++++ libnymea-gpio/gpiobutton.h | 61 +++++++++++++ libnymea-gpio/gpiomonitor.cpp | 51 +++++++++++ libnymea-gpio/gpiomonitor.h | 2 +- libnymea-gpio/libnymea-gpio.pro | 10 ++- 8 files changed, 337 insertions(+), 8 deletions(-) create mode 100644 libnymea-gpio/gpiobutton.cpp create mode 100644 libnymea-gpio/gpiobutton.h diff --git a/debian/changelog b/debian/changelog index 99fdfbb..eb4ea68 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -nymea-gpio (1.0.0) bionic; urgency=medium +nymea-gpio (1.0.0) UNRELEASED; urgency=medium * Initial release. diff --git a/debian/libnymea-gpio-dev.install.in b/debian/libnymea-gpio-dev.install.in index 6be4c17..a7b9db4 100644 --- a/debian/libnymea-gpio-dev.install.in +++ b/debian/libnymea-gpio-dev.install.in @@ -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/ diff --git a/libnymea-gpio/gpio.cpp b/libnymea-gpio/gpio.cpp index 3b49755..199b9c9 100644 --- a/libnymea-gpio/gpio.cpp +++ b/libnymea-gpio/gpio.cpp @@ -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. */ diff --git a/libnymea-gpio/gpiobutton.cpp b/libnymea-gpio/gpiobutton.cpp new file mode 100644 index 0000000..b197eea --- /dev/null +++ b/libnymea-gpio/gpiobutton.cpp @@ -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(); +} diff --git a/libnymea-gpio/gpiobutton.h b/libnymea-gpio/gpiobutton.h new file mode 100644 index 0000000..5c69d19 --- /dev/null +++ b/libnymea-gpio/gpiobutton.h @@ -0,0 +1,61 @@ +#ifndef GPIOBUTTON_H +#define GPIOBUTTON_H + +#include +#include +#include + +#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 diff --git a/libnymea-gpio/gpiomonitor.cpp b/libnymea-gpio/gpiomonitor.cpp index b8589d7..bb10ac8 100644 --- a/libnymea-gpio/gpiomonitor.cpp +++ b/libnymea-gpio/gpiomonitor.cpp @@ -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 #include +/*! 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"; diff --git a/libnymea-gpio/gpiomonitor.h b/libnymea-gpio/gpiomonitor.h index bb109dd..f84488d 100644 --- a/libnymea-gpio/gpiomonitor.h +++ b/libnymea-gpio/gpiomonitor.h @@ -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; diff --git a/libnymea-gpio/libnymea-gpio.pro b/libnymea-gpio/libnymea-gpio.pro index 033393e..ed3db59 100644 --- a/libnymea-gpio/libnymea-gpio.pro +++ b/libnymea-gpio/libnymea-gpio.pro @@ -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