diff --git a/.gitignore b/.gitignore
index fbbe195..749ee0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
# Default ignored files
/shelf/
/.idea/workspace.xml
+/out
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..933ff36
--- /dev/null
+++ b/README.md
@@ -0,0 +1,82 @@
+# Лазерный дальномер
+
+Создан для микроконтроллера RP2040.
+
+## Подключение
+
+Схема подключения OLED дисплея SSD1306 (интерфейс SPI):
+
+
+
+ MCU |
+ SSD1306 |
+
+
+
+
+ GND |
+ GND |
+
+
+ 3V3 |
+ VCC |
+
+
+ P18 (SPI0_SCK) |
+ D0 (SCLK) |
+
+
+ P19 (SPI0_TX) |
+ D1 (SDIN) |
+
+
+ P20 |
+ RES (сброс) |
+
+
+ P21 |
+ DC (данные/команда) |
+
+
+
+
+Схема подключения лазерного дальномера VL53L0X (интерфейс I2C):
+
+
+
+ MCU |
+ SSD1306 |
+
+
+
+
+ 3V3 |
+ VCC |
+
+
+ GND |
+ GND |
+
+
+ P17 (I2C0_SCL) |
+ SCL |
+
+
+ P16 (I2C0_SDA) |
+ SDA |
+
+
+
+
+## Прошивка
+
+Первым делом нужно зашить Micropython на микроконтроллер.
+
+Для этого нужно выполнить следующие шаги:
+* зажать кнопку BOOT на плате
+* подключить микроконтроллер к ПК
+* скопировать на появившийся съемный диск "RPI-RP2" специальный файл _.uf2_
+
+Далее необходимо открыть любую IDE, поддерживающую Micropython (обычно Thonny IDE)
+и залить проект из этого репозитория.
+
diff --git a/src/VL53L0X.py b/src/VL53L0X.py
new file mode 100644
index 0000000..1cc59f7
--- /dev/null
+++ b/src/VL53L0X.py
@@ -0,0 +1,651 @@
+"""
+Library from https://github.com/uceeatz/VL53L0X/blob/master/VL53L0X.py
+"""
+import time
+
+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)
+
+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):
+ self._register(SYSRANGE_START, 0x01|vhv_init_byte)
+ start = time.ticks_ms()
+ while self._register((RESULT_INTERRUPT_STATUS & 0x07) == 0):
+ time_elapsed = time.ticks_diff(time.ticks_ms(), start)
+ if time_elapsed > _IO_TIMEOUT:
+ return False
+ self._register(SYSTEM_INTERRUPT_CLEAR, 0x01)
+ self._register(SYSRANGE_START, 0x00)
+ return True
diff --git a/src/main.py b/src/main.py
new file mode 100644
index 0000000..30491a2
--- /dev/null
+++ b/src/main.py
@@ -0,0 +1,58 @@
+import time
+from machine import Pin, SPI
+from machine import I2C
+import VL53L0X
+from ssd1306 import Ssd1306Spi
+
+onboard_led = Pin(25, Pin.OUT)
+
+# OLED
+oled_spi = SPI(0, 1000000, mosi=Pin(19), sck=Pin(18))
+oled = Ssd1306Spi(width=128, height=64, spi=oled_spi, dc=Pin(21), res=Pin(20), cs=None)
+
+# бут лого
+oled.fill(0)
+oled.text("Lazer", 0, 0)
+oled.text("rangefinder", 0, 15)
+
+oled.text("By bigboy_2010", 0, 52)
+oled.show()
+time.sleep_ms(2000)
+
+i2c = I2C(0, sda=Pin(16), scl=Pin(17))
+
+# Create a VL53L0X object
+tof = VL53L0X.VL53L0X(i2c)
+tof.set_Vcsel_pulse_period(tof.vcsel_period_type[0], 18)
+tof.set_Vcsel_pulse_period(tof.vcsel_period_type[1], 14)
+
+
+while True:
+ # Start ranging
+
+ ms = []
+ for _ in range(10):
+ tof.start()
+ distance = tof.read()
+ tof.stop()
+ ms.append(distance)
+
+ distance = 0
+ for m in ms:
+ distance += m
+ distance = int(distance / 10) / 10 # сразу получаем сантиметры
+
+ if distance >= 200:
+ distance = ''
+
+ if onboard_led.value() != 0:
+ onboard_led.off()
+ else:
+ onboard_led.on()
+
+ oled.fill(0)
+ oled.text("Distance:", 0, 0)
+ oled.text(f"{distance} cm", 0, 25)
+ oled.show()
+
+ # time.sleep_ms(100)
diff --git a/src/ssd1306.py b/src/ssd1306.py
new file mode 100644
index 0000000..2701fd8
--- /dev/null
+++ b/src/ssd1306.py
@@ -0,0 +1,163 @@
+# MicroPython SSD1306 OLED driver, I2C and SPI interfaces
+from micropython import const
+import framebuf
+
+SET_CONTRAST = const(0x81)
+SET_ENTIRE_ON = const(0xA4)
+SET_NORM_INV = const(0xA6)
+SET_DISP = const(0xAE)
+SET_MEM_ADDR = const(0x20)
+SET_COL_ADDR = const(0x21)
+SET_PAGE_ADDR = const(0x22)
+SET_DISP_START_LINE = const(0x40)
+SET_SEG_REMAP = const(0xA0)
+SET_MUX_RATIO = const(0xA8)
+SET_COM_OUT_DIR = const(0xC0)
+SET_DISP_OFFSET = const(0xD3)
+SET_COM_PIN_CFG = const(0xDA)
+SET_DISP_CLK_DIV = const(0xD5)
+SET_PRECHARGE = const(0xD9)
+SET_VCOM_DESEL = const(0xDB)
+SET_CHARGE_PUMP = const(0x8D)
+
+
+class Ssd1306(framebuf.FrameBuffer):
+ def __init__(self, width, height, external_vcc):
+ self.width = width
+ self.height = height
+ self.external_vcc = external_vcc
+ self.pages = self.height // 8
+ self.buffer = bytearray(self.pages * self.width)
+ super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
+ self.init_display()
+
+ def init_display(self):
+ for cmd in (
+ SET_DISP | 0x00, # off
+ # address setting
+ SET_MEM_ADDR,
+ 0x00, # horizontal
+ # resolution and layout
+ SET_DISP_START_LINE | 0x00,
+ SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
+ SET_MUX_RATIO,
+ self.height - 1,
+ SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
+ SET_DISP_OFFSET,
+ 0x00,
+ SET_COM_PIN_CFG,
+ 0x02 if self.width > 2 * self.height else 0x12,
+ # timing and driving scheme
+ SET_DISP_CLK_DIV,
+ 0x80,
+ SET_PRECHARGE,
+ 0x22 if self.external_vcc else 0xF1,
+ SET_VCOM_DESEL,
+ 0x30, # 0.83*Vcc
+ # display
+ SET_CONTRAST,
+ 0xFF, # maximum
+ SET_ENTIRE_ON, # output follows RAM contents
+ SET_NORM_INV, # not inverted
+ # charge pump
+ SET_CHARGE_PUMP,
+ 0x10 if self.external_vcc else 0x14,
+ SET_DISP | 0x01,
+ ): # on
+ self.write_cmd(cmd)
+ self.fill(0)
+ self.show()
+
+ def poweroff(self):
+ self.write_cmd(SET_DISP | 0x00)
+
+ def poweron(self):
+ self.write_cmd(SET_DISP | 0x01)
+
+ def contrast(self, contrast):
+ self.write_cmd(SET_CONTRAST)
+ self.write_cmd(contrast)
+
+ def invert(self, invert):
+ self.write_cmd(SET_NORM_INV | (invert & 1))
+
+ def show(self):
+ x0 = 0
+ x1 = self.width - 1
+ if self.width == 64:
+ # displays with width of 64 pixels are shifted by 32
+ x0 += 32
+ x1 += 32
+ self.write_cmd(SET_COL_ADDR)
+ self.write_cmd(x0)
+ self.write_cmd(x1)
+ self.write_cmd(SET_PAGE_ADDR)
+ self.write_cmd(0)
+ self.write_cmd(self.pages - 1)
+ self.write_data(self.buffer)
+
+ def write_cmd(self, cmd):
+ pass
+
+ def write_data(self, buffer):
+ pass
+
+
+class Ssd1306I2C(Ssd1306):
+ def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):
+ self.i2c = i2c
+ self.addr = addr
+ self.temp = bytearray(2)
+ self.write_list = [b"\x40", None] # Co=0, D/C#=1
+ super().__init__(width, height, external_vcc)
+
+ def write_cmd(self, cmd):
+ self.temp[0] = 0x80 # Co=1, D/C#=0
+ self.temp[1] = cmd
+ self.i2c.writeto(self.addr, self.temp)
+
+ def write_data(self, buf):
+ self.write_list[1] = buf
+ self.i2c.writevto(self.addr, self.write_list)
+
+
+class Ssd1306Spi(Ssd1306):
+ def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
+ self.rate = 10 * 1024 * 1024
+ dc.init(dc.OUT, value=0)
+ res.init(res.OUT, value=0)
+ if cs is not None:
+ cs.init(cs.OUT, value=1)
+ self.spi = spi
+ self.dc = dc
+ self.res = res
+ self.cs = cs
+ import time
+ self.res(1)
+ time.sleep_ms(1)
+ self.res(0)
+ time.sleep_ms(10)
+ self.res(1)
+ super().__init__(width, height, external_vcc)
+
+ def write_cmd(self, cmd):
+ self.spi.init(baudrate=self.rate, polarity=0, phase=0)
+ if self.cs is not None:
+ self.cs(1)
+ self.dc(0)
+ if self.cs is not None:
+ self.cs(0)
+ self.spi.write(bytearray([cmd]))
+ if self.cs is not None:
+ self.cs(1)
+
+ def write_data(self, buf):
+ self.spi.init(baudrate=self.rate, polarity=0, phase=0)
+ if self.cs is not None:
+ self.cs(1)
+ self.dc(1)
+ if self.cs is not None:
+ self.cs(0)
+ self.spi.write(buf)
+ if self.cs is not None:
+ self.cs(1)