initial commit

master
Tibor 2 years ago
parent 5b184cd85d
commit 1bd70155e2
  1. 4
      .gitignore
  2. 110
      3rdparty/cppgpio/Makefile
  3. 252
      3rdparty/cppgpio/README.md
  4. 356
      3rdparty/cppgpio/demo.cpp
  5. 20
      3rdparty/cppgpio/include/cppgpio.hpp
  6. 318
      3rdparty/cppgpio/include/cppgpio/buttons.hpp
  7. 345
      3rdparty/cppgpio/include/cppgpio/gpio.hpp
  8. 63
      3rdparty/cppgpio/include/cppgpio/i2c.hpp
  9. 285
      3rdparty/cppgpio/include/cppgpio/lcd.hpp
  10. 216
      3rdparty/cppgpio/include/cppgpio/output.hpp
  11. 47
      3rdparty/cppgpio/src/Makefile
  12. 549
      3rdparty/cppgpio/src/buttons.cpp
  13. 661
      3rdparty/cppgpio/src/gpio.cpp
  14. 132
      3rdparty/cppgpio/src/i2c.cpp
  15. 607
      3rdparty/cppgpio/src/lcd.cpp
  16. 54
      3rdparty/cppgpio/src/make_unique.hpp
  17. 137
      3rdparty/cppgpio/src/output.cpp
  18. 91
      3rdparty/cppgpio/src/tools.cpp
  19. 199
      3rdparty/cppgpio/src/tools.hpp
  20. 53
      main.cpp
  21. 35
      taibell-nogui.sln
  22. 91
      taibell-nogui.vcxproj
  23. 4
      taibell-nogui.vcxproj.user

4
.gitignore vendored

@ -32,3 +32,7 @@
*.out
*.app
# compiler / vs stuff
.vs
obj
bin

@ -0,0 +1,110 @@
appname := demo
PREFIX := /usr
HEADERINST := $(PREFIX)/include/cppgpio
CHEADERINST := $(PREFIX)/include
LIBINST := $(PREFIX)/lib
LIBTOOL := ar rucs
MKDIR := mkdir -p
CP := cp
CD := cd
CHOWN := chown
CHMOD := chmod
LN := ln -sf
RM := rm -f
RMDIR := rm -rf
MAKE := make
LDCONFIG := ldconfig
sourceprefix := src
includeprefix:= include
headerprefix := include/cppgpio
SRCS := $(shell find $(sourceprefix) -maxdepth 1 -name "*.cpp")
SRCS := $(SRCS:./%=%)
HDRS := $(shell find $(headerprefix) -maxdepth 1 -name "*.hpp")
HDRS := $(HDRS:./%=%)
relheaderfiles:= $(HDRS:$(headerprefix)/%=%)
cnvheader := cppgpio.hpp
OBJS := $(SRCS:.cpp=.o)
libname := cppgpio
libnamea := lib$(libname).a
libnameso := lib$(libname).so
libnamesover := $(libnameso).1
libnamesoverx:= $(libnameso).1.0.0
CXX := g++
CXXFLAGS := -Wall -O2 -std=c++11 -pthread
LDFLAGS :=
LDLIBS := -lpthread -lcppgpio
SUBDIRS := $(sourceprefix)
MAKE_SUBDIRS = for subdir in $(SUBDIRS); do \
(cd $$subdir && $(MAKE) $@); \
if [ $$? -ne 0 ]; then \
exit -1; \
fi; \
done
all: lib
.PHONY: all lib depend clean dist-clean install uninstall
lib:
+@$(MAKE_SUBDIRS)
$(LIBTOOL) $(libnamea) $(OBJS)
$(CXX) -shared -Wl,-soname,$(libnameso) -o $(libnamesoverx) $(OBJS)
$(appname): demo.o
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(appname) demo.o $(LDLIBS)
clean:
+@$(MAKE_SUBDIRS)
$(RM) demo.o demo
depend: .depend
+@$(MAKE_SUBDIRS)
.depend: demo.cpp
$(RM) ./.depend
$(CXX) $(CXXFLAGS) -MM $^ >> ./.depend;
dist-clean: clean uninstall
+@$(MAKE_SUBDIRS)
$(RM) *~ .depend
install:
$(MKDIR) $(CHEADERINST)
$(CP) $(includeprefix)/$(cnvheader) $(CHEADERINST)
$(CHOWN) root:root $(CHEADERINST)/$(cnvheader)
$(CHMOD) 0644 $(CHEADERINST)/$(cnvheader)
$(MKDIR) $(HEADERINST)
$(CP) $(HDRS) $(HEADERINST)
$(CD) $(HEADERINST) && $(CHOWN) root:root $(relheaderfiles)
$(CD) $(HEADERINST) && $(CHMOD) 0644 $(relheaderfiles)
$(MKDIR) $(LIBINST)
$(CP) $(libnamea) $(LIBINST)
$(CD) $(LIBINST) && $(CHOWN) root:root $(libnamea)
$(CD) $(LIBINST) && $(CHMOD) 0755 $(libnamea)
$(CP) $(libnamesoverx) $(LIBINST)
$(CD) $(LIBINST) && $(CHOWN) root:root $(libnamesoverx)
$(CD) $(LIBINST) && $(CHMOD) 0755 $(libnamesoverx)
$(CD) $(LIBINST) && $(LN) $(libnamesoverx) $(libnamesover)
$(CD) $(LIBINST) && $(LN) $(libnamesover) $(libnameso)
$(LDCONFIG)
uninstall:
$(CD) $(CHEADERINST) && $(RM) $(cnvheader)
$(CD) $(HEADERINST) && $(RM) $(relheaderfiles)
$(CD) $(LIBINST) && $(RM) $(libnamea)
$(CD) $(LIBINST) && $(RM) $(libnamesoverx)
$(CD) $(LIBINST) && $(RM) $(libnamesover)
$(CD) $(LIBINST) && $(RM) $(libnameso)
$(LDCONFIG)
-include .depend

@ -0,0 +1,252 @@
# CppGPIO
C++14 GPIO library for Raspberry Pi and other embedded systems
### What is CppGPIO
CppGPIO is a C++ library for the GPIOs of embedded systems like the Raspberry Pi written entirely in the
modern C++ dialect C++14.
It provides a fallback to C++11 however. Please read below.
With the current source CppGPIO only works on the Pi, but it has enough abstraction to be ported to other boards.
It implements a high speed low level access on the GPIO pins of the embedded CPU, much like the C
libraries like [wiringPi](http://wiringpi.com) or the [bcm2835](http://www.airspayce.com/mikem/bcm2835/) library.
It also supports I2C communication via the Linux I2C abstraction.
Inspired by the implementation in wiringPi, it supports Soft PWM and tone outputs on all GPIO ports. Hardware
PWM is supported when run as root, by addressing the registers in the bcm2835.
In addition it implements a number of hardware abstractions based on the low level model, such as PushButtons,
RotaryDials, LCD displays, and other inputs and outputs.
It performs proper software debouncing on input ports. If you want to, you can modify the default parameters
for the debouncing. You can also set the pullup/pulldown mode for all inputs.
The library works event based for input objects. This means that you do not have to poll for state changes
of e.g. a switch, but can simply register a function from your own class that will be called from the object
when the state changes. This sounds complicated but is actually quite simple with C++11/14 features like lambda
expressions. The demo.cpp file has samples for this.
The speed of the library is at the upper limit of the hardware capabilities of the Raspberry Pi (2). Only 12
nanoseconds are needed to switch an output pin on or off. This results in a 44.67 Mhz square wave output
just by soft control.
### C++11 fallback
CppGPIO compiles with any C++11 compiler, as the only (but important) feature used from C++14 is
std::make_unique<>(), and the library provides a local copy of the standard implementation of that
feature. The main reason for this is that this provides support for the previous Raspian version,
based on debian wheezy, once you install g++-4.8 with `sudo apt-get install g++-4.8` (which is available on
wheezy). On wheezy, you then need however specify the newer compiler explicitly, e.g. by changing the Makefiles
supplied with this project to explicitly call g++-4.8 instead of g++ .
In result, CppGPIO is a pure C++11 library. You cannot use it from C nor from non-C++11 capable compilers.
### How to install
Just clone a revision from here, and get into the source directory and type
```
make -j4
sudo make install
make demo
```
### How to use
After you have installed the library and the include headers, you can simply include it into your own projects
by adding
```
#include <cppgpio.hpp>
```
to the files in which you want to use the functionality. For proper linking, you have to add
```
-lcppgpio
```
to your linker arguments. It will normally be added as a shared library, but if you want to force static linking,
it provides a static version as well.
### Documentation
All header files have fully commented public and protected methods. When using IDEs like Eclipse or XCode, you get
the methods "intellisensed", with documentation. If in doubt, just look at the header files in include/cppgpio/.
In the following days I may be adding doxygen generated documentation from the source files.
### Supported operating environments
The only GPIO model is currently the BCM2835 as used in the Raspberry Pi boards. It adapts automatically to the
various versions (I have it only tested though on a B, A+, and 2 B). The code itself should compile fine on any
OS with a C++14 compiler. I typically develop and test build my projects on OSX with XCode, and then copy them
to the Pi. This works because the GPIO automatically switches to simulation mode when not on Linux
(or can be forced to do with Linux, too).
### License
CppGPIO is open source. The terms of the BSD license apply. Copyright (c) 2016 Joachim Schurig.
### Some examples
The following examples are taken from demo.cpp in this project.
#### Writing to a digital output
```
#include <chrono>
#include <cppgpio.hpp>
using namespace GPIO;
int main()
{
// use gpio #18
DigitalOut out(18);
// switch output to logical 1 (3.3V)
out.on();
// wait some time
std::this_thread::sleep_for(std::chrono::milliseconds(1));
// switch it off again
out.off();
return 0;
}
```
When DigitalOut goes out of scope, the port is automatically
reset to input mode and any resource associated with it is freed
#### Example with a PWM output
In the following example we output a PWM signal to a port, not
regarding if it will be generated by a hardware PWM circuit or
by the software emulation, which is a function of the port number
and the initialisation mode.
It could e.g. dim a LED on and off.
```
#include <chrono>
#include <cppgpio.hpp>
using namespace GPIO;
int main()
{
// create PWM on GPIO 23, set range to 100, inital value to 0
PWMOut pwm(23, 100, 0);
// now dim a LED 20 times from off to on to off
for (int l = 0; l < 20; ++l) {
for (int p = 0; p < 100; ++p) {
pwm.set_ratio(p);
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
for (int p = 100; p > 0; --p) {
pwm.set_ratio(p);
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
}
return 0;
}
```
#### Example with an LCD display, a rotary dial and a push button
In the following example we use the event driven approach with a
rotary dial with integrated push button. We connect callables
(in this case member functions of the Rotary1 class) to the
rotary and button classes, and have them called whenever an
event occurs. Remark there is no polling, and no control loop.
```
#include <string>
#include <chrono>
#include <stdlib.h>
#include <cppgpio.hpp>
using namespace GPIO;
class Rotary1 {
public:
Rotary1()
: lcd(4, 20, "/dev/i2c-1", 0x27)
, dial(6, 12, GPIO_PULL::UP)
, push(5, GPIO_PULL::UP)
{
lcd.fill();
lcd.write(0, 0, "Please dial me!");
lcd.write(1, 0, "Will exit at #42");
// register a lambda function at the dial to connect it to this class
dial.f_dialed = [&](bool up, long value) { dialed(up, value); };
// could also use std::bind():
// dial.f_dialed = std::bind(&Rotary1::dialed, this, std::placeholders::_1, std::placeholders::_2);
push.f_pushed = [&]() { pushed(); };
push.f_released = [&](std::chrono::nanoseconds nano) { released(nano); };
// after finishing the initialization of the event driven input objects
// start the event threads of the input objects
dial.start();
push.start();
}
private:
HitachiLCD lcd;
RotaryDial dial;
PushButton push;
void dialed(bool up, long value)
{
std::string out = "Value: ";
out += std::to_string(value);
lcd.write(0, 0, out);
if (value == 42) {
lcd.write(0, 0, "Goodbye");
lcd.write(1, 0, "");
std::this_thread::sleep_for(std::chrono::seconds(2));
lcd.backlight(false);
exit(0);
}
}
void pushed()
{
lcd.write(1, 0, "Button: pushed");
}
void released(std::chrono::nanoseconds nano)
{
lcd.write(1, 0, "Button: released");
}
};
int main()
{
Rotary1 rotary;
// the rotary object will function properly on any
// event alltough the main thread will now sleep for an hour
std::this_thread::sleep_for(std::chrono::hours(1));
return 0;
}
```
When Rotary1 goes out of scope, all GPIO objects used inside are
properly reset and freed.

@ -0,0 +1,356 @@
//
// Copyright © 2016 Joachim Schurig. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#include <iostream>
#include <cppgpio.hpp>
using namespace GPIO;
/// In this example we output text to an LCD display (and scroll it around)
void lcdtest()
{
// either configure the lcd via separate gpio pins
// HitachiLCD lcd( 2, 16,
// 26, 19,
// 21, 20, 16, 12);
// or via the i2c bus
HitachiLCD lcd(4, 20, "/dev/i2c-1", 0x27);
for (unsigned int ct = 0; ct < lcd.rows(); ++ct) {
lcd.write(ct, 0, std::string("Hello World ") + std::to_string(ct+1) + "!");
}
std::this_thread::sleep_for(std::chrono::seconds(1));
for (unsigned int ct = 0; ct < lcd.rows(); ++ct) {
lcd.scroll(true);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
lcd.wrap();
lcd.home();
for (int i = 0; i < 30; ++i) {
lcd.write("And a test. ");
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
std::this_thread::sleep_for(std::chrono::seconds(1));
lcd.home();
for (int i = 0; i < 30; ++i) {
lcd.write("And a test. ");
}
std::this_thread::sleep_for(std::chrono::seconds(1));
{
lcd.clear();
std::string s = "1234567890abcdef";
for (int x = 0; x < 50; ++x) {
lcd.write(0, 0, s);
std::string::value_type ch = s[0];
s.erase(0, 1);
s += ch;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
{
lcd.clear();
std::string s1 = "The first line. ";
std::string s2 = "And another one.";
for (int x = 0; x < 50; ++x) {
if ((x & 1) == 0) {
lcd.write(0, 0, s1);
lcd.write(1, 0, s2);
} else {
lcd.write(0, 0, s2);
lcd.write(1, 0, s1);
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
lcd.clear();
lcd.write(0, 0, "Goodbye!");
}
/// In the following example we actively poll a rotary dial with
/// integrated push button every 100ms. As the rotary dial is updating its
/// value in its own thread internally, we do not risk that it misses a turn.
/// However, we could miss a push. Therefore better use the event driven
/// approach from the Rotary1 example below.
void buttontest()
{
// either configure the lcd via separate gpio pins
// HitachiLCD lcd( 2, 16,
// 26, 19,
// 21, 20, 16, 12);
// or via the i2c bus
HitachiLCD lcd(4, 20, "/dev/i2c-1", 0x27);
// create a RotaryDial object
RotaryDial dial(6, 12, GPIO_PULL::UP);
// limit the internal counter to a range from 0 to 100
dial.set_range(0, 100);
// and start the event detection of the dial (even if it is only
// used internally to increment the counter)
dial.start();
// create a PushButton object
PushButton push(5, GPIO_PULL::UP);
// and start the event detection of the push button
push.start();
lcd.fill();
lcd.write(1, 0, "Hello!");
long value = 0;
bool pushed = false;
for(;;) {
long new_value = dial.get_value();
if (value != new_value) {
value = new_value;
std::string out = "Value: ";
out += std::to_string(value);
lcd.write(0, 0, out);
}
bool new_push = push.is_on();
if (new_push != pushed) {
pushed = new_push;
std::string out = "Button: ";
out += pushed ? "pushed" : "released";
lcd.write(1, 0, out);
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
/// In the following example we use the event driven approach with a
/// rotary dial with integrated push button. We connect callables
/// (in this case member functions of the Rotary1 class) to the
/// rotary and button classes, and have them called whenever an
/// event occurs. Remark there is no polling, and no control loop.
class Rotary1 {
public:
Rotary1()
// the constructor for a 2x16 display connected to 6 discrete GPIO pins:
// : lcd(2, 16, 26, 19, 21, 20, 16, 12)
// the display could also be connected to the i2c bus, and have 4x20 characters display size
: lcd(4, 20, "/dev/i2c-1", 0x27)
, dial(6, 12, GPIO_PULL::UP)
, push(5, GPIO_PULL::UP)
{
lcd.fill();
lcd.write(0, 0, "Please dial me!");
lcd.write(1, 0, "Will exit at #42");
// register a lambda function at the dial to connect it to this class
dial.f_dialed = [&](bool up, long value) { dialed(up, value); };
// could also use std::bind():
// dial.f_dialed = std::bind(&Rotary1::dialed, this, std::placeholders::_1, std::placeholders::_2);
push.f_pushed = [&]() { pushed(); };
push.f_released = [&](std::chrono::nanoseconds nano) { released(nano); };
// after finishing the initialization of the event driven input objects
// start the event threads of the input objects
dial.start();
push.start();
}
private:
HitachiLCD lcd;
RotaryDial dial;
PushButton push;
void dialed(bool up, long value)
{
std::string out = "Value: ";
out += std::to_string(value);
lcd.write(0, 0, out);
if (value == 42) {
lcd.write(0, 0, "Goodbye!");
lcd.write(1, 0, "");
std::this_thread::sleep_for(std::chrono::seconds(2));
lcd.backlight(false);
exit(0);
}
}
void pushed()
{
lcd.write(1, 0, "Button: pushed");
}
void released(std::chrono::nanoseconds nano)
{
lcd.write(1, 0, "Button: released");
}
};
void ioperformance()
{
// bind gpio 18
DigitalOut out(18);
// Test the maximum output frequency we can reach on a GPIO output by simply switching it on and off.
int64_t one_loop = 0;
{
auto start = std::chrono::steady_clock::now();
for (int x = 0; x < 10000000; ++x) {
out.on();
out.off();
}
auto end = std::chrono::steady_clock::now();
int64_t ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end-start).count();
one_loop = ns / 10000000;
std::cout << ns << " nanoseconds for 10.000.000 IOPS = "
<< one_loop << " nanoseconds per one IOP, = "
<< 1000*1000*1000/(ns/10000000) << " Hz" << std::endl;
}
// Test again, but this time with 10 output switches in one loop - to see the effect the
// loop control has
{
auto start = std::chrono::steady_clock::now();
for (int x = 0; x < 1000000; ++x) {
out.on();
out.off();
out.on();
out.off();
out.on();
out.off();
out.on();
out.off();
out.on();
out.off();
out.on();
out.off();
out.on();
out.off();
out.on();
out.off();
out.on();
out.off();
out.on();
out.off();
}
auto end = std::chrono::steady_clock::now();
int64_t ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end-start).count();
one_loop = ns / 10000000;
std::cout << ns << " nanoseconds for 10.000.000 IOPS = "
<< one_loop << " nanoseconds per one IOP, = "
<< 1000*1000*1000/(ns/10000000) << " Hz" << std::endl;
}
// Determine how long a minimum sleep() request of 1 nanoseconds really takes until it returns
// (compare result to nanoseconds measured in above loop)
{
auto start = std::chrono::steady_clock::now();
auto sleep = std::chrono::nanoseconds(1);
for (int x = 0; x < 100000; ++x) {
std::this_thread::sleep_for(sleep);
out.on();
out.off();
}
auto end = std::chrono::steady_clock::now();
int64_t ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end-start).count();
std::cout << ns << " nanoseconds for 100.000 IOPS with 1ns sleep each = "
<< ns / 100000 << " nanoseconds per one IOP with 1ns sleep, = "
<< 1000*1000*1000/(ns/100000) << " Hz" << std::endl;
auto minsleep = ns / 100000 - one_loop;
std::cout << "minimum sleep time is " << minsleep << " nanoseconds" << std::endl;
}
}
void pwmtest()
{
std::cout << "testing PWM output" << std::endl;
PWMOut pwm(23, 100, 0);
for (int l = 0; l < 20; ++l) {
for (int p = 0; p < 100; ++p) {
pwm.set_ratio(p);
std::this_thread::sleep_for(std::chrono::microseconds(5000));
}
for (int p = 100; p > 0; --p) {
pwm.set_ratio(p);
std::this_thread::sleep_for(std::chrono::microseconds(5000));
}
}
}
int main(int argc, const char * argv[])
{
ioperformance();
// pwmtest();
lcdtest();
// buttontest();
Rotary1 rotary;
// do something (or just sleep)
std::this_thread::sleep_for(std::chrono::hours(1));
return 0;
}

@ -0,0 +1,20 @@
//
// cppgpio.hpp
// GPIO
//
// Created by Joachim Schurig on 22.02.16.
// Copyright © 2016 Joachim Schurig. All rights reserved.
//
// Convenience header for cppgpio
#ifndef cppgpio_hpp_AJGSFHGCOJAISLHVZAGJSVBAUKNV
#define cppgpio_hpp_AJGSFHGCOJAISLHVZAGJSVBAUKNV
#include <cppgpio/gpio.hpp>
#include <cppgpio/i2c.hpp>
#include <cppgpio/buttons.hpp>
#include <cppgpio/lcd.hpp>
#include <cppgpio/output.hpp>
#endif /* cppgpio_hpp */

@ -0,0 +1,318 @@
//
// Copyright © 2016 Joachim Schurig. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#ifndef BUTTONS_HPP_SUKJHKDJSHSDFJKSFJKJLKSFHGKFAJGFDF
#define BUTTONS_HPP_SUKJHKDJSHSDFJKSFJKJLKSFHGKFAJGFDF
#include <chrono>
#include <atomic>
#include <memory>
#include <poll.h>
#include <cppgpio/gpio.hpp>
namespace GPIO {
/// The base class for "interrupt" based detection of changes on input lines. It
/// actually does not use interrupts itself, but events signalled through poll()
/// - therefore we do not have to care for timing and restarting of system calls.
class InputDetect : public ObjectBase {
public:
typedef std::vector<unsigned int> gpiovec_t;
/// start event detection on select gpio pins
InputDetect(const gpiovec_t& gpios, GPIO_EDGE mode = GPIO_EDGE::BOTH);
/// Properly destroy this class. This involves stopping the started event thread
/// and closing all opened inputs.
virtual ~InputDetect();
/// After completion of class initialization (that is, registration of callables
/// at the various function variables), signal here that the input detection
/// thread could finally be run. If start() is never called, no detection will
/// ever happen.
bool start();
/// it is also possible to stop the running thread, but you would normally not
/// need to do it - the destructor does it automatically. If you call stop(), then
/// be prepared that it could take up to 500ms for it to return (or even longer if
/// the thread is currently not waiting for an event, but executing some long
/// running custom task in your code).
bool stop();
protected:
/// gets called by self when an input change is detected, and tells which gpio
virtual void triggered(unsigned int gpio) {}
private:
typedef std::vector<pollfd> pollvec_t;
pollvec_t m_pollvec;
gpiovec_t m_gpiovec;
bool m_terminate;
std::unique_ptr<std::thread> m_waiter;
/// the internal function that actually waits for status changes
void event_loop();
};
/// The class DigitalIn models one input pin and its state, and performs digital debouncing of the input.
/// It is used as class member in the various hardware modelling classes below.
class DigitalIn {
public:
DigitalIn(/// GPIO pin to use
unsigned int pin,
/// pullup / down / none
GPIO_PULL pullupdown = GPIO_PULL::OFF,
/// define the interval that has to pass between two events
std::chrono::nanoseconds min_trigger_interval = std::chrono::nanoseconds(2 * 1000 * 1000),
/// define the minimum time the gpio pin has to be triggered
/// before a trigger is detected (to protect against noise on
/// the line)
std::chrono::nanoseconds min_hold_interval = std::chrono::nanoseconds(1 * 1000 * 1000));
/// returns true if real state change (and no line noise or switch crackle)
bool triggered() { return debounce(); }
unsigned int get_pin() const { return m_pin; }
unsigned int get_state() const { return m_state ? 1 : 0; }
private:
bool m_state;
unsigned int m_pin;
GPIO_PULL m_pullupdown;
std::chrono::nanoseconds m_min_trigger_interval;
std::chrono::nanoseconds m_min_hold_interval;
std::chrono::steady_clock::time_point m_last_triggered;
GPIOBase m_gpio;
/// test against last triggered time, on pin
bool debounce();
};
/// The class Switch models a physical switch, something that can be on or off at any given time. It offers
/// function variables which will be called whenever the state changes. Register own functions to be called
/// by simply assigning a lambda or a callable constructed with std::bind() to the function variables.
class Switch : public InputDetect {
public:
Switch(/// GPIO pin to use
unsigned int pin,
/// pullup / down / none
GPIO_PULL pullupdown = GPIO_PULL::OFF,
/// define the interval that has to pass between two events
std::chrono::nanoseconds min_trigger_interval = std::chrono::nanoseconds(2 * 1000 * 1000), // 2 ms
/// define the minimum time the gpio pin has to be triggered
/// before a trigger is detected (to protect against noise on
/// the line)
std::chrono::nanoseconds min_hold_interval = std::chrono::nanoseconds(1 * 1000 * 1000)) // 1 ms
: InputDetect({ pin }, GPIO_EDGE::BOTH)
, m_pin(pin,
pullupdown,
min_trigger_interval,
min_hold_interval)
{}
/// destructor
virtual ~Switch() {}
/// queries the state of the switch
bool is_on() const { return m_pin.get_state(); }
/// hooks for depending classes; get called when the button is switched.
/// Register with a lambda expression or std::bind()
/// see demo.cpp for examples
std::function<void(bool)> f_switched {nullptr};
std::function<void()> f_on {nullptr};
std::function<void()> f_off {nullptr};
protected:
DigitalIn m_pin;
virtual void triggered(unsigned int gpio);
};
/// The class DirectIn does not use a debouncer and is therefore optimized for maximum performance (it is in fact
/// the Switch class with debouncer disabled). You could use it to act on events on inputs which are
/// directly coupled to other digital outputs. The function variables of Switch are inherited for registering
/// own callables.
class DirectIn : public Switch {
public:
DirectIn(/// GPIO pin to use
unsigned int pin,
/// pullup / down / none
GPIO_PULL pullupdown = GPIO_PULL::OFF)
: Switch(pin, pullupdown, std::chrono::nanoseconds(0), std::chrono::nanoseconds(0))
{}
virtual ~DirectIn() {}
};
/// The class Counter is a simple counter of the pulses on the GPIO pin. You can read the current count with get_count()
/// and reset it with clear_count()
class Counter : public Switch {
public:
Counter(/// GPIO pin to use
unsigned int pin,
/// pullup / down / none
GPIO_PULL pullupdown = GPIO_PULL::OFF,
/// define the interval that has to pass between two events
std::chrono::nanoseconds min_trigger_interval = std::chrono::nanoseconds(2 * 1000 * 1000), // 2 ms
/// define the minimum time the gpio pin has to be triggered
/// before a trigger is detected (to protect against noise on
/// the line)
std::chrono::nanoseconds min_hold_interval = std::chrono::nanoseconds(1 * 1000 * 1000)); // 1 ms
virtual ~Counter() {}
unsigned long get_count() const { return m_count; }
void clear_count() { m_count = 0; }
protected:
std::atomic_ulong m_count;
};
/// This is a PushButton control class. It tells when a button is pushed, and how long it took
/// to become released again. It offers function variables where you could register own callables
/// which get called when a state change happens.
class PushButton : public Switch {
public:
PushButton(/// GPIO pin to use
unsigned int pin,
/// pullup / down / none
GPIO_PULL pullupdown = GPIO_PULL::OFF,
/// define the interval that has to pass between two events
std::chrono::nanoseconds min_trigger_interval = std::chrono::nanoseconds(2 * 1000 * 1000), // 2 ms
/// define the minimum time the gpio pin has to be triggered
/// before a trigger is detected (to protect against noise on
/// the line)
std::chrono::nanoseconds min_hold_interval = std::chrono::nanoseconds(1 * 1000 * 1000)); // 1 ms
/// destructor
virtual ~PushButton() {}
/// hook for depending classes; gets called when the button is pushed
/// Register with a lambda expression or std::bind()
/// see demo.cpp for examples
std::function<void()> f_pushed {nullptr};
/// hook for depending classes; gets called when the button is released, value is time pushed in ns
/// Register with a lambda expression or std::bind()
/// see demo.cpp for examples
std::function<void(std::chrono::nanoseconds)> f_released {nullptr};
private:
std::chrono::steady_clock::time_point last_pushed;
};
/// A rotary encoder class. Counts a value up/down with turning the dial, and permits setting a range.
/// Also measures how fast the dial is turned. Register own callables at the function variables to be
/// called on state changes.
class RotaryDial : public InputDetect {
public:
RotaryDial(/// GPIO pin A to use
unsigned int pin_a,
/// GPIO pin B to use
unsigned int pin_b,
/// pullup / down / none
GPIO_PULL pullupdown = GPIO_PULL::OFF,
/// define the interval that has to pass between two events
std::chrono::nanoseconds min_trigger_interval = std::chrono::nanoseconds(2 * 1000 * 1000), // 2 ms
/// define the minimum time the gpio pin has to be triggered
/// before a trigger is detected (to protect against noise on
/// the line)
std::chrono::nanoseconds min_hold_interval = std::chrono::nanoseconds(1 * 1000 * 1000), // 1 ms
/// how many internal grey code steps mark one detent? 1, 2, and 4 are typical
int divider = 4);
/// destructor
virtual ~RotaryDial() {}
void set_range(long min, long max);
void set_value(long value) { m_value = value; }
long get_value() const { return m_value; }
unsigned int get_speed() const { return m_speed; }
/// hooks for depending classes; get called when the dial is turned
/// Register with a lambda expression or std::bind()
/// see demo.cpp for examples
std::function<void(bool, long)> f_dialed {nullptr};
std::function<void()> f_up {nullptr};
std::function<void()> f_down {nullptr};
protected:
void decode_dial();
virtual void triggered(unsigned int gpio);
private:
DigitalIn m_pin_a, m_pin_b;
std::atomic_long m_value {0};
std::atomic_uint m_speed {0};
long m_encvalue {0};
int m_divider {4};
long m_min {std::numeric_limits<long>::min()};
long m_max {std::numeric_limits<long>::max()};
bool m_use_range {false};
unsigned int m_lastseq {0};
std::chrono::steady_clock::time_point m_last_triggered;
std::chrono::steady_clock::time_point m_last_dialed;
void set_speed(std::chrono::nanoseconds timediff);
};
}
#endif // #ifndef BUTTONS_HPP_SUKJHKDJSHSDFJKSFJKJLKSFHGKFAJGFDF

@ -0,0 +1,345 @@
//
// gpio.hpp
//
// Copyright © 2016 Joachim Schurig. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#ifndef GPIO_H_ASZJAHSGCHCLNISKUGDHSILJDSV
#define GPIO_H_ASZJAHSGCHCLNISKUGDHSILJDSV
#include <exception>
#include <thread>
#include <functional>
#include <vector>
#include <string>
#include <map>
#include <stdexcept>
#include <atomic>
#include <cstdint>
#include <cstring>
#include <typeinfo>
#include <memory>
#include <mutex>
namespace GPIO {
class GPIOError : public std::runtime_error {
using std::runtime_error::runtime_error;
};
typedef std::mutex Mutex;
typedef std::lock_guard<Mutex> Lock;
typedef std::recursive_mutex RecursiveMutex;
typedef std::lock_guard<RecursiveMutex> RecursiveLock;
/// base class for all GPIO objects (to allow common storage and safe destruction
/// from the base object via virtual destructor)
class ObjectBase {
public:
ObjectBase() = default;
virtual ~ObjectBase() = 0;
};
typedef std::unique_ptr<ObjectBase> UniqueObjectBase;
typedef std::shared_ptr<ObjectBase> SharedObjectBase;
/// This is the implementation of a concrete GPIO control, the one in the BCM2835.
/// This is a temporary solution which will get replaced by a factory model
/// to choose from different GPIOs, with the below public functionality being
/// the factory abstract base.
/// See peripherals documentation of BCM2835 at
/// https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
enum class GPIO_MODE : uint8_t {
GPIO_INPUT = 0,
GPIO_OUTPUT = 1,
PWM_OUTPUT = 2,
SOFT_PWM_OUTPUT = 3,
PWM_TONE_OUTPUT = 4,
SOFT_TONE_OUTPUT = 5
};
// this "happen" to be the real values needed for the bcm2835, but for other
// chips they might need translation
enum class GPIO_PULL : uint8_t {
OFF = 0,
DOWN = 1,
UP = 2
};
// this "happen" to be the real values needed for the bcm2835, but for other
// chips they might need translation
enum class GPIO_EDGE : uint8_t {
FALLING = 0,
RISING = 1,
BOTH = 2
};
enum class PWM_MODE : uint8_t {
MARKSPACE = 0,
BALANCED = 1,
INVALID = 2
};
/// This class has no non-static member variables, but its public functions
/// are nonetheless declared non-static, to force initialization of the underlying
/// memory map through the default constructor.
class GPIOBase {
public:
GPIOBase();
/// Set "simulation" mode - that is, no real IO, but instead dummy operating
/// only. Simulation mode is set automatically when this code is run on OS
/// other than Linux, but if you want to run in this mode on Linux you
/// have to call simulation(true) explicitly.
static void simulation(bool yes);
/// returns true if simulation (dummy) mode is set
static bool simulation() { return simulate; }
/// Call before any instantiation of a GPIOBase object if you want to
/// force full register access via IO mapping even when /dev/gpiomem is
/// available. When you switch into this mode and are not running as root,
/// the first instantiation of a GPIOBase object will throw an exception.
/// When you are root, you gain access on all registers of the bcm2835, not
/// only on I/O pins, but with the current implementation of this library the
/// only advantage is that you can then set the PWM clock frequency and use
/// hardware PWM on two outputs.
static bool force_full_mapping();
/// sets the I/O mode of a gpio
void mode(unsigned int gpio, GPIO_MODE mode) const;
/// sets pullup/down for a gpio
void pullupdown(unsigned int gpio, GPIO_PULL pud) const;
/// read status of an input
bool read(unsigned int gpio) const
{
return int_read(gpio);
}
/// clear (set to 0V) an output
void clear(unsigned int gpio) const
{
int_clear(gpio);
}
/// set (set to 3.3V) an output
void set(unsigned int gpio) const
{
int_set(gpio);
}
/// if value is 0 the output will be cleared, else will be set
void write(unsigned int gpio, bool value) const
{
(value) ? set(gpio) : clear(gpio);
}
/// returns true if this GPIO can be run in hardware PWM mode
bool has_hard_pwm(unsigned int gpio) const;
/// set the on-off ratio for the hardware PWM output
void pwm_write(unsigned int gpio, unsigned int value) const;
/// set the mode (mark/space or balanced) for the hardware PWM output
void pwm_mode(unsigned int gpio, PWM_MODE mode) const;
/// set the value range for the hardware PWM output
void pwm_range(unsigned int gpio, unsigned int range) const;
/// set the clock divider for the hardware PWM output
void pwm_clock(unsigned int gpio, unsigned int divisor) const;
/// Set the value range for the soft PWM output (the soft PWM is
/// generated by a looping thread, not by PWM hardware - it is
/// less accurate, as the thread will be scheduled in and out
/// of execution, but it can be run on any output, and it does
/// not need root priviledges to be run).
void soft_pwm_range(unsigned int gpio, unsigned int range) const;
/// Set the on-off ratio for the soft PWM output (the soft PWM is
/// generated by a looping thread, not by PWM hardware - it is
/// less accurate, as the thread will be scheduled in and out
/// of execution, but it can be run on any output, and it does
/// not need root priviledges to be run).
void soft_pwm_write(unsigned int gpio, unsigned int value) const;
/// set the frequency for the hardware PWM tone (square wave) output
void pwm_tone_write(unsigned int gpio, unsigned int frequency) const;
/// set the frequency for the soft PWM tone (square wave) output
/// (range is from 0..10000 Hz)
void soft_tone_write(unsigned int gpio, unsigned int frequency) const;
private:
enum class PWM_CHANNEL : std::uint8_t {
CH0 = 0,
CH1 = 1
};
enum {
PWM_CHANNELS = 2
};
enum class FSEL : uint8_t {
INPUT = 0b000,
OUTPUT = 0b001,
ALT0 = 0b100,
ALT1 = 0b101,
ALT2 = 0b110,
ALT3 = 0b111,
ALT4 = 0b011,
ALT5 = 0b010
};
static std::once_flag initlock;
static void init();
struct bcm2835_peripheral_t {
unsigned long addr_p;
int mem_fd;
void *map;
volatile unsigned int *addr;
};
static bool simulate;
static bool want_full_mapping;
static bool gpio_only;
static unsigned long peripherals_base;
static unsigned long peripherals_size;
static std::unique_ptr<unsigned long[]> peripherals_sim;
static volatile unsigned long* peripherals;
static volatile unsigned long* gpio_addr;
static volatile unsigned long* pwm_addr;
static volatile unsigned long* clk_addr;
static volatile unsigned long* pads_addr;
static volatile unsigned long* spi0_addr;
static volatile unsigned long* bsc0_addr;
static volatile unsigned long* bsc1_addr;
static volatile unsigned long* st_addr;
static Mutex pullupdown_mutex;
class SoftThread {
private:
std::unique_ptr<std::thread> m_thread;
unsigned int m_range;
unsigned int m_value;
std::atomic_uint m_mark;
std::atomic_uint m_space;
unsigned int m_gpio;
void run_tone();
void run_pwm();
public:
void set_range(unsigned int range);
void set_value(unsigned int value);
void terminate() { set_range(0); }
SoftThread(GPIO_MODE what = GPIO_MODE::SOFT_PWM_OUTPUT,
unsigned int gpio = 0,
unsigned int range = 1024,
unsigned int value = 0);
~SoftThread();
};
typedef std::unique_ptr<SoftThread> UniqueSoftThread;
typedef std::map<unsigned int, UniqueSoftThread> soft_thread_map_t;
static soft_thread_map_t soft_thread_map;
static Mutex m_soft_thread_mutex;
struct pwm_t {
// converting helper constructor for init
pwm_t(unsigned int fsel_p, unsigned int channel_p)
: fsel(static_cast<FSEL>(fsel_p))
, channel(static_cast<PWM_CHANNEL>(channel_p)) {}
pwm_t(FSEL fsel_p, PWM_CHANNEL channel_p)
: fsel(fsel_p)
, channel(channel_p) {}
FSEL fsel;
PWM_CHANNEL channel;
};
typedef std::vector<pwm_t> pwm_vec_t;
static pwm_vec_t pwm_reg;
static unsigned int pwm_range_val[PWM_CHANNELS];
static PWM_MODE pwm_mode_val[PWM_CHANNELS];
static unsigned int pwm_clock_val; // one clock for all channels..
static void int_set_pwm_mode(PWM_CHANNEL channel, PWM_MODE mode);
static void int_set_pwm_range(PWM_CHANNEL channel, unsigned int range);
static void int_set_pwm_clock(PWM_CHANNEL channel, unsigned int divisor);
static void int_pwm_write(PWM_CHANNEL channel, unsigned int value);
static void int_set_mode(unsigned int gpio, FSEL mode);
static inline void int_set(unsigned int gpio)
{
*(gpio_addr + 7 + gpio/32) = 1 << (gpio%32);
}
static inline void int_clear(unsigned int gpio)
{
*(gpio_addr + 10 + gpio/32) = 1 << (gpio%32);
}
static inline bool int_read(unsigned int gpio)
{
return *(gpio_addr + 13 + gpio/32) &= (1 << (gpio%32));
}
void check_hard_pwm(unsigned int gpio) const;
};
}
#endif

@ -0,0 +1,63 @@
//
// i2c.hpp
//
// Copyright © 2016 Joachim Schurig. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#ifndef i2c_hpp_IASZJGFISOLJDFPOSIDUHGZAUHIJDALSFASVYESF
#define i2c_hpp_IASZJGFISOLJDFPOSIDUHGZAUHIJDALSFASVYESF
#include <string>
namespace GPIO {
class I2C {
public:
I2C(const std::string& interface, unsigned int device_id)
{
open(interface, device_id);
}
I2C() {}
~I2C()
{
close();
}
void open(const std::string& interface, unsigned int device_id);
void close();
int read() const;
int regread8(int reg) const;
int regread16(int reg) const;
void write(int data) const;
void regwrite8(int reg, int value) const;
void regwrite16(int reg, int value) const;
private:
int m_fd = -1;
};
}
#endif /* i2c_hpp */

@ -0,0 +1,285 @@
//
// lcd.hpp
//
// Wrapper classes for Hitachi HD44780U LCD driver compatible chips
//
// Copyright © 2016 Joachim Schurig. All rights reserved.
//
// Redistribution and use in sou