Skip to content

Commit

Permalink
tla2518: implement support
Browse files Browse the repository at this point in the history
  • Loading branch information
dalegaard committed Dec 6, 2023
1 parent a997aa5 commit 91d8836
Show file tree
Hide file tree
Showing 4 changed files with 356 additions and 8 deletions.
52 changes: 45 additions & 7 deletions docs/Config_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,11 @@ pins such as "extra_mcu:ar9" may then be used elsewhere in the config
[mcu my_extra_mcu]
# See the "mcu" section for configuration parameters.
```

## ⚠️ Danger Options

A collection of DangerKlipper-specific system options

```
[danger_options]
Expand Down Expand Up @@ -360,7 +363,7 @@ example deltesian kinematics config file.

Only parameters specific to deltesian printers are described here - see
[common kinematic settings](#common-kinematic-settings) for available
parameters.
parameters.

```
[printer]
Expand Down Expand Up @@ -909,6 +912,7 @@ See the [bed mesh guide](Bed_Mesh.md) and
[command reference](G-Codes.md#bed_mesh) for additional information.

Visual Examples:

```
rectangular bed, probe_count = 3, 3:
x---x---x (max_point)
Expand Down Expand Up @@ -1229,6 +1233,7 @@ WARNING: Using this on a moving bed may lead to undesirable results.
If this section is present then a QUAD_GANTRY_LEVEL extended G-Code
command becomes available. This routine assumes the following Z motor
configuration:

```
----------------
|Z1 Z2|
Expand All @@ -1239,6 +1244,7 @@ configuration:
|Z Z3|
----------------
```

Where x is the 0, 0 point on the bed

```
Expand Down Expand Up @@ -1647,6 +1653,7 @@ Enable the "M118" and "RESPOND" extended
```

### [exclude_object]

Enables support to exclude or cancel individual objects during the printing
process.

Expand Down Expand Up @@ -1863,7 +1870,7 @@ aliases_<name>:

Include file support. One may include additional config file from the
main printer config file. Wildcards may also be used (eg,
"configs/*.cfg").
"configs/\*.cfg").

```
[include my_other_config.cfg]
Expand Down Expand Up @@ -4716,6 +4723,37 @@ i2c_address:
# above parameters.
```

### [tla2518]

Configure a TLA2518 8-channel 12-bit ADC. These can be used as `sensor pin`s
within `[temperature_sensor]` blocks. Any number of TLA2518 chips can be
interfaced. Each TLA2518 provides 8 analog inputs, named `adc0` through `adc7`.
The chip name will be `tla2518_<name>`.

```
[tla2518 my_tla2518]
cs_pin:
# The pin corresponding to the TLA2518 chip select line. This pin
# will be set to low at the start of SPI messages and raised to high
# after the message completes. This parameter must be provided.
#spi_speed:
#spi_bus:
#spi_software_sclk_pin:
#spi_software_mosi_pin:
#spi_software_miso_pin:
# See the "common SPI settings" section for a description of the
# above parameters.
```

A configured TLA2518 can be used as a temperature sensor input, e.g.:

```
[temperature_sensor chamber]
sensor_type: PT1000
sensor_pin: tla2518_my_tla2518:adc3
pullup_resistor: 2200
```

### [samd_sercom]

SAMD SERCOM configuration to specify which pins to use on a given
Expand Down Expand Up @@ -4852,8 +4890,8 @@ Octoprint as they will conflict, and 1 will fail to initialize
properly likely aborting your print.

If you use Octoprint and stream gcode over the serial port instead of
printing from virtual_sd, then remo **M1** and **M0** from *Pausing commands*
in *Settings > Serial Connection > Firmware & protocol* will prevent
printing from virtual_sd, then remo **M1** and **M0** from _Pausing commands_
in _Settings > Serial Connection > Firmware & protocol_ will prevent
the need to start print on the Palette 2 and unpausing in Octoprint
for your print to begin.

Expand All @@ -4876,7 +4914,7 @@ serial:
### [angle]

Magnetic hall angle sensor support for reading stepper motor angle
shaft measurements using a1333, as5047d, or tle5012b SPI chips. The
shaft measurements using a1333, as5047d, or tle5012b SPI chips. The
measurements are available via the [API Server](API_Server.md) and
[motion analysis tool](Debugging.md#motion-analysis-and-data-logging).
See the [G-Code reference](G-Codes.md#angle) for available commands.
Expand Down Expand Up @@ -4944,8 +4982,8 @@ It is generally recommended to only use I2C devices that are on the
same printed circuit board as the micro-controller.

Most Klipper micro-controller implementations only support an
`i2c_speed` of 100000 (*standard mode*, 100kbit/s). The Klipper "Linux"
micro-controller supports a 400000 speed (*fast mode*, 400kbit/s), but it must be
`i2c_speed` of 100000 (_standard mode_, 100kbit/s). The Klipper "Linux"
micro-controller supports a 400000 speed (_fast mode_, 400kbit/s), but it must be
[set in the operating system](RPi_microcontroller.md#optional-enabling-i2c)
and the `i2c_speed` parameter is otherwise ignored. The Klipper
"RP2040" micro-controller and ATmega AVR family support a rate of 400000
Expand Down
180 changes: 180 additions & 0 deletions klippy/extras/tla2518.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# TLA2518 8-channel 12-bit ADC support
#
# Copyright (C) 2023 Lasse Dalegaard <[email protected]>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
from . import bus

TLA2518_CHANNEL_COUNT = 8
REPORT_TIME = 0.1
SAMPLE_TIME = 128 / 1e6


class TLA2518:
def __init__(self, config):
self.printer = config.get_printer()
self.ppins = config.get_printer().load_object(config, "pins")
self.config_error = config.error
self.reactor = self.printer.get_reactor()
self.name = " ".join(config.get_name().split()[1:])
self.spi = bus.MCU_SPI_from_config(config, 0, default_speed=5000000)
self.mcu = self.spi.get_mcu()
self.mcu.register_config_callback(self._build_config)
self.oid = self.mcu.create_oid()
self.mcu.register_response(
self._handle_response, "tla2518_result", self.oid
)
self.printer.register_event_handler(
"klippy:connect", self._handle_connect
)

self.channels = [None for _ in range(TLA2518_CHANNEL_COUNT)]

def _build_config(self):
channels = 0
for channel in self.channels:
if channel is not None:
channels |= channel.bit

self.adc_channel_mask = channels
if channels != 0:
self.mcu.add_config_cmd(
"config_tla2518 oid=%u spi_oid=%u channels=%u"
% (self.oid, self.spi.get_oid(), channels)
)

def _chip_reset(self):
self._reg_write(0x01, 0x1) # Write RST to GENERAL_CFG

# Wait for reset
timeout = self.reactor.monotonic() + 5.0
while True:
if self._reg_read(0x01) & 0x01 == 0:
break
now = self.reactor.monotonic()
if now > timeout:
msg = f"Timeout trying to reset TLA2518 {self.name}"
self.printer.invoke_shutdown(msg)
raise
self.reactor.pause(now + 0.1)
if self._reg_read(0x00) != 0x81:
msg = f"TLA2518 '{self.name}' could not be reset"
self.printer.invoke_shutdown(msg)
raise

def _handle_connect(self):
if self.adc_channel_mask == 0:
return

self._chip_reset()
# DATA_CFG: APPEND_STATUS = 01b, include channel ID
self._reg_write(0x2, 1 << 4)
# OSR_CFG: OSR_CFG = 0111b, 128 sample oversampling
self._reg_write(0x3, 0x7)
# SEQUENCE_CFG: SEQ_MODE=10b, on the fly mode
self._reg_write(0x10, 0b10)
clock = self.mcu.get_query_slot(self.oid)
cmd = self.mcu.lookup_command(
"query_tla2518 oid=%c clock=%u rest_ticks=%u sample_ticks=%u"
)
cmd.send(
[
self.oid,
clock,
self.mcu.seconds_to_clock(REPORT_TIME),
self.mcu.seconds_to_clock(SAMPLE_TIME),
]
)

def _handle_response(self, params):
idx = params["channel"]
if idx >= len(self.channels):
return
channel = self.channels[idx]
if channel is not None:
channel._handle_response(params)

def _reg_write(self, addr, value):
self.spi.spi_send([0x08, addr, value])

def _reg_read(self, addr):
params = self.spi.spi_transfer_with_preface(
[0x10, addr, 0x00],
[0x00, 0x00, 0x00],
)
return params["response"][0]

def _register_channel(self, channel, output):
orig = channel
if channel.startswith("adc"):
channel = channel[3:]
try:
channel = int(channel)
except ValueError:
raise self.config_error(f"invalid TLA2518 channel '{orig}'")
if channel < 0 or channel >= TLA2518_CHANNEL_COUNT:
raise self.config_error(
f"invalid TLA2518 channel '{orig}', valid range 0 to "
f"{TLA2518_CHANNEL_COUNT-1}"
)
cur = self.channels[channel]
if cur is None:
cur = self.channels[channel] = TLA2518Channel(self, channel)
cur._register_handler(output)
return cur

# Pins interface

def setup_pin(self, type, params):
if type != "adc":
raise self.ppins.error(
"pin type %s not supported on TLA2518" % (type,)
)
return TLA2518ADC(self, params)


class TLA2518Channel:
def __init__(self, chip, idx):
self.idx = idx
self.bit = 1 << idx
self.handlers = []

def _register_handler(self, output):
self.handlers.append(output)

def _handle_response(self, params):
for handler in self.handlers:
handler._handle_response(params)


class TLA2518ADC:
def __init__(self, chip, params):
self.channel = chip._register_channel(params["pin"], self)
self.chip = chip
self._callback = None

def _handle_response(self, params):
if self._callback:
read_time = self.chip.mcu.clock_to_print_time(params["clock"])
self._callback(read_time, params["value"] / 65535)

# MCU_adc interface

def setup_adc_callback(self, _report_time, callback):
self._callback = callback

def setup_minmax(
self,
sample_time,
sample_count,
minval=0.0,
maxval=1.0,
range_check_count=0,
):
pass


def load_config_prefix(config):
obj = TLA2518(config)
chip_name = "tla2518_" + obj.name
obj.ppins.register_chip(chip_name, obj)
2 changes: 1 addition & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ src-$(CONFIG_WANT_DISPLAYS) += lcd_st7920.c lcd_hd44780.c
src-$(CONFIG_WANT_SOFTWARE_SPI) += spi_software.c
src-$(CONFIG_WANT_SOFTWARE_I2C) += i2c_software.c
sensors-src-$(CONFIG_HAVE_GPIO_SPI) := thermocouple.c sensor_adxl345.c \
sensor_angle.c
sensor_angle.c tla2518.c
src-$(CONFIG_WANT_LIS2DW) += sensor_lis2dw.c
sensors-src-$(CONFIG_HAVE_GPIO_I2C) += sensor_mpu9250.c
src-$(CONFIG_WANT_SENSORS) += $(sensors-src-y)
Loading

0 comments on commit 91d8836

Please sign in to comment.