From 7ec153d397754d383f6b71b26376395c3d9b57cf Mon Sep 17 00:00:00 2001
From: William Clark <wrvc96@gmail.com>
Date: Thu, 28 Dec 2023 18:10:34 +0000
Subject: [PATCH] interrupt generation

---
 Makefile    |   3 +-
 interrupt.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 interrupt.h |  11 +++++
 main.c      |  46 +++++++++++++-------
 4 files changed, 162 insertions(+), 17 deletions(-)
 create mode 100644 interrupt.c
 create mode 100644 interrupt.h

diff --git a/Makefile b/Makefile
index 8003108..9514ba9 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,9 @@
 CC=gcc
 CFLAGS=-O2 -std=c89 -W -Werror -Wall -Wextra -pedantic -I. 
 LFLAGS=-lm
+CFILES=$(wildcard ./*.c)
 all:
-	$(CC) $(CFLAGS) main.c i2c.c lis3dh.c -o lis3dh $(LFLAGS)
+	$(CC) $(CFLAGS) $(CFILES) -o lis3dh $(LFLAGS)
 
 clean:
 	rm -rf lis3dh
diff --git a/interrupt.c b/interrupt.c
new file mode 100644
index 0000000..9919da4
--- /dev/null
+++ b/interrupt.c
@@ -0,0 +1,119 @@
+/* 
+  SYSFS gpio interrupt example for Raspberry Pi
+*/
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <poll.h>
+#include "interrupt.h"
+
+
+static int write_file(const char *path, const char *str) {
+    int fd = 0;
+
+    if ((fd = open(path, O_WRONLY)) == 0) {
+        fprintf(stderr, "unable to open %s\n", path);
+        return 1;
+    }
+
+    if (write(fd, str, strlen(str)) < 1) {
+        fprintf(stderr, "unable to write to %s\n", path);
+        close(fd);
+        return 1;
+    }
+
+    close(fd);
+    return 0;
+}
+
+/* Register a sysfs GPIO pin to be interruptable
+   similar to:
+   $ echo 12 > /sys/class/gpio/export
+   $ echo both > /sys/class/gpio12/edge
+   $ echo in > /sys/class/gpio12/direction
+
+*/
+int int_register(int pin) {
+
+    char path[1024];
+    char buffer[64];
+
+    /* begin by unregistering supplied pin, just in case */
+    (void) int_unregister(pin);
+
+    memset(buffer, 0, 64);
+    sprintf(buffer, "%d", pin);
+    if (write_file("/sys/class/gpio/export", buffer) != 0) {
+        return 1;
+    }
+
+    /* sleep 500 ms to let linux set up the dir .. */
+    usleep(500000);
+
+    memset(path, 0, 1024);
+    sprintf(path, "%s%d/%s", "/sys/class/gpio/gpio", pin, "edge");
+    if (write_file(path, "both") != 0) {
+        return 1;
+    }
+
+    memset(path, 0, 1024);
+    sprintf(path, "%s%d/%s", "/sys/class/gpio/gpio", pin, "direction");
+    if (write_file(path, "in") != 0) {
+        return 1;
+    }
+
+
+    return 0;
+}
+
+/* unregister an interrupt gpio config 
+   similar to:
+   $ echo 12 > /sys/class/gpio/unexport
+*/
+int int_unregister(int pin) {
+    char buffer[64];
+
+    memset(buffer, 0, 64);
+    sprintf(buffer, "%d", pin);
+    if (write_file("/sys/class/gpio/unexport", buffer) != 0) {
+        return 1;
+    }
+
+    return 0;
+}
+
+/* poll a gpio pin for interrupt event 
+   blocking
+*/
+int int_poll(int pin) {
+    int fd;
+    struct pollfd pfd;
+    char path[1024];
+    char buf[16];
+
+    memset(path, 0, 1024);
+    sprintf(path, "%s%d/%s", "/sys/class/gpio/gpio", pin, "value");
+
+    if ((fd = open(path, O_RDONLY)) == 0) {
+        fprintf(stderr, "unable to open %s\n", path);
+        return 1;
+    }
+
+    pfd.fd = fd;
+    pfd.events = POLLPRI | POLLERR;
+
+    lseek(fd, 0, SEEK_SET);
+    read(fd, buf, sizeof buf);
+
+    poll(&pfd, 1, -1);
+
+    lseek(fd, 0, SEEK_SET);
+    read(fd, buf, sizeof buf);
+
+    return 0;
+}
diff --git a/interrupt.h b/interrupt.h
new file mode 100644
index 0000000..70da9dc
--- /dev/null
+++ b/interrupt.h
@@ -0,0 +1,11 @@
+#ifndef INTERRUPT_H
+#define INTERRUPT_H
+
+/*
+    Note: `pin' is GPIO No.
+*/
+int int_register(int pin);
+int int_unregister(int pin);
+int int_poll(int pin);
+
+#endif
\ No newline at end of file
diff --git a/main.c b/main.c
index 4802e92..7a0da58 100644
--- a/main.c
+++ b/main.c
@@ -4,8 +4,11 @@
 #include <unistd.h>
 #include <math.h>
 #include "lis3dh.h"
+#include "interrupt.h"
 #include "i2c.h"
 
+#define GPIO_INTERRUPT_PIN 12
+
 /* calc magnitude of accel [x y z] vector */
 float mag(float x, float y, float z) {
     return sqrt( powf(x, 2) + powf(y, 2) + powf(z, 2) );
@@ -14,6 +17,7 @@ float mag(float x, float y, float z) {
 /* print message then exit */
 void quit(const char *msg, lis3dh_t *lis) {
     lis->dev.deinit();
+    int_unregister(GPIO_INTERRUPT_PIN);
     fprintf(stderr, "%s\n", msg);
     exit(1);
 }
@@ -22,7 +26,7 @@ int main() {
 
     lis3dh_t lis;
     struct lis3dh_fifo_data fifo;
-    int i, k;
+    int k;
 
     /* set fn ptrs to rw on bus (i2c or SPI) */
     lis.dev.init = i2c_init;
@@ -37,34 +41,44 @@ int main() {
         quit("init()", &lis);
     }
 
+    /* register interrupt */
+    if (int_register(GPIO_INTERRUPT_PIN)) {
+        quit("int_register()", &lis);
+    }
+
     /* set up config */
     lis.cfg.mode = LIS3DH_MODE_NORMAL;
     lis.cfg.range = LIS3DH_FS_2G;
     lis.cfg.rate = LIS3DH_ODR_100_HZ;
     lis.cfg.fifo.mode = LIS3DH_FIFO_MODE_STREAM;
+    lis.cfg.fifo.trig = LIS3DH_FIFO_TRIG_INT1;
+    lis.cfg.pin1.overrun = 1;
+    
 
     /* write device config */
     if (lis3dh_configure(&lis)) {
         quit("configure()", &lis);
     }
-
-    for (i=0; i<100; i++) {
     
-        /* poll fifo reg */
-        if (lis3dh_poll_fifo(&lis)) {
-            quit("poll_fifo()", &lis);
-        }
+    /* wait for interrupt from LIS3DH */
+    if (int_poll(GPIO_INTERRUPT_PIN)) {
+        quit("int_poll()", &lis);
+    }
 
-        /* read stored fifo data into `fifo' struct */
-        if (lis3dh_read_fifo(&lis, &fifo)) {
-            quit("read_fifo()", &lis);
-        }
+    /* read stored fifo data into `fifo' struct */
+    if (lis3dh_read_fifo(&lis, &fifo)) {
+        quit("read_fifo()", &lis);
+    }
 
-        for(k=0; k<fifo.size; k++) {
-            printf("%04.04f %04.04f %04.04f %04.04f\n",
-                fifo.x[k], fifo.y[k], fifo.z[k],
-                mag(fifo.x[k], fifo.y[k], fifo.z[k]));
-        }
+    for(k=0; k<fifo.size; k++) {
+        printf("%04.04f %04.04f %04.04f %04.04f\n",
+            fifo.x[k], fifo.y[k], fifo.z[k],
+            mag(fifo.x[k], fifo.y[k], fifo.z[k]));
+    }
+    
+    /* unregister interrupt */
+    if (int_unregister(GPIO_INTERRUPT_PIN)) {
+        quit("int_unregister()", &lis);
     }
 
     /* deinitalise struct */