From f7ddb4003762f3f5baf4164a7125b3db582dab96 Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Sat, 8 Feb 2025 16:03:27 +0100 Subject: [PATCH] ldc1612: handle i2c errors I2C error means we don't know the sensor status. Force data output to the host and cancel homing. Signed-off-by: Timofey Titovets Signed-off-by: Kevin O'Connor --- klippy/extras/ldc1612.py | 19 ++++++ klippy/extras/probe_eddy_current.py | 4 +- src/sensor_ldc1612.c | 100 ++++++++++++++++++++++------ 3 files changed, 101 insertions(+), 22 deletions(-) diff --git a/klippy/extras/ldc1612.py b/klippy/extras/ldc1612.py index fea4eba9c..66099b9f8 100644 --- a/klippy/extras/ldc1612.py +++ b/klippy/extras/ldc1612.py @@ -84,6 +84,7 @@ class LDC1612: default_addr=LDC1612_ADDR, default_speed=400000) self.mcu = mcu = self.i2c.get_mcu() + self._sensor_errors = {} self.oid = oid = mcu.create_oid() self.query_ldc1612_cmd = None self.ldc1612_setup_home_cmd = self.query_ldc1612_home_state_cmd = None @@ -132,6 +133,8 @@ class LDC1612: "query_ldc1612_home_state oid=%c", "ldc1612_home_state oid=%c homing=%c trigger_clock=%u", oid=self.oid, cq=cmdqueue) + errors = self.mcu.get_enumerations().get("ldc1612_error:", {}) + self._sensor_errors = {v: k for k, v in errors.items()} def get_mcu(self): return self.i2c.get_mcu() def read_reg(self, reg): @@ -157,16 +160,32 @@ class LDC1612: params = self.query_ldc1612_home_state_cmd.send([self.oid]) tclock = self.mcu.clock32_to_clock64(params['trigger_clock']) return self.mcu.clock_to_print_time(tclock) + def lookup_sensor_error(self, error): + return self._sensor_errors.get(error, "Unknown ldc1612 error") # Measurement decoding def _convert_samples(self, samples): freq_conv = self.freq_conv count = 0 + errors = {} + def log_once(msg): + if not errors.get(msg, 0): + errors[msg] = 0 + errors[msg] += 1 for ptime, val in samples: mv = val & 0x0fffffff if mv != val: self.last_error_count += 1 + if (val >> 16 & 0xffff) == 0xffff: + # Encoded error from sensor_ldc1612.c + log_once(self.lookup_sensor_error(val & 0xffff)) + continue + error_bits = (val >> 28) & 0x0f + log_once("Sensor reports error (%s)" % (bin(error_bits),)) samples[count] = (round(ptime, 6), round(freq_conv * mv, 3), 999.9) count += 1 + del samples[count:] + for msg in errors: + logging.error("%s: %s (%d)" % (self.name, msg, errors[msg])) # Start, stop, and process message batches def _start_measurements(self): # In case of miswiring, testing LDC1612 device ID prevents treating diff --git a/klippy/extras/probe_eddy_current.py b/klippy/extras/probe_eddy_current.py index b5c4b2897..c7b415d02 100644 --- a/klippy/extras/probe_eddy_current.py +++ b/klippy/extras/probe_eddy_current.py @@ -415,7 +415,9 @@ class EddyDescend: if res == mcu.MCU_trsync.REASON_COMMS_TIMEOUT: raise self._printer.command_error( "Communication timeout during homing") - raise self._printer.command_error("Eddy current sensor error") + error_code = res - self.REASON_SENSOR_ERROR + error_msg = self._sensor_helper.lookup_sensor_error(error_code) + raise self._printer.command_error(error_msg) if res != mcu.MCU_trsync.REASON_ENDSTOP_HIT: return 0. if self._mcu.is_fileoutput(): diff --git a/src/sensor_ldc1612.c b/src/sensor_ldc1612.c index 8b67884f1..e5ab60e3b 100644 --- a/src/sensor_ldc1612.c +++ b/src/sensor_ldc1612.c @@ -37,6 +37,16 @@ struct ldc1612 { static struct task_wake ldc1612_wake; +// Internal errors transmitted in sample reports (or trsync error) +enum { + SE_SENSOR_ERROR, SE_I2C_STATUS, SE_I2C_DATA, SE_INVALID_DATA +}; + +DECL_ENUMERATION("ldc1612_error:", "SENSOR_REPORTS_ERROR", SE_SENSOR_ERROR); +DECL_ENUMERATION("ldc1612_error:", "I2C_STATUS_ERROR", SE_I2C_STATUS); +DECL_ENUMERATION("ldc1612_error:", "I2C_DATA_ERROR", SE_I2C_DATA); +DECL_ENUMERATION("ldc1612_error:", "INVALID_READ_DATA", SE_INVALID_DATA); + // Check if the intb line is "asserted" static int check_intb_asserted(struct ldc1612 *ld) @@ -111,19 +121,34 @@ command_query_ldc1612_home_state(uint32_t *args) DECL_COMMAND(command_query_ldc1612_home_state, "query_ldc1612_home_state oid=%c"); +// Cancel homing due to an error +static void +cancel_homing(struct ldc1612 *ld, int error_code) +{ + if (!(ld->homing_flags & LH_CAN_TRIGGER)) + return; + ld->homing_flags = 0; + trsync_do_trigger(ld->ts, ld->error_reason + error_code); +} + +static int +check_data_bits(struct ldc1612 *ld, uint32_t raw_data) { + if (raw_data < 0x0fffffff) + return 0; + cancel_homing(ld, SE_SENSOR_ERROR); + return -1; +} + // Check if a sample should trigger a homing event static void -check_home(struct ldc1612 *ld, uint32_t data) +check_home(struct ldc1612 *ld, uint32_t raw_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, raw_data)) return; - } + uint32_t data = raw_data & 0x0fffffff; uint32_t time = timer_read_time(); if ((homing_flags & LH_AWAIT_HOMING) && timer_is_before(time, ld->homing_clock)) @@ -143,41 +168,69 @@ 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); } // Read the status register on the ldc1612 -static uint16_t -read_reg_status(struct ldc1612 *ld) +static int +read_reg_status(struct ldc1612 *ld, uint16_t *status) { uint8_t data_status[2]; - read_reg(ld, REG_STATUS, data_status); - return (data_status[0] << 8) | data_status[1]; + int ret = read_reg(ld, REG_STATUS, data_status); + *status = (data_status[0] << 8) | data_status[1]; + return ret; } +#define STATUS_UNREADCONV0 (1 << 3) #define BYTES_PER_SAMPLE 4 +static void +report_sample_error(struct ldc1612 *ld, int error_code) +{ + cancel_homing(ld, error_code); + + uint8_t *d = &ld->sb.data[ld->sb.data_count]; + d[0] = 0xff; + d[1] = 0xff; + d[2] = 0; + d[3] = error_code; +} + // Query ldc1612 data static void ldc1612_query(struct ldc1612 *ld, uint8_t oid) { // Check if data available (and clear INTB line) - uint16_t status = read_reg_status(ld); + uint16_t status; + int ret = read_reg_status(ld, &status); irq_disable(); ld->flags &= ~LDC_PENDING; irq_enable(); - if (!(status & 0x08)) + if (ret) { + report_sample_error(ld, SE_I2C_STATUS); + goto out; + } + if (!(status & STATUS_UNREADCONV0)) + // No data available 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]); - ld->sb.data_count += BYTES_PER_SAMPLE; + ret |= read_reg(ld, REG_DATA0_MSB, &d[0]); + ret |= read_reg(ld, REG_DATA0_LSB, &d[2]); + + if (ret) { + report_sample_error(ld, SE_I2C_DATA); + goto out; + } + if (d[0] == 0xff && d[1] == 0xff) { + // Invalid data from sensor (conflict with internal error indicator) + report_sample_error(ld, SE_INVALID_DATA); + goto out; + } // Check for endstop trigger uint32_t data = ((uint32_t)d[0] << 24) @@ -185,7 +238,8 @@ ldc1612_query(struct ldc1612 *ld, uint8_t oid) | ((uint32_t)d[2] << 8) | ((uint32_t)d[3]); check_home(ld, data); - +out: + ld->sb.data_count += BYTES_PER_SAMPLE; // Flush local buffer if needed if (ld->sb.data_count + BYTES_PER_SAMPLE > ARRAY_SIZE(ld->sb.data)) sensor_bulk_report(&ld->sb, oid); @@ -228,11 +282,15 @@ command_query_status_ldc1612(uint32_t *args) } // Query sensor to see if a sample is pending + uint16_t status; uint32_t time1 = timer_read_time(); - uint16_t status = read_reg_status(ld); + int ret = read_reg_status(ld, &status); uint32_t time2 = timer_read_time(); - uint32_t fifo = status & 0x08 ? BYTES_PER_SAMPLE : 0; + if (ret) + // Query error - don't send response - host will retry + return; + uint32_t fifo = status & STATUS_UNREADCONV0 ? 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");