Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wrap MRAA lib's Gpio::isr() for IRQ support #970

Merged
merged 4 commits into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
20 changes: 9 additions & 11 deletions examples_linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ set(EXAMPLES_LIST
streamingData
multiceiverDemo
scanner
interruptConfigure
)

project(RF24Examples CXX)
Expand All @@ -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}
Expand All @@ -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})
Expand Down
5 changes: 1 addition & 4 deletions examples_linux/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 7 additions & 1 deletion examples_linux/interruptConfigure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion utility/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
24 changes: 16 additions & 8 deletions utility/MRAA/gpio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

#include <map>
#include <mraa.h> // mraa_strresult()
#include "gpio.h"

// cache for mraa::Gpio instances
Expand All @@ -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<rf24_gpio_pin_t, mraa::Gpio*>::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);
}
}

Expand All @@ -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<rf24_gpio_pin_t, mraa::Gpio*>::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");
}
}
1 change: 1 addition & 0 deletions utility/MRAA/includes.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
#endif

#include "MRAA/RF24_arch_config.h"
#include "MRAA/interrupt.h"

#endif // RF24_UTILITY_INCLUDES_H_
58 changes: 58 additions & 0 deletions utility/MRAA/interrupt.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#include <map>
#include <mraa.h> // mraa_strresult()
#include <mraa.hpp> // mraa::Gpio
#include "interrupt.h"
#include "gpio.h" // rf24_gpio_pin_t

#ifdef __cplusplus
extern "C" {
#endif

std::map<rf24_gpio_pin_t, mraa::Gpio*> 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<std::map<rf24_gpio_pin_t, mraa::Gpio*>::iterator, bool> indexPair = irqCache.insert(std::pair<rf24_gpio_pin_t, mraa::Gpio*>(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<rf24_gpio_pin_t, mraa::Gpio*>::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
44 changes: 44 additions & 0 deletions utility/MRAA/interrupt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#ifndef RF24_UTILITY_MRAA_INTERRUPT_H_
#define RF24_UTILITY_MRAA_INTERRUPT_H_

#include <stdexcept> // 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_
29 changes: 25 additions & 4 deletions utility/MRAA/spi.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

#include <mraa.h> // mraa_strresult(), mraa_result_t
#include "spi.h"
#include "mraa.h"

SPI::SPI()
{
Expand All @@ -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()
Expand Down
23 changes: 20 additions & 3 deletions utility/MRAA/spi.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,23 @@
* Class declaration for SPI helper files
*/

#include "mraa.h"
#include "mraa.hpp"
#include <stdexcept> // std::exception, std::string
#include <mraa.hpp> // 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
{
Expand All @@ -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);
};

Expand Down