ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
vl53l0x.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file vl53l0x.h
6 * @brief ST VL53L0X / VL53L1X optical time-of-flight ranging codec (PROTOCORE_ENABLE_VL53L0X).
7 *
8 * The VL53L0X emits an infrared pulse and times the round-trip to a target, reporting distance in
9 * millimeters - contactless ranging and gesture, bridged to the same telemetry sink as the other field
10 * sensors. Its documented register interface is small: check IDENTIFICATION_MODEL_ID (0xC0 == 0xEE),
11 * start ranging via SYSRANGE_START, poll RESULT_INTERRUPT_STATUS for data-ready, read the 16-bit range
12 * from RESULT_RANGE_STATUS + 10, then clear the interrupt.
13 *
14 * This codec is pure and host-tested: ::protocore_vl53l0x_range_mm combines the range register pair,
15 * ::protocore_vl53l0x_data_ready decodes the interrupt-status byte, and ::protocore_vl53l0x_range_valid checks the
16 * device range-status field. On an ESP32 the binding runs the ranging loop over I2C (Wire); only that touches hardware.
17 * Note: ST's optional tuning blob (for best accuracy) is not applied - default-settings ranging via the documented
18 * registers.
19 */
20
21#ifndef PROTOCORE_VL53L0X_H
22#define PROTOCORE_VL53L0X_H
23
24#include "protocore_config.h" // the entry point: protocore_types.h for the widths
25
26#if PROTOCORE_ENABLE_VL53L0X
27
29
30// PROTOCORE_I2C_DEVICE_BORROW - the bytes this module runs out of - is stated in protocore_config.h, which sums
31// it into its arena. A caller takes them once and passes the pointer to every call. How they
32// are carved is this module's and is never named here.
33
34#define VL53L0X_REG_SYSRANGE_START 0x00
35
36#define VL53L0X_REG_SYSTEM_INTERRUPT_CLEAR 0x0B
37
38#define VL53L0X_REG_RESULT_INTERRUPT_STATUS 0x13
39
40#define VL53L0X_REG_RESULT_RANGE_STATUS 0x14
41
42#define VL53L0X_REG_IDENTIFICATION_MODEL_ID 0xC0
43
44#define VL53L0X_MODEL_ID 0xEE ///< IDENTIFICATION_MODEL_ID for the VL53L0X.
45
46#define VL53L0X_RANGE_VALID 11 ///< DeviceRangeStatus value that means a valid measurement.
47
48/** @brief What range_mm takes: hi, lo. */
49typedef struct
50{
51 uint8_t hi;
52 uint8_t lo;
53} Vl53l0xRangeMmArgs;
54
55/** @brief What data_ready takes: interrupt_status. */
56typedef struct
57{
58 uint8_t interrupt_status;
59} Vl53l0xDataReadyArgs;
60
61/** @brief What range_status takes: range_status_reg. */
62typedef struct
63{
64 uint8_t range_status_reg;
65} Vl53l0xRangeStatusArgs;
66
67/** @brief What range_valid takes: range_status_reg. */
68typedef struct
69{
70 uint8_t range_status_reg;
71} Vl53l0xRangeValidArgs;
72
73/** @brief What begin takes: addr. */
74typedef struct
75{
76 uint8_t addr;
77} Vl53l0xBeginArgs;
78
79/** @brief What read_mm takes: mm. */
80typedef struct
81{
82 uint16_t *mm;
83} Vl53l0xReadMmArgs;
84
85/**
86 * @brief ST VL53L0X / VL53L1X optical time-of-flight ranging codec (PROTOCORE_ENABLE_VL53L0X).
87 *
88 * A caller sets the members a call takes, invokes it through ::Vl53l0x with the bytes it runs
89 * out of, and reads the outcome off the same handle.
90 *
91 * Vl53l0x.range_mm_args.hi = ...;
92 * Vl53l0x.range_mm_args.lo = ...;
93 * Vl53l0x.range_mm(work);
94 * // Vl53l0x.mm is what the call reports
95 *
96 * @var Vl53l0xNs::range_mm_args what range_mm takes: hi, lo
97 * @var Vl53l0xNs::data_ready_args what data_ready takes: interrupt_status
98 * @var Vl53l0xNs::range_status_args what range_status takes: range_status_reg
99 * @var Vl53l0xNs::range_valid_args what range_valid takes: range_status_reg
100 * @var Vl53l0xNs::begin_args what begin takes: addr
101 * @var Vl53l0xNs::read_mm_args what read_mm takes: mm
102 * @var Vl53l0xNs::ok true on a fresh, valid reading; false if not ready / invalid / I2C ...
103 * @var Vl53l0xNs::mm what a call reports
104 * @var Vl53l0xNs::status what a call reports
105 * @var Vl53l0xNs::range_mm combine the range high/low bytes (RESULT_RANGE_STATUS+10 / +11) ...
106 * @var Vl53l0xNs::data_ready true if a new measurement is ready (any of the low 3 ...
107 * @var Vl53l0xNs::range_status the DeviceRangeStatus field (bits 6:3) of the RESULT_RANGE_STATUS ...
108 * @var Vl53l0xNs::range_valid true if the range-status field reports a valid measurement (== ...
109 * @var Vl53l0xNs::begin verify the model id and start continuous back-to-back ranging at ...
110 * @var Vl53l0xNs::read_mm if a measurement is ready, read the distance into mm and clear the ...
111 *
112 * @c work is PROTOCORE_I2C_DEVICE_BORROW bytes the CALLER took, at an address it knows. It is not held past the call,
113 * so nothing here aliases it. How those bytes are carved is this module's and is never named here.
114 */
115typedef struct
116{
117 Vl53l0xRangeMmArgs range_mm_args;
118 Vl53l0xDataReadyArgs data_ready_args;
119 Vl53l0xRangeStatusArgs range_status_args;
120 Vl53l0xRangeValidArgs range_valid_args;
121 Vl53l0xBeginArgs begin_args;
122 Vl53l0xReadMmArgs read_mm_args;
123 proto_bool ok;
124 uint16_t mm;
125 uint8_t status;
126} Vl53l0xVars;
127
128/** @brief The operands and the outcome. */
129extern Vl53l0xVars Vl53l0xV;
130
131/** @brief The entries. */
132typedef struct
133{
134 void (*const range_mm)(uint8_t *work);
135 void (*const data_ready)(uint8_t *work);
136 void (*const range_status)(uint8_t *work);
137 void (*const range_valid)(uint8_t *work);
138 void (*const begin)(uint8_t *work);
139 void (*const read_mm)(uint8_t *work);
140} Vl53l0xNs;
141
142// What the table binds, defined once in the .c and taking one parameter each: everything
143// else an entry needs is an operand in Vl53l0xV or a region of the borrow at a fixed offset.
144void protocore_vl53l0x_range_mm(uint8_t *work);
145void protocore_vl53l0x_data_ready(uint8_t *work);
146void protocore_vl53l0x_range_status(uint8_t *work);
147void protocore_vl53l0x_range_valid(uint8_t *work);
148void protocore_vl53l0x_begin(uint8_t *work);
149void protocore_vl53l0x_read_mm(uint8_t *work);
150
151// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
152// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
153// `Vl53l0x.range_mm(work)` resolves to a named function and becomes a DIRECT call. An extern table
154// leaves the call indirect and the symbol live at every level, -O2 -flto included.
155static const Vl53l0xNs Vl53l0x __attribute__((unused)) = {
156 .range_mm = protocore_vl53l0x_range_mm,
157 .data_ready = protocore_vl53l0x_data_ready,
158 .range_status = protocore_vl53l0x_range_status,
159 .range_valid = protocore_vl53l0x_range_valid,
160 .begin = protocore_vl53l0x_begin,
161 .read_mm = protocore_vl53l0x_read_mm,
162};
163
164/**
165 * @brief The PROTOCORE_I2C_DEVICE_BORROW bytes this module's state lives in.
166 *
167 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
168 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
169 * walks, so the state lasts the life of the program.
170 *
171 * @return the span.
172 */
173uint8_t *protocore_vl53l0x_span(void);
174
176
177#endif // PROTOCORE_ENABLE_VL53L0X
178
179#endif // PROTOCORE_VL53L0X_H
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97