Skip to content

Commit

Permalink
IRQ support using Linux kernel Character Device + Posix threads (#961)
Browse files Browse the repository at this point in the history
* use pthread with Linux kernel's char-dev API
* [example] change IRQ_PIN number
* Add timeout to interrupt example
* read() kernel event buffer and track event sequence number
* code cleanup (in all Linux driver files)
* switch RPi driver to char-dev IRQ
* wrap wiringPiIsr() into attachInterrupt()
* adjust build systems to not rely on pigpio for IRQ support
* use IRQException instead of GPIOException
* make defined RF24_LINUX_GPIO_CHIP more agnostic of selected driver (applied to RPi and SPIDEV drivers)
* fix compiling wiringPi with examples using old makefile
* add C linkage instead of declaring extern functions
* reviewed wiringPi/spi wrapper
* move gpiochipX init out of cache c'tors
* add wiringPi pin number for CE_PIN to linux examples

---------

Co-authored-by: TMRh20 <tmrh20@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
3 people committed Mar 19, 2024
1 parent 05efe34 commit c6c0191
Show file tree
Hide file tree
Showing 58 changed files with 799 additions and 583 deletions.
33 changes: 18 additions & 15 deletions .github/workflows/build_linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ jobs:
- "--driver=SPIDEV"
- "--driver=MRAA"
- "--driver=pigpio"
# disable wiringPi due to needing cross-compiled deps (see comment below)
# - "--soc=BCM2835 --driver=wiringPi"

env:
Expand All @@ -83,18 +84,20 @@ jobs:
arm-linux-gnueabihf-gcc -v
arm-linux-gnueabihf-g++ -v
- name: provide WiringPi
if: ${{ matrix.config-options == '--soc=BCM2835 --driver=wiringPi' }}
env:
CC: /usr/bin/arm-linux-gnueabihf-gcc
CFLAGS: "-I /usr/local/include -L /usr/local/lib -marm -mtune=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard -lcrypt -lrt"
run: |
git clone https://github.com/WiringPi/WiringPi
cd WiringPi
./build
# NOTE: To cross-compile wiringPi, there needs to be cross-compiled `crypt` and `rt` libs
# Skip cross-compiling wiringPi to avoid this headache.
# - name: provide WiringPi
# if: ${{ matrix.config-options == '--soc=BCM2835 --driver=wiringPi' }}
# env:
# CC: /usr/bin/arm-linux-gnueabihf-gcc
# CFLAGS: "-I /usr/local/include -L /usr/local/lib -marm -mtune=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard -lcrypt -lrt"
# run: |
# git clone https://github.com/WiringPi/WiringPi
# cd WiringPi
# ./build

- name: provide pigpio
if: ${{ matrix.config-options == '--driver=pigpio' || endsWith(matrix.config-options, '--driver=RPi') }}
if: ${{ matrix.config-options == '--driver=pigpio' }}
run: |
git clone https://github.com/joan2937/pigpio.git
cd pigpio
Expand All @@ -118,7 +121,11 @@ jobs:
cd mraa
mkdir build
cd build
cmake .. -D BUILDSWIGNODE=OFF -D BUILDARCH=arm
cmake .. \
-D BUILDSWIGNODE=OFF \
-D BUILDARCH=arm \
-D CMAKE_INSTALL_PREFIX=/usr/arm-linux-gnueabihf \
-D CMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/cmake/toolchains/armhf.cmake
sudo make install
sudo bash -c 'echo "/usr/local/lib/arm-linux-gnueabihf" >> /etc/ld.so.conf'
sudo ldconfig
Expand All @@ -133,10 +140,6 @@ jobs:
run: sudo make install

- name: make linux examples
# compiling examples for wiringPi is broken see issue #669
# executables linked to wiringPi additionally need to be linked to crypt and shm_open
# interruptConfigure.cpp example is incompatible with MRAA & wiringPi drivers
if: ${{ matrix.config-options != '--soc=BCM2835 --driver=wiringPi' && matrix.config-options != '--driver=MRAA' }}
run: |
cd examples_linux
make
Expand Down
11 changes: 3 additions & 8 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,12 @@ if(DEFINED RF24_SPI_SPEED)
)
endif()
# allow user customization of default GPIO chip used with the SPIDEV driver
if(DEFINED RF24_SPIDEV_GPIO_CHIP)
message(STATUS "RF24_SPIDEV_GPIO_CHIP set to ${RF24_SPIDEV_GPIO_CHIP}")
if(DEFINED RF24_LINUX_GPIO_CHIP)
message(STATUS "RF24_LINUX_GPIO_CHIP set to ${RF24_LINUX_GPIO_CHIP}")
target_compile_definitions(${LibTargetName} PUBLIC
RF24_SPIDEV_GPIO_CHIP="${RF24_SPIDEV_GPIO_CHIP}"
RF24_LINUX_GPIO_CHIP="${RF24_LINUX_GPIO_CHIP}"
)
endif()
# conditionally disable interruot support (a pigpio specific feature)
if("${LibPIGPIO}" STREQUAL "LibPIGPIO-NOTFOUND" OR DEFINED RF24_NO_INTERRUPT)
message(STATUS "Disabling IRQ pin support")
target_compile_definitions(${LibTargetName} PUBLIC RF24_NO_INTERRUPT)
endif()


#####################################
Expand Down
10 changes: 2 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,9 @@ OBJECTS=RF24.o
ifeq ($(DRIVER), MRAA)
OBJECTS+=spi.o gpio.o compatibility.o
else ifeq ($(DRIVER), RPi)
OBJECTS+=spi.o bcm2835.o compatibility.o
ifneq (,$(findstring -lpigpio,$(SHARED_LINKER_LIBS)))
OBJECTS+=interrupt.o
endif
OBJECTS+=spi.o bcm2835.o compatibility.o interrupt.o
else ifeq ($(DRIVER), SPIDEV)
OBJECTS+=spi.o gpio.o compatibility.o
ifneq (,$(findstring -lpigpio,$(SHARED_LINKER_LIBS)))
OBJECTS+=interrupt.o
endif
OBJECTS+=spi.o gpio.o compatibility.o interrupt.o
else ifeq ($(DRIVER), wiringPi)
OBJECTS+=spi.o
else ifeq ($(DRIVER), pigpio)
Expand Down
29 changes: 3 additions & 26 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
CROSS_CC=arm-linux-gnueabihf-gcc
CROSS_CXX=arm-linux-gnueabihf-g++

pigpio_detected=0

function help {
cat <<EOF
configure script for RF24 library.
Expand Down Expand Up @@ -58,11 +56,6 @@ function execute_check {
fi
}

if [[ -f "/usr/lib/libpigpio.so" || -f "/usr/local/lib/libpigpio.so" || -f "/usr/arm-linux-gnueabihf/lib/libpigpio.so" ]]; then
echo "[INFO] pigpio lib found."
pigpio_detected=1
fi

function die {
echo "[ERROR] $1"
exit $2
Expand Down Expand Up @@ -192,7 +185,7 @@ function detect_driver {
result=MRAA
elif [[ $(execute_check "${REMOTE_LDCONFIG} -p | grep liblittlewire-spi") ]]; then
result=LittleWire
elif [ $pigpio_detected -eq 1 ]; then
elif [[ -f "/usr/lib/libpigpio.so" || -f "/usr/local/lib/libpigpio.so" || -f "/usr/arm-linux-gnueabihf/lib/libpigpio.so" ]]; then
result=pigpio
else
result=""
Expand Down Expand Up @@ -407,24 +400,12 @@ fi

case ${DRIVER} in
wiringPi)
SHARED_LINKER_LIBS+=" -lpigpio -lwiringPi"
SHARED_LINKER_LIBS+=" -lwiringPi"
CFLAGS+=" -lwiringPi"
;;
SPIDEV)
if [ $pigpio_detected -eq 1 ]; then
echo "[INFO] linking to pigpio for interrupt compatibility"
SHARED_LINKER_LIBS+=" -lpigpio"
else
CFLAGS+=" -DRF24_NO_INTERRUPT"
fi
;;
RPi)
if [ $pigpio_detected -eq 1 ]; then
echo "[INFO] linking to pigpio for interrupt compatibility"
SHARED_LINKER_LIBS+=" -lpigpio"
else
CFLAGS+=" -DRF24_NO_INTERRUPT"
fi
;;
MRAA)
SHARED_LINKER_LIBS+=" -lmraa"
Expand All @@ -433,11 +414,7 @@ LittleWire)
SHARED_LINKER_LIBS+=" -llittlewire-spi"
;;
pigpio)
if [ $pigpio_detected -eq 0 ]; then
die "[ERROR] pigpio was not detected. Make sure pigpio is installed." 2
else
SHARED_LINKER_LIBS+=" -lpigpio"
fi
SHARED_LINKER_LIBS+=" -lpigpio"
;;
*)
die "Unsupported DRIVER: ${DRIVER}." 2
Expand Down
17 changes: 3 additions & 14 deletions examples_linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ find_library(RF24 rf24 REQUIRED)
message(STATUS "using RF24 library: ${RF24}")

# conditionally append "interruptConfigure" to the EXAMPLES_LIST
if("${RF24_DRIVER}" STREQUAL "MRAA" OR "${RF24_DRIVER}" STREQUAL "wiringPi" OR "${LibPIGPIO}" STREQUAL "LibPIGPIO-NOTFOUND")
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)
Expand Down Expand Up @@ -52,26 +52,15 @@ elseif("${RF24_DRIVER}" STREQUAL "wiringPi")
else()
message(FATAL "Lib ${RF24_DRIVER} not found.")
endif()
elseif(NOT "${LibPIGPIO}" STREQUAL "LibPIGPIO-NOTFOUND" AND NOT DEFINED RF24_NO_INTERUPT)
if(NOT "${RF24_DRIVER}" STREQUAL "pigpio")
message(STATUS "linking to ${LibPIGPIO} for interrupt support")
else()
message(STATUS "linking to ${LibPIGPIO}")
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)
else()
message(STATUS "Disabling IRQ pin support")
endif()

foreach(example ${EXAMPLES_LIST})
#make a target
add_executable(${example} ${example}.cpp)

# avoid including interrupt.h when pigpio is not available
if("${LibPIGPIO}" STREQUAL "LibPIGPIO-NOTFOUND" OR DEFINED RF24_NO_INTERRUPT)
target_compile_definitions(${example} PUBLIC RF24_NO_INTERRUPT)
endif()

target_link_libraries(${example} PUBLIC ${linked_libs})
endforeach()
Expand Down
2 changes: 1 addition & 1 deletion examples_linux/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ include ../Makefile.inc

# define all programs
PROGRAMS = gettingstarted acknowledgementPayloads manualAcknowledgements streamingData multiceiverDemo scanner
ifneq (,$(findstring -lpigpio,$(SHARED_LINKER_LIBS)))
ifneq ($(DRIVER), MRAA)
PROGRAMS+=interruptConfigure
endif

Expand Down
2 changes: 2 additions & 0 deletions examples_linux/Makefile.examples
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ SOURCES = $(PROGRAMS:=.cpp)
LIBS=-l$(LIB)
ifeq ($(DRIVER), LittleWire)
LIBS+= -llittlewire-spi
else ifeq ($(DRIVER), wiringPi)
LIBS+= -lwiringPi -lcrypt -lrt
endif

all: $(PROGRAMS)
Expand Down
2 changes: 2 additions & 0 deletions examples_linux/acknowledgementPayloads.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ using namespace std;
#define CSN_PIN 0
#ifdef MRAA
#define CE_PIN 15 // GPIO22
#elif defined(RF24_WIRINGPI)
#define CE_PIN 3 // GPIO22
#else
#define CE_PIN 22
#endif
Expand Down
2 changes: 2 additions & 0 deletions examples_linux/gettingstarted.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ using namespace std;
#define CSN_PIN 0
#ifdef MRAA
#define CE_PIN 15 // GPIO22
#elif defined(RF24_WIRINGPI)
#define CE_PIN 3 // GPIO22
#else
#define CE_PIN 22
#endif
Expand Down
15 changes: 11 additions & 4 deletions examples_linux/interruptConfigure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
using namespace std;

// We will be using the nRF24L01's IRQ pin for this example
#define IRQ_PIN 12 // this needs to be a digital input capable pin
#define IRQ_PIN 24 // this needs to be a digital input capable pin

// 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 All @@ -35,6 +35,8 @@ volatile bool got_interrupt = false; // used to signify that the event started
#define CSN_PIN 0
#ifdef MRAA
#define CE_PIN 15 // GPIO22
#elif defined(RF24_WIRINGPI)
#define CE_PIN 3 // GPIO22
#else
#define CE_PIN 22
#endif
Expand Down Expand Up @@ -167,14 +169,14 @@ void master()
// Test the "data ready" event with the IRQ pin
cout << "\nConfiguring IRQ pin to ignore the 'data sent' event\n";
radio.maskIRQ(true, false, false); // args = "data_sent", "data_fail", "data_ready"
cout << " Pinging RX node for 'data ready' event...";
cout << " Pinging RX node for 'data ready' event..." << endl;
ping_n_wait(); // transmit a payload and detect the IRQ pin
pl_iterator++; // increment iterator for next test

// Test the "data sent" event with the IRQ pin
cout << "\nConfiguring IRQ pin to ignore the 'data ready' event\n";
radio.maskIRQ(false, false, true); // args = "data_sent", "data_fail", "data_ready"
cout << " Pinging RX node for 'data sent' event...";
cout << " Pinging RX node for 'data sent' event..." << endl;
radio.flush_tx(); // flush payloads from any failed prior test
ping_n_wait(); // transmit a payload and detect the IRQ pin
pl_iterator++; // increment iterator for next test
Expand All @@ -196,7 +198,7 @@ void master()
// test the "data fail" event with the IRQ pin
cout << "\nConfiguring IRQ pin to reflect all events\n";
radio.maskIRQ(0, 0, 0); // args = "data_sent", "data_fail", "data_ready"
cout << " Pinging inactive RX node for 'data fail' event...";
cout << " Pinging inactive RX node for 'data fail' event..." << endl;
ping_n_wait(); // transmit a payload and detect the IRQ pin

// CE pin is still HIGH which consumes more power. Example is now idling so...
Expand Down Expand Up @@ -250,7 +252,12 @@ void ping_n_wait()
// use the non-blocking call to write a payload and begin transmission
// the "false" argument means we are expecting an ACK packet response
radio.startFastWrite(tx_payloads[pl_iterator], tx_pl_size, false);
uint32_t timer = millis();
while (!got_interrupt) {
if (millis() - timer > 500) {
cout << "\tIRQ NOT received" << endl;
break;
}
/*
* IRQ pin is LOW when activated. Otherwise it is always HIGH
* Wait in this empty loop until IRQ pin is activated.
Expand Down
2 changes: 2 additions & 0 deletions examples_linux/manualAcknowledgements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ using namespace std;
#define CSN_PIN 0
#ifdef MRAA
#define CE_PIN 15 // GPIO22
#elif defined(RF24_WIRINGPI)
#define CE_PIN 3 // GPIO22
#else
#define CE_PIN 22
#endif
Expand Down
2 changes: 2 additions & 0 deletions examples_linux/multiceiverDemo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ using namespace std;
#define CSN_PIN 0
#ifdef MRAA
#define CE_PIN 15 // GPIO22
#elif defined(RF24_WIRINGPI)
#define CE_PIN 3 // GPIO22
#else
#define CE_PIN 22
#endif
Expand Down
3 changes: 0 additions & 3 deletions examples_linux/ncurses/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,3 @@ target_link_libraries(${example} PUBLIC
${linked_libs}
${CURSES_LIBRARIES}
)
if("${LibPIGPIO}" STREQUAL "LibPIGPIO-NOTFOUND" OR DEFINED RF24_NO_INTERRUPT)
target_compile_definitions(${example} PUBLIC RF24_NO_INTERRUPT)
endif()
2 changes: 2 additions & 0 deletions examples_linux/ncurses/scanner_curses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ using namespace std;
#define CSN_PIN 0
#ifdef MRAA
#define CE_PIN 15 // GPIO22
#elif defined(RF24_WIRINGPI)
#define CE_PIN 3 // GPIO22
#else
#define CE_PIN 22
#endif
Expand Down
2 changes: 1 addition & 1 deletion examples_linux/readme.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Note: These examples were originally designed for RPi, but should work on any supported Linux platform, with the proper pin configuration.

See http://tmrh20.github.io/RF24 for more information
See http://nRF24.github.io/RF24 for more information
2 changes: 2 additions & 0 deletions examples_linux/scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ using namespace std;
#define CSN_PIN 0
#ifdef MRAA
#define CE_PIN 15 // GPIO22
#elif defined(RF24_WIRINGPI)
#define CE_PIN 3 // GPIO22
#else
#define CE_PIN 22
#endif
Expand Down
2 changes: 2 additions & 0 deletions examples_linux/streamingData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ using namespace std;
#define CSN_PIN 0
#ifdef MRAA
#define CE_PIN 15 // GPIO22
#elif defined(RF24_WIRINGPI)
#define CE_PIN 3 // GPIO22
#else
#define CE_PIN 22
#endif
Expand Down
11 changes: 1 addition & 10 deletions pyRF24/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
os.environ[identifier] = value

except FileNotFoundError: # assuming lib was built & installed with CMake

# get LIB_VERSION from library.properties file for Arduino IDE
with open(os.path.join(git_dir, "library.properties"), "r", encoding="utf-8") as f:
for line in f.read().splitlines():
Expand All @@ -44,7 +43,6 @@

# check C++ RF24 lib is installed
finally:

# check for possible linker flags set via CFLAGS environment variable
for flag in cflags.split("-"):
if flag.startswith("L"):
Expand All @@ -71,13 +69,6 @@
)
)

# avoid IRQ support if pigpio is not available; link to pigpio if it is found
found_pigpio = False
for symlink_loc in symlink_directory:
if os.path.exists(symlink_loc + "/libpigpio.so"):
found_pigpio = True
# IRQ pin features will be implemented in python via pigpio's python API or RPi.GPIO
cflags += " -DRF24_NO_INTERRUPT"

# append any additionally found compiler flags
os.environ["CFLAGS"] = cflags
Expand Down Expand Up @@ -114,7 +105,7 @@
Extension(
"RF24",
sources=["pyRF24.cpp"],
libraries=["rf24", BOOST_LIB] + (["pigpio"] if found_pigpio else [])
libraries=["rf24", BOOST_LIB],
)
],
)
Loading

0 comments on commit c6c0191

Please sign in to comment.