/* 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" #ifdef LINUX # define GPIO_SYS_CLASS #endif #ifdef GPIO_SYS_CLASS # include # include # include #endif /*! \class PIGPIO * \brief GPIO support * * \section PIGPIO_sec0 Synopsis * This class provide initialize, get/set and watch functions for GPIO. * * Currently supported only \"/sys/class/gpio\" mechanism on Linux. * * This class should be used with \a PIGPIO::instance() singleton. * * * * \section PIGPIO_sec1 API * 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. * */ PIGPIO::PIGPIO(): PIThread() { } PIGPIO::~PIGPIO() { stop(); waitForFinish(100); 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() { 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()) { bool v = getPinState(g.num); //piCoutObj << "*** pin state ***" << ids[i] << "=" << v; if (watch_state[g.num] != v) { watch_state[g.num] = v; pinChanged(g.num, v); } } } } 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; 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; 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); } 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); } void PIGPIO::pinEndWatch(int gpio_num) { PIMutexLocker ml(mutex); watch_state.remove(gpio_num); } void PIGPIO::clearWatch() { PIMutexLocker ml(mutex); watch_state.clear(); } #ifdef __GNUC__ # pragma GCC diagnostic pop #endif