From 200b62bcb4ea37e27e533d0d5d87c9c4deefe9ac Mon Sep 17 00:00:00 2001 From: Dmitry Butyugin Date: Fri, 26 Mar 2021 19:32:32 +0100 Subject: [PATCH] adxl345: Verify the register writes, added debug adxl345 commands Signed-off-by: Dmitry Butyugin --- docs/G-Codes.md | 7 +++ klippy/extras/adxl345.py | 97 +++++++++++++++++++++++++++++++--------- 2 files changed, 83 insertions(+), 21 deletions(-) diff --git a/docs/G-Codes.md b/docs/G-Codes.md index 70af24ef..b8b96636 100644 --- a/docs/G-Codes.md +++ b/docs/G-Codes.md @@ -717,6 +717,13 @@ The following commands are available when an is used. This command is useful to test the connection to the ADXL345 accelerometer: one of the returned values should be a free-fall acceleration (+/- some noise of the chip). +- `ADXL345_DEBUG_READ [CHIP=] REG=`: queries + ADXL345 register (e.g. 44 or 0x2C). Can be useful for + debugging purposes. +- `ADXL345_DEBUG_WRITE [CHIP=] REG= VAL=`: + writes raw into a register . Both and + can be a decimal or a hexadecimal integer. Use with care, + and refer to ADXL345 data sheet for the reference. ## Resonance Testing Commands diff --git a/klippy/extras/adxl345.py b/klippy/extras/adxl345.py index 7bf1ab59..5753a088 100644 --- a/klippy/extras/adxl345.py +++ b/klippy/extras/adxl345.py @@ -20,7 +20,10 @@ QUERY_RATES = { 800: 0xd, 1600: 0xe, 3200: 0xf, } -SCALE = 0.004 * 9.80665 * 1000. # 4mg/LSB * Earth gravity in mm/s**2 +ADXL345_DEV_ID = 0xe5 + +FREEFALL_ACCEL = 9.80665 * 1000. +SCALE = 0.0039 * FREEFALL_ACCEL # 3.9mg/LSB * Earth gravity in mm/s**2 Accel_Measurement = collections.namedtuple( 'Accel_Measurement', ('time', 'accel_x', 'accel_y', 'accel_z')) @@ -93,7 +96,7 @@ class ADXL345Results: write_proc.daemon = True write_proc.start() -# Printer class that controls measurments +# Printer class that controls ADXL345 chip class ADXL345: def __init__(self, config): self.printer = config.get_printer() @@ -135,11 +138,36 @@ class ADXL345: gcode.register_mux_command("ACCELEROMETER_QUERY", "CHIP", self.name, self.cmd_ACCELEROMETER_QUERY, desc=self.cmd_ACCELEROMETER_QUERY_help) + gcode.register_mux_command("ADXL345_DEBUG_READ", "CHIP", self.name, + self.cmd_ADXL345_DEBUG_READ, + desc=self.cmd_ADXL345_DEBUG_READ_help) + gcode.register_mux_command("ADXL345_DEBUG_WRITE", "CHIP", self.name, + self.cmd_ADXL345_DEBUG_WRITE, + desc=self.cmd_ADXL345_DEBUG_WRITE_help) if self.name == "default": gcode.register_mux_command("ACCELEROMETER_MEASURE", "CHIP", None, self.cmd_ACCELEROMETER_MEASURE) gcode.register_mux_command("ACCELEROMETER_QUERY", "CHIP", None, self.cmd_ACCELEROMETER_QUERY) + gcode.register_mux_command("ADXL345_DEBUG_READ", "CHIP", None, + self.cmd_ADXL345_DEBUG_READ, + desc=self.cmd_ADXL345_DEBUG_READ_help) + gcode.register_mux_command("ADXL345_DEBUG_WRITE", "CHIP", None, + self.cmd_ADXL345_DEBUG_WRITE, + desc=self.cmd_ADXL345_DEBUG_WRITE_help) + def is_initialized(self): + # In case of miswiring, testing ADXL345 device ID prevents treating + # noise or wrong signal as a correctly initialized device + return (self.read_reg(REG_DEVID) == ADXL345_DEV_ID and + (self.read_reg(REG_DATA_FORMAT) & 0xB) != 0) + def initialize(self): + # Setup ADXL345 parameters and verify chip connectivity + self.set_reg(REG_POWER_CTL, 0x00) + dev_id = self.read_reg(REG_DEVID) + if dev_id != ADXL345_DEV_ID: + raise self.printer.command_error("Invalid adxl345 id (got %x vs %x)" + % (dev_id, ADXL345_DEV_ID)) + self.set_reg(REG_DATA_FORMAT, 0x0B) def _build_config(self): self.query_adxl345_cmd = self.mcu.lookup_command( "query_adxl345 oid=%c clock=%u rest_ticks=%u", @@ -170,23 +198,35 @@ class ADXL345: if sequence < self.last_sequence: sequence += 0x10000 return sequence - def start_measurements(self, rate=None): - rate = rate or self.data_rate - # Verify chip connectivity - params = self.spi.spi_transfer([REG_DEVID | REG_MOD_READ, 0x00]) + def read_reg(self, reg): + params = self.spi.spi_transfer([reg | REG_MOD_READ, 0x00]) response = bytearray(params['response']) - if response[1] != 0xe5: - raise self.printer.command_error("Invalid adxl345 id (got %x vs %x)" - % (response[1], 0xe5)) + return response[1] + def set_reg(self, reg, val, minclock=0): + self.spi.spi_send([reg, val & 0xFF], minclock=minclock) + stored_val = self.read_reg(reg) + if stored_val != val: + raise self.printer.command_error( + "Failed to set ADXL345 register [0x%x] to 0x%x: got 0x%x. " + "This is generally indicative of connection problems " + "(e.g. faulty wiring) or a faulty adxl345 chip." % ( + reg, val, stored_val)) + def is_measuring(self): + return self.query_rate > 0 + def start_measurements(self, rate=None): + if self.is_measuring(): + return + rate = rate or self.data_rate + if not self.is_initialized(): + self.initialize() # Setup chip in requested query rate clock = 0 if self.last_tx_time: clock = self.mcu.print_time_to_clock(self.last_tx_time) - self.spi.spi_send([REG_POWER_CTL, 0x00], minclock=clock) - self.spi.spi_send([REG_FIFO_CTL, 0x00]) - self.spi.spi_send([REG_DATA_FORMAT, 0x0B]) - self.spi.spi_send([REG_BW_RATE, QUERY_RATES[rate]]) - self.spi.spi_send([REG_FIFO_CTL, 0x80]) + self.set_reg(REG_POWER_CTL, 0x00, minclock=clock) + self.set_reg(REG_FIFO_CTL, 0x00) + self.set_reg(REG_BW_RATE, QUERY_RATES[rate]) + self.set_reg(REG_FIFO_CTL, 0x80) # Setup samples print_time = self.printer.lookup_object('toolhead').get_last_move_time() self.raw_samples = [] @@ -200,8 +240,7 @@ class ADXL345: self.query_adxl345_cmd.send([self.oid, reqclock, rest_ticks], reqclock=reqclock) def finish_measurements(self): - query_rate = self.query_rate - if not query_rate: + if not self.is_measuring(): return ADXL345Results() # Halt bulk reading print_time = self.printer.lookup_object('toolhead').get_last_move_time() @@ -224,8 +263,8 @@ class ADXL345: logging.info("ADXL345 finished %d measurements: %s", res.total_count, res.get_stats()) return res - def end_query(self, name): - if not self.query_rate: + def end_query(self, name, gcmd): + if not self.is_measuring(): return res = self.finish_measurements() # Write data to file @@ -234,13 +273,15 @@ class ADXL345: else: filename = "/tmp/adxl345-%s-%s.csv" % (self.name, name,) res.write_to_file(filename) + gcmd.respond_info( + "Writing raw accelerometer data to %s file" % (filename,)) cmd_ACCELEROMETER_MEASURE_help = "Start/stop accelerometer" def cmd_ACCELEROMETER_MEASURE(self, gcmd): - if self.query_rate: + if self.is_measuring(): name = gcmd.get("NAME", time.strftime("%Y%m%d_%H%M%S")) if not name.replace('-', '').replace('_', '').isalnum(): raise gcmd.error("Invalid adxl345 NAME parameter") - self.end_query(name) + self.end_query(name, gcmd) gcmd.respond_info("adxl345 measurements stopped") else: rate = gcmd.get_int("RATE", self.data_rate) @@ -250,7 +291,7 @@ class ADXL345: gcmd.respond_info("adxl345 measurements started") cmd_ACCELEROMETER_QUERY_help = "Query accelerometer for the current values" def cmd_ACCELEROMETER_QUERY(self, gcmd): - if self.query_rate: + if self.is_measuring(): raise gcmd.error("adxl345 measurements in progress") self.start_measurements() reactor = self.printer.get_reactor() @@ -266,6 +307,20 @@ class ADXL345: _, accel_x, accel_y, accel_z = values[-1] gcmd.respond_info("adxl345 values (x, y, z): %.6f, %.6f, %.6f" % ( accel_x, accel_y, accel_z)) + cmd_ADXL345_DEBUG_READ_help = "Query accelerometer register (for debugging)" + def cmd_ADXL345_DEBUG_READ(self, gcmd): + if self.is_measuring(): + raise gcmd.error("adxl345 measurements in progress") + reg = gcmd.get("REG", minval=29, maxval=57, parser=lambda x: int(x, 0)) + val = self.read_reg(reg) + gcmd.respond_info("ADXL345 REG[0x%x] = 0x%x" % (reg, val)) + cmd_ADXL345_DEBUG_WRITE_help = "Set accelerometer register (for debugging)" + def cmd_ADXL345_DEBUG_WRITE(self, gcmd): + if self.is_measuring(): + raise gcmd.error("adxl345 measurements in progress") + reg = gcmd.get("REG", minval=29, maxval=57, parser=lambda x: int(x, 0)) + val = gcmd.get("VAL", minval=0, maxval=255, parser=lambda x: int(x, 0)) + self.set_reg(reg, val) def load_config(config): return ADXL345(config)