Backup vom Rp2040Zero 16.05.2026 hinzugefügt
This commit is contained in:
649
VL53L0X_backup.py
Normal file
649
VL53L0X_backup.py
Normal file
@@ -0,0 +1,649 @@
|
||||
from micropython import const
|
||||
import ustruct
|
||||
import utime
|
||||
from machine import Timer
|
||||
import time
|
||||
#
|
||||
_IO_TIMEOUT = 1000
|
||||
_SYSRANGE_START = const(0x00)
|
||||
_EXTSUP_HV = const(0x89)
|
||||
_MSRC_CONFIG = const(0x60)
|
||||
_FINAL_RATE_RTN_LIMIT = const(0x44)
|
||||
_SYSTEM_SEQUENCE = const(0x01)
|
||||
_SPAD_REF_START = const(0x4f)
|
||||
_SPAD_ENABLES = const(0xb0)
|
||||
_REF_EN_START_SELECT = const(0xb6)
|
||||
_SPAD_NUM_REQUESTED = const(0x4e)
|
||||
_INTERRUPT_GPIO = const(0x0a)
|
||||
_INTERRUPT_CLEAR = const(0x0b)
|
||||
_GPIO_MUX_ACTIVE_HIGH = const(0x84)
|
||||
_RESULT_INTERRUPT_STATUS = const(0x13)
|
||||
_RESULT_RANGE_STATUS = const(0x14)
|
||||
_OSC_CALIBRATE = const(0xf8)
|
||||
_MEASURE_PERIOD = const(0x04)
|
||||
|
||||
SYSRANGE_START = 0x00
|
||||
|
||||
SYSTEM_THRESH_HIGH = 0x0C
|
||||
SYSTEM_THRESH_LOW = 0x0E
|
||||
|
||||
SYSTEM_SEQUENCE_CONFIG = 0x01
|
||||
SYSTEM_RANGE_CONFIG = 0x09
|
||||
SYSTEM_INTERMEASUREMENT_PERIOD = 0x04
|
||||
|
||||
SYSTEM_INTERRUPT_CONFIG_GPIO = 0x0A
|
||||
|
||||
GPIO_HV_MUX_ACTIVE_HIGH = 0x84
|
||||
|
||||
SYSTEM_INTERRUPT_CLEAR = 0x0B
|
||||
|
||||
RESULT_INTERRUPT_STATUS = 0x13
|
||||
RESULT_RANGE_STATUS = 0x14
|
||||
|
||||
RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN = 0xBC
|
||||
RESULT_CORE_RANGING_TOTAL_EVENTS_RTN = 0xC0
|
||||
RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF = 0xD0
|
||||
RESULT_CORE_RANGING_TOTAL_EVENTS_REF = 0xD4
|
||||
RESULT_PEAK_SIGNAL_RATE_REF = 0xB6
|
||||
|
||||
ALGO_PART_TO_PART_RANGE_OFFSET_MM = 0x28
|
||||
|
||||
I2C_SLAVE_DEVICE_ADDRESS = 0x8A
|
||||
|
||||
MSRC_CONFIG_CONTROL = 0x60
|
||||
|
||||
PRE_RANGE_CONFIG_MIN_SNR = 0x27
|
||||
PRE_RANGE_CONFIG_VALID_PHASE_LOW = 0x56
|
||||
PRE_RANGE_CONFIG_VALID_PHASE_HIGH = 0x57
|
||||
PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT = 0x64
|
||||
|
||||
FINAL_RANGE_CONFIG_MIN_SNR = 0x67
|
||||
FINAL_RANGE_CONFIG_VALID_PHASE_LOW = 0x47
|
||||
FINAL_RANGE_CONFIG_VALID_PHASE_HIGH = 0x48
|
||||
FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT = 0x44
|
||||
|
||||
PRE_RANGE_CONFIG_SIGMA_THRESH_HI = 0x61
|
||||
PRE_RANGE_CONFIG_SIGMA_THRESH_LO = 0x62
|
||||
|
||||
PRE_RANGE_CONFIG_VCSEL_PERIOD = 0x50
|
||||
PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI = 0x51
|
||||
PRE_RANGE_CONFIG_TIMEOUT_MACROP_LO = 0x52
|
||||
|
||||
SYSTEM_HISTOGRAM_BIN = 0x81
|
||||
HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT = 0x33
|
||||
HISTOGRAM_CONFIG_READOUT_CTRL = 0x55
|
||||
|
||||
FINAL_RANGE_CONFIG_VCSEL_PERIOD = 0x70
|
||||
FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI = 0x71
|
||||
FINAL_RANGE_CONFIG_TIMEOUT_MACROP_LO = 0x72
|
||||
CROSSTALK_COMPENSATION_PEAK_RATE_MCPS = 0x20
|
||||
|
||||
MSRC_CONFIG_TIMEOUT_MACROP = 0x46
|
||||
|
||||
SOFT_RESET_GO2_SOFT_RESET_N = 0xBF
|
||||
IDENTIFICATION_MODEL_ID = 0xC0
|
||||
IDENTIFICATION_REVISION_ID = 0xC2
|
||||
|
||||
OSC_CALIBRATE_VAL = 0xF8
|
||||
|
||||
GLOBAL_CONFIG_VCSEL_WIDTH = 0x32
|
||||
GLOBAL_CONFIG_SPAD_ENABLES_REF_0 = 0xB0
|
||||
GLOBAL_CONFIG_SPAD_ENABLES_REF_1 = 0xB1
|
||||
GLOBAL_CONFIG_SPAD_ENABLES_REF_2 = 0xB2
|
||||
GLOBAL_CONFIG_SPAD_ENABLES_REF_3 = 0xB3
|
||||
GLOBAL_CONFIG_SPAD_ENABLES_REF_4 = 0xB4
|
||||
GLOBAL_CONFIG_SPAD_ENABLES_REF_5 = 0xB5
|
||||
|
||||
GLOBAL_CONFIG_REF_EN_START_SELECT = 0xB6
|
||||
DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD = 0x4E
|
||||
DYNAMIC_SPAD_REF_EN_START_OFFSET = 0x4F
|
||||
POWER_MANAGEMENT_GO1_POWER_FORCE = 0x80
|
||||
|
||||
VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV = 0x89
|
||||
|
||||
ALGO_PHASECAL_LIM = 0x30
|
||||
ALGO_PHASECAL_CONFIG_TIMEOUT = 0x30
|
||||
|
||||
|
||||
class TimeoutError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
class VL53L0X:
|
||||
def __init__(self, i2c, address=0x29):
|
||||
self.i2c = i2c
|
||||
self.address = address
|
||||
self.init()
|
||||
self._started = False
|
||||
self.measurement_timing_budget_us = 0
|
||||
self.set_measurement_timing_budget(self.measurement_timing_budget_us)
|
||||
self.enables = {"tcc": 0,
|
||||
"dss": 0,
|
||||
"msrc": 0,
|
||||
"pre_range": 0,
|
||||
"final_range": 0}
|
||||
self.timeouts = {"pre_range_vcsel_period_pclks": 0,
|
||||
"msrc_dss_tcc_mclks": 0,
|
||||
"msrc_dss_tcc_us": 0,
|
||||
"pre_range_mclks": 0,
|
||||
"pre_range_us": 0,
|
||||
"final_range_vcsel_period_pclks": 0,
|
||||
"final_range_mclks": 0,
|
||||
"final_range_us": 0
|
||||
}
|
||||
self.vcsel_period_type = ["VcselPeriodPreRange", "VcselPeriodFinalRange"]
|
||||
|
||||
def _registers(self, register, values=None, struct='B'):
|
||||
if values is None:
|
||||
size = ustruct.calcsize(struct)
|
||||
data = self.i2c.readfrom_mem(self.address, register, size)
|
||||
values = ustruct.unpack(struct, data)
|
||||
return values
|
||||
data = ustruct.pack(struct, *values)
|
||||
self.i2c.writeto_mem(self.address, register, data)
|
||||
|
||||
def _register(self, register, value=None, struct='B'):
|
||||
if value is None:
|
||||
return self._registers(register, struct=struct)[0]
|
||||
self._registers(register, (value,), struct=struct)
|
||||
|
||||
def _flag(self, register=0x00, bit=0, value=None):
|
||||
data = self._register(register)
|
||||
mask = 1 << bit
|
||||
if value is None:
|
||||
return bool(data & mask)
|
||||
elif value:
|
||||
data |= mask
|
||||
else:
|
||||
data &= ~mask
|
||||
self._register(register, data)
|
||||
|
||||
def _config(self, *config):
|
||||
for register, value in config:
|
||||
self._register(register, value)
|
||||
|
||||
def init(self, power2v8=True):
|
||||
self._flag(_EXTSUP_HV, 0, power2v8)
|
||||
|
||||
# I2C standard mode
|
||||
self._config(
|
||||
(0x88, 0x00),
|
||||
|
||||
(0x80, 0x01),
|
||||
(0xff, 0x01),
|
||||
(0x00, 0x00),
|
||||
)
|
||||
self._stop_variable = self._register(0x91)
|
||||
self._config(
|
||||
(0x00, 0x01),
|
||||
(0xff, 0x00),
|
||||
(0x80, 0x00),
|
||||
)
|
||||
|
||||
# disable signal_rate_msrc and signal_rate_pre_range limit checks
|
||||
self._flag(_MSRC_CONFIG, 1, True)
|
||||
self._flag(_MSRC_CONFIG, 4, True)
|
||||
|
||||
# rate_limit = 0.25
|
||||
self._register(_FINAL_RATE_RTN_LIMIT, int(0.1 * (1 << 7)),
|
||||
struct='>H')
|
||||
|
||||
self._register(_SYSTEM_SEQUENCE, 0xff)
|
||||
|
||||
spad_count, is_aperture = self._spad_info()
|
||||
spad_map = bytearray(self._registers(_SPAD_ENABLES, struct='6B'))
|
||||
|
||||
# set reference spads
|
||||
self._config(
|
||||
(0xff, 0x01),
|
||||
(_SPAD_REF_START, 0x00),
|
||||
(_SPAD_NUM_REQUESTED, 0x2c),
|
||||
(0xff, 0x00),
|
||||
(_REF_EN_START_SELECT, 0xb4),
|
||||
)
|
||||
|
||||
spads_enabled = 0
|
||||
for i in range(48):
|
||||
if i < 12 and is_aperture or spads_enabled >= spad_count:
|
||||
spad_map[i // 8] &= ~(1 << (i >> 2))
|
||||
elif spad_map[i // 8] & (1 << (i >> 2)):
|
||||
spads_enabled += 1
|
||||
|
||||
self._registers(_SPAD_ENABLES, spad_map, struct='6B')
|
||||
|
||||
self._config(
|
||||
(0xff, 0x01),
|
||||
(0x00, 0x00),
|
||||
|
||||
(0xff, 0x00),
|
||||
(0x09, 0x00),
|
||||
(0x10, 0x00),
|
||||
(0x11, 0x00),
|
||||
|
||||
(0x24, 0x01),
|
||||
(0x25, 0xFF),
|
||||
(0x75, 0x00),
|
||||
|
||||
(0xFF, 0x01),
|
||||
(0x4E, 0x2C),
|
||||
(0x48, 0x00),
|
||||
(0x30, 0x20),
|
||||
|
||||
(0xFF, 0x00),
|
||||
(0x30, 0x09),
|
||||
(0x54, 0x00),
|
||||
(0x31, 0x04),
|
||||
(0x32, 0x03),
|
||||
(0x40, 0x83),
|
||||
(0x46, 0x25),
|
||||
(0x60, 0x00),
|
||||
(0x27, 0x00),
|
||||
(0x50, 0x06),
|
||||
(0x51, 0x00),
|
||||
(0x52, 0x96),
|
||||
(0x56, 0x08),
|
||||
(0x57, 0x30),
|
||||
(0x61, 0x00),
|
||||
(0x62, 0x00),
|
||||
(0x64, 0x00),
|
||||
(0x65, 0x00),
|
||||
(0x66, 0xA0),
|
||||
|
||||
(0xFF, 0x01),
|
||||
(0x22, 0x32),
|
||||
(0x47, 0x14),
|
||||
(0x49, 0xFF),
|
||||
(0x4A, 0x00),
|
||||
|
||||
(0xFF, 0x00),
|
||||
(0x7A, 0x0A),
|
||||
(0x7B, 0x00),
|
||||
(0x78, 0x21),
|
||||
|
||||
(0xFF, 0x01),
|
||||
(0x23, 0x34),
|
||||
(0x42, 0x00),
|
||||
(0x44, 0xFF),
|
||||
(0x45, 0x26),
|
||||
(0x46, 0x05),
|
||||
(0x40, 0x40),
|
||||
(0x0E, 0x06),
|
||||
(0x20, 0x1A),
|
||||
(0x43, 0x40),
|
||||
|
||||
(0xFF, 0x00),
|
||||
(0x34, 0x03),
|
||||
(0x35, 0x44),
|
||||
|
||||
(0xFF, 0x01),
|
||||
(0x31, 0x04),
|
||||
(0x4B, 0x09),
|
||||
(0x4C, 0x05),
|
||||
(0x4D, 0x04),
|
||||
|
||||
(0xFF, 0x00),
|
||||
(0x44, 0x00),
|
||||
(0x45, 0x20),
|
||||
(0x47, 0x08),
|
||||
(0x48, 0x28),
|
||||
(0x67, 0x00),
|
||||
(0x70, 0x04),
|
||||
(0x71, 0x01),
|
||||
(0x72, 0xFE),
|
||||
(0x76, 0x00),
|
||||
(0x77, 0x00),
|
||||
|
||||
(0xFF, 0x01),
|
||||
(0x0D, 0x01),
|
||||
|
||||
(0xFF, 0x00),
|
||||
(0x80, 0x01),
|
||||
(0x01, 0xF8),
|
||||
|
||||
(0xFF, 0x01),
|
||||
(0x8E, 0x01),
|
||||
(0x00, 0x01),
|
||||
(0xFF, 0x00),
|
||||
(0x80, 0x00),
|
||||
)
|
||||
|
||||
self._register(_INTERRUPT_GPIO, 0x04)
|
||||
self._flag(_GPIO_MUX_ACTIVE_HIGH, 4, False)
|
||||
self._register(_INTERRUPT_CLEAR, 0x01)
|
||||
|
||||
# XXX Need to implement this.
|
||||
# budget = self._timing_budget()
|
||||
# self._register(_SYSTEM_SEQUENCE, 0xe8)
|
||||
# self._timing_budget(budget)
|
||||
|
||||
self._register(_SYSTEM_SEQUENCE, 0x01)
|
||||
self._calibrate(0x40)
|
||||
self._register(_SYSTEM_SEQUENCE, 0x02)
|
||||
self._calibrate(0x00)
|
||||
|
||||
self._register(_SYSTEM_SEQUENCE, 0xe8)
|
||||
|
||||
def _spad_info(self):
|
||||
self._config(
|
||||
(0x80, 0x01),
|
||||
(0xff, 0x01),
|
||||
(0x00, 0x00),
|
||||
|
||||
(0xff, 0x06),
|
||||
)
|
||||
self._flag(0x83, 3, True)
|
||||
self._config(
|
||||
(0xff, 0x07),
|
||||
(0x81, 0x01),
|
||||
|
||||
(0x80, 0x01),
|
||||
|
||||
(0x94, 0x6b),
|
||||
(0x83, 0x00),
|
||||
)
|
||||
for timeout in range(_IO_TIMEOUT):
|
||||
if self._register(0x83):
|
||||
break
|
||||
utime.sleep_ms(1)
|
||||
else:
|
||||
raise TimeoutError()
|
||||
self._config(
|
||||
(0x83, 0x01),
|
||||
)
|
||||
value = self._register(0x92)
|
||||
self._config(
|
||||
(0x81, 0x00),
|
||||
(0xff, 0x06),
|
||||
)
|
||||
self._flag(0x83, 3, False)
|
||||
self._config(
|
||||
(0xff, 0x01),
|
||||
(0x00, 0x01),
|
||||
|
||||
(0xff, 0x00),
|
||||
(0x80, 0x00),
|
||||
)
|
||||
count = value & 0x7f
|
||||
is_aperture = bool(value & 0b10000000)
|
||||
return count, is_aperture
|
||||
|
||||
def _calibrate(self, vhv_init_byte):
|
||||
self._register(_SYSRANGE_START, 0x01 | vhv_init_byte)
|
||||
for timeout in range(_IO_TIMEOUT):
|
||||
if self._register(_RESULT_INTERRUPT_STATUS) & 0x07:
|
||||
break
|
||||
utime.sleep_ms(1)
|
||||
else:
|
||||
raise TimeoutError()
|
||||
self._register(_INTERRUPT_CLEAR, 0x01)
|
||||
self._register(_SYSRANGE_START, 0x00)
|
||||
|
||||
def start(self, period=0):
|
||||
self._config(
|
||||
(0x80, 0x01),
|
||||
(0xFF, 0x01),
|
||||
(0x00, 0x00),
|
||||
(0x91, self._stop_variable),
|
||||
(0x00, 0x01),
|
||||
(0xFF, 0x00),
|
||||
(0x80, 0x00),
|
||||
)
|
||||
if period:
|
||||
oscilator = self._register(_OSC_CALIBRATE, struct='>H')
|
||||
if oscilator:
|
||||
period *= oscilator
|
||||
self._register(_MEASURE_PERIOD, period, struct='>H')
|
||||
self._register(_SYSRANGE_START, 0x04)
|
||||
else:
|
||||
self._register(_SYSRANGE_START, 0x02)
|
||||
self._started = True
|
||||
|
||||
def stop(self):
|
||||
self._register(_SYSRANGE_START, 0x01)
|
||||
self._config(
|
||||
(0xFF, 0x01),
|
||||
(0x00, 0x00),
|
||||
(0x91, self._stop_variable),
|
||||
(0x00, 0x01),
|
||||
(0xFF, 0x00),
|
||||
)
|
||||
self._started = False
|
||||
|
||||
def read(self):
|
||||
if not self._started:
|
||||
self._config(
|
||||
(0x80, 0x01),
|
||||
(0xFF, 0x01),
|
||||
(0x00, 0x00),
|
||||
(0x91, self._stop_variable),
|
||||
(0x00, 0x01),
|
||||
(0xFF, 0x00),
|
||||
(0x80, 0x00),
|
||||
(_SYSRANGE_START, 0x01),
|
||||
)
|
||||
for timeout in range(_IO_TIMEOUT):
|
||||
if not self._register(_SYSRANGE_START) & 0x01:
|
||||
break
|
||||
utime.sleep_ms(1)
|
||||
else:
|
||||
raise TimeoutError()
|
||||
for timeout in range(_IO_TIMEOUT):
|
||||
if self._register(_RESULT_INTERRUPT_STATUS) & 0x07:
|
||||
break
|
||||
utime.sleep_ms(1)
|
||||
else:
|
||||
raise TimeoutError()
|
||||
value = self._register(_RESULT_RANGE_STATUS + 10, struct='>H')
|
||||
self._register(_INTERRUPT_CLEAR, 0x01)
|
||||
return value
|
||||
|
||||
def set_signal_rate_limit(self, limit_Mcps):
|
||||
if limit_Mcps < 0 or limit_Mcps > 511.99:
|
||||
return False
|
||||
self._register(0x44, limit_Mcps * (1 << 7))
|
||||
return True
|
||||
|
||||
def decode_Vcsel_period(self, reg_val):
|
||||
return (((reg_val) + 1) << 1)
|
||||
|
||||
def encode_Vcsel_period(self, period_pclks):
|
||||
return (((period_pclks) >> 1) - 1)
|
||||
|
||||
def set_Vcsel_pulse_period(self, type, period_pclks):
|
||||
vcsel_period_reg = self.encode_Vcsel_period(period_pclks)
|
||||
|
||||
self.get_sequence_step_enables()
|
||||
self.get_sequence_step_timeouts()
|
||||
|
||||
if type == self.vcsel_period_type[0]:
|
||||
if period_pclks == 12:
|
||||
self._register(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x18)
|
||||
elif period_pclks == 14:
|
||||
self._register(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x30)
|
||||
elif period_pclks == 16:
|
||||
self._register(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x40)
|
||||
elif period_pclks == 18:
|
||||
self._register(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x50)
|
||||
else:
|
||||
return False
|
||||
|
||||
self._register(PRE_RANGE_CONFIG_VALID_PHASE_LOW, 0x08)
|
||||
self._register(PRE_RANGE_CONFIG_VCSEL_PERIOD, vcsel_period_reg)
|
||||
|
||||
new_pre_range_timeout_mclks = self.timeout_microseconds_to_Mclks(self.timeouts["pre_range_us"],
|
||||
period_pclks)
|
||||
self._register(PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI, self.encode_timeout(new_pre_range_timeout_mclks))
|
||||
|
||||
new_msrc_timeout_mclks = self.timeout_microseconds_to_Mclks(self.timeouts["msrc_dss_tcc_us"],
|
||||
period_pclks)
|
||||
self._register(MSRC_CONFIG_TIMEOUT_MACROP, 255 if new_msrc_timeout_mclks > 256 else (new_msrc_timeout_mclks - 1))
|
||||
elif type == self.vcsel_period_type[1]:
|
||||
if period_pclks == 8:
|
||||
self._register(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x10)
|
||||
self._register(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08)
|
||||
self._register(GLOBAL_CONFIG_VCSEL_WIDTH, 0x02)
|
||||
self._(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x0C)
|
||||
self._register(0xFF, 0x01)
|
||||
self._register(ALGO_PHASECAL_LIM, 0x30)
|
||||
self._register(0xFF, 0x00)
|
||||
elif period_pclks == 10:
|
||||
self._register(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x28)
|
||||
self._register(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08)
|
||||
self._register(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03)
|
||||
self._register(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x09)
|
||||
self._register(0xFF, 0x01)
|
||||
self._register(ALGO_PHASECAL_LIM, 0x20)
|
||||
self._register(0xFF, 0x00)
|
||||
elif period_pclks == 12:
|
||||
self._register(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x38)
|
||||
self._register(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08)
|
||||
self._register(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03)
|
||||
self._register(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x08)
|
||||
self._register(0xFF, 0x01)
|
||||
self._register(ALGO_PHASECAL_LIM, 0x20)
|
||||
self._register(0xFF, 0x00)
|
||||
elif period_pclks == 14:
|
||||
self._register(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x48)
|
||||
self._register(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08)
|
||||
self._register(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03)
|
||||
self._register(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x07)
|
||||
self._register(0xFF, 0x01)
|
||||
self._register(ALGO_PHASECAL_LIM, 0x20)
|
||||
self._register(0xFF, 0x00)
|
||||
else:
|
||||
return False
|
||||
|
||||
self._register(FINAL_RANGE_CONFIG_VCSEL_PERIOD, vcsel_period_reg)
|
||||
|
||||
new_final_range_timeout_mclks = self.timeout_microseconds_to_Mclks(self.timeouts["final_range_us"], period_pclks)
|
||||
|
||||
if self.enables["pre_range"]:
|
||||
new_final_range_timeout_mclks += 1
|
||||
self._register(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI, self.encode_timeout(new_final_range_timeout_mclks))
|
||||
else:
|
||||
return False
|
||||
self.set_measurement_timing_budget(self.measurement_timing_budget_us)
|
||||
sequence_config = self._register(SYSTEM_SEQUENCE_CONFIG)
|
||||
self._register(SYSTEM_SEQUENCE_CONFIG, 0x02)
|
||||
self.perform_single_ref_calibration(0x0)
|
||||
self._register(SYSTEM_SEQUENCE_CONFIG, sequence_config)
|
||||
|
||||
return True
|
||||
|
||||
def get_sequence_step_enables(self):
|
||||
sequence_config = self._register(0x01)
|
||||
|
||||
self.enables["tcc"] = (sequence_config >> 4) & 0x1
|
||||
self.enables["dss"] = (sequence_config >> 3) & 0x1
|
||||
self.enables["msrc"] = (sequence_config >> 2) & 0x1
|
||||
self.enables["pre_range"] = (sequence_config >> 6) & 0x1
|
||||
self.enables["final_range"] = (sequence_config >> 7) & 0x1
|
||||
|
||||
def get_vcsel_pulse_period(self, type):
|
||||
if type == self.vcsel_period_type[0]:
|
||||
return self.decode_Vcsel_period(0x50)
|
||||
elif type == self.vcsel_period_type[1]:
|
||||
return self.decode_Vcsel_period(0x70)
|
||||
else:
|
||||
return 255
|
||||
|
||||
def get_sequence_step_timeouts(self):
|
||||
self.timeouts["pre_range_vcsel_period_pclks"] = self.get_vcsel_pulse_period(self.vcsel_period_type[0])
|
||||
self.timeouts["msrc_dss_tcc_mclks"] = int(self._register(MSRC_CONFIG_TIMEOUT_MACROP)) + 1
|
||||
self.timeouts["msrc_dss_tcc_us"] = self.timeout_Mclks_to_microseconds(self.timeouts["msrc_dss_tcc_mclks"],
|
||||
self.timeouts[
|
||||
"pre_range_vcsel_period_pclks"])
|
||||
self.timeouts["pre_range_mclks"] = self.decode_timeout(PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI)
|
||||
self.timeouts["pre_range_us"] = self.timeout_Mclks_to_microseconds(self.timeouts["pre_range_mclks"],
|
||||
self.timeouts[
|
||||
"pre_range_vcsel_period_pclks"])
|
||||
self.timeouts["final_range_vcsel_period_pclks"] = self.get_vcsel_pulse_period(self.vcsel_period_type[1])
|
||||
self.timeouts["final_range_mclks"] = self.decode_timeout(self._register(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI))
|
||||
|
||||
if self.enables["pre_range"]:
|
||||
self.timeouts["final_range_mclks"] -= self.timeouts["pre_range_mclks"]
|
||||
self.timeouts["final_range_us"] = self.timeout_Mclks_to_microseconds(self.timeouts["final_range_mclks"],
|
||||
self.timeouts[
|
||||
"final_range_vcsel_period_pclks"])
|
||||
|
||||
def timeout_Mclks_to_microseconds(self, timeout_period_mclks, vcsel_period_pclks):
|
||||
macro_period_ns = self.calc_macro_period(vcsel_period_pclks)
|
||||
return ((timeout_period_mclks * macro_period_ns) + (macro_period_ns / 2)) / 1000
|
||||
|
||||
def timeout_microseconds_to_Mclks(self, timeout_period_us, vcsel_period_pclks):
|
||||
macro_period_ns = self.calc_macro_period(vcsel_period_pclks)
|
||||
return (((timeout_period_us * 1000) + (macro_period_ns / 2)) / macro_period_ns)
|
||||
|
||||
def calc_macro_period(self, vcsel_period_pclks):
|
||||
return (((2304 * (vcsel_period_pclks) * 1655) + 500) / 1000)
|
||||
|
||||
def decode_timeout(self, reg_val):
|
||||
return ((reg_val & 0x00FF) << ((reg_val & 0xFF00) >> 8)) + 1
|
||||
|
||||
def encode_timeout(self, timeout_mclks):
|
||||
timeout_mclks = int(timeout_mclks)
|
||||
ls_byte = 0
|
||||
ms_byte = 0
|
||||
|
||||
if timeout_mclks > 0:
|
||||
ls_byte = timeout_mclks - 1
|
||||
|
||||
while (ls_byte & 0xFFFFFF00) > 0:
|
||||
ls_byte >>= 1
|
||||
ms_byte += 1
|
||||
return (ms_byte << 8) or (ls_byte & 0xFF)
|
||||
else:
|
||||
return 0
|
||||
|
||||
def set_measurement_timing_budget(self, budget_us):
|
||||
start_overhead = 1320
|
||||
end_overhead = 960
|
||||
msrc_overhead = 660
|
||||
tcc_overhead = 590
|
||||
dss_overhead = 690
|
||||
pre_range_overhead = 660
|
||||
final_range_overhead = 550
|
||||
|
||||
min_timing_budget = 20000
|
||||
|
||||
if budget_us < min_timing_budget:
|
||||
return False
|
||||
used_budget_us = start_overhead + end_overhead
|
||||
|
||||
self.get_sequence_step_enables()
|
||||
self.get_sequence_step_timeouts()
|
||||
|
||||
if self.enables["tcc"]:
|
||||
used_budget_us += self.timeouts["msrc_dss_tcc_us"] + tcc_overhead
|
||||
if self.enables["dss"]:
|
||||
used_budget_us += 2* self.timeouts["msrc_dss_tcc_us"] + dss_overhead
|
||||
if self.enables["msrc"]:
|
||||
used_budget_us += self.timeouts["msrc_dss_tcc_us"] + msrc_overhead
|
||||
if self.enables["pre_range"]:
|
||||
used_budget_us += self.timeouts["pre_range_us"] + pre_range_overhead
|
||||
if self.enables["final_range"]:
|
||||
used_budget_us += final_range_overhead
|
||||
|
||||
if used_budget_us > budget_us:
|
||||
return False
|
||||
final_range_timeout_us = budget_us - used_budget_us
|
||||
final_range_timeout_mclks = self.timeout_microseconds_to_Mclks(final_range_timeout_us, self.timeouts["final_range_vcsel_period_pclks"])
|
||||
|
||||
if self.enables["pre_range"]:
|
||||
final_range_timeout_mclks += self.timeouts["pre_range_mclks"]
|
||||
self._register(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI, self.encode_timeout(final_range_timeout_mclks))
|
||||
self.measurement_timing_budget_us = budget_us
|
||||
return True
|
||||
|
||||
def perform_single_ref_calibration(self, vhv_init_byte):
|
||||
chrono = Timer.Chrono()
|
||||
self._register(SYSRANGE_START, 0x01|vhv_init_byte)
|
||||
chrono.start()
|
||||
while self._register((RESULT_INTERRUPT_STATUS & 0x07) == 0):
|
||||
time_elapsed = chrono.read_ms()
|
||||
if time_elapsed > _IO_TIMEOUT:
|
||||
return False
|
||||
self._register(SYSTEM_INTERRUPT_CLEAR, 0x01)
|
||||
self._register(SYSRANGE_START, 0x00)
|
||||
return True
|
||||
|
||||
50
lcd1602_backup.py
Normal file
50
lcd1602_backup.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import time
|
||||
from machine import Pin
|
||||
|
||||
class LCD1602:
|
||||
def __init__(self, rs, en, d4, d5, d6, d7):
|
||||
self.rs = Pin(rs, Pin.OUT)
|
||||
self.en = Pin(en, Pin.OUT)
|
||||
self.data_pins = [
|
||||
Pin(d4, Pin.OUT),
|
||||
Pin(d5, Pin.OUT),
|
||||
Pin(d6, Pin.OUT),
|
||||
Pin(d7, Pin.OUT)
|
||||
]
|
||||
self._init_lcd()
|
||||
|
||||
def _pulse(self):
|
||||
self.en.value(1)
|
||||
time.sleep_us(1)
|
||||
self.en.value(0)
|
||||
time.sleep_us(100)
|
||||
|
||||
def _send(self, value, mode=0):
|
||||
self.rs.value(mode)
|
||||
for i in range(2):
|
||||
for j in range(4):
|
||||
self.data_pins[j].value((value >> (4 * (1 - i) + j)) & 0x01)
|
||||
self._pulse()
|
||||
time.sleep_ms(2)
|
||||
|
||||
def _init_lcd(self):
|
||||
time.sleep_ms(20)
|
||||
self._send(0x33) # init
|
||||
self._send(0x32) # 4-bit mode
|
||||
self._send(0x28) # 2 lines, 5x7 matrix
|
||||
self._send(0x0C) # display on, cursor off
|
||||
self._send(0x06) # entry mode
|
||||
self.clear()
|
||||
|
||||
def clear(self):
|
||||
self._send(0x01)
|
||||
time.sleep_ms(2)
|
||||
|
||||
def putstr(self, string):
|
||||
for char in string:
|
||||
self._send(ord(char), 1)
|
||||
|
||||
def move_to(self, col, row):
|
||||
addr = col + (0x40 if row else 0x00)
|
||||
self._send(0x80 | addr)
|
||||
|
||||
46
main_backup.py
Normal file
46
main_backup.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import time
|
||||
from machine import Pin, I2C, time_pulse_us
|
||||
from lcd1602 import LCD1602
|
||||
from vl53l0x import VL53L0X # muss als vl53l0x.py auf dem Board liegen
|
||||
|
||||
# --- LCD Setup (RS=6, E=7, D4=8, D5=9, D6=10, D7=11) ---
|
||||
lcd = LCD1602(6, 7, 8, 9, 10, 11)
|
||||
|
||||
# --- HC-SR04 Setup ---
|
||||
trig = Pin(3, Pin.OUT)
|
||||
echo = Pin(2, Pin.IN)
|
||||
|
||||
def read_hcsr04():
|
||||
trig.low()
|
||||
time.sleep_us(2)
|
||||
trig.high()
|
||||
time.sleep_us(10)
|
||||
trig.low()
|
||||
try:
|
||||
duration = time_pulse_us(echo, 1, 30000) # Warte max. 30ms
|
||||
distance = (duration / 2) / 29.1 # cm
|
||||
except OSError:
|
||||
distance = -1 # kein Messwert
|
||||
return distance
|
||||
|
||||
# --- VL53L0X Setup ---
|
||||
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400000)
|
||||
sensor = VL53L0X(i2c)
|
||||
|
||||
# --- Hauptloop ---
|
||||
while True:
|
||||
dist_us = read_hcsr04()
|
||||
dist_tof = sensor.range/10 # Adafruit-Treiber liefert .range in mm modifiziert zu cm
|
||||
|
||||
lcd.clear()
|
||||
lcd.move_to(0, 0)
|
||||
if dist_us >= 0:
|
||||
lcd.putstr("US: {:.1f} cm".format(dist_us))
|
||||
else:
|
||||
lcd.putstr("US: ---")
|
||||
|
||||
lcd.move_to(0, 1)
|
||||
lcd.putstr("ToF: {} cm".format(dist_tof))
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
544
vl53l0x_backup.py
Normal file
544
vl53l0x_backup.py
Normal file
@@ -0,0 +1,544 @@
|
||||
# SPDX-FileCopyrightText: 2017 Tony DiCola for Adafruit Industries
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Modified by Kevin Zhu to work on the ESP32 running Micropython 1.14
|
||||
# Modified by Salvatore Sanfilippo for non-blocking API.
|
||||
#
|
||||
# This code is released under the MIT license.
|
||||
|
||||
import math
|
||||
import utime
|
||||
|
||||
__version__ = "0.0.0-auto.0"
|
||||
__repo__ = "https://github.com/antirez/VL53L0X.git"
|
||||
|
||||
# Configuration constants:
|
||||
_SYSRANGE_START = const(0x00)
|
||||
_SYSTEM_THRESH_HIGH = const(0x0C)
|
||||
_SYSTEM_THRESH_LOW = const(0x0E)
|
||||
_SYSTEM_SEQUENCE_CONFIG = const(0x01)
|
||||
_SYSTEM_RANGE_CONFIG = const(0x09)
|
||||
_SYSTEM_INTERMEASUREMENT_PERIOD = const(0x04)
|
||||
_SYSTEM_INTERRUPT_CONFIG_GPIO = const(0x0A)
|
||||
_GPIO_HV_MUX_ACTIVE_HIGH = const(0x84)
|
||||
_SYSTEM_INTERRUPT_CLEAR = const(0x0B)
|
||||
_RESULT_INTERRUPT_STATUS = const(0x13)
|
||||
_RESULT_RANGE_STATUS = const(0x14)
|
||||
_RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN = const(0xBC)
|
||||
_RESULT_CORE_RANGING_TOTAL_EVENTS_RTN = const(0xC0)
|
||||
_RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF = const(0xD0)
|
||||
_RESULT_CORE_RANGING_TOTAL_EVENTS_REF = const(0xD4)
|
||||
_RESULT_PEAK_SIGNAL_RATE_REF = const(0xB6)
|
||||
_ALGO_PART_TO_PART_RANGE_OFFSET_MM = const(0x28)
|
||||
_I2C_SLAVE_DEVICE_ADDRESS = const(0x8A)
|
||||
_MSRC_CONFIG_CONTROL = const(0x60)
|
||||
_PRE_RANGE_CONFIG_MIN_SNR = const(0x27)
|
||||
_PRE_RANGE_CONFIG_VALID_PHASE_LOW = const(0x56)
|
||||
_PRE_RANGE_CONFIG_VALID_PHASE_HIGH = const(0x57)
|
||||
_PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT = const(0x64)
|
||||
_FINAL_RANGE_CONFIG_MIN_SNR = const(0x67)
|
||||
_FINAL_RANGE_CONFIG_VALID_PHASE_LOW = const(0x47)
|
||||
_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH = const(0x48)
|
||||
_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT = const(0x44)
|
||||
_PRE_RANGE_CONFIG_SIGMA_THRESH_HI = const(0x61)
|
||||
_PRE_RANGE_CONFIG_SIGMA_THRESH_LO = const(0x62)
|
||||
_PRE_RANGE_CONFIG_VCSEL_PERIOD = const(0x50)
|
||||
_PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI = const(0x51)
|
||||
_PRE_RANGE_CONFIG_TIMEOUT_MACROP_LO = const(0x52)
|
||||
_SYSTEM_HISTOGRAM_BIN = const(0x81)
|
||||
_HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT = const(0x33)
|
||||
_HISTOGRAM_CONFIG_READOUT_CTRL = const(0x55)
|
||||
_FINAL_RANGE_CONFIG_VCSEL_PERIOD = const(0x70)
|
||||
_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI = const(0x71)
|
||||
_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_LO = const(0x72)
|
||||
_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS = const(0x20)
|
||||
_MSRC_CONFIG_TIMEOUT_MACROP = const(0x46)
|
||||
_SOFT_RESET_GO2_SOFT_RESET_N = const(0xBF)
|
||||
_IDENTIFICATION_MODEL_ID = const(0xC0)
|
||||
_IDENTIFICATION_REVISION_ID = const(0xC2)
|
||||
_OSC_CALIBRATE_VAL = const(0xF8)
|
||||
_GLOBAL_CONFIG_VCSEL_WIDTH = const(0x32)
|
||||
_GLOBAL_CONFIG_SPAD_ENABLES_REF_0 = const(0xB0)
|
||||
_GLOBAL_CONFIG_SPAD_ENABLES_REF_1 = const(0xB1)
|
||||
_GLOBAL_CONFIG_SPAD_ENABLES_REF_2 = const(0xB2)
|
||||
_GLOBAL_CONFIG_SPAD_ENABLES_REF_3 = const(0xB3)
|
||||
_GLOBAL_CONFIG_SPAD_ENABLES_REF_4 = const(0xB4)
|
||||
_GLOBAL_CONFIG_SPAD_ENABLES_REF_5 = const(0xB5)
|
||||
_GLOBAL_CONFIG_REF_EN_START_SELECT = const(0xB6)
|
||||
_DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD = const(0x4E)
|
||||
_DYNAMIC_SPAD_REF_EN_START_OFFSET = const(0x4F)
|
||||
_POWER_MANAGEMENT_GO1_POWER_FORCE = const(0x80)
|
||||
_VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV = const(0x89)
|
||||
_ALGO_PHASECAL_LIM = const(0x30)
|
||||
_ALGO_PHASECAL_CONFIG_TIMEOUT = const(0x30)
|
||||
_VCSEL_PERIOD_PRE_RANGE = const(0)
|
||||
_VCSEL_PERIOD_FINAL_RANGE = const(1)
|
||||
|
||||
|
||||
def _decode_timeout(val):
|
||||
# format: "(LSByte * 2^MSByte) + 1"
|
||||
return float(val & 0xFF) * math.pow(2.0, ((val & 0xFF00) >> 8)) + 1
|
||||
|
||||
|
||||
def _encode_timeout(timeout_mclks):
|
||||
# format: "(LSByte * 2^MSByte) + 1"
|
||||
timeout_mclks = int(timeout_mclks) & 0xFFFF
|
||||
ls_byte = 0
|
||||
ms_byte = 0
|
||||
if timeout_mclks > 0:
|
||||
ls_byte = timeout_mclks - 1
|
||||
while ls_byte > 255:
|
||||
ls_byte >>= 1
|
||||
ms_byte += 1
|
||||
return ((ms_byte << 8) | (ls_byte & 0xFF)) & 0xFFFF
|
||||
return 0
|
||||
|
||||
|
||||
def _timeout_mclks_to_microseconds(timeout_period_mclks, vcsel_period_pclks):
|
||||
macro_period_ns = ((2304 * (vcsel_period_pclks) * 1655) + 500) // 1000
|
||||
return ((timeout_period_mclks * macro_period_ns) + (macro_period_ns // 2)) // 1000
|
||||
|
||||
|
||||
def _timeout_microseconds_to_mclks(timeout_period_us, vcsel_period_pclks):
|
||||
macro_period_ns = ((2304 * (vcsel_period_pclks) * 1655) + 500) // 1000
|
||||
return ((timeout_period_us * 1000) + (macro_period_ns // 2)) // macro_period_ns
|
||||
|
||||
|
||||
class VL53L0X:
|
||||
"""Driver for the VL53L0X distance sensor."""
|
||||
|
||||
# Class-level buffer for reading and writing data with the sensor.
|
||||
# This reduces memory allocations but means the code is not re-entrant or
|
||||
# thread safe!
|
||||
_BUFFER = bytearray(3)
|
||||
|
||||
def __init__(self, i2c, address=41, io_timeout_s=0):
|
||||
# pylint: disable=too-many-statements
|
||||
self._i2c = i2c
|
||||
self.address = address
|
||||
self.io_timeout_s = io_timeout_s
|
||||
self.range_started = False # True if a measure was initiated.
|
||||
|
||||
# Check identification registers for expected values.
|
||||
# From section 3.2 of the datasheet.
|
||||
if (
|
||||
self._read_u8(0xC0) != 0xEE
|
||||
or self._read_u8(0xC1) != 0xAA
|
||||
or self._read_u8(0xC2) != 0x10
|
||||
):
|
||||
raise RuntimeError(
|
||||
"Failed to find expected ID register values. Check wiring! C0,C1,C2:", self._i2c.readfrom_mem(self.address, 0xc0, 3)
|
||||
)
|
||||
# Initialize access to the sensor. This is based on the logic from:
|
||||
# https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
|
||||
# Set I2C standard mode.
|
||||
for pair in ((0x88, 0x00), (0x80, 0x01), (0xFF, 0x01), (0x00, 0x00)):
|
||||
self._write_u8(pair[0], pair[1])
|
||||
self._stop_variable = self._read_u8(0x91)
|
||||
for pair in ((0x00, 0x01), (0xFF, 0x00), (0x80, 0x00)):
|
||||
self._write_u8(pair[0], pair[1])
|
||||
# disable SIGNAL_RATE_MSRC (bit 1) and SIGNAL_RATE_PRE_RANGE (bit 4)
|
||||
# limit checks
|
||||
config_control = self._read_u8(_MSRC_CONFIG_CONTROL) | 0x12
|
||||
self._write_u8(_MSRC_CONFIG_CONTROL, config_control)
|
||||
# set final range signal rate limit to 0.25 MCPS (million counts per
|
||||
# second)
|
||||
self.signal_rate_limit = 0.25
|
||||
self._write_u8(_SYSTEM_SEQUENCE_CONFIG, 0xFF)
|
||||
spad_count, spad_is_aperture = self._get_spad_info()
|
||||
# The SPAD map (RefGoodSpadMap) is read by
|
||||
# VL53L0X_get_info_from_device() in the API, but the same data seems to
|
||||
# be more easily readable from GLOBAL_CONFIG_SPAD_ENABLES_REF_0 through
|
||||
# _6, so read it from there.
|
||||
ref_spad_map = bytearray(7)
|
||||
self._i2c.readfrom_mem_into(self.address, _GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map)
|
||||
|
||||
for pair in (
|
||||
(0xFF, 0x01),
|
||||
(_DYNAMIC_SPAD_REF_EN_START_OFFSET, 0x00),
|
||||
(_DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD, 0x2C),
|
||||
(0xFF, 0x00),
|
||||
(_GLOBAL_CONFIG_REF_EN_START_SELECT, 0xB4),
|
||||
):
|
||||
self._write_u8(pair[0], pair[1])
|
||||
|
||||
first_spad_to_enable = 12 if spad_is_aperture else 0
|
||||
spads_enabled = 0
|
||||
for i in range(48):
|
||||
if i < first_spad_to_enable or spads_enabled == spad_count:
|
||||
# This bit is lower than the first one that should be enabled,
|
||||
# or (reference_spad_count) bits have already been enabled, so
|
||||
# zero this bit.
|
||||
ref_spad_map[1 + (i // 8)] &= ~(1 << (i % 8))
|
||||
elif (ref_spad_map[1 + (i // 8)] >> (i % 8)) & 0x1 > 0:
|
||||
spads_enabled += 1
|
||||
self._i2c.writeto_mem(self.address, _GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map)
|
||||
for pair in (
|
||||
(0xFF, 0x01),
|
||||
(0x00, 0x00),
|
||||
(0xFF, 0x00),
|
||||
(0x09, 0x00),
|
||||
(0x10, 0x00),
|
||||
(0x11, 0x00),
|
||||
(0x24, 0x01),
|
||||
(0x25, 0xFF),
|
||||
(0x75, 0x00),
|
||||
(0xFF, 0x01),
|
||||
(0x4E, 0x2C),
|
||||
(0x48, 0x00),
|
||||
(0x30, 0x20),
|
||||
(0xFF, 0x00),
|
||||
(0x30, 0x09),
|
||||
(0x54, 0x00),
|
||||
(0x31, 0x04),
|
||||
(0x32, 0x03),
|
||||
(0x40, 0x83),
|
||||
(0x46, 0x25),
|
||||
(0x60, 0x00),
|
||||
(0x27, 0x00),
|
||||
(0x50, 0x06),
|
||||
(0x51, 0x00),
|
||||
(0x52, 0x96),
|
||||
(0x56, 0x08),
|
||||
(0x57, 0x30),
|
||||
(0x61, 0x00),
|
||||
(0x62, 0x00),
|
||||
(0x64, 0x00),
|
||||
(0x65, 0x00),
|
||||
(0x66, 0xA0),
|
||||
(0xFF, 0x01),
|
||||
(0x22, 0x32),
|
||||
(0x47, 0x14),
|
||||
(0x49, 0xFF),
|
||||
(0x4A, 0x00),
|
||||
(0xFF, 0x00),
|
||||
(0x7A, 0x0A),
|
||||
(0x7B, 0x00),
|
||||
(0x78, 0x21),
|
||||
(0xFF, 0x01),
|
||||
(0x23, 0x34),
|
||||
(0x42, 0x00),
|
||||
(0x44, 0xFF),
|
||||
(0x45, 0x26),
|
||||
(0x46, 0x05),
|
||||
(0x40, 0x40),
|
||||
(0x0E, 0x06),
|
||||
(0x20, 0x1A),
|
||||
(0x43, 0x40),
|
||||
(0xFF, 0x00),
|
||||
(0x34, 0x03),
|
||||
(0x35, 0x44),
|
||||
(0xFF, 0x01),
|
||||
(0x31, 0x04),
|
||||
(0x4B, 0x09),
|
||||
(0x4C, 0x05),
|
||||
(0x4D, 0x04),
|
||||
(0xFF, 0x00),
|
||||
(0x44, 0x00),
|
||||
(0x45, 0x20),
|
||||
(0x47, 0x08),
|
||||
(0x48, 0x28),
|
||||
(0x67, 0x00),
|
||||
(0x70, 0x04),
|
||||
(0x71, 0x01),
|
||||
(0x72, 0xFE),
|
||||
(0x76, 0x00),
|
||||
(0x77, 0x00),
|
||||
(0xFF, 0x01),
|
||||
(0x0D, 0x01),
|
||||
(0xFF, 0x00),
|
||||
(0x80, 0x01),
|
||||
(0x01, 0xF8),
|
||||
(0xFF, 0x01),
|
||||
(0x8E, 0x01),
|
||||
(0x00, 0x01),
|
||||
(0xFF, 0x00),
|
||||
(0x80, 0x00),
|
||||
):
|
||||
self._write_u8(pair[0], pair[1])
|
||||
|
||||
self._write_u8(_SYSTEM_INTERRUPT_CONFIG_GPIO, 0x04)
|
||||
gpio_hv_mux_active_high = self._read_u8(_GPIO_HV_MUX_ACTIVE_HIGH)
|
||||
self._write_u8(
|
||||
_GPIO_HV_MUX_ACTIVE_HIGH, gpio_hv_mux_active_high & ~0x10
|
||||
) # active low
|
||||
self._write_u8(_SYSTEM_INTERRUPT_CLEAR, 0x01)
|
||||
self._measurement_timing_budget_us = self.measurement_timing_budget
|
||||
self._write_u8(_SYSTEM_SEQUENCE_CONFIG, 0xE8)
|
||||
self.measurement_timing_budget = self._measurement_timing_budget_us
|
||||
self._write_u8(_SYSTEM_SEQUENCE_CONFIG, 0x01)
|
||||
self._perform_single_ref_calibration(0x40)
|
||||
self._write_u8(_SYSTEM_SEQUENCE_CONFIG, 0x02)
|
||||
self._perform_single_ref_calibration(0x00)
|
||||
# "restore the previous Sequence Config"
|
||||
self._write_u8(_SYSTEM_SEQUENCE_CONFIG, 0xE8)
|
||||
|
||||
def _read_u8(self, register):
|
||||
# Read an 8-bit unsigned value from the specified 8-bit address.
|
||||
data = self._i2c.readfrom_mem(self.address, register, 1)
|
||||
return int.from_bytes(data, 'big')
|
||||
|
||||
def _read_u16(self, register):
|
||||
# Read a 16-bit BE unsigned value from the specified 8-bit address.
|
||||
data = self._i2c.readfrom_mem(self.address, register, 2)
|
||||
return int.from_bytes(data, 'big')
|
||||
|
||||
def _write_u8(self, register, val):
|
||||
# Write an 8-bit unsigned value to the specified 8-bit address.
|
||||
self._i2c.writeto_mem(self.address, register, val.to_bytes(1, 'big'))
|
||||
|
||||
def _write_u16(self, register, val):
|
||||
# Write a 16-bit BE unsigned value to the specified 8-bit address.
|
||||
self._i2c.writeto_mem(self.address, register, val.to_bytes(2, 'big'))
|
||||
|
||||
def _get_spad_info(self):
|
||||
# Get reference SPAD count and type, returned as a 2-tuple of
|
||||
# count and boolean is_aperture. Based on code from:
|
||||
# https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
|
||||
for pair in ((0x80, 0x01), (0xFF, 0x01), (0x00, 0x00), (0xFF, 0x06)):
|
||||
self._write_u8(pair[0], pair[1])
|
||||
self._write_u8(0x83, self._read_u8(0x83) | 0x04)
|
||||
for pair in (
|
||||
(0xFF, 0x07),
|
||||
(0x81, 0x01),
|
||||
(0x80, 0x01),
|
||||
(0x94, 0x6B),
|
||||
(0x83, 0x00),
|
||||
):
|
||||
self._write_u8(pair[0], pair[1])
|
||||
start = utime.ticks_ms() / 1000
|
||||
while self._read_u8(0x83) == 0x00:
|
||||
now = utime.ticks_ms() / 1000
|
||||
if now < start:
|
||||
start = utime.ticks_ms() / 1000
|
||||
if (
|
||||
self.io_timeout_s > 0
|
||||
and (now - start) >= self.io_timeout_s
|
||||
and now > start
|
||||
):
|
||||
raise RuntimeError("Timeout waiting for VL53L0X!")
|
||||
self._write_u8(0x83, 0x01)
|
||||
tmp = self._read_u8(0x92)
|
||||
count = tmp & 0x7F
|
||||
is_aperture = ((tmp >> 7) & 0x01) == 1
|
||||
for pair in ((0x81, 0x00), (0xFF, 0x06)):
|
||||
self._write_u8(pair[0], pair[1])
|
||||
self._write_u8(0x83, self._read_u8(0x83) & ~0x04)
|
||||
for pair in ((0xFF, 0x01), (0x00, 0x01), (0xFF, 0x00), (0x80, 0x00)):
|
||||
self._write_u8(pair[0], pair[1])
|
||||
return (count, is_aperture)
|
||||
|
||||
def _perform_single_ref_calibration(self, vhv_init_byte):
|
||||
# based on VL53L0X_perform_single_ref_calibration() from ST API.
|
||||
self._write_u8(_SYSRANGE_START, 0x01 | vhv_init_byte & 0xFF)
|
||||
start = utime.ticks_ms() / 1000
|
||||
while (self._read_u8(_RESULT_INTERRUPT_STATUS) & 0x07) == 0:
|
||||
now = utime.ticks_ms() / 1000
|
||||
if now < start:
|
||||
now = utime.ticks_ms() / 1000
|
||||
if (
|
||||
self.io_timeout_s > 0
|
||||
and (now - start) >= self.io_timeout_s
|
||||
):
|
||||
raise RuntimeError("Timeout waiting for VL53L0X!")
|
||||
self._write_u8(_SYSTEM_INTERRUPT_CLEAR, 0x01)
|
||||
self._write_u8(_SYSRANGE_START, 0x00)
|
||||
|
||||
def _get_vcsel_pulse_period(self, vcsel_period_type):
|
||||
# pylint: disable=no-else-return
|
||||
# Disable should be removed when refactor can be tested
|
||||
if vcsel_period_type == _VCSEL_PERIOD_PRE_RANGE:
|
||||
val = self._read_u8(_PRE_RANGE_CONFIG_VCSEL_PERIOD)
|
||||
return (((val) + 1) & 0xFF) << 1
|
||||
elif vcsel_period_type == _VCSEL_PERIOD_FINAL_RANGE:
|
||||
val = self._read_u8(_FINAL_RANGE_CONFIG_VCSEL_PERIOD)
|
||||
return (((val) + 1) & 0xFF) << 1
|
||||
return 255
|
||||
|
||||
def _get_sequence_step_enables(self):
|
||||
# based on VL53L0X_GetSequenceStepEnables() from ST API
|
||||
sequence_config = self._read_u8(_SYSTEM_SEQUENCE_CONFIG)
|
||||
tcc = (sequence_config >> 4) & 0x1 > 0
|
||||
dss = (sequence_config >> 3) & 0x1 > 0
|
||||
msrc = (sequence_config >> 2) & 0x1 > 0
|
||||
pre_range = (sequence_config >> 6) & 0x1 > 0
|
||||
final_range = (sequence_config >> 7) & 0x1 > 0
|
||||
return (tcc, dss, msrc, pre_range, final_range)
|
||||
|
||||
def _get_sequence_step_timeouts(self, pre_range):
|
||||
# based on get_sequence_step_timeout() from ST API but modified by
|
||||
# pololu here:
|
||||
# https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
|
||||
pre_range_vcsel_period_pclks = self._get_vcsel_pulse_period(
|
||||
_VCSEL_PERIOD_PRE_RANGE
|
||||
)
|
||||
msrc_dss_tcc_mclks = (self._read_u8(_MSRC_CONFIG_TIMEOUT_MACROP) + 1) & 0xFF
|
||||
msrc_dss_tcc_us = _timeout_mclks_to_microseconds(
|
||||
msrc_dss_tcc_mclks, pre_range_vcsel_period_pclks
|
||||
)
|
||||
pre_range_mclks = _decode_timeout(
|
||||
self._read_u16(_PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI)
|
||||
)
|
||||
pre_range_us = _timeout_mclks_to_microseconds(
|
||||
pre_range_mclks, pre_range_vcsel_period_pclks
|
||||
)
|
||||
final_range_vcsel_period_pclks = self._get_vcsel_pulse_period(
|
||||
_VCSEL_PERIOD_FINAL_RANGE
|
||||
)
|
||||
final_range_mclks = _decode_timeout(
|
||||
self._read_u16(_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI)
|
||||
)
|
||||
if pre_range:
|
||||
final_range_mclks -= pre_range_mclks
|
||||
final_range_us = _timeout_mclks_to_microseconds(
|
||||
final_range_mclks, final_range_vcsel_period_pclks
|
||||
)
|
||||
return (
|
||||
msrc_dss_tcc_us,
|
||||
pre_range_us,
|
||||
final_range_us,
|
||||
final_range_vcsel_period_pclks,
|
||||
pre_range_mclks,
|
||||
)
|
||||
|
||||
@property
|
||||
def signal_rate_limit(self):
|
||||
"""The signal rate limit in mega counts per second."""
|
||||
val = self._read_u16(_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT)
|
||||
# Return value converted from 16-bit 9.7 fixed point to float.
|
||||
return val / (1 << 7)
|
||||
|
||||
@signal_rate_limit.setter
|
||||
def signal_rate_limit(self, val):
|
||||
assert 0.0 <= val <= 511.99
|
||||
# Convert to 16-bit 9.7 fixed point value from a float.
|
||||
val = int(val * (1 << 7))
|
||||
self._write_u16(_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT, val)
|
||||
|
||||
@property
|
||||
def measurement_timing_budget(self):
|
||||
"""The measurement timing budget in microseconds."""
|
||||
budget_us = 1910 + 960 # Start overhead + end overhead.
|
||||
tcc, dss, msrc, pre_range, final_range = self._get_sequence_step_enables()
|
||||
step_timeouts = self._get_sequence_step_timeouts(pre_range)
|
||||
msrc_dss_tcc_us, pre_range_us, final_range_us, _, _ = step_timeouts
|
||||
if tcc:
|
||||
budget_us += msrc_dss_tcc_us + 590
|
||||
if dss:
|
||||
budget_us += 2 * (msrc_dss_tcc_us + 690)
|
||||
elif msrc:
|
||||
budget_us += msrc_dss_tcc_us + 660
|
||||
if pre_range:
|
||||
budget_us += pre_range_us + 660
|
||||
if final_range:
|
||||
budget_us += final_range_us + 550
|
||||
self._measurement_timing_budget_us = budget_us
|
||||
return budget_us
|
||||
|
||||
@measurement_timing_budget.setter
|
||||
def measurement_timing_budget(self, budget_us):
|
||||
# pylint: disable=too-many-locals
|
||||
assert budget_us >= 20000
|
||||
used_budget_us = 1320 + 960 # Start (diff from get) + end overhead
|
||||
tcc, dss, msrc, pre_range, final_range = self._get_sequence_step_enables()
|
||||
step_timeouts = self._get_sequence_step_timeouts(pre_range)
|
||||
msrc_dss_tcc_us, pre_range_us, _ = step_timeouts[:3]
|
||||
final_range_vcsel_period_pclks, pre_range_mclks = step_timeouts[3:]
|
||||
if tcc:
|
||||
used_budget_us += msrc_dss_tcc_us + 590
|
||||
if dss:
|
||||
used_budget_us += 2 * (msrc_dss_tcc_us + 690)
|
||||
elif msrc:
|
||||
used_budget_us += msrc_dss_tcc_us + 660
|
||||
if pre_range:
|
||||
used_budget_us += pre_range_us + 660
|
||||
if final_range:
|
||||
used_budget_us += 550
|
||||
# "Note that the final range timeout is determined by the timing
|
||||
# budget and the sum of all other timeouts within the sequence.
|
||||
# If there is no room for the final range timeout, then an error
|
||||
# will be set. Otherwise the remaining time will be applied to
|
||||
# the final range."
|
||||
if used_budget_us > budget_us:
|
||||
raise ValueError("Requested timeout too big.")
|
||||
final_range_timeout_us = budget_us - used_budget_us
|
||||
final_range_timeout_mclks = _timeout_microseconds_to_mclks(
|
||||
final_range_timeout_us, final_range_vcsel_period_pclks
|
||||
)
|
||||
if pre_range:
|
||||
final_range_timeout_mclks += pre_range_mclks
|
||||
self._write_u16(
|
||||
_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI,
|
||||
_encode_timeout(final_range_timeout_mclks),
|
||||
)
|
||||
self._measurement_timing_budget_us = budget_us
|
||||
|
||||
def start_range_request(self):
|
||||
if self.range_started == True: return # Already started
|
||||
"""Start a single reading of the range for an object in front of
|
||||
the sensor. This is used both for blocking range requests (called
|
||||
by the .range property), and non-blocking range requests in the form:
|
||||
sensor.start_range_request()
|
||||
...
|
||||
if sensor.reading_available():
|
||||
print(sensor.get_range_value())
|
||||
"""
|
||||
# Adapted from readRangeSingleMillimeters &
|
||||
# readRangeContinuousMillimeters in pololu code at:
|
||||
# https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
|
||||
for pair in (
|
||||
(0x80, 0x01),
|
||||
(0xFF, 0x01),
|
||||
(0x00, 0x00),
|
||||
(0x91, self._stop_variable),
|
||||
(0x00, 0x01),
|
||||
(0xFF, 0x00),
|
||||
(0x80, 0x00),
|
||||
(_SYSRANGE_START, 0x01),
|
||||
):
|
||||
self._write_u8(pair[0], pair[1])
|
||||
self.range_started = True
|
||||
|
||||
# Return true if a range request initiated by start_range_request
|
||||
# is available to be read via self.get_range_value().
|
||||
def reading_available(self):
|
||||
if self.range_started == False: return False
|
||||
if (self._read_u8(_RESULT_INTERRUPT_STATUS) & 0x07) == 0: return False
|
||||
return True
|
||||
|
||||
# After start_range_request(), once the sensor completed a reading
|
||||
# this function will return the final range value in millimeters.
|
||||
# As a side effect this function will also clear the interrupt status
|
||||
# flag.
|
||||
def get_range_value(self):
|
||||
if self.range_started == False: return None
|
||||
|
||||
# assumptions: Linearity Corrective Gain is 1000 (default)
|
||||
# fractional ranging is not enabled
|
||||
range_mm = self._read_u16(_RESULT_RANGE_STATUS + 10)
|
||||
self._write_u8(_SYSTEM_INTERRUPT_CLEAR, 0x01)
|
||||
self.range_started = False # Don't return the same data again.
|
||||
return range_mm
|
||||
|
||||
@property
|
||||
def range(self):
|
||||
# If there is a non-blocking measure ongoing, don't initiate
|
||||
# a blocking one.
|
||||
if self.range_started: return None
|
||||
|
||||
self.start_range_request()
|
||||
|
||||
start = utime.ticks_ms()
|
||||
while self.reading_available() == False:
|
||||
if (utime.ticks_diff(utime.ticks_ms(),start) > 1000):
|
||||
raise RuntimeError("Timeout waiting for VL53L0X!")
|
||||
utime.sleep_ms(5)
|
||||
|
||||
return self.get_range_value()
|
||||
|
||||
def set_address(self, new_address):
|
||||
self._write_u8(_I2C_SLAVE_DEVICE_ADDRESS, new_address & 0x7F)
|
||||
# self._device = i2c_device.I2CDevice(self._i2c, new_address)
|
||||
print("new address is ")
|
||||
return new_address
|
||||
|
||||
Reference in New Issue
Block a user