310 lines
7.6 KiB
C++
310 lines
7.6 KiB
C++
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "pigpio.h"
|
|
|
|
#include "pitime.h"
|
|
#ifdef LINUX
|
|
# define GPIO_SYS_CLASS
|
|
#endif
|
|
#ifdef GPIO_SYS_CLASS
|
|
# include <cstdlib>
|
|
# include <fcntl.h>
|
|
# include <unistd.h>
|
|
#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<int> 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<int> 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<PIPair<int, bool>> 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<int> 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
|