From 071c14e277daa681c33de5d35a29cb6c32ded753 Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Sat, 8 Feb 2025 16:03:27 +0100 Subject: [PATCH 1/6] ldc1612: handle i2c errors Signed-off-by: Timofey Titovets --- klippy/extras/probe_eddy_current.py | 3 ++ src/sensor_ldc1612.c | 55 ++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/klippy/extras/probe_eddy_current.py b/klippy/extras/probe_eddy_current.py index 779a904fb..465c9d2c6 100644 --- a/klippy/extras/probe_eddy_current.py +++ b/klippy/extras/probe_eddy_current.py @@ -366,6 +366,7 @@ class EddyGatherSamples: # Helper for implementing PROBE style commands (descend until trigger) class EddyDescend: REASON_SENSOR_ERROR = mcu.MCU_trsync.REASON_COMMS_TIMEOUT + 1 + REASON_BUS_IO_ERROR = REASON_SENSOR_ERROR + 12 def __init__(self, config, sensor_helper, calibration, param_helper): self._printer = config.get_printer() self._sensor_helper = sensor_helper @@ -398,6 +399,8 @@ class EddyDescend: if res == mcu.MCU_trsync.REASON_COMMS_TIMEOUT: raise self._printer.command_error( "Communication timeout during homing") + if res == self.REASON_BUS_IO_ERROR: + raise self._printer.command_error("Eddy I2C IO error") raise self._printer.command_error("Eddy current sensor error") if res != mcu.MCU_trsync.REASON_ENDSTOP_HIT: return 0. diff --git a/src/sensor_ldc1612.c b/src/sensor_ldc1612.c index 8b67884f1..d2ff55b8a 100644 --- a/src/sensor_ldc1612.c +++ b/src/sensor_ldc1612.c @@ -111,6 +111,26 @@ command_query_ldc1612_home_state(uint32_t *args) DECL_COMMAND(command_query_ldc1612_home_state, "query_ldc1612_home_state oid=%c"); +#define DATA_ERROR_AMPLITUDE (1 << 0) +#define DATA_ERROR_WATCHDOG (1 << 1) +#define DATA_ERROR_OVER_RANGE (1 << 2) +#define DATA_ERROR_UNDER_RANGE (1 << 3) +#define DATA_ERROR_I2C (DATA_ERROR_OVER_RANGE | DATA_ERROR_UNDER_RANGE) + +static uint_fast8_t +check_data_bits(struct ldc1612 *ld, uint32_t *data) { + if (*data < 0x0fffffff) + return 0; + // Sensor reports an issue - cancel homing + ld->homing_flags = 0; + uint8_t error_bits = *data >> 28; + uint8_t error_reason = ld->error_reason; + if ((error_bits & DATA_ERROR_I2C) == DATA_ERROR_I2C) + error_reason = ld->error_reason + DATA_ERROR_I2C; + trsync_do_trigger(ld->ts, error_reason); + return 1; +} + // Check if a sample should trigger a homing event static void check_home(struct ldc1612 *ld, uint32_t data) @@ -118,12 +138,8 @@ check_home(struct ldc1612 *ld, uint32_t data) uint8_t homing_flags = ld->homing_flags; if (!(homing_flags & LH_CAN_TRIGGER)) return; - if (data > 0x0fffffff) { - // Sensor reports an issue - cancel homing - ld->homing_flags = 0; - trsync_do_trigger(ld->ts, ld->error_reason); + if (check_data_bits(ld, &data)) return; - } uint32_t time = timer_read_time(); if ((homing_flags & LH_AWAIT_HOMING) && timer_is_before(time, ld->homing_clock)) @@ -143,19 +159,24 @@ check_home(struct ldc1612 *ld, uint32_t data) #define REG_STATUS 0x18 // Read a register on the ldc1612 -static void +static int read_reg(struct ldc1612 *ld, uint8_t reg, uint8_t *res) { - int ret = i2c_dev_read(ld->i2c, sizeof(reg), ®, 2, res); - i2c_shutdown_on_err(ret); + return i2c_dev_read(ld->i2c, sizeof(reg), ®, 2, res); } +#define STATUS_I2C_ERROR (3 << 4) +#define STATUS_DRDY (1 << 3) + // Read the status register on the ldc1612 static uint16_t read_reg_status(struct ldc1612 *ld) { uint8_t data_status[2]; - read_reg(ld, REG_STATUS, data_status); + int ret = read_reg(ld, REG_STATUS, data_status); + // Bits 5:4 are unused, report I2C + if (ret != I2C_BUS_SUCCESS) + data_status[0] |= STATUS_I2C_ERROR; return (data_status[0] << 8) | data_status[1]; } @@ -165,20 +186,25 @@ read_reg_status(struct ldc1612 *ld) static void ldc1612_query(struct ldc1612 *ld, uint8_t oid) { + int ret; // Check if data available (and clear INTB line) uint16_t status = read_reg_status(ld); irq_disable(); ld->flags &= ~LDC_PENDING; irq_enable(); - if (!(status & 0x08)) + // Force data read on I2C error + if (!(status & (STATUS_DRDY | STATUS_I2C_ERROR))) return; // Read coil0 frequency uint8_t *d = &ld->sb.data[ld->sb.data_count]; - read_reg(ld, REG_DATA0_MSB, &d[0]); - read_reg(ld, REG_DATA0_LSB, &d[2]); + ret = read_reg(ld, REG_DATA0_MSB, &d[0]); + ret |= read_reg(ld, REG_DATA0_LSB, &d[2]); ld->sb.data_count += BYTES_PER_SAMPLE; + if (ret != I2C_BUS_SUCCESS || status & STATUS_I2C_ERROR) + d[0] |= DATA_ERROR_I2C << 4; + // Check for endstop trigger uint32_t data = ((uint32_t)d[0] << 24) | ((uint32_t)d[1] << 16) @@ -232,7 +258,10 @@ command_query_status_ldc1612(uint32_t *args) uint16_t status = read_reg_status(ld); uint32_t time2 = timer_read_time(); - uint32_t fifo = status & 0x08 ? BYTES_PER_SAMPLE : 0; + if (status & STATUS_I2C_ERROR) + // Query error - don't send response - host will retry + return; + uint32_t fifo = status & STATUS_DRDY ? BYTES_PER_SAMPLE : 0; sensor_bulk_status(&ld->sb, args[0], time1, time2-time1, fifo); } DECL_COMMAND(command_query_status_ldc1612, "query_status_ldc1612 oid=%c"); From 31c61173cd8f71ffdc804a6158680540f35611d6 Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Fri, 19 Dec 2025 06:10:17 +0100 Subject: [PATCH 2/6] ldc1612: detect and forward zero count error Signed-off-by: Timofey Titovets --- src/sensor_ldc1612.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sensor_ldc1612.c b/src/sensor_ldc1612.c index d2ff55b8a..41c8476c4 100644 --- a/src/sensor_ldc1612.c +++ b/src/sensor_ldc1612.c @@ -116,6 +116,7 @@ DECL_COMMAND(command_query_ldc1612_home_state, #define DATA_ERROR_OVER_RANGE (1 << 2) #define DATA_ERROR_UNDER_RANGE (1 << 3) #define DATA_ERROR_I2C (DATA_ERROR_OVER_RANGE | DATA_ERROR_UNDER_RANGE) +#define DATA_ERROR_ZERO_COUNT (DATA_ERROR_WATCHDOG | DATA_ERROR_AMPLITUDE) static uint_fast8_t check_data_bits(struct ldc1612 *ld, uint32_t *data) { @@ -127,6 +128,8 @@ check_data_bits(struct ldc1612 *ld, uint32_t *data) { uint8_t error_reason = ld->error_reason; if ((error_bits & DATA_ERROR_I2C) == DATA_ERROR_I2C) error_reason = ld->error_reason + DATA_ERROR_I2C; + if ((error_bits & DATA_ERROR_ZERO_COUNT) == DATA_ERROR_ZERO_COUNT) + error_reason = ld->error_reason + DATA_ERROR_ZERO_COUNT; trsync_do_trigger(ld->ts, error_reason); return 1; } @@ -167,6 +170,7 @@ read_reg(struct ldc1612 *ld, uint8_t reg, uint8_t *res) #define STATUS_I2C_ERROR (3 << 4) #define STATUS_DRDY (1 << 3) +#define STATUS_ZERO_COUNT (1 << 8) // Read the status register on the ldc1612 static uint16_t @@ -192,8 +196,8 @@ ldc1612_query(struct ldc1612 *ld, uint8_t oid) irq_disable(); ld->flags &= ~LDC_PENDING; irq_enable(); - // Force data read on I2C error - if (!(status & (STATUS_DRDY | STATUS_I2C_ERROR))) + // Force data read on I2C error or Zero Count + if (!(status & (STATUS_DRDY | STATUS_I2C_ERROR | STATUS_ZERO_COUNT))) return; // Read coil0 frequency @@ -202,6 +206,9 @@ ldc1612_query(struct ldc1612 *ld, uint8_t oid) ret |= read_reg(ld, REG_DATA0_LSB, &d[2]); ld->sb.data_count += BYTES_PER_SAMPLE; + // Forward Zero Count + if (status & STATUS_ZERO_COUNT) + d[0] |= DATA_ERROR_ZERO_COUNT << 4; if (ret != I2C_BUS_SUCCESS || status & STATUS_I2C_ERROR) d[0] |= DATA_ERROR_I2C << 4; From 11319c06fa6b99f0281bd3c532e2a22894d95733 Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Fri, 19 Dec 2025 04:51:47 +0100 Subject: [PATCH 3/6] ldc1612: trigger error on high frequency Signed-off-by: Timofey Titovets --- src/sensor_ldc1612.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sensor_ldc1612.c b/src/sensor_ldc1612.c index 41c8476c4..e6ad093e4 100644 --- a/src/sensor_ldc1612.c +++ b/src/sensor_ldc1612.c @@ -120,14 +120,19 @@ DECL_COMMAND(command_query_ldc1612_home_state, static uint_fast8_t check_data_bits(struct ldc1612 *ld, uint32_t *data) { - if (*data < 0x0fffffff) + // Datasheet define valid frequency input as < F_ref / 4 + // Use half as sanity check + if (*data < 0x07ffffff) return 0; // Sensor reports an issue - cancel homing ld->homing_flags = 0; uint8_t error_bits = *data >> 28; uint8_t error_reason = ld->error_reason; + uint8_t freq_overflow = (*data & 0x0fffffff) > 0x7fffffff; if ((error_bits & DATA_ERROR_I2C) == DATA_ERROR_I2C) error_reason = ld->error_reason + DATA_ERROR_I2C; + else if (freq_overflow || error_bits & DATA_ERROR_OVER_RANGE) + error_reason = ld->error_reason + DATA_ERROR_OVER_RANGE; if ((error_bits & DATA_ERROR_ZERO_COUNT) == DATA_ERROR_ZERO_COUNT) error_reason = ld->error_reason + DATA_ERROR_ZERO_COUNT; trsync_do_trigger(ld->ts, error_reason); From 1e11777f1ea8748352856d0c0f6be90885a6628d Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Thu, 27 Nov 2025 00:54:00 +0100 Subject: [PATCH 4/6] ldc1612: ignore amplitude errors during homing Signed-off-by: Timofey Titovets --- src/sensor_ldc1612.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sensor_ldc1612.c b/src/sensor_ldc1612.c index e6ad093e4..384d89bb3 100644 --- a/src/sensor_ldc1612.c +++ b/src/sensor_ldc1612.c @@ -120,6 +120,8 @@ DECL_COMMAND(command_query_ldc1612_home_state, static uint_fast8_t check_data_bits(struct ldc1612 *ld, uint32_t *data) { + // Ignore amplitude errors + *data &= ~(DATA_ERROR_AMPLITUDE << 28); // Datasheet define valid frequency input as < F_ref / 4 // Use half as sanity check if (*data < 0x07ffffff) From e451c9fea2dcbadd5f056780a2243e9f8b6525d4 Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Thu, 27 Nov 2025 00:47:59 +0100 Subject: [PATCH 5/6] ldc1612: decode error flags Signed-off-by: Timofey Titovets --- klippy/extras/ldc1612.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/klippy/extras/ldc1612.py b/klippy/extras/ldc1612.py index 973556af1..575467191 100644 --- a/klippy/extras/ldc1612.py +++ b/klippy/extras/ldc1612.py @@ -157,9 +157,33 @@ class LDC1612: def _convert_samples(self, samples): freq_conv = float(self.frequency) / (1<<28) count = 0 + errors = {} + def log_once(msg, warning=False): + if errors.get(msg, False): + return + if warning: + logging.warning(msg) + else: + logging.error(msg) + errors[msg] = True for ptime, val in samples: mv = val & 0x0fffffff if mv != val: + flags = val >> 28 + if flags & (0x8 | 0x4) == (0x8 | 0x4): + log_once("LDC1612: I2C IO error") + else: + if flags & 0x4 or mv > 0x7ffffff: + log_once("LDC1612: Frequency over valid range") + if flags & 0x8 or mv == 0x0000000: + log_once("LDC1612: Frequency under valid range") + if flags & (0x2 | 0x1) == (0x2 | 0x1): + log_once("LDC1612: Zero conversion count") + else: + if flags & 0x2: + log_once("LDC1612: Conversion Watchdog timeout") + if flags & 0x1: + log_once("LDC1612: Amplitude Low/High") self.last_error_count += 1 samples[count] = (round(ptime, 6), round(freq_conv * mv, 3), 999.9) count += 1 From ee137d74b8c13f904160289716fb0d385a39b993 Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Mon, 22 Dec 2025 01:16:59 +0100 Subject: [PATCH 6/6] ldc1612: implement 25ms watchdog timeout Signed-off-by: Timofey Titovets --- klippy/extras/probe_eddy_current.py | 3 +++ src/sensor_ldc1612.c | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/klippy/extras/probe_eddy_current.py b/klippy/extras/probe_eddy_current.py index 465c9d2c6..5ba22a484 100644 --- a/klippy/extras/probe_eddy_current.py +++ b/klippy/extras/probe_eddy_current.py @@ -367,6 +367,7 @@ class EddyGatherSamples: class EddyDescend: REASON_SENSOR_ERROR = mcu.MCU_trsync.REASON_COMMS_TIMEOUT + 1 REASON_BUS_IO_ERROR = REASON_SENSOR_ERROR + 12 + REASON_WATCHDOG_TIMEOUT = REASON_SENSOR_ERROR + 15 def __init__(self, config, sensor_helper, calibration, param_helper): self._printer = config.get_printer() self._sensor_helper = sensor_helper @@ -401,6 +402,8 @@ class EddyDescend: "Communication timeout during homing") if res == self.REASON_BUS_IO_ERROR: raise self._printer.command_error("Eddy I2C IO error") + if res == self.REASON_WATCHDOG_TIMEOUT: + raise self._printer.command_error("Eddy watchdog timeout") raise self._printer.command_error("Eddy current sensor error") if res != mcu.MCU_trsync.REASON_ENDSTOP_HIT: return 0. diff --git a/src/sensor_ldc1612.c b/src/sensor_ldc1612.c index 384d89bb3..86ea56039 100644 --- a/src/sensor_ldc1612.c +++ b/src/sensor_ldc1612.c @@ -31,6 +31,7 @@ struct ldc1612 { struct trsync *ts; uint8_t homing_flags; uint8_t trigger_reason, error_reason; + uint32_t watchdog_deadline; uint32_t trigger_threshold; uint32_t homing_clock; }; @@ -111,6 +112,27 @@ command_query_ldc1612_home_state(uint32_t *args) DECL_COMMAND(command_query_ldc1612_home_state, "query_ldc1612_home_state oid=%c"); +// Default TRSYNC_TIMEOUT, limits ODR > 40 +#define WATCHDOG_TIMEOUT timer_from_us(25000) +#define WATCHDOG_TIMEOUT_ERROR (0xf) + +static void +watchdog_check(struct ldc1612 *ld) +{ + if (!ld->homing_flags) + return; + if (timer_is_before(timer_read_time(), ld->watchdog_deadline)) + return; + trsync_do_trigger(ld->ts, ld->error_reason + WATCHDOG_TIMEOUT_ERROR); + ld->homing_flags = 0; +} + +static void +watchdog_reset(struct ldc1612 *ld) +{ + ld->watchdog_deadline = timer_read_time() + WATCHDOG_TIMEOUT; +} + #define DATA_ERROR_AMPLITUDE (1 << 0) #define DATA_ERROR_WATCHDOG (1 << 1) #define DATA_ERROR_OVER_RANGE (1 << 2) @@ -203,6 +225,7 @@ ldc1612_query(struct ldc1612 *ld, uint8_t oid) irq_disable(); ld->flags &= ~LDC_PENDING; irq_enable(); + watchdog_check(ld); // Force data read on I2C error or Zero Count if (!(status & (STATUS_DRDY | STATUS_I2C_ERROR | STATUS_ZERO_COUNT))) return; @@ -225,6 +248,7 @@ ldc1612_query(struct ldc1612 *ld, uint8_t oid) | ((uint32_t)d[2] << 8) | ((uint32_t)d[3]); check_home(ld, data); + watchdog_reset(ld); // Flush local buffer if needed if (ld->sb.data_count + BYTES_PER_SAMPLE > ARRAY_SIZE(ld->sb.data)) @@ -248,6 +272,7 @@ command_query_ldc1612(uint32_t *args) irq_disable(); ld->timer.waketime = timer_read_time() + ld->rest_ticks; sched_add_timer(&ld->timer); + watchdog_reset(ld); irq_enable(); } DECL_COMMAND(command_query_ldc1612, "query_ldc1612 oid=%c rest_ticks=%u");