diff --git a/Makefile b/Makefile index 84b15d47..fe7280a9 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ include $(CONFIG_FILE) # Objects to compile OBJECTS=RF24.o ifeq ($(DRIVER), MRAA) -OBJECTS+=spi.o gpio.o compatibility.o +OBJECTS+=spi.o gpio.o compatibility.o interrupt.o else ifeq ($(DRIVER), RPi) OBJECTS+=spi.o bcm2835.o compatibility.o interrupt.o else ifeq ($(DRIVER), SPIDEV) diff --git a/examples_linux/CMakeLists.txt b/examples_linux/CMakeLists.txt index 40101a60..d48c1a98 100644 --- a/examples_linux/CMakeLists.txt +++ b/examples_linux/CMakeLists.txt @@ -8,6 +8,7 @@ set(EXAMPLES_LIST streamingData multiceiverDemo scanner + interruptConfigure ) project(RF24Examples CXX) @@ -23,13 +24,6 @@ include(../cmake/AutoConfig_RF24_DRIVER.cmake) find_library(RF24 rf24 REQUIRED) message(STATUS "using RF24 library: ${RF24}") -# conditionally append "interruptConfigure" to the EXAMPLES_LIST -if("${RF24_DRIVER}" STREQUAL "MRAA") - message(STATUS "Skipping interruptConfigure.cpp example as it is incompatible with selected driver library") -else() # not using MRAA or wiringPi drivers (or pigpio lib was found) - list(APPEND EXAMPLES_LIST interruptConfigure) -endif() - set(linked_libs ${RF24} @@ -52,10 +46,14 @@ elseif("${RF24_DRIVER}" STREQUAL "wiringPi") else() message(FATAL "Lib ${RF24_DRIVER} not found.") endif() -elseif(NOT "${LibPIGPIO}" STREQUAL "LibPIGPIO-NOTFOUND") - message(STATUS "linking to ${LibPIGPIO}") - # linking to pigpio requires pthread to be listed as last linked lib - list(APPEND linked_libs ${LibPIGPIO} pthread) +elseif("${RF24_DRIVER}" STREQUAL "pigpio") + if(NOT "${LibPIGPIO}" STREQUAL "LibPIGPIO-NOTFOUND") + message(STATUS "linking to ${LibPIGPIO}") + # linking to pigpio requires pthread to be listed as last linked lib + list(APPEND linked_libs ${LibPIGPIO} pthread) + else() + message(FATAL "Lib ${RF24_DRIVER} not found") + endif() endif() foreach(example ${EXAMPLES_LIST}) diff --git a/examples_linux/Makefile b/examples_linux/Makefile index 901a7402..e3a3c499 100644 --- a/examples_linux/Makefile +++ b/examples_linux/Makefile @@ -18,9 +18,6 @@ endif include ../Makefile.inc # define all programs -PROGRAMS = gettingstarted acknowledgementPayloads manualAcknowledgements streamingData multiceiverDemo scanner -ifneq ($(DRIVER), MRAA) -PROGRAMS+=interruptConfigure -endif +PROGRAMS = gettingstarted acknowledgementPayloads manualAcknowledgements streamingData multiceiverDemo scanner interruptConfigure include Makefile.examples diff --git a/examples_linux/interruptConfigure.cpp b/examples_linux/interruptConfigure.cpp index 91686846..ec71b7a5 100644 --- a/examples_linux/interruptConfigure.cpp +++ b/examples_linux/interruptConfigure.cpp @@ -22,7 +22,13 @@ using namespace std; // We will be using the nRF24L01's IRQ pin for this example -#define IRQ_PIN 24 // this needs to be a digital input capable pin +#ifdef MRAA + #define IRQ_PIN 18 // GPIO24 +#elif defined(RF24_WIRINGPI) + #define IRQ_PIN 5 // GPIO24 +#else + #define IRQ_PIN 24 // GPIO24 +#endif // this example is a sequential program. so we need to wait for the event to be handled volatile bool got_interrupt = false; // used to signify that the event started diff --git a/utility/CMakeLists.txt b/utility/CMakeLists.txt index d18bce7a..0ea66b9c 100644 --- a/utility/CMakeLists.txt +++ b/utility/CMakeLists.txt @@ -67,6 +67,7 @@ elseif("${RF24_DRIVER}" STREQUAL "MRAA") # use MRAA ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/spi.cpp ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/compatibility.cpp ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/RF24_arch_config.h + ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/interrupt.cpp PARENT_SCOPE ) install(FILES @@ -75,7 +76,8 @@ elseif("${RF24_DRIVER}" STREQUAL "MRAA") # use MRAA ${RF24_DRIVER}/spi.h ${RF24_DRIVER}/compatibility.h ${RF24_DRIVER}/RF24_arch_config.h - DESTINATION include/RF24/utility/${RF24_DRIVER} + ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/interrupt.h + DESTINATION include/RF24/utility/${RF24_DRIVER} ) elseif("${RF24_DRIVER}" STREQUAL "pigpio") # use pigpio set(RF24_LINKED_DRIVER ${LibPIGPIO} PARENT_SCOPE) diff --git a/utility/MRAA/gpio.cpp b/utility/MRAA/gpio.cpp index 543228fb..ab41421a 100644 --- a/utility/MRAA/gpio.cpp +++ b/utility/MRAA/gpio.cpp @@ -4,6 +4,7 @@ */ #include +#include // mraa_strresult() #include "gpio.h" // cache for mraa::Gpio instances @@ -25,15 +26,22 @@ GPIO::~GPIO() void GPIO::open(rf24_gpio_pin_t port, mraa::Dir DDR) { + mraa::Result status; + // check that mraa::Gpio context doesn't already exist std::map::iterator i = gpio_cache.find(port); if (i == gpio_cache.end()) { mraa::Gpio* gpio_inst = new mraa::Gpio(port); gpio_cache[port] = gpio_inst; - gpio_inst->dir(DDR); + status = gpio_inst->dir(DDR); } else { - i->second->dir(DDR); + status = i->second->dir(DDR); + } + if (status != mraa::SUCCESS) { + std::string msg = "[GPIO::open] Could not set the pin direction; "; + msg += mraa_strresult((mraa_result_t)status); + throw GPIOException(msg); } } @@ -60,17 +68,17 @@ int GPIO::read(rf24_gpio_pin_t port) void GPIO::write(rf24_gpio_pin_t port, int value) { - mraa::Result result = mraa::Result::ERROR_UNSPECIFIED; // a default // get cache gpio instance std::map::iterator i = gpio_cache.find(port); if (i != gpio_cache.end()) { - result = i->second->write(value); + mraa::Result result = i->second->write(value); + if (result != mraa::Result::SUCCESS) { + std::string msg = "[GPIO::write] Could not set pin output value; "; + msg += mraa_strresult((mraa_result_t)result); + throw GPIOException(msg); + } } else { throw GPIOException("[GPIO::write] pin was not initialized with GPIO::open()"); } - - if (result != mraa::Result::SUCCESS) { - throw GPIOException("GPIO::write() failed"); - } } diff --git a/utility/MRAA/includes.h b/utility/MRAA/includes.h index d5d69790..6a46304e 100644 --- a/utility/MRAA/includes.h +++ b/utility/MRAA/includes.h @@ -6,5 +6,6 @@ #endif #include "MRAA/RF24_arch_config.h" +#include "MRAA/interrupt.h" #endif // RF24_UTILITY_INCLUDES_H_ diff --git a/utility/MRAA/interrupt.cpp b/utility/MRAA/interrupt.cpp new file mode 100644 index 00000000..cbe8e433 --- /dev/null +++ b/utility/MRAA/interrupt.cpp @@ -0,0 +1,58 @@ +#include +#include // mraa_strresult() +#include // mraa::Gpio +#include "interrupt.h" +#include "gpio.h" // rf24_gpio_pin_t + +#ifdef __cplusplus +extern "C" { +#endif + +std::map irqCache; + +int attachInterrupt(rf24_gpio_pin_t pin, uint8_t mode, void (*function)(void)) +{ + // ensure pin is not already being used in a separate thread + detachInterrupt(pin); + GPIO::close(pin); + + mraa::Gpio* gpio = new mraa::Gpio(pin); + mraa::Result status = gpio->dir(mraa::DIR_IN); + if (status != mraa::SUCCESS) { + std::string msg = "[attachInterrupt] Could not set the pin as an input; "; + msg += mraa_strresult((mraa_result_t)status); + throw IRQException(msg); + return 0; + } + status = gpio->isr((mraa::Edge)mode, (void (*)(void*))function, NULL); + if (status != mraa::SUCCESS) { + std::string msg = "[attachInterrupt] Could not setup the ISR; "; + msg += mraa_strresult((mraa_result_t)status); + throw IRQException(msg); + return 0; + } + + std::pair::iterator, bool> indexPair = irqCache.insert(std::pair(pin, gpio)); + if (!indexPair.second) { + // this should not be reached, but indexPair.first needs to be the inserted map element + gpio->close(); + throw IRQException("[attachInterrupt] Could not cache the mraa::Gpio object"); + return 0; + } + return 1; +} + +int detachInterrupt(rf24_gpio_pin_t pin) +{ + std::map::iterator cachedPin = irqCache.find(pin); + if (cachedPin == irqCache.end()) { + return 0; // pin not in cache; just exit + } + cachedPin->second->close(); + irqCache.erase(cachedPin); + return 1; +} + +#ifdef __cplusplus +} +#endif diff --git a/utility/MRAA/interrupt.h b/utility/MRAA/interrupt.h new file mode 100644 index 00000000..5fe06ea8 --- /dev/null +++ b/utility/MRAA/interrupt.h @@ -0,0 +1,44 @@ +#ifndef RF24_UTILITY_MRAA_INTERRUPT_H_ +#define RF24_UTILITY_MRAA_INTERRUPT_H_ + +#include // std::exception, std::string +#include "mraa.hpp" // mraa:: +#include "gpio.h" // rf24_gpio_pin_t + +#ifdef __cplusplus +extern "C" { +#endif + +enum Edge +{ + INT_EDGE_FALLING = mraa::Edge::EDGE_FALLING, + INT_EDGE_RISING = mraa::Edge::EDGE_RISING, + INT_EDGE_BOTH = mraa::Edge::EDGE_BOTH, +}; + +/** Specific exception for IRQ errors */ +class IRQException : public std::runtime_error +{ +public: + explicit IRQException(const std::string& msg) + : std::runtime_error(msg) + { + } +}; + +/** + * Take the details and create an interrupt handler that will + * callback to the user-supplied function. + */ +int attachInterrupt(rf24_gpio_pin_t pin, uint8_t mode, void (*function)(void)); + +/** + * Will cancel the interrupt thread, close the filehandle and release the pin. + */ +int detachInterrupt(rf24_gpio_pin_t pin); + +#ifdef __cplusplus +} +#endif + +#endif // RF24_UTILITY_MRAA_INTERRUPT_H_ diff --git a/utility/MRAA/spi.cpp b/utility/MRAA/spi.cpp index 1e5c0634..a689576d 100644 --- a/utility/MRAA/spi.cpp +++ b/utility/MRAA/spi.cpp @@ -1,6 +1,6 @@ +#include // mraa_strresult(), mraa_result_t #include "spi.h" -#include "mraa.h" SPI::SPI() { @@ -12,11 +12,32 @@ void SPI::begin(int busNo, uint32_t spi_speed) // init mraa spi bus, it will handle chip select internally. For CS pin wiring user must check SPI details in hardware manual mspi = new mraa::Spi(busNo); - mspi->mode(mraa::SPI_MODE0); - mspi->bitPerWord(8); + mraa::Result result; + + result = mspi->mode(mraa::SPI_MODE0); + if (result != mraa::Result::SUCCESS) { + std::string msg = "[SPI::begin] Could not set bus mode;"; + msg += mraa_strresult((mraa_result_t)result); + throw SPIException(msg); + return; + } + + result = mspi->bitPerWord(8); + if (result != mraa::Result::SUCCESS) { + std::string msg = "[SPI::begin] Could not set bus bits per word;"; + msg += mraa_strresult((mraa_result_t)result); + throw SPIException(msg); + return; + } // Prophet: this will try to set 8MHz, however MRAA will reset to max platform speed and syslog a message of it - mspi->frequency(spi_speed); + result = mspi->frequency(spi_speed); + if (result != mraa::Result::SUCCESS) { + std::string msg = "[SPI::begin] Could not set bus frequency;"; + msg += mraa_strresult((mraa_result_t)result); + throw SPIException(msg); + return; + } } void SPI::end() diff --git a/utility/MRAA/spi.h b/utility/MRAA/spi.h index f5fb246c..3475b363 100644 --- a/utility/MRAA/spi.h +++ b/utility/MRAA/spi.h @@ -10,10 +10,23 @@ * Class declaration for SPI helper files */ -#include "mraa.h" -#include "mraa.hpp" +#include // std::exception, std::string +#include // mraa:: -#include "../../RF24_config.h" // This is cyclical and should be fixed +/** @brief The default SPI speed (in Hz) */ +#ifndef RF24_SPI_SPEED + #define RF24_SPI_SPEED 10000000 +#endif + +/** Specific exception for SPI errors */ +class SPIException : public std::runtime_error +{ +public: + explicit SPIException(const std::string& msg) + : std::runtime_error(msg) + { + } +}; class SPI { @@ -34,12 +47,16 @@ class SPI void end(); + // not actually used in Linux void setBitOrder(uint8_t bit_order); + // not actually used in Linux void setDataMode(uint8_t data_mode); + // not actually used in Linux void setClockDivider(uint32_t spi_speed); + // not actually used in Linux void chipSelect(int csn_pin); };