/* C++ class to read and write Raspberry Pi GPIO pins as fast as possible. GPIOCPP version 0.09, march 18, 2017 COPYLEFT - Pieter Suurmond. Thanks to Dom and Gert: http://elinux.org/RPi_GPIO_Code_Samples#C and Pieter-Jan van de Maele: https://github.com/Pieter-Jan/PJ_RPI and others. To compile this class, use GCC option '-std=c++11' to enable constexpr. For example: g++ -Wall -O2 -std=c++11 -c -o gpiocpp.o gpiocpp.cpp In case your C++ compiler cannot handle 'constexpr', simply remove it or replace it by 'inline'. Sorry for so much inline code here, in the headerfile, but when moved to the .cpp implementation file, I just cannot get it linked inline (ld). We use Broadcom (BCM) GPIO numbering here (not board numbering)! */ #ifndef uint8_t #include #endif // Helper functions for configuring GPIO pins. Probably, in most cases, the pin // number will be known at compile time. In these cases, the corresponding bit- // masks and addresses can be calculated on beforehand, yielding faster code. constexpr uint8_t cfg_add(uint8_t p) { return p / 10; } constexpr uint8_t cfg_bit(uint8_t p) { return (p % 10) * 3; } constexpr uint32_t cfg_rd(uint8_t p) { return ~(7 << cfg_bit(p)); } constexpr uint32_t cfg_wr(uint8_t p) { return 1 << cfg_bit(p); } // Constants to be used in calls to function config_pull(pin): enum class PULL : uint32_t { OFF = 0, DOWN = 1, UP = 2 }; class GPIOCPP { // Address offsets (in bytes/4). See 'BCM2835-ARM-Peripherals.pdf': #define GPSET0 (7) #define GPCLR0 (10) #define GPLEV0 (13) public: GPIOCPP(uint8_t model, // Use 1 for ARMv6; 2 for ARMv7. const char* path); // Use "/dev/mem" or "/dev/gpiomem". ~GPIOCPP(); //--------------------------------------------------- CONFIGURE SINGLE PINS: // When possible, use const or a macro for the GPIO pin number when calling // the following functions (with argument 'pin'), that yields faster code: inline void config_read(uint8_t pin) { // Pin numbers range from address[cfg_add(pin)] &= cfg_rd(pin); // to 0 to 31 inclusive. }; inline void config_write(uint8_t pin) { // Slow but safe. address[cfg_add(pin)] &= cfg_rd(pin); // First clear 3 bits. address[cfg_add(pin)] |= cfg_wr(pin); // Then set 1 of them. }; // A faster function that may be called after an earlier config_write() or // config_read() call with the same pin number. For cases where you need to // toggle between read and write more quickly: inline void config_write_fast(uint8_t pin) { address[cfg_add(pin)] |= cfg_wr(pin); }; // The following function is slow anyway. Use PULL::UP, PULL::DOWN or // PULL::OFF as last argument: void config_pull(uint8_t pin, PULL pull); //------------------------------------------ READ AND WRITE INDIVIDUAL PINS: inline bool readpin(uint8_t pin) { return address[GPLEV0] & (1 << pin); }; inline void setpin(uint8_t pin) { address[GPSET0] = 1 << pin; }; inline void clearpin(uint8_t pin) { address[GPCLR0] = 1 << pin; }; //------------------------------------------ READ AND WRITE 32 PINS AT ONCE: inline uint32_t readpins() { return address[GPLEV0]; }; inline void setpins(uint32_t bits) { // Zero bits are ignored. address[GPSET0] = bits; }; inline void clearpins(uint32_t bits) { // Bits that are set will be cleared. address[GPCLR0] = bits; }; private: const uint16_t blocksize; volatile uint32_t* address; // Content is volatile, but pointer? // Dom and Gert (and others (repeating?)) emphasize this pointer should be // volatile, but I don't see why it should be. Of course the contents of // 'address' (and of higher addresses, up to 'blocksize') is volatile // (compiler should not optimize-out rd and wr actions). But the memory // block allocated by mmap does it move? };