# `LIS3DH.Config`

Encoding and decoding of the LIS3DH's `CTRL_REG1` (0x20) and `CTRL_REG4`
(0x23) bytes, plus the per-mode/per-range sensitivity table used for
converting raw ADC samples into physical units.

Bit layouts:

```text
CTRL_REG1 (0x20)
| 7 6 5 4 | 3    | 2   1   0   |
|   ODR   | LPen | Zen Yen Xen |

CTRL_REG4 (0x23)
| 7   | 6   | 5 4 | 3  | 2  1 | 0   |
| BDU | BLE | FS  | HR |  ST  | SIM |
```

Operating mode is set by the combination of `CTRL_REG1.LPen` and
`CTRL_REG4.HR`:

```text
LPen  HR  Mode               Data width
 1    0   Low-power           8-bit
 0    0   Normal              10-bit
 0    1  High-resolution     12-bit
 1    1   (Not allowed)
```

All `OUT_*` data is signed 16-bit two's complement **left-justified** — the
meaningful bits occupy the MSB end and the lower bits are zero. Use
`sensitivity/2` to obtain the per-mode mg/LSB conversion factor.

# `axis`

```elixir
@type axis() :: :x | :y | :z
```

Axes to enable in `CTRL_REG1`.

# `bdu`

```elixir
@type bdu() :: :continuous | :hold
```

Block-data-update mode for `CTRL_REG4.BDU`.

# `hpf_cutoff`

```elixir
@type hpf_cutoff() :: 0..3
```

High-pass filter cutoff selector. The actual −3 dB cutoff depends on ODR; lower codes give higher cutoffs.

# `hpf_mode`

```elixir
@type hpf_mode() :: :normal_with_reset | :reference | :normal | :autoreset
```

High-pass filter mode for `CTRL_REG2.HPM`.

  * `:normal_with_reset` — continuous HPF; the internal state can be reset
    by reading the `REFERENCE` register.
  * `:reference` — HPF uses the `REFERENCE` register as the filtered
    reference signal.
  * `:normal` — continuous HPF with no reset hook.
  * `:autoreset` — HPF auto-resets on every interrupt event.

# `odr`

```elixir
@type odr() :: :power_down | 1 | 10 | 25 | 50 | 100 | 200 | 400 | 1344 | 1600 | 5376
```

Output data rate in Hz, or `:power_down` to disable the sensor.

# `operating_mode`

```elixir
@type operating_mode() :: :low_power | :normal | :high_resolution
```

Operating mode. Selects the ADC bit width and the LPen / HR bit
combination.

# `range`

```elixir
@type range() :: 2 | 4 | 8 | 16
```

Full-scale measurement range in g.

# `self_test_mode`

```elixir
@type self_test_mode() :: :off | :self_test_0 | :self_test_1
```

Self-test mode selection for `CTRL_REG4.ST`.

# `aux_adc_width`

```elixir
@spec aux_adc_width(operating_mode()) :: 8 | 10
```

Returns the bit width of the auxiliary ADC for the given operating mode.

Per datasheet §3.7, the auxiliary ADC is 8-bit when `LPen=1` (low-power)
and 10-bit otherwise — including in high-resolution mode, even though the
accelerometer itself is 12-bit there.

# `decode_ctrl_reg_1`

```elixir
@spec decode_ctrl_reg_1(&lt;&lt;_::8&gt;&gt;) :: %{lpen: boolean(), odr: odr(), axes: [axis()]}
```

Decode a `CTRL_REG1` byte into a map of its fields.

Note that the `LPen` bit alone doesn't fully determine the operating mode —
the `HR` bit in `CTRL_REG4` is also needed. This function reports `:lpen`
as a boolean; combine it with `decode_ctrl_reg_4/1` to recover the full
`t:operating_mode/0`.

# `decode_ctrl_reg_2`

```elixir
@spec decode_ctrl_reg_2(&lt;&lt;_::8&gt;&gt;) :: %{
  mode: hpf_mode(),
  cutoff: hpf_cutoff(),
  filtered_data_output: boolean(),
  enable_for_click: boolean(),
  enable_for_interrupt_1: boolean(),
  enable_for_interrupt_2: boolean()
}
```

Decode a `CTRL_REG2` byte into a map of its fields.

# `decode_ctrl_reg_4`

```elixir
@spec decode_ctrl_reg_4(&lt;&lt;_::8&gt;&gt;) :: %{
  hr: boolean(),
  range: range(),
  block_data_update: bdu()
}
```

Decode a `CTRL_REG4` byte into a map of its fields.

# `encode_ctrl_reg_1`

```elixir
@spec encode_ctrl_reg_1(keyword()) :: &lt;&lt;_::8&gt;&gt;
```

Encode a `CTRL_REG1` byte from keyword options.

## Options

  * `:mode` — `t:operating_mode/0` (required). Sets the `LPen` bit.
  * `:odr` — `t:odr/0` (required).
  * `:axes` — list of `t:axis/0` to enable (default `[:x, :y, :z]`).

# `encode_ctrl_reg_2`

```elixir
@spec encode_ctrl_reg_2(keyword()) :: &lt;&lt;_::8&gt;&gt;
```

Encode a `CTRL_REG2` byte (high-pass filter configuration) from keyword
options.

## Options

  * `:mode` — `t:hpf_mode/0` (default `:normal_with_reset`).
  * `:cutoff` — `t:hpf_cutoff/0` (default `0`). The actual −3 dB cutoff
    depends on ODR per the datasheet figures.
  * `:filtered_data_output` (default `false`) — when `true`, the HPF output
    replaces the unfiltered data in `OUT_*` and the FIFO. When `false`
    (`FDS=0`) the HPF only affects the click / interrupt detectors.
  * `:enable_for_click` (default `false`) — apply HPF to the click
    detector.
  * `:enable_for_interrupt_1` (default `false`) — apply HPF to inertial
    interrupt 1 (AOI 1).
  * `:enable_for_interrupt_2` (default `false`) — apply HPF to inertial
    interrupt 2 (AOI 2).

# `encode_ctrl_reg_4`

```elixir
@spec encode_ctrl_reg_4(keyword()) :: &lt;&lt;_::8&gt;&gt;
```

Encode a `CTRL_REG4` byte from keyword options. Leaves `BLE`, `ST`, and `SIM`
at their reset values; callers that need to override them should compose
the resulting binary with their own bit twiddling.

## Options

  * `:mode` — `t:operating_mode/0` (required). Sets the `HR` bit.
  * `:range` — `t:range/0` (default `2`).
  * `:block_data_update` — `t:bdu/0` (default `:hold`, recommended to avoid
    reading LSB/MSB pairs from different samples).

# `native_width`

```elixir
@spec native_width(operating_mode()) :: 8 | 10 | 12
```

Returns the native accelerometer ADC bit width for the given operating
mode — equivalent to the right-shift required to recover the N-bit signed
value from the 16-bit left-justified `OUT_*` registers.

# `operating_mode`

```elixir
@spec operating_mode(boolean(), boolean()) :: operating_mode()
```

Resolve the operating mode from the `LPen` and `HR` bits returned by
`decode_ctrl_reg_1/1` and `decode_ctrl_reg_4/1`.

Raises `ArgumentError` for the disallowed combination LPen=1, HR=1.

# `self_test_code`

```elixir
@spec self_test_code(self_test_mode()) :: 0..2
```

Encode a self-test mode atom to its 2-bit `CTRL_REG4.ST` code.

# `sensitivity`

```elixir
@spec sensitivity(operating_mode(), range()) :: pos_integer()
```

Returns the per-LSB sensitivity in milli-g at the native bit width for the
given operating mode and range.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
