mirror of
https://github.com/Klipper3d/klipper.git
synced 2026-02-08 09:10:56 -07:00
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 <nefelim4ag@gmail.com> Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
e605fd1856
commit
f7ddb40037
3 changed files with 101 additions and 22 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue