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

Use BCM2835 lib's Edge Detection Status for IRQ support #969

Merged
merged 4 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
12 changes: 8 additions & 4 deletions examples_linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,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
151 changes: 30 additions & 121 deletions utility/RPi/interrupt.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
/**
* Interrupt implementations
*/
#include "linux/gpio.h"
#include <unistd.h> // close()
#include <fcntl.h> // open()
#include <sys/ioctl.h> // ioctl()
#include <errno.h> // errno, strerror()
#include <string.h> // std::string, strcpy()
#include <pthread.h>
#include <map>
#include "bcm2835.h"
#include "interrupt.h"

#ifdef __cplusplus
Expand All @@ -18,152 +13,64 @@ extern "C" {
static pthread_mutex_t irq_mutex = PTHREAD_MUTEX_INITIALIZER;
std::map<rf24_gpio_pin_t, IrqPinCache> irqCache;

void IrqChipCache::openDevice()
// A simple struct instantiated privately to:
// 1. properly clean up open threads
// 2. clear BCM2835 lib's Edge Detection Status settings
struct IrqCacheDestructor
{
if (fd < 0) {
fd = open(chip, O_RDONLY);
if (fd < 0) {
std::string msg = "Can't open device ";
msg += chip;
msg += "; ";
msg += strerror(errno);
throw IRQException(msg);
return;
~IrqCacheDestructor()
{
for (std::map<rf24_gpio_pin_t, IrqPinCache>::iterator i = irqCache.begin(); i != irqCache.end(); ++i) {
pthread_cancel(i->second.id);
pthread_join(i->second.id, NULL);
bcm2835_gpio_clr_ren(i->second.pin);
bcm2835_gpio_clr_fen(i->second.pin);
bcm2835_gpio_set_eds(i->second.pin);
}
irqCache.clear();
}
chipInitialized = true;
}

void IrqChipCache::closeDevice()
{
if (fd >= 0) {
close(fd);
fd = -1;
}
}

IrqChipCache::IrqChipCache()
{
}

IrqChipCache::~IrqChipCache()
{
for (std::map<rf24_gpio_pin_t, IrqPinCache>::iterator i = irqCache.begin(); i != irqCache.end(); ++i) {
pthread_cancel(i->second.id); // send cancel request
pthread_join(i->second.id, NULL); // wait till thread terminates
close(i->second.fd);
}
irqCache.clear();
}

IrqChipCache irqChipCache;
} irqCacheMgr;

void* poll_irq(void* arg)
{
IrqPinCache* pinCache = (IrqPinCache*)(arg);
unsigned int lastEventSeqNo = 0;
gpio_v2_line_event irqEventInfo;
memset(&irqEventInfo, 0, sizeof(irqEventInfo));

for (;;) {
int ret = read(pinCache->fd, &irqEventInfo, sizeof(gpio_v2_line_event));
if (ret < 0) {
std::string msg = "[poll_irq] Could not read event info; ";
msg += strerror(errno);
throw IRQException(msg);
return NULL;
}
if (ret > 0 && irqEventInfo.line_seqno != lastEventSeqNo) {
lastEventSeqNo = irqEventInfo.line_seqno;
int ret = bcm2835_gpio_eds(pinCache->pin);
if (ret > 0) {
bcm2835_gpio_set_eds(pinCache->pin);
pinCache->function();
}
pthread_testcancel();
}
return NULL;
}

int attachInterrupt(rf24_gpio_pin_t pin, int mode, void (*function)(void))
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);

try {
irqChipCache.openDevice();
}
catch (IRQException& exc) {
if (irqChipCache.chipInitialized) {
throw exc;
return 0;
}
irqChipCache.chip = "/dev/gpiochip0";
irqChipCache.openDevice();
}

// get chip info
gpiochip_info info;
memset(&info, 0, sizeof(info));
int ret = ioctl(irqChipCache.fd, GPIO_GET_CHIPINFO_IOCTL, &info);
if (ret < 0) {
std::string msg = "[attachInterrupt] Could not gather info about ";
msg += irqChipCache.chip;
throw IRQException(msg);
return 0;
}

if (pin > info.lines) {
std::string msg = "[attachInterrupt] pin " + std::to_string(pin) + " is not available on " + irqChipCache.chip;
throw IRQException(msg);
return 0;
}

// create a request object to configure the specified pin
gpio_v2_line_request request;
memset(&request, 0, sizeof(request));
strcpy(request.consumer, "RF24 IRQ");
request.num_lines = 1U;
request.offsets[0] = pin;
request.event_buffer_size = sizeof(gpio_v2_line_event);

// set debounce for the pin
// request.config.attrs[0].mask = 1LL;
// request.config.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE;
// request.config.attrs[0].attr.debounce_period_us = 10U;
// request.config.num_attrs = 1U;

// set pin as input and configure edge detection
request.config.flags = GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME;
// configure the specified pin
switch (mode) {
case INT_EDGE_BOTH:
bcm2835_gpio_ren(pin);
bcm2835_gpio_fen(pin);
break;
case INT_EDGE_RISING:
bcm2835_gpio_ren(pin);
break;
case INT_EDGE_FALLING:
request.config.flags |= mode;
bcm2835_gpio_fen(pin);
break;
default:
// bad user input!
return 0; // stop here
}

// write pin request's config
ret = ioctl(irqChipCache.fd, GPIO_V2_GET_LINE_IOCTL, &request);
if (ret < 0 || request.fd <= 0) {
std::string msg = "[attachInterrupt] Could not get line handle from ioctl; ";
msg += strerror(errno);
throw IRQException(msg);
return 0;
}
irqChipCache.closeDevice();

ret = ioctl(request.fd, GPIO_V2_LINE_SET_CONFIG_IOCTL, &request.config);
if (ret < 0) {
std::string msg = "[attachInterrupt] Could not set line config; ";
msg += strerror(errno);
throw IRQException(msg);
return 0;
}

// cache details
IrqPinCache irqPinCache;
irqPinCache.fd = request.fd;
irqPinCache.pin = pin;
irqPinCache.function = function;
std::pair<std::map<rf24_gpio_pin_t, IrqPinCache>::iterator, bool> indexPair = irqCache.insert(std::pair<rf24_gpio_pin_t, IrqPinCache>(pin, irqPinCache));

Expand All @@ -189,7 +96,9 @@ int detachInterrupt(rf24_gpio_pin_t pin)
}
pthread_cancel(cachedPin->second.id); // send cancel request
pthread_join(cachedPin->second.id, NULL); // wait till thread terminates
close(cachedPin->second.fd);
bcm2835_gpio_clr_ren(cachedPin->second.pin);
bcm2835_gpio_clr_fen(cachedPin->second.pin);
bcm2835_gpio_set_eds(cachedPin->second.pin);
irqCache.erase(cachedPin);
return 1;
}
Expand Down
46 changes: 12 additions & 34 deletions utility/RPi/interrupt.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@
#ifndef RF24_UTILITY_RPI_INTERRUPT_H_
#define RF24_UTILITY_RPI_INTERRUPT_H_

#include <pthread.h> // pthread_t
#include <stdexcept>
#include "linux/gpio.h" // GPIO_V2_LINE_FLAG_EDGE_***
#include <pthread.h> // pthread_t
#include <stdexcept> // std::exception, std::string
#include "RF24_arch_config.h" // rf24_gpio_pin_t

#define INT_EDGE_FALLING GPIO_V2_LINE_FLAG_EDGE_FALLING
#define INT_EDGE_RISING GPIO_V2_LINE_FLAG_EDGE_RISING
#define INT_EDGE_BOTH GPIO_V2_LINE_FLAG_EDGE_FALLING | GPIO_V2_LINE_FLAG_EDGE_RISING

#ifdef __cplusplus
extern "C" {
#endif

enum IrqEvent : uint8_t
{
INT_EDGE_FALLING,
INT_EDGE_RISING,
INT_EDGE_BOTH,
};

/** Specific exception for IRQ errors */
class IRQException : public std::runtime_error
{
Expand All @@ -27,35 +29,11 @@ class IRQException : public std::runtime_error
}
};

/// A struct to manage the GPIO chip file descriptor.
/// This struct's destructor should close any cached GPIO pin requests' file descriptors.
struct IrqChipCache
{
const char* chip = RF24_LINUX_GPIO_CHIP;
int fd = -1;
bool chipInitialized = false;

/// Open the File Descriptor for the GPIO chip
void openDevice();

/// Close the File Descriptor for the GPIO chip
void closeDevice();

/// should be called automatically on program start.
/// Here, we do some one-off configuration.
IrqChipCache();

/// Should be called automatically on program exit.
/// What we need here is to make sure that the File Descriptors used to
/// control GPIO pins are properly closed.
~IrqChipCache();
};

/** Details related to a certain pin's ISR. */
struct IrqPinCache
{
/// The pin request's file descriptor
int fd = 0;
/// The pin number
rf24_gpio_pin_t pin = 0;

/// The posix thread ID.
pthread_t id = 0;
Expand All @@ -68,7 +46,7 @@ struct IrqPinCache
* Take the details and create an interrupt handler that will
* callback to the user-supplied function.
*/
int attachInterrupt(rf24_gpio_pin_t pin, int mode, void (*function)(void));
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.
Expand Down