TMF882X

Build StatusHex.pmDocumentation

This library interfaces with TMF8820 (and theoretically TMF8821) multi-zone Time-of-Flight (ToF) sensors. From the manufacturer:

The TMF8820 is a direct time-of-flight (dToF) sensor in a single modular package with associated VCSEL. The dToF device is based on SPAD, TDC and histogram technology and achieves 5000 mm detection range. Due to its lens on the SPAD, it supports 3x3 multizone output data and a wide, dynamically adjustable, field of view. A multi-lens-array (MLA) inside the package above the VCSEL widens up the FoI (field of illumination). All processing of the raw data is performed on-chip and the TMF8820 provides distance information together with confidence values on its I2C interface.

A breakout board is available from SparkFun among others.

SparkFun TMF8820 Breakout Board

Details about the device and many of the configuration options can be found in the appropriate datasheet

Installation

Add tmf882x to your list of dependencies in mix.exs:

def deps do
  [ {:tmf882x, "~> 0.1"} ]
end

Usage

Start a TMF882X it directly from another process (See Options section below for more info):

{:ok, pid} = TMF882X.start_link(bus: "i2c-1")

The TMF882X process will handle the initialization and initial configuration of the device. You can check the status of the device and application.

iex> TMF882X.app_ready?(pid)
true

iex> TMF882X.running?(pid)
true

Configuration

A configuration keyword list can be passed to the start_link function containing the following parameters:

Results

The calling process will receive messages of the format {:tmf882x, %TMF882X.Result{}}:

def handle_info({:tmf882x, %TMF882X.Result{} = result}) do
  ...
end

The TMF882X.Result struct contains a list of measurements from each channel. Each measurement is a tuple containing a distance (in millimeters) and confidence (out of 255) value:

%TMF882X.Result{
  tid: 200,
  size: 128,
  number: 200,
  temperature: 41,
  valid_results: 11,
  ambient: 283,
  photon_count: 16971,
  reference_count: 60573,
  sys_tick: 1215866837,
  measurements: [
    {844, 61},
    {841, 106},
    {1010, 56},
    ...
  ]
}

Other fields in the Result struct are directly from the decode of the Result register.

Device Configuration

Each configuration item specified in the configuration register is represented and can be read, though only some are currently implemented to be written to the device (period and spad_map_id).

iex> TMF8820.get_config(pid)
%{
  alg_setting_0: %{distances: true, logarithmic_confidence: false},
  confidence_threshold: 6,
  gpio_1: %{driver_strength: 0, gpio: 0, pre_delay: 0},
  gpio_2: %{driver_strength: 0, gpio: 0, pre_delay: 0},
  hist_dump: false,
  i2c_addr_change: 0,
  i2c_slave_address: 65,
  int_persistence: 0,
  int_threshold_high: 65535,
  int_threshold_low: 0,
  int_zone_mask_0: 0,
  int_zone_mask_1: 0,
  int_zone_mask_2: 0,
  kilo_iterations: 537,
  osc_trim_value: 508,
  period: 33,
  power_cfg: %{
    allow_osc_retrim: false,
    goto_standby_timed: false,
    keep_pll_running: false,
    low_power_osc_on: false,
    pulse_interrupt: false
  },
  spad_map_id: 1
}

iex> TMF8820.apply_config(%{spad_map_id: 3})
:ok

SPAD Configuration

The TMF8820 and TMF8821 come with a set of built-in SPAD maps. Drawings and considerations for these can be found in the datasheet. Each of these devices also supports a custom SPAD map that can be written to the device at runtime and selected by setting the spad_map_id to 14.

Creating a custom SPAD map is not a trivial task and requires following a set of rules specified in the datasheet. There are a few SPAD map examples in the SPAD.md file in this repo.

custom_spad = %{
      x_offset: 0,
      y_offset: 0,
      x_size: 14,
      y_size: 6,
      mask: [
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
      ],
      map: [
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
      ]
    }

# You must change the spat_map_id before setting the custom spad
:ok = TMF882X.apply_config(pid, %{spad_map_id: 14})
:ok = TMF882X.set_spad(pid, custom_spad)

Future Work