Merge pull request #413 from WiringPi/wiringPiISR

Release 2026
This commit is contained in:
mstroh
2026-01-17 11:11:53 +01:00
committed by GitHub
26 changed files with 940 additions and 98 deletions

View File

@@ -1 +1 @@
3.16
3.18

View File

@@ -473,7 +473,7 @@ static void wfi(struct WPIWfiStatus wfiStatus, void* userdata) {
edgeType = "falling";
else
edgeType = "none";
printf("gpio BCM = %d, IRQ edge = %s, timestamp = %lld microseconds, timenow = %lld, diff = %lld\n", wfiStatus.gpioPin, edgeType, wfiStatus.timeStamp_us, timenow, diff);
printf("gpio BCM = %d, IRQ edge = %s, timestamp = %lld microseconds, timenow = %lld, diff = %lld\n", wfiStatus.pinBCM, edgeType, wfiStatus.timeStamp_us, timenow, diff);
if (toggle == 0) {
digitalWrite (OUTpin, HIGH);
toggle = 1;
@@ -500,19 +500,19 @@ int main (void)
printf("Testing waitForInterrupt on both edges IRQ @ GPIO%d, timeout is %d\n", IRQpin, TIMEOUT);
struct WPIWfiStatus wfiStatus = waitForInterrupt2(IRQpin, INT_EDGE_BOTH, TIMEOUT, BOUNCETIME_WFI);
if (wfiStatus.status < 0) {
if (wfiStatus.statusOK < 0) {
printf("waitForInterrupt returned error\n");
pinMode(OUTpin, INPUT);
return 0;
}
else if (wfiStatus.status == 0) {
else if (wfiStatus.statusOK == 0) {
printf("waitForInterrupt timed out\n\n");
}
else {
if (wfiStatus.edge == INT_EDGE_FALLING)
printf("waitForInterrupt: GPIO pin %d falling edge fired at %lld microseconds\n\n", wfiStatus.gpioPin, wfiStatus.timeStamp_us);
printf("waitForInterrupt: GPIO pin %d falling edge fired at %lld microseconds\n\n", wfiStatus.pinBCM, wfiStatus.timeStamp_us);
else
printf("waitForInterrupt: GPIO pin %d rising edge fired at %lld microseconds\n\n", wfiStatus.gpioPin, wfiStatus.timeStamp_us);
printf("waitForInterrupt: GPIO pin %d rising edge fired at %lld microseconds\n\n", wfiStatus.pinBCM, wfiStatus.timeStamp_us);
}
printf("Testing IRQ @ GPIO%d on both edges and bouncetime %d microseconds. Toggle LED @ GPIO%d on IRQ.\n\n", IRQpin, BOUNCETIME, OUTpin);
@@ -809,8 +809,8 @@ int wiringPiSPISetupMode(int channel, int speed, int mode)
int wiringPiSPIxSetupMode(const int number, const int channel, const int speed, const int mode)
```
``number``: SPI Nummer (typisch 0, bei Compute Modul 0-7).
``channel``: SPI Kanal (typisch 0 oder 1, bei Compute Modul 0-3).
``number``: SPI Nummer (typisch 0, bei Compute Modul 0-6).
``channel``: SPI Kanal (typisch 0 oder 1, bei Compute Modul oder entsprechenden overlay 0-4).
``speed``: SPI Taktrate.
``mode``: SPI Modus (https://www.kernel.org/doc/Documentation/spi/spidev).
``Rückgabewert``: Datei Handle zum SPI-Bus

View File

@@ -470,7 +470,7 @@ static void wfi(struct WPIWfiStatus wfiStatus, void* userdata) {
edgeType = "falling";
else
edgeType = "none";
printf("gpio BCM = %d, IRQ edge = %s, timestamp = %lld microseconds, timenow = %lld, diff = %lld\n", wfiStatus.gpioPin, edgeType, wfiStatus.timeStamp_us, timenow, diff);
printf("gpio BCM = %d, IRQ edge = %s, timestamp = %lld microseconds, timenow = %lld, diff = %lld\n", wfiStatus.pinBCM, edgeType, wfiStatus.timeStamp_us, timenow, diff);
if (toggle == 0) {
digitalWrite (OUTpin, HIGH);
toggle = 1;
@@ -497,19 +497,19 @@ int main (void)
printf("Testing waitForInterrupt on both edges IRQ @ GPIO%d, timeout is %d\n", IRQpin, TIMEOUT);
struct WPIWfiStatus wfiStatus = waitForInterrupt2(IRQpin, INT_EDGE_BOTH, TIMEOUT, BOUNCETIME_WFI);
if (wfiStatus.status < 0) {
if (wfiStatus.statusOK < 0) {
printf("waitForInterrupt returned error\n");
pinMode(OUTpin, INPUT);
return 0;
}
else if (wfiStatus.status == 0) {
else if (wfiStatus.statusOK == 0) {
printf("waitForInterrupt timed out\n\n");
}
else {
if (wfiStatus.edge == INT_EDGE_FALLING)
printf("waitForInterrupt: GPIO pin %d falling edge fired at %lld microseconds\n\n", wfiStatus.gpioPin, wfiStatus.timeStamp_us);
printf("waitForInterrupt: GPIO pin %d falling edge fired at %lld microseconds\n\n", wfiStatus.pinBCM, wfiStatus.timeStamp_us);
else
printf("waitForInterrupt: GPIO pin %d rising edge fired at %lld microseconds\n\n", wfiStatus.gpioPin, wfiStatus.timeStamp_us);
printf("waitForInterrupt: GPIO pin %d rising edge fired at %lld microseconds\n\n", wfiStatus.pinBCM, wfiStatus.timeStamp_us);
}
printf("Testing IRQ @ GPIO%d on both edges and bouncetime %d microseconds. Toggle LED @ GPIO%d on IRQ.\n\n", IRQpin, BOUNCETIME, OUTpin);
@@ -798,8 +798,8 @@ int wiringPiSPISetupMode(int channel, int speed, int mode);
int wiringPiSPIxSetupMode(const int number, const int channel, const int speed, const int mode);
```
``number``: SPI number (typically 0, on Compute Module 0-7).
``channel``: SPI channel (typically 0 or 1, on Compute Module 0-3).
``number``: SPI number (typically 0, on Compute Module 0-6).
``channel``: SPI channel (typically 0 or 1, on Compute Module or the corresponding overlay 0-4).
``speed``: SPI clock speed in Hz (500,000 to 32,000,000).
``mode``: SPI mode ([www.kernel.org/doc/Documentation/spi/spidev](https://www.kernel.org/doc/Documentation/spi/spidev)).
``Return Value``: File handle to the SPI bus, or -1 on error.

View File

@@ -282,7 +282,7 @@ Please report bugs to https://github.com/WiringPi/WiringPi/issues
.SH COPYRIGHT
Copyright (c) 2012-2025 Gordon Henderson and contributors
Copyright (c) 20122019 Gordon Henderson; 20192026 Contributors
.br
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

View File

@@ -2,7 +2,7 @@
* gpio.c:
* Swiss-Army-Knife, Set-UID command-line interface to the Raspberry
* Pi's GPIO.
* Copyright (c) 2012-2025 Gordon Henderson and contributors
* Copyright (c) 20122019 Gordon Henderson; 20192026 Contributors
***********************************************************************
* This file is part of wiringPi:
* https://github.com/WiringPi/WiringPi/
@@ -916,7 +916,7 @@ static void doVersion (char *argv [])
wiringPiVersion (&vMaj, &vMin) ;
printf ("gpio version: %d.%d\n", vMaj, vMin) ;
printf ("Copyright (c) 2012-2025 Gordon Henderson and contributors\n") ;
printf ("Copyright (c) 20122019 Gordon Henderson; 20192026 Contributors\n") ;
printf ("This is free software with ABSOLUTELY NO WARRANTY.\n") ;
printf ("For details type: %s -warranty\n", argv [0]) ;
printf ("\n") ;
@@ -1033,7 +1033,7 @@ int main (int argc, char *argv [])
if (strcasecmp (argv [1], "-warranty") == 0)
{
printf ("gpio version: %s\n", VERSION) ;
printf ("Copyright (c) 2012-2025 Gordon Henderson and contributors\n") ;
printf ("Copyright (c) 20122019 Gordon Henderson; 20192026 Contributors\n") ;
printf ("\n") ;
printf (" This program is free software; you can redistribute it and/or modify\n") ;
printf (" it under the terms of the GNU Leser General Public License as published\n") ;

View File

@@ -1,7 +1,7 @@
/*
* readall.c:
* The readall functions - getting a bit big, so split them out.
* Copyright (c) 2012-2024 Gordon Henderson and contributors
* Copyright (c) 20122019 Gordon Henderson; 20192026 Contributors
***********************************************************************
* This file is part of wiringPi:
* https://github.com/WiringPi/WiringPi/

90
gpio/test/gpio_test1_piface1.sh Executable file
View File

@@ -0,0 +1,90 @@
#!/bin/bash
echo -e "🔧 Starting relay unit test for MCP23S17@Piface unit test hw\n\n"
# set pin 200207 as output
for i in {200..207}; do
gpio -x mcp23s17:200:0:0 mode $i out
done
# set pin 208215 as input
for i in {208..215}; do
gpio -x mcp23s17:200:0:0 mode $i in
gpio -x mcp23s17:200:0:0 mode $i up
done
# Define relay output addresses and corresponding feedback input addresses
relay_addresses=(200 201 207 206)
feedback_addresses=(214 215 212 213)
for round in {1..3}; do
echo -e "🔄 Round $round / 3\n"
# Loop through each relay
for i in "${!relay_addresses[@]}"; do
out=${relay_addresses[$i]}
in=${feedback_addresses[$i]}
echo "➡️ Relay/IO write test $((i+1)): Address $out → Feedback at $in"
# Turn ON
gpio -x mcp23s17:200:0:0 write "$out" 1
sleep 0.5
feedback_on=$(gpio -x mcp23s17:200:0:0 read "$in")
if [ "$feedback_on" -eq 0 ]; then
echo "✅ ON Test passed: Feedback is LOW as expected."
else
echo "❌ ON Test failed: Feedback is HIGH."
fi
# Turn OFF
gpio -x mcp23s17:200:0:0 write "$out" 0
sleep 0.5
feedback_off=$(gpio -x mcp23s17:200:0:0 read "$in")
if [ "$feedback_off" -eq 1 ]; then
echo "✅ OFF Test passed: Feedback is HIGH as expected."
else
echo "❌ OFF Test failed: Feedback is LOW."
fi
echo -e "\n"
sleep 0.5
done
for addr in {208..211}; do
echo "🔃 Pullup resistor test address $addr: "
gpio -x mcp23s17:200:0:0 mode "$addr" up
sleep 0.5
feedback_on=$(gpio -x mcp23s17:200:0:0 read "$addr")
if [ "$feedback_on" -eq 1 ]; then
echo "✅ PULLUP Test passed: Feedback is HIGH as expected."
else
echo "❌ PULLUP Test failed: Feedback is LOW."
fi
sleep 0.5
gpio -x mcp23s17:200:0:0 mode "$addr" down
sleep 0.5
feedback_on=$(gpio -x mcp23s17:200:0:0 read "$addr")
if [ "$feedback_on" -eq 1 ]; then
echo "✅ PULLDOWN Test passed: Feedback is LOW as expected."
else
echo "❌ PULLDOW Test failed: Feedback is HIGH."
fi
echo -e "\n"
sleep 0.5
done
done
#echo "🧹 Cleanup complete. All relays turned OFF."

View File

@@ -1,3 +1,3 @@
#define VERSION "3.16"
#define VERSION "3.18"
#define VERSION_MAJOR 3
#define VERSION_MINOR 16
#define VERSION_MINOR 18

2
wiringPi/gtest/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
bin
googletest-main

92
wiringPi/gtest/Makefile Normal file
View File

@@ -0,0 +1,92 @@
CXX := g++
CXXFLAGS := -std=c++17 -Wall -Wextra -Werror -g \
-Igoogletest-main/googletest/include -Igoogletest-main/googlemock/include
LDFLAGS := -pthread
WIRING_PI_LIBS := -lwiringPi
# Where to download googletest
GTEST_URL := https://github.com/google/googletest/archive/refs/heads/main.zip
GTEST_ZIP := googletest.zip
GTEST_DIR := googletest-main
# Output directory
BIN_DIR := bin
# Helper program
RUN_ISR_SRC := run_isr.cpp
RUN_ISR_BIN := $(BIN_DIR)/run_isr
# Need BCM17 <-> BCM18 connection
TEST_SRCS := \
test_isr_both.cpp \
test_isr_conflict_between_processes.cpp \
test_isr_falling.cpp \
test_isr_restart.cpp \
test_isr_restart2.cpp \
test_isr_rising.cpp \
test_isr_rising2.cpp
# Derive test binaries (in bin/)
TEST_BINS := $(addprefix $(BIN_DIR)/, $(TEST_SRCS:.cpp=))
# gtest/gmock sources
GTEST_SRCS := $(GTEST_DIR)/googletest/src/gtest-all.cc
GMOCK_SRCS := $(GTEST_DIR)/googlemock/src/gmock-all.cc
GTEST_MAIN := $(GTEST_DIR)/googletest/src/gtest_main.cc
GTEST_INCLUDES := -I$(GTEST_DIR)/googletest/include -I$(GTEST_DIR)/googlemock/include
GTEST_OBJS := $(GTEST_SRCS:.cc=.o)
GMOCK_OBJS := $(GMOCK_SRCS:.cc=.o)
GTEST_MAIN_OBJ := $(GTEST_MAIN:.cc=.o)
# Default: build everything
all: $(GTEST_DIR) $(BIN_DIR) $(TEST_BINS) $(RUN_ISR_BIN)
# Rule: build normal test binaries
$(filter-out $(BIN_DIR)/test_conflict_between_processes,$(TEST_BINS)): $(BIN_DIR)/%: %.cpp $(GTEST_OBJS) $(GMOCK_OBJS) $(GTEST_MAIN_OBJ)
$(CXX) $(CXXFLAGS) $< $(GTEST_OBJS) $(GMOCK_OBJS) $(GTEST_MAIN_OBJ) -o $@ $(LDFLAGS) $(WIRING_PI_LIBS)
# Special case: test_conflict_between_processes depends on run_isr
$(BIN_DIR)/test_conflict_between_processes: test_conflict_between_processes.cpp $(GTEST_OBJS) $(GMOCK_OBJS) $(GTEST_MAIN_OBJ) $(RUN_ISR_BIN)
$(CXX) $(CXXFLAGS) $< $(GTEST_OBJS) $(GMOCK_OBJS) $(GTEST_MAIN_OBJ) -o $@ $(LDFLAGS) $(WIRING_PI_LIBS)
# Build run_isr
$(RUN_ISR_BIN): $(RUN_ISR_SRC) | $(BIN_DIR)
$(CXX) $(CXXFLAGS) $< -o $@ $(WIRING_PI_LIBS)
# Ensure bin directory exists
$(BIN_DIR):
mkdir -p $(BIN_DIR)
# Download googletest if not already present
$(GTEST_DIR):
@echo "Downloading GoogleTest..."
curl -L $(GTEST_URL) -o $(GTEST_ZIP)
unzip -q $(GTEST_ZIP)
rm -f $(GTEST_ZIP)
# Generic rules for compiling gtest/gmock sources
%.o: %.cc
$(CXX) $(CXXFLAGS) $(GTEST_INCLUDES) -I$(GTEST_DIR)/googletest -I$(GTEST_DIR)/googlemock -c $< -o $@
# Run all tests
test: all
@failed=0; \
for t in $(TEST_BINS); do \
echo "Running $$t..."; \
./$$t || failed=1; \
done; \
if [ $$failed -eq 0 ]; then \
echo "✅ All tests passed."; \
else \
echo "❌ Some tests failed."; \
exit 1; \
fi
clean:
rm -f $(TEST_BINS) $(RUN_ISR_BIN)
distclean: clean
rm -rf $(GTEST_DIR) $(BIN_DIR)
.PHONY: all clean distclean test

26
wiringPi/gtest/README.md Normal file
View File

@@ -0,0 +1,26 @@
## Quick Start
g++ is required to build the test cases.
Connect wPi pin 1 (BCM 18) and wPi pin 0 (BCM 17) to run ISR tests.
Also, wPi pin 6 (BCM 25) should be unconnected.
Run `make test` or do following to run the test:
```
make googletest-main
make -j 4
make test
```
To clean up the environment:
```
make clean
```
## Dependencies
Googletest is downloaded during
the build for the first time. The downloaded items are in directory `googletest-main`. `make clean` keeps this directory. Run `make distclean`
to erase all built images including googletest.

View File

@@ -0,0 +1,31 @@
/*
* Helper program of test_conflict_between_processes.
*
* The program configures ISR on pin 1 (BCM 18) and polls for a termination request.
*/
#include <stdint.h>
#include <stdio.h>
#include <wiringPi.h>
#define LISTENER_PIN 1 // BCM 18
static uint8_t ok_to_stop = 0;
static void noop() {
fprintf(stderr, "interrupted\n");
ok_to_stop = 1;
}
int main(int, char *[]) {
fprintf(stderr, "start\n");
wiringPiSetup();
if (wiringPiISR(LISTENER_PIN, INT_EDGE_RISING, noop)) {
fprintf(stderr, "failed to start listener\n");
return -1;
}
fprintf(stderr, "start listening\n");
while (!ok_to_stop) {
delay(1000);
}
fprintf(stderr, "bye\n");
}

View File

@@ -0,0 +1,51 @@
#include <gtest/gtest.h>
#include <time.h>
#include <wiringPi.h>
#define TRIGGER_PIN 0 // BCM 17
#define LISTENER_PIN 1 // BCM 18
/**
* Tests ISR for both edges.
*/
class ISRBoth : public ::testing::Test {
public:
static void SetUpTestSuite() {
wiringPiSetup();
pinMode(TRIGGER_PIN, OUTPUT);
digitalWrite(TRIGGER_PIN, LOW);
wiringPiISR(LISTENER_PIN, INT_EDGE_BOTH, ISRBoth::TriggerCount);
delay(100);
}
static uint32_t count;
static void TriggerCount() { ++count; }
protected:
virtual void SetUp() { count = 0; }
virtual void TearDown() {
digitalWrite(TRIGGER_PIN, LOW);
delay(10);
}
};
uint32_t ISRBoth::count = 0;
TEST_F(ISRBoth, Fundamental) {
EXPECT_EQ(count, 0u);
digitalWrite(TRIGGER_PIN, HIGH);
delay(10);
EXPECT_EQ(count, 1u);
digitalWrite(TRIGGER_PIN, LOW);
delay(10);
EXPECT_EQ(count, 2u);
}
TEST_F(ISRBoth, ThreeTriggers) {
for (int i = 0; i < 5; ++i) {
digitalWrite(TRIGGER_PIN, (i + 1) % 2);
delay(10);
}
EXPECT_EQ(count, 5u);
}

View File

@@ -0,0 +1,68 @@
#include <gtest/gtest.h>
#include <pthread.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <wiringPi.h>
#define TRIGGER_PIN 0 // BCM 17
#define LISTENER_PIN 1 // BCM 18
#define ANOTHER_LISTENER_PIN 6 // BCM 25
static void *remote_isr(void *) {
std::system("./bin/run_isr");
return NULL;
}
/**
* Tests ISR setup conflict between two processes.
*
* Another program 'run_isr' starts first that occupies the pin 31 for an ISR.
* This program should fail to start ISR on the same pin.
*/
class ISRConflict : public ::testing::Test {
public:
static void SetUpTestSuite() {
wiringPiSetup();
digitalWrite(TRIGGER_PIN, LOW);
pthread_t tid;
pthread_create(&tid, NULL, remote_isr, NULL);
delay(1000);
}
static void TearDownTestSuite() {
delay(1000);
}
static uint32_t count;
static void TriggerCount() { ++count; }
static void TriggerCount2(WPIWfiStatus, void *userdata) {
++*(uint32_t *)userdata;
}
protected:
virtual void SetUp() { count = 0; }
virtual void TearDown() {
digitalWrite(TRIGGER_PIN, LOW);
delay(10);
}
};
uint32_t ISRConflict::count = 0;
TEST_F(ISRConflict, TestConflictBetweenProcesses) {
// The ISRs should fail to start because run_isr is listening on the
// same port
EXPECT_EQ(
wiringPiISR(LISTENER_PIN, INT_EDGE_FALLING, ISRConflict::TriggerCount),
-1);
EXPECT_EQ(wiringPiISR2(LISTENER_PIN, INT_EDGE_FALLING,
ISRConflict::TriggerCount2, 0, &ISRConflict::count),
-1);
// to exit run_isr
digitalWrite(TRIGGER_PIN, HIGH);
}

View File

@@ -0,0 +1,57 @@
#include <gtest/gtest.h>
#include <time.h>
#include <wiringPi.h>
#define TRIGGER_PIN 0 // BCM 17
#define LISTENER_PIN 1 // BCM 18
/**
* Tests ISR for falling edges.
*/
class ISRFalling : public ::testing::Test {
public:
static void SetUpTestSuite() {
wiringPiSetup();
pinMode(TRIGGER_PIN, OUTPUT);
digitalWrite(TRIGGER_PIN, HIGH);
wiringPiISR(LISTENER_PIN, INT_EDGE_FALLING, ISRFalling::TriggerCount);
delay(100);
}
static void TearDownTestSuite() { digitalWrite(TRIGGER_PIN, LOW); }
static uint32_t count;
static void TriggerCount() { ++count; }
protected:
virtual void SetUp() { count = 0; }
virtual void TearDown() {
digitalWrite(TRIGGER_PIN, HIGH);
delay(10);
}
};
uint32_t ISRFalling::count = 0;
TEST_F(ISRFalling, Fundamental) {
EXPECT_EQ(count, 0u);
digitalWrite(TRIGGER_PIN, LOW);
delay(10);
EXPECT_EQ(count, 1u);
digitalWrite(TRIGGER_PIN, HIGH);
delay(10);
EXPECT_EQ(count, 1u);
}
TEST_F(ISRFalling, ThreeTriggers) {
digitalWrite(TRIGGER_PIN, LOW);
delay(10);
count = 0;
for (int i = 0; i < 6; ++i) {
delay(10);
digitalWrite(TRIGGER_PIN, (i + 1) % 2);
}
delay(10);
EXPECT_EQ(count, 3u);
}

View File

@@ -0,0 +1,68 @@
#include <gtest/gtest.h>
#include <time.h>
#include <wiringPi.h>
#define TRIGGER_PIN 0 // BCM 17
#define LISTENER_PIN 1 // BCM 18
#define ANOTHER_LISTENER_PIN 6 // BCM 25
/**
* Tests restarting ISR.
*/
class ISRRestart : public ::testing::Test {
public:
static void SetUpTestSuite() {
wiringPiSetup();
pinMode(TRIGGER_PIN, OUTPUT);
digitalWrite(TRIGGER_PIN, LOW);
wiringPiISR(LISTENER_PIN, INT_EDGE_RISING, ISRRestart::TriggerCount);
delay(100);
}
static uint32_t count;
static void TriggerCount() { ++count; }
static void TriggerCountDown() { --count; }
protected:
virtual void SetUp() { count = 0; }
virtual void TearDown() {
digitalWrite(TRIGGER_PIN, LOW);
delay(10);
}
};
uint32_t ISRRestart::count = 0;
TEST_F(ISRRestart, RestartISR) {
// fundamental ISR test
EXPECT_EQ(count, 0u);
digitalWrite(TRIGGER_PIN, HIGH);
delay(10);
EXPECT_EQ(count, 1u);
digitalWrite(TRIGGER_PIN, LOW);
delay(10);
EXPECT_EQ(count, 1u);
// stop the ISR
EXPECT_EQ(wiringPiISRStop(LISTENER_PIN), 0);
digitalWrite(TRIGGER_PIN, HIGH);
delay(10);
EXPECT_EQ(count, 1u);
digitalWrite(TRIGGER_PIN, LOW);
// start another ISR on the same pin
EXPECT_EQ(
wiringPiISR(LISTENER_PIN, INT_EDGE_FALLING, ISRRestart::TriggerCountDown),
0);
count=1;
delay(100);
digitalWrite(TRIGGER_PIN, HIGH);
delay(10);
EXPECT_EQ(count, 1u);
digitalWrite(TRIGGER_PIN, LOW);
delay(10);
EXPECT_EQ(count, 0u);
}

View File

@@ -0,0 +1,109 @@
#include <gtest/gtest.h>
#include <time.h>
#include <wiringPi.h>
#define TRIGGER_PIN 0 // BCM 17
#define LISTENER_PIN 1 // BCM 18
#define ANOTHER_LISTENER_PIN 26 // BCM 12
struct counters {
int32_t *primary;
int32_t *secondary;
};
/**
* Tests restarting ISR using function wiringPiISR2().
*/
class ISRRestart2 : public ::testing::Test {
public:
static void SetUpTestSuite() {
wiringPiSetup();
pinMode(TRIGGER_PIN, OUTPUT);
digitalWrite(TRIGGER_PIN, LOW);
counters_a.primary = &count;
counters_a.secondary = &count2;
counters_b.primary = &count;
counters_b.secondary = &count3;
wiringPiISR2(LISTENER_PIN, INT_EDGE_RISING, ISRRestart2::TriggerCount, 1000,
&counters_a);
delay(100);
}
static int32_t count;
static int32_t count2;
static int32_t count3;
static counters counters_a;
static counters counters_b;
static void TriggerCount(WPIWfiStatus, void *userdata) {
struct counters *counters = (struct counters *)userdata;
++*counters->primary;
++*counters->secondary;
}
static void TriggerCountDown(WPIWfiStatus, void *userdata) {
struct counters *counters = (struct counters *)userdata;
++*counters->primary;
--*counters->secondary;
}
protected:
virtual void SetUp() {
count = 0;
count2 = 0;
count3 = 0;
}
virtual void TearDown() {
digitalWrite(TRIGGER_PIN, LOW);
delay(10);
}
};
int32_t ISRRestart2::count = 0;
int32_t ISRRestart2::count2 = 0;
int32_t ISRRestart2::count3 = 0;
struct counters ISRRestart2::counters_a;
struct counters ISRRestart2::counters_b;
TEST_F(ISRRestart2, RestartISR2) {
// fundamental ISR2 test
EXPECT_EQ(count, 0);
EXPECT_EQ(count2, 0);
digitalWrite(TRIGGER_PIN, HIGH);
delay(20);
EXPECT_EQ(count, 1);
EXPECT_EQ(count2, 1);
digitalWrite(TRIGGER_PIN, LOW);
delay(20);
EXPECT_EQ(count, 1);
EXPECT_EQ(count2, 1);
// stop the ISR
EXPECT_EQ(wiringPiISRStop(LISTENER_PIN), 0);
delay(100);
digitalWrite(TRIGGER_PIN, HIGH);
delay(20);
EXPECT_EQ(count, 1);
EXPECT_EQ(count2, 1);
EXPECT_EQ(count3, 0);
digitalWrite(TRIGGER_PIN, LOW);
// start another ISR2 on the same pin
EXPECT_EQ(wiringPiISR2(LISTENER_PIN, INT_EDGE_FALLING,
ISRRestart2::TriggerCountDown, 0, &counters_b),
0);
delay(100);
digitalWrite(TRIGGER_PIN, HIGH);
delay(20);
EXPECT_EQ(count, 1);
EXPECT_EQ(count2, 1);
EXPECT_EQ(count3, 0);
digitalWrite(TRIGGER_PIN, LOW);
delay(20);
EXPECT_EQ(count, 2);
EXPECT_EQ(count2, 1);
EXPECT_EQ(count3, -1);
}

View File

@@ -0,0 +1,80 @@
#include <gtest/gtest.h>
#include <time.h>
#include <wiringPi.h>
#define TRIGGER_PIN 0 // BCM 17
#define LISTENER_PIN 1 // BCM 18
#define ANOTHER_LISTENER_PIN 26 // BCM 12
/**
* Tests ISR for rising edges.
*/
class ISRRising : public ::testing::Test {
public:
static void SetUpTestSuite() {
wiringPiSetup();
pinMode(TRIGGER_PIN, OUTPUT);
digitalWrite(TRIGGER_PIN, LOW);
wiringPiISR(LISTENER_PIN, INT_EDGE_RISING, ISRRising::TriggerCount);
delay(100);
}
static uint32_t count;
static void TriggerCount() { ++count; }
static void TriggerCountDown() { --count; }
protected:
virtual void SetUp() { count = 0; }
virtual void TearDown() {
digitalWrite(TRIGGER_PIN, LOW);
delay(10);
}
};
uint32_t ISRRising::count = 0;
TEST_F(ISRRising, Fundamental) {
EXPECT_EQ(count, 0u);
digitalWrite(TRIGGER_PIN, HIGH);
delay(10);
EXPECT_EQ(count, 1u);
digitalWrite(TRIGGER_PIN, LOW);
delay(10);
EXPECT_EQ(count, 1u);
}
TEST_F(ISRRising, ThreeTriggers) {
digitalWrite(TRIGGER_PIN, HIGH);
delay(10);
count = 0;
for (int i = 0; i < 6; ++i) {
delay(10);
digitalWrite(TRIGGER_PIN, i % 2);
}
delay(10);
EXPECT_EQ(count, 3u);
}
TEST_F(ISRRising, ISRInitConflict) {
// this initialization should fail for ISR conflict
EXPECT_EQ(
wiringPiISR(LISTENER_PIN, INT_EDGE_FALLING, ISRRising::TriggerCountDown),
-1);
// ensure the initialization above has released the mutex lock
EXPECT_EQ(wiringPiISR(ANOTHER_LISTENER_PIN, INT_EDGE_RISING,
ISRRising::TriggerCountDown),
0);
delay(100);
// ensure the existing ISR has not broken
EXPECT_EQ(count, 0u);
digitalWrite(TRIGGER_PIN, HIGH);
delay(10);
EXPECT_EQ(count, 1u);
digitalWrite(TRIGGER_PIN, LOW);
delay(10);
EXPECT_EQ(count, 1u);
}

View File

@@ -0,0 +1,136 @@
#include <gtest/gtest.h>
#include <time.h>
#include <wiringPi.h>
#define TRIGGER_PIN 0 // BCM 17
#define LISTENER_PIN 1 // BCM 18
#define ANOTHER_LISTENER_PIN 6 // BCM 25
struct counters {
uint32_t *primary;
uint32_t *secondary;
};
/**
* Tests ISR for rising edges.
* Function wiringPiISR2 is used for the configuraiton.
*/
class ISRRising2 : public ::testing::Test {
public:
static void SetUpTestSuite() {
wiringPiSetup();
pinMode(TRIGGER_PIN, OUTPUT);
digitalWrite(TRIGGER_PIN, LOW);
counters_a.primary = &count;
counters_a.secondary = &count2;
counters_b.primary = &count;
counters_b.secondary = &count3;
wiringPiISR2(LISTENER_PIN, INT_EDGE_RISING, ISRRising2::TriggerCount, 1000,
&counters_a);
delay(100);
}
static uint32_t count;
static uint32_t count2;
static uint32_t count3;
static counters counters_a;
static counters counters_b;
static void TriggerCount(WPIWfiStatus, void *userdata) {
struct counters *counters = (struct counters *)userdata;
++*counters->primary;
++*counters->secondary;
}
static void TriggerCountDown(WPIWfiStatus, void *userdata) {
struct counters *counters = (struct counters *)userdata;
++*counters->primary;
--*counters->secondary;
}
protected:
virtual void SetUp() {
count = 0;
count2 = 0;
count3 = 0;
}
virtual void TearDown() {
digitalWrite(TRIGGER_PIN, LOW);
delay(10);
}
};
uint32_t ISRRising2::count = 0;
uint32_t ISRRising2::count2 = 0;
uint32_t ISRRising2::count3 = 0;
struct counters ISRRising2::counters_a;
struct counters ISRRising2::counters_b;
TEST_F(ISRRising2, Fundamental) {
EXPECT_EQ(count, 0u);
EXPECT_EQ(count2, 0u);
digitalWrite(TRIGGER_PIN, HIGH);
delay(20);
EXPECT_EQ(count, 1u);
EXPECT_EQ(count2, 1u);
digitalWrite(TRIGGER_PIN, LOW);
delay(20);
EXPECT_EQ(count, 1u);
EXPECT_EQ(count2, 1u);
}
TEST_F(ISRRising2, ThreeTriggers) {
digitalWrite(TRIGGER_PIN, HIGH);
delay(10);
count = 0;
for (int i = 0; i < 6; ++i) {
delay(20);
digitalWrite(TRIGGER_PIN, i % 2);
}
delay(20);
EXPECT_EQ(count, 3u);
}
TEST_F(ISRRising2, Debounce) {
delay(20);
for (int i = 0; i < 7; ++i) {
digitalWrite(TRIGGER_PIN, (i + 1) % 2);
}
delay(20);
EXPECT_EQ(count, 1u);
}
TEST_F(ISRRising2, ISRInitConflict) {
// this initialization should fail for ISR conflict
EXPECT_EQ(wiringPiISR2(LISTENER_PIN, INT_EDGE_FALLING,
ISRRising2::TriggerCountDown, 0, &counters_b),
-1);
// ensure the initialization above has released the mutex lock
EXPECT_EQ(wiringPiISR2(ANOTHER_LISTENER_PIN, INT_EDGE_BOTH,
ISRRising2::TriggerCountDown, 0, &counters_b),
0);
delay(100);
// ensure the existing ISR has not broken
EXPECT_EQ(count, 0u);
EXPECT_EQ(count2, 0u);
digitalWrite(TRIGGER_PIN, HIGH);
delay(20);
EXPECT_EQ(count, 1u);
EXPECT_EQ(count2, 1u);
digitalWrite(TRIGGER_PIN, LOW);
delay(20);
EXPECT_EQ(count, 1u);
EXPECT_EQ(count2, 1u);
delay(20);
count = 0;
for (int i = 0; i < 7; ++i) {
delay(1);
digitalWrite(TRIGGER_PIN, (i + 1) % 2);
}
delay(20);
EXPECT_EQ(count, 1u);
}

View File

@@ -83,7 +83,8 @@ int main (void) {
case PI_MODEL_400:
case PI_MODEL_CM4:
case PI_MODEL_CM4S:
fExpectTimedigitalWrite = 0.020; //us
//fExpectTimedigitalWrite = 0.020; //us - with kernel <6.12.47
fExpectTimedigitalWrite = 0.016; //us
fExpectTimedigitalRead = 0.038; //us
fExpectTimepinMode = 0.121; //us
fWriteReadDelayFactor = 1.86;

View File

@@ -1,7 +1,7 @@
/*
* wiringPi:
* Arduino look-a-like Wiring library for the Raspberry Pi
* Copyright (c) 2012-2025 Gordon Henderson and contributors
* Copyright (c) 20122019 Gordon Henderson; 20192026 Contributors
* Additional code for pwmSetClock by Chris Hall <chris@kchall.plus.com>
*
* Thanks to code samples from Gert Jan van Loo and the
@@ -2868,35 +2868,25 @@ int waitForInterruptClose(int pin) {
}
/*
* interruptHandlerV2:
* This is a thread and gets started to wait for the interrupt we're
* hoping to catch. It will call the user-function when the interrupt
* fires.
* interruptHandlerInit:
* Initializes an interrupt handler before starting the listener loop in a
* separate thread.
* Returns: >0 on successful initialization, 0 if the listener loop does not
* have to start, -1 on error.
*********************************************************************************
*/
void *interruptHandlerV2(void *arg)
static int interruptHandlerInit(int pin, int EdgeMode, unsigned long debounce_period_us)
{
const char* strmode = "";
int pin, EdgeMode, ret, fd, attr, i;
unsigned int readret;
unsigned long debounce_period_us;
struct pollfd polls ;
int ret, attr;
struct gpio_v2_line_config config;
struct gpio_v2_line_request req;
struct gpio_v2_line_event evdat[64];
struct WPIWfiStatus wfiStatus;
struct timespec tspec = {0, 5e5}; /* 0.5 ms timeout {0, 1e6} */
pin = *(int *)arg;
if (wiringPiGpioDeviceGetFd()<0) {
return NULL;
if (wiringPiGpioDeviceGetFd() < 0) {
return -1;
}
EdgeMode = isrEdgeMode[pin];
debounce_period_us = isrDebouncePeriodUs[pin];
if (wiringPiDebug) {
printf ("interruptHandlerV2: GPIO line %d, edge mode %d, debounce_period_us %lu \n", pin, EdgeMode, debounce_period_us) ;
}
@@ -2912,7 +2902,7 @@ void *interruptHandlerV2(void *arg)
if (wiringPiDebug) {
printf ("interruptHandlerV2: waitForInterruptMode edge mode INT_EDGE_SETUP - exiting\n") ;
}
return NULL;
return 0;
case INT_EDGE_FALLING:
config.flags |= GPIO_V2_LINE_FLAG_EDGE_FALLING;
strmode = "falling";
@@ -2944,14 +2934,44 @@ void *interruptHandlerV2(void *arg)
ret = ioctl(chipFd, GPIO_V2_GET_LINE_IOCTL, &req);
if (ret == -1) {
ReportDeviceError("interruptHandlerV2: get line event", pin , strmode, ret);
return NULL;
return -1;
}
if (wiringPiDebug)
printf ("interruptHandlerV2: GPIO get line %d , mode %s succeded, fd=%d\n", pin, strmode, req.fd) ;
if (wiringPiDebug) {
printf ("interruptHandlerV2: GPIO get line %d , mode %s succeded, fd=%d\n", pin, strmode, req.fd);
}
return req.fd;
}
struct interrupt_handler_params {
int pin;
int fd;
};
/*
* interruptHandlerV2:
* This is a thread and gets started to wait for the interrupt we're
* hoping to catch. It will call the user-function when the interrupt
* fires.
*********************************************************************************
*/
static void *interruptHandlerV2(void *arg)
{
struct interrupt_handler_params *params;
int pin, ret, fd, i;
unsigned int readret;
struct pollfd polls ;
struct gpio_v2_line_event evdat[64];
struct WPIWfiStatus wfiStatus;
struct timespec tspec = {0, 5e5}; /* 0.5 ms timeout {0, 1e6} */
params = (struct interrupt_handler_params *)arg;
pin = params->pin;
fd = params->fd;
/* set event fd */
fd = req.fd;
isrFds [pin] = fd;
(void)piHiPri (55) ; // Only effective if we run as root
@@ -3041,9 +3061,8 @@ void *interruptHandlerV2(void *arg)
}
}
/*
* wiringPiISR:
* wiringPiISRInternal:
* Pi Specific.
* Take the details and create an interrupt handler that will do a call-
* back to the user supplied function.
@@ -3064,39 +3083,49 @@ int wiringPiISRInternal(int pin, int edgeMode, void (*function)(struct WPIWfiSta
printf("wiringPi: wiringPiISR pin %d, edgeMode %d\n", pin, edgeMode);
}
if (isrFunctions[pin] || isrFunctionsV2[pin]) {
fprintf(stderr, "wiringPi: ISR function already active, ignoring \n");
fprintf(stderr, "wiringPi: ISR function already active\n");
}
if (wiringPiDebug) {
printf("wiringPi: mutex in\n");
}
pthread_mutex_lock (&pinMutex) ;
struct interrupt_handler_params params = {
.pin = pin,
};
params.fd = interruptHandlerInit(pin, edgeMode, debounce_period_us);
if (params.fd < 0) {
pthread_mutex_unlock (&pinMutex) ;
return -1;
}
// OK to start the new ISR. Update the table.
isrFunctionsV2[pin] = function;
isrUserdata[pin] = userdata;
isrFunctions[pin] = functionClassic;
isrEdgeMode[pin] = edgeMode;
isrDebouncePeriodUs[pin] = debounce_period_us;
if (wiringPiDebug) {
printf("wiringPi: mutex in\n");
}
pthread_mutex_lock (&pinMutex) ;
pinPass = pin ;
if (params.fd > 0) {
if (wiringPiDebug) {
printf("wiringPi: pthread_create before 0x%lX\n", (unsigned long)isrThreads[pin]);
}
if (pthread_create (&isrThreads[pin], NULL, interruptHandlerV2, &pin)==0) {
if (pthread_create (&isrThreads[pin], NULL, interruptHandlerV2, &params)==0) {
if (wiringPiDebug) {
printf("wiringPi: pthread_create successed, 0x%lX\n", (unsigned long)isrThreads[pin]);
}
/* while (pinPass != -1)
delay (1) ; */
// wait so that interruptHandler is up und running.
// when interruptHandler is running, the calling function wiringPiISR
// must be still alive, otherwise the thread argument &pin points into nirwana,
// when it is picked up from interruptHandler.
delay (10);
} else {
if (wiringPiDebug) {
printf("wiringPi: pthread_create failed\n");
}
}
// wait so that interruptHandler is up und running.
// when interruptHandler is running, the calling function wiringPiISRInternal
// must be still alive, otherwise the thread argument &param points into nirwana,
// when it is picked up from interruptHandlerV2.
delay(10);
}
if (wiringPiDebug) {
printf("wiringPi: mutex out\n");

View File

@@ -1,7 +1,7 @@
/*
* wiringPi.h:
* Arduino like Wiring library for the Raspberry Pi.
* Copyright (c) 2012-2025 Gordon Henderson
* Copyright (c) 20122019 Gordon Henderson; 20192026 Contributors
***********************************************************************
* This file is part of wiringPi:
* https://github.com/WiringPi/WiringPi/

View File

@@ -1,7 +1,7 @@
/*
* wiringPiI2C.c:
* Simplified I2C access routines
* Copyright (c) 2013-2024 Gordon Henderson and contributors
* Copyright (c) 20132019 Gordon Henderson; 20192026 Contributors
***********************************************************************
* This file is part of wiringPi:
* https://github.com/WiringPi/WiringPi/

View File

@@ -1,7 +1,7 @@
/*
* wiringPiI2C.h:
* Simplified I2C access routines
* Copyright (c) 2013-2024 Gordon Henderson and contributors
* Copyright (c) 20132019 Gordon Henderson; 20192026 Contributors
***********************************************************************
* This file is part of wiringPi:
* https://github.com/WiringPi/WiringPi/

View File

@@ -1,7 +1,7 @@
/*
* wiringPiSPI.c:
* Simplified SPI access routines
* Copyright (c) 2012-2025 Gordon Henderson and contributors
* Copyright (c) 20122019 Gordon Henderson; 20192026 Contributors
***********************************************************************
* This file is part of wiringPi:
* https://github.com/WiringPi/WiringPi/
@@ -44,9 +44,11 @@
//static const char *spiDev1 = "/dev/spidev0.1" ;
static const uint8_t spiBPW = 8 ;
static const uint16_t spiDelay = 0 ;
//https://datasheets.raspberrypi.com/cm4/cm4-datasheet.pdf
//https://datasheets.raspberrypi.com/cm4/cm4-datasheet.pdf SPI0-6, CS0-2
//cm5-datasheet.pdf SPI0-5, CS depend on SPInumber, can be up to 4
//spi0-4cs.dts extention for supporting CS0-4
const uint8_t WPI_MaxSPINumbers = 7 ;
const uint8_t WPI_MaxSPIChannels = 3 ;
const uint8_t WPI_MaxSPIChannels = 5 ;
static uint32_t spiSpeeds [7][3] =

View File

@@ -1,7 +1,7 @@
/*
* wiringPiSPI.h:
* Simplified SPI access routines
* Copyright (c) 2012-2025 Gordon Henderson and contributors
* Copyright (c) 20122019 Gordon Henderson; 20192026 Contributors
***********************************************************************
* This file is part of wiringPi:
* https://github.com/WiringPi/WiringPi/