From 04c562941c65c0f62f39179c0c2cf4f580c6960f Mon Sep 17 00:00:00 2001
From: Kevin O'Connor <kevin@koconnor.net>
Date: Fri, 3 May 2024 11:17:28 -0400
Subject: [PATCH] sensor_ldc1612: Add support for chips with INTB line routed
 to mcu

If the INTB line is available it can reduce the MCU load.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
---
 docs/Config_Reference.md |  3 +++
 klippy/extras/ldc1612.py | 13 ++++++++++--
 src/sensor_ldc1612.c     | 46 +++++++++++++++++++++++++++++++++-------
 3 files changed, 52 insertions(+), 10 deletions(-)

diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md
index 1338d46f9..6b42fe48d 100644
--- a/docs/Config_Reference.md
+++ b/docs/Config_Reference.md
@@ -2007,6 +2007,9 @@ Support for eddy current inductive probes. One may define this section
 sensor_type: ldc1612
 #   The sensor chip used to perform eddy current measurements. This
 #   parameter must be provided and must be set to ldc1612.
+#intb_pin:
+#   MCU gpio pin connected to the ldc1612 sensor's INTB pin (if
+#   available). The default is to not use the INTB pin.
 #z_offset:
 #   The nominal distance (in mm) between the nozzle and bed that a
 #   probing attempt should stop at. This parameter must be provided.
diff --git a/klippy/extras/ldc1612.py b/klippy/extras/ldc1612.py
index 2ae4dd7d7..08cab965e 100644
--- a/klippy/extras/ldc1612.py
+++ b/klippy/extras/ldc1612.py
@@ -87,8 +87,17 @@ class LDC1612:
         self.oid = oid = mcu.create_oid()
         self.query_ldc1612_cmd = None
         self.ldc1612_setup_home_cmd = self.query_ldc1612_home_state_cmd = None
-        mcu.add_config_cmd("config_ldc1612 oid=%d i2c_oid=%d"
-                           % (oid, self.i2c.get_oid()))
+        if config.get('intb_pin', None) is not None:
+            ppins = config.get_printer().lookup_object("pins")
+            pin_params = ppins.lookup_pin(config.get('intb_pin'))
+            if pin_params['chip'] != mcu:
+                raise config.error("ldc1612 intb_pin must be on same mcu")
+            mcu.add_config_cmd(
+                "config_ldc1612_with_intb oid=%d i2c_oid=%d intb_pin=%s"
+                % (oid, self.i2c.get_oid(), pin_params['pin']))
+        else:
+            mcu.add_config_cmd("config_ldc1612 oid=%d i2c_oid=%d"
+                               % (oid, self.i2c.get_oid()))
         mcu.add_config_cmd("query_ldc1612 oid=%d rest_ticks=0"
                            % (oid,), on_restart=True)
         mcu.register_config_callback(self._build_config)
diff --git a/src/sensor_ldc1612.c b/src/sensor_ldc1612.c
index 2e3f5694e..3db4de4b2 100644
--- a/src/sensor_ldc1612.c
+++ b/src/sensor_ldc1612.c
@@ -17,7 +17,7 @@
 #include "trsync.h" // trsync_do_trigger
 
 enum {
-    LDC_PENDING = 1<<0,
+    LDC_PENDING = 1<<0, LDC_HAVE_INTB = 1<<1,
     LH_AWAIT_HOMING = 1<<1, LH_CAN_TRIGGER = 1<<2
 };
 
@@ -27,6 +27,7 @@ struct ldc1612 {
     struct i2cdev_s *i2c;
     uint8_t flags;
     struct sensor_bulk sb;
+    struct gpio_in intb_pin;
     // homing
     struct trsync *ts;
     uint8_t homing_flags;
@@ -37,6 +38,13 @@ struct ldc1612 {
 
 static struct task_wake ldc1612_wake;
 
+// Check if the intb line is "asserted"
+static int
+check_intb_asserted(struct ldc1612 *ld)
+{
+    return !gpio_in_read(ld->intb_pin);
+}
+
 // Query ldc1612 data
 static uint_fast8_t
 ldc1612_event(struct timer *timer)
@@ -44,8 +52,10 @@ ldc1612_event(struct timer *timer)
     struct ldc1612 *ld = container_of(timer, struct ldc1612, timer);
     if (ld->flags & LDC_PENDING)
         ld->sb.possible_overflows++;
-    ld->flags |= LDC_PENDING;
-    sched_wake_task(&ldc1612_wake);
+    if (!(ld->flags & LDC_HAVE_INTB) || check_intb_asserted(ld)) {
+        ld->flags |= LDC_PENDING;
+        sched_wake_task(&ldc1612_wake);
+    }
     ld->timer.waketime += ld->rest_ticks;
     return SF_RESCHEDULE;
 }
@@ -60,6 +70,17 @@ command_config_ldc1612(uint32_t *args)
 }
 DECL_COMMAND(command_config_ldc1612, "config_ldc1612 oid=%c i2c_oid=%c");
 
+void
+command_config_ldc1612_with_intb(uint32_t *args)
+{
+    command_config_ldc1612(args);
+    struct ldc1612 *ld = oid_lookup(args[0], command_config_ldc1612);
+    ld->intb_pin = gpio_in_setup(args[2], 1);
+    ld->flags = LDC_HAVE_INTB;
+}
+DECL_COMMAND(command_config_ldc1612_with_intb,
+             "config_ldc1612_with_intb oid=%c i2c_oid=%c intb_pin=%c");
+
 void
 command_ldc1612_setup_home(uint32_t *args)
 {
@@ -117,13 +138,11 @@ read_reg_status(struct ldc1612 *ld)
 static void
 ldc1612_query(struct ldc1612 *ld, uint8_t oid)
 {
-    // Clear pending flag
+    // Check if data available (and clear INTB line)
+    uint16_t status = read_reg_status(ld);
     irq_disable();
     ld->flags &= ~LDC_PENDING;
     irq_enable();
-
-    // Check if data available
-    uint16_t status = read_reg_status(ld);
     if (!(status & 0x08))
         return;
 
@@ -161,7 +180,7 @@ command_query_ldc1612(uint32_t *args)
     struct ldc1612 *ld = oid_lookup(args[0], command_config_ldc1612);
 
     sched_del_timer(&ld->timer);
-    ld->flags = 0;
+    ld->flags &= ~LDC_PENDING;
     if (!args[1])
         // End measurements
         return;
@@ -181,6 +200,17 @@ command_query_ldc1612_status(uint32_t *args)
 {
     struct ldc1612 *ld = oid_lookup(args[0], command_config_ldc1612);
 
+    if (ld->flags & LDC_HAVE_INTB) {
+        // Check if a sample is pending in the chip via the intb line
+        irq_disable();
+        uint32_t time = timer_read_time();
+        int p = check_intb_asserted(ld);
+        irq_enable();
+        sensor_bulk_status(&ld->sb, args[0], time, 0, p ? BYTES_PER_SAMPLE : 0);
+        return;
+    }
+
+    // Query sensor to see if a sample is pending
     uint32_t time1 = timer_read_time();
     uint16_t status = read_reg_status(ld);
     uint32_t time2 = timer_read_time();