#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; }