changeset 0:2712087921a8 draft

Initial commit
author Radomir Dopieralski <sheep-devel@sheep.art.pl>
date Sat, 13 May 2017 23:00:43 +0200
parents
children 21a56b01a9b1
files vl53l0x.py
diffstat 1 files changed, 334 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vl53l0x.py	Sat May 13 23:00:43 2017 +0200
@@ -0,0 +1,334 @@
+from micropython import const
+import ustruct
+import utime
+
+
+_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)
+
+
+class TimeoutError(RuntimeError):
+    pass
+
+
+class VL53L0X:
+    def __init__(self, i2c, address=0x29):
+        self.i2c = i2c
+        self.address = address
+        self.init()
+        self._started = False
+
+    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),
+            (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, 2, True)
+        self._flag(_MSRC_CONFIG, 3, True)
+
+        # rate_limit = 0.25
+        self._register(_FINAL_RATE_RTN_LIMIT, int(0.25 * (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),
+        )
+
+    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