/* PIP - Platform Independent Primitives GPIO Andrey Bychkov work.a.b@yandex.ru, Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "pigpio.h" #include "pitime.h" #ifdef LINUX # define GPIO_SYS_CLASS #endif #ifdef GPIO_SYS_CLASS # include # include # include #endif #include "piliterals.h" //! \class PIGPIO pigpio.h //! \~english \section PIGPIO_sec0 Synopsis //! \~russian \section PIGPIO_sec0 Краткий обзор //! \~english //! This class provide initialize, get/set and watch functions for GPIO. //! //! This class should be used with \a PIGPIO::instance() singleton. //! //! Currently supported only "/sys/class/gpio" mechanism on Linux. //! //! //! \~russian //! Этот класс предоставляет инициализацию, установку, чтение и наблюдение //! за GPIO. //! //! Этот класс используется только через синглтон \a PIGPIO::instance(). //! //! Сейчас поддерживается только "/sys/class/gpio" для Linux. //! //! //! \~\section PIGPIO_sec1 API //! \~english //! There are several function to directly read or write pin states. //! //! Also you can start %PIGPIO as thread to watch pin states and receive //! \a pinChanged() event. //! //! //! \~russian //! Имеется несколько методов для прямой записи и чтения состояний пинов. //! //! Также можно запустить %PIGPIO как поток для наблюдения за пинами и //! принимать события \a pinChanged(). //! PIGPIO::PIGPIO(): PIThread() {} PIGPIO::~PIGPIO() { stop(); waitForFinish(100_ms); PIMutexLocker ml(mutex); #ifdef GPIO_SYS_CLASS PIVector ids = gpio_.keys(); for (int i = 0; i < ids.size_s(); i++) { GPIOData & g(gpio_[ids[i]]); if (g.fd != -1) { ::close(g.fd); g.fd = -1; } } gpio_.clear(); #endif } PIGPIO * PIGPIO::instance() { static PIGPIO ret; return &ret; } PIString PIGPIO::GPIOName(int gpio_num) { return PIStringAscii("gpio") + PIString::fromNumber(gpio_num); } void PIGPIO::exportGPIO(int gpio_num) { #ifdef GPIO_SYS_CLASS PIString valfile = "/sys/class/gpio/" + GPIOName(gpio_num) + "/value"; int fd = ::open(valfile.dataAscii(), O_RDONLY); if (fd != -1) { ::close(fd); return; } int ret = 0; ret = system(PIString("echo " + PIString::fromNumber(gpio_num) + " >> /sys/class/gpio/export").dataAscii()); if (ret == 0) { PITimeMeasurer tm; while (tm.elapsed_s() < 1.) { fd = ::open(valfile.dataAscii(), O_RDWR); if (fd != -1) { ::close(fd); return; } piMSleep(1); } } #endif } void PIGPIO::openGPIO(GPIOData & g) { #ifdef GPIO_SYS_CLASS if (g.fd != -1) { ::close(g.fd); g.fd = -1; } PIString fp = "/sys/class/gpio/" + g.name + "/value"; g.fd = ::open(fp.dataAscii(), O_RDWR); // piCoutObj << "initGPIO" << g.num << ":" << fp << g.fd << errorString(); #endif } bool PIGPIO::getPinState(int gpio_num) { #ifdef GPIO_SYS_CLASS GPIOData & g(gpio_[gpio_num]); char r = 0; int ret = 0; if (g.fd != -1) { ::lseek(g.fd, 0, SEEK_SET); ret = ::read(g.fd, &r, sizeof(r)); if (ret > 0) { if (r == '1') return true; if (r == '0') return false; } } // piCoutObj << "pinState" << gpio_num << ":" << ret << (int)r << errorString(); #endif return false; } void PIGPIO::begin() { PIMutexLocker ml(mutex); if (watch_state.isEmpty()) return; PIVector ids = watch_state.keys(); for (int i = 0; i < ids.size_s(); i++) { GPIOData & g(gpio_[ids[i]]); if (g.num != -1 && !g.name.isEmpty()) { openGPIO(g); watch_state[ids[i]] = getPinState(ids[i]); } } } void PIGPIO::run() { mutex.lock(); if (watch_state.isEmpty()) { mutex.unlock(); return; } PIVector> changed; auto it = watch_state.makeIterator(); while (it.next()) { GPIOData & g(gpio_[it.key()]); if (g.num == -1 || g.name.isEmpty()) continue; bool v = getPinState(g.num); // piCoutObj << "*** pin state ***" << ids[i] << "=" << v; if (watch_state[g.num] != v) { watch_state[g.num] = v; changed.push_back({g.num, v}); } } mutex.unlock(); for (const auto & i: changed) pinChanged(i.first, i.second); } void PIGPIO::end() { PIMutexLocker ml(mutex); if (watch_state.isEmpty()) return; PIVector ids = watch_state.keys(); for (int i = 0; i < ids.size_s(); i++) { GPIOData & g(gpio_[ids[i]]); if (g.fd != -1) { #ifdef GPIO_SYS_CLASS ::close(g.fd); #endif g.fd = -1; } } } void PIGPIO::initPin(int gpio_num, Direction dir) { #ifdef GPIO_SYS_CLASS PIMutexLocker ml(mutex); GPIOData & g(gpio_[gpio_num]); if (g.num == -1) { g.num = gpio_num; g.name = GPIOName(gpio_num); exportGPIO(gpio_num); } g.dir = dir; int ret = 0; NO_UNUSED(ret); switch (dir) { case In: ret = system(("echo in >> /sys/class/gpio/" + g.name + "/direction").dataAscii()); break; case Out: ret = system(("echo out >> /sys/class/gpio/" + g.name + "/direction").dataAscii()); break; default: break; } openGPIO(g); #endif } void PIGPIO::pinSet(int gpio_num, bool value) { #ifdef GPIO_SYS_CLASS PIMutexLocker ml(mutex); GPIOData & g(gpio_[gpio_num]); int ret = 0; NO_UNUSED(ret); if (g.fd != -1) { if (value) ret = ::write(g.fd, "1", 1); else ret = ::write(g.fd, "0", 1); } // piCoutObj << "pinSet" << gpio_num << ":" << ret << errorString(); #endif } bool PIGPIO::pinState(int gpio_num) { PIMutexLocker ml(mutex); return getPinState(gpio_num); } //! \~\details //! \~english //! This function doesn`t affect thread state! //! Pins watching starts only with \a PIThread::start() function //! //! \~russian //! Этот метод не меняет состояние потока наблюдения! //! Наблюдение за пинами начинается методом \a PIThread::start() void PIGPIO::pinBeginWatch(int gpio_num) { PIMutexLocker ml(mutex); GPIOData & g(gpio_[gpio_num]); if (g.fd != -1) { #ifdef GPIO_SYS_CLASS ::close(g.fd); #endif g.fd = -1; } watch_state.insert(gpio_num, false); } //! \~\details //! \~english //! This function doesn`t affect thread state! //! Pins watching starts only with \a PIThread::start() function //! //! \~russian //! Этот метод не меняет состояние потока наблюдения! //! Наблюдение за пинами начинается методом \a PIThread::start() void PIGPIO::pinEndWatch(int gpio_num) { PIMutexLocker ml(mutex); watch_state.remove(gpio_num); } //! \~\details //! \~english //! This function doesn`t affect thread state! //! Pins watching starts only with \a PIThread::start() function //! //! \~russian //! Этот метод не меняет состояние потока наблюдения! //! Наблюдение за пинами начинается методом \a PIThread::start() void PIGPIO::clearWatch() { PIMutexLocker ml(mutex); watch_state.clear(); } #ifdef __GNUC__ // # pragma GCC diagnostic pop #endif