From 40a3cb68fa773fc58b6b8bda0063c8e24db4e493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 4 Sep 2019 11:59:07 +0200 Subject: [PATCH] Add debian folder and basic structure of the library --- gpio.cpp | 341 ++++++++++++++++++++++++++++++++++++++++++++++ gpio.h | 69 ++++++++++ libnymea-gpio.pro | 40 ++++++ 3 files changed, 450 insertions(+) create mode 100644 gpio.cpp create mode 100644 gpio.h create mode 100644 libnymea-gpio.pro diff --git a/gpio.cpp b/gpio.cpp new file mode 100644 index 0000000..c47ba0b --- /dev/null +++ b/gpio.cpp @@ -0,0 +1,341 @@ +#include "gpio.h" + +Q_LOGGING_CATEGORY(dcGpio, "Gpio") + +/*! Constructs a \l{Gpio} object to represent a GPIO with the given \a gpio number and \a parent. */ +Gpio::Gpio(int gpio, QObject *parent) : + QObject(parent), + m_gpio(gpio), + m_direction(Gpio::DirectionInvalid), + m_gpioDirectory(QDir(QString("/sys/class/gpio/gpio%1/").arg(QString::number(gpio)))) +{ + +} + +/*! Destroys and unexports the \l{Gpio}. */ +Gpio::~Gpio() +{ + unexportGpio(); +} + +/*! Returns true if the directories \tt {/sys/class/gpio} and \tt {/sys/class/gpio/export} do exist. */ +bool Gpio::isAvailable() +{ + return QFile("/sys/class/gpio/export").exists(); +} + +/*! Returns the directory \tt {/sys/class/gpio/gpio} of this Gpio. */ +QString Gpio::gpioDirectory() const +{ + return m_gpioDirectory.canonicalPath(); +} + +/*! Returns the number of this \l{Gpio}. \note The Gpio number is mostly not equivalent with the pin number. */ +int Gpio::gpioNumber() const +{ + return m_gpio; +} + +/*! Returns true if this \l{Gpio} could be exported in the system file \tt {/sys/class/gpio/export}. If this Gpio is already exported, this function will return true. */ +bool Gpio::exportGpio() +{ + // Check if already exported + if (m_gpioDirectory.exists()) + return true; + + QFile exportFile("/sys/class/gpio/export"); + if (!exportFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO export file:" << exportFile.errorString(); + return false; + } + + QTextStream out(&exportFile); + out << m_gpio; + exportFile.close(); + return true; +} + +/*! Returns true if this \l{Gpio} could be unexported in the system file \tt {/sys/class/gpio/unexport}. */ +bool Gpio::unexportGpio() +{ + QFile unexportFile("/sys/class/gpio/unexport"); + if (!unexportFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO unexport file:" << unexportFile.errorString(); + return false; + } + + QTextStream out(&unexportFile); + out << m_gpio; + unexportFile.close(); + return true; +} + +/*! Returns true if the \a direction of this GPIO could be set. \sa Gpio::Direction, */ +bool Gpio::setDirection(Gpio::Direction direction) +{ + if (direction == Gpio::DirectionInvalid) { + qCWarning(dcGpio()) << "Setting an invalid direction is forbidden."; + return false; + } + + QFile directionFile(m_gpioDirectory.path() + "/direction"); + if (!directionFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "direction file:" << directionFile.errorString(); + return false; + } + + m_direction = direction; + + QTextStream out(&directionFile); + switch (m_direction) { + case DirectionInput: + out << "in"; + break; + case DirectionOutput: + out << "out"; + break; + default: + break; + } + + directionFile.close(); + return true; +} + +/*! Returns the direction of this \l{Gpio}. */ +Gpio::Direction Gpio::direction() +{ + QFile directionFile(m_gpioDirectory.path() + "/direction"); + if (!directionFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "direction file:" << directionFile.fileName() << directionFile.errorString(); + return Gpio::DirectionInvalid; + } + + QString direction; + QTextStream in(&directionFile); + in >> direction; + directionFile.close(); + + if (direction == "in") { + m_direction = DirectionInput; + return Gpio::DirectionInput; + } else if (direction == "out") { + m_direction = DirectionOutput; + return Gpio::DirectionOutput; + } + + return Gpio::DirectionInvalid; +} + +/*! Returns true if the digital \a value of this \l{Gpio} could be set correctly. */ +bool Gpio::setValue(Gpio::Value value) +{ + // Check given value + if (value == Gpio::ValueInvalid) { + qCWarning(dcGpio()) << "Setting an invalid value is forbidden."; + return false; + } + + // Check current direction + if (m_direction == Gpio::DirectionInput) { + qCWarning(dcGpio()) << "Setting the value of an input GPIO is forbidden."; + return false; + } + + if (m_direction == Gpio::DirectionInvalid) { + qCWarning(dcGpio()) << "The direction of GPIO" << m_gpio << "is invalid."; + return false; + } + + QFile valueFile(m_gpioDirectory.path() + "/value"); + if (!valueFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "value file:" << valueFile.errorString(); + return false; + } + + QTextStream out(&valueFile); + switch (value) { + case ValueLow: + out << "0"; + break; + case ValueHigh: + out << "1"; + break; + default: + valueFile.close(); + return false; + } + + valueFile.close(); + return true; +} + +/*! Returns the current digital value of this \l{Gpio}. */ +Gpio::Value Gpio::value() +{ + QFile valueFile(m_gpioDirectory.path() + "/value"); + if (!valueFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "value file:" << valueFile.errorString(); + return Gpio::ValueInvalid; + } + + QString value; + QTextStream in(&valueFile); + in >> value; + valueFile.close(); + + if (value == "0") { + return Gpio::ValueLow; + } else if (value == "1") { + return Gpio::ValueHigh; + } + + return Gpio::ValueInvalid; +} + +/*! This method allows to invert the logic of this \l{Gpio}. Returns true, if the GPIO could be set \a activeLow. */ +bool Gpio::setActiveLow(bool activeLow) +{ + QFile activeLowFile(m_gpioDirectory.path() + "/active_low"); + if (!activeLowFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "active_low file:" << activeLowFile.errorString(); + return false; + } + + QTextStream out(&activeLowFile); + if (activeLow) { + out << "0"; + } else { + out << "1"; + } + + activeLowFile.close(); + return true; +} + +/*! Returns true if the logic of this \l{Gpio} is inverted (1 = low, 0 = high). */ +bool Gpio::activeLow() +{ + QFile activeLowFile(m_gpioDirectory.path() + "/active_low"); + if (!activeLowFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "active_low file:" << activeLowFile.errorString(); + return false; + } + + QString value; + QTextStream in(&activeLowFile); + in >> value; + activeLowFile.close(); + + if (value == "0") + return true; + + return false; +} + +/*! Returns true if the \a edge of this GPIO could be set correctly. The \a edge parameter specifies, + * when an interrupt occurs. */ +bool Gpio::setEdgeInterrupt(Gpio::Edge edge) +{ + if (m_direction == Gpio::DirectionOutput) { + qCWarning(dcGpio()) << "Could not set edge interrupt, GPIO is configured as an output."; + return false; + } + + QFile edgeFile(m_gpioDirectory.path() + "/edge"); + if (!edgeFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "edge file:" << edgeFile.errorString(); + return false; + } + + QTextStream out(&edgeFile); + switch (edge) { + case EdgeFalling: + out << "falling"; + break; + case EdgeRising: + out << "rising"; + break; + case EdgeBoth: + out << "both"; + break; + case EdgeNone: + out << "none"; + break; + } + + edgeFile.close(); + return true; +} + +/*! Returns the edge interrupt of this \l{Gpio}. */ +Gpio::Edge Gpio::edgeInterrupt() +{ + QFile edgeFile(m_gpioDirectory.path() + "/edge"); + if (!edgeFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCWarning(dcGpio()) << "Could not open GPIO" << m_gpio << "edge file:" << edgeFile.errorString(); + return Gpio::EdgeNone; + } + + QString edge; + QTextStream in(&edgeFile); + in >> edge; + edgeFile.close(); + + if (edge.contains("falling")) { + return Gpio::EdgeFalling; + } else if (edge.contains("rising")) { + return Gpio::EdgeRising; + } else if (edge.contains("both")) { + return Gpio::EdgeBoth; + } else if (edge.contains("none")) { + return Gpio::EdgeNone; + } + + return Gpio::EdgeNone; +} + + + +QDebug operator<<(QDebug debug, Gpio *gpio) +{ + debug.nospace() << "Gpio(" << gpio->gpioNumber() << ", "; + if (gpio->direction() == Gpio::DirectionInput) { + debug.nospace() << "Input, "; + } else if (gpio->direction() == Gpio::DirectionOutput) { + debug.nospace() << "Output, "; + } else { + debug.nospace() << "Invalid, "; + } + + switch (gpio->edgeInterrupt()) { + case Gpio::EdgeFalling: + debug.nospace() << "Ir: Falling, "; + break; + case Gpio::EdgeRising: + debug.nospace() << "Ir: Rising, "; + break; + case Gpio::EdgeBoth: + debug.nospace() << "Ir: Both, "; + break; + case Gpio::EdgeNone: + debug.nospace() << "Ir: None, "; + break; + } + + if (gpio->activeLow()) { + debug.nospace() << "Active Low: 1, "; + } else { + debug.nospace() << "Active Low: 0, "; + } + + if (gpio->value() == Gpio::ValueHigh) { + debug.nospace() << "Value: 1)"; + } else if (gpio->value() == Gpio::ValueLow) { + debug.nospace() << "Value: 0)"; + } else { + debug.nospace() << "Value: Invalid)"; + } + + return debug; +} diff --git a/gpio.h b/gpio.h new file mode 100644 index 0000000..d914619 --- /dev/null +++ b/gpio.h @@ -0,0 +1,69 @@ +#ifndef GPIO_H +#define GPIO_H + +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(dcGpio) + +class Gpio : public QObject +{ + Q_OBJECT +public: + enum Direction { + DirectionInvalid, + DirectionInput, + DirectionOutput + }; + Q_ENUM(Direction) + + enum Value { + ValueInvalid = -1, + ValueLow = 0, + ValueHigh = 1 + }; + Q_ENUM(Value) + + enum Edge { + EdgeFalling, + EdgeRising, + EdgeBoth, + EdgeNone + }; + Q_ENUM(Edge) + + explicit Gpio(int gpio, QObject *parent = nullptr); + ~Gpio(); + + static bool isAvailable(); + + QString gpioDirectory() const; + int gpioNumber() const; + + bool exportGpio(); + bool unexportGpio(); + + bool setDirection(Gpio::Direction direction); + Gpio::Direction direction(); + + bool setValue(Gpio::Value value); + Gpio::Value value(); + + bool setActiveLow(bool activeLow); + bool activeLow(); + + bool setEdgeInterrupt(Gpio::Edge edge); + Gpio::Edge edgeInterrupt(); + +private: + int m_gpio = 0; + Gpio::Direction m_direction = Gpio::DirectionOutput; + QDir m_gpioDirectory; + +}; + +QDebug operator<< (QDebug debug, Gpio *gpio); + +#endif // GPIO_H diff --git a/libnymea-gpio.pro b/libnymea-gpio.pro new file mode 100644 index 0000000..ee75995 --- /dev/null +++ b/libnymea-gpio.pro @@ -0,0 +1,40 @@ +TARGET = nymea-gpio +TEMPLATE = lib + +QT -= gui + +QMAKE_CXXFLAGS *= -Werror -std=c++11 -g +QMAKE_LFLAGS *= -std=c++11 + +VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"') + + +DEFINES += LIBNYMEAGPIO_LIBRARY + +SOURCES += \ + gpio.cpp + +HEADERS += \ + gpio.h + +target.path = $$[QT_INSTALL_LIBS] +INSTALLS += target + +# install header file with relative subdirectory +for(header, HEADERS) { + path = /usr/include/libnymea-gpio/$${dirname(header)} + eval(headers_$${path}.files += $${header}) + eval(headers_$${path}.path = $${path}) + eval(INSTALLS *= headers_$${path}) +} + +# Create pkgconfig file +CONFIG += create_pc create_prl no_install_prl +QMAKE_PKGCONFIG_NAME = libnymea-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_LIBDIR = $$target.path +QMAKE_PKGCONFIG_VERSION = $$VERSION_STRING +QMAKE_PKGCONFIG_FILE = libnymea-gpio +QMAKE_PKGCONFIG_DESTDIR = pkgconfig