You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
141 lines
4.7 KiB
141 lines
4.7 KiB
import os
|
|
import math
|
|
import time
|
|
|
|
from cereal import log
|
|
from openpilot.system.sensord.sensors.i2c_sensor import Sensor
|
|
|
|
class LSM6DS3_Gyro(Sensor):
|
|
LSM6DS3_GYRO_I2C_REG_DRDY_CFG = 0x0B
|
|
LSM6DS3_GYRO_I2C_REG_INT1_CTRL = 0x0D
|
|
LSM6DS3_GYRO_I2C_REG_CTRL2_G = 0x11
|
|
LSM6DS3_GYRO_I2C_REG_CTRL5_C = 0x14
|
|
LSM6DS3_GYRO_I2C_REG_STAT_REG = 0x1E
|
|
LSM6DS3_GYRO_I2C_REG_OUTX_L_G = 0x22
|
|
|
|
LSM6DS3_GYRO_ODR_104HZ = (0b0100 << 4)
|
|
LSM6DS3_GYRO_INT1_DRDY_G = 0b10
|
|
LSM6DS3_GYRO_DRDY_GDA = 0b10
|
|
LSM6DS3_GYRO_DRDY_PULSE_MODE = (1 << 7)
|
|
|
|
LSM6DS3_GYRO_ODR_208HZ = (0b0101 << 4)
|
|
LSM6DS3_GYRO_FS_2000dps = (0b11 << 2)
|
|
LSM6DS3_GYRO_POSITIVE_TEST = (0b01 << 2)
|
|
LSM6DS3_GYRO_NEGATIVE_TEST = (0b11 << 2)
|
|
LSM6DS3_GYRO_MIN_ST_LIMIT_mdps = 150000.0
|
|
LSM6DS3_GYRO_MAX_ST_LIMIT_mdps = 700000.0
|
|
|
|
@property
|
|
def device_address(self) -> int:
|
|
return 0x6A
|
|
|
|
def init(self):
|
|
chip_id = self.verify_chip_id(0x0F, [0x69, 0x6A])
|
|
if chip_id == 0x6A:
|
|
self.source = log.SensorEventData.SensorSource.lsm6ds3trc
|
|
else:
|
|
self.source = log.SensorEventData.SensorSource.lsm6ds3
|
|
|
|
# self-test
|
|
if "LSM_SELF_TEST" in os.environ:
|
|
self.self_test(self.LSM6DS3_GYRO_POSITIVE_TEST)
|
|
self.self_test(self.LSM6DS3_GYRO_NEGATIVE_TEST)
|
|
|
|
# actual init
|
|
self.writes((
|
|
# TODO: set scale. Default is +- 250 deg/s
|
|
(self.LSM6DS3_GYRO_I2C_REG_CTRL2_G, self.LSM6DS3_GYRO_ODR_104HZ),
|
|
# Configure data ready signal to pulse mode
|
|
(self.LSM6DS3_GYRO_I2C_REG_DRDY_CFG, self.LSM6DS3_GYRO_DRDY_PULSE_MODE),
|
|
))
|
|
value = self.read(self.LSM6DS3_GYRO_I2C_REG_INT1_CTRL, 1)[0]
|
|
value |= self.LSM6DS3_GYRO_INT1_DRDY_G
|
|
self.write(self.LSM6DS3_GYRO_I2C_REG_INT1_CTRL, value)
|
|
|
|
def get_event(self, ts: int | None = None) -> log.SensorEventData:
|
|
assert ts is not None # must come from the IRQ event
|
|
|
|
# Check if gyroscope data is ready, since it's shared with accelerometer
|
|
status_reg = self.read(self.LSM6DS3_GYRO_I2C_REG_STAT_REG, 1)[0]
|
|
if not (status_reg & self.LSM6DS3_GYRO_DRDY_GDA):
|
|
raise self.DataNotReady
|
|
|
|
b = self.read(self.LSM6DS3_GYRO_I2C_REG_OUTX_L_G, 6)
|
|
x = self.parse_16bit(b[0], b[1])
|
|
y = self.parse_16bit(b[2], b[3])
|
|
z = self.parse_16bit(b[4], b[5])
|
|
scale = (8.75 / 1000.0) * (math.pi / 180.0)
|
|
xyz = [y * scale, -x * scale, z * scale]
|
|
|
|
event = log.SensorEventData.new_message()
|
|
event.timestamp = ts
|
|
event.version = 2
|
|
event.sensor = 5 # SENSOR_GYRO_UNCALIBRATED
|
|
event.type = 16 # SENSOR_TYPE_GYROSCOPE_UNCALIBRATED
|
|
event.source = self.source
|
|
g = event.init('gyroUncalibrated')
|
|
g.v = xyz
|
|
g.status = 1
|
|
return event
|
|
|
|
def shutdown(self) -> None:
|
|
# Disable data ready interrupt on INT1
|
|
value = self.read(self.LSM6DS3_GYRO_I2C_REG_INT1_CTRL, 1)[0]
|
|
value &= ~self.LSM6DS3_GYRO_INT1_DRDY_G
|
|
self.write(self.LSM6DS3_GYRO_I2C_REG_INT1_CTRL, value)
|
|
|
|
# Power down by clearing ODR bits
|
|
value = self.read(self.LSM6DS3_GYRO_I2C_REG_CTRL2_G, 1)[0]
|
|
value &= 0x0F
|
|
self.write(self.LSM6DS3_GYRO_I2C_REG_CTRL2_G, value)
|
|
|
|
# *** self-test stuff ***
|
|
def _wait_for_data_ready(self):
|
|
while True:
|
|
drdy = self.read(self.LSM6DS3_GYRO_I2C_REG_STAT_REG, 1)[0]
|
|
if drdy & self.LSM6DS3_GYRO_DRDY_GDA:
|
|
break
|
|
|
|
def _read_and_avg_data(self) -> list[float]:
|
|
out_buf = [0.0, 0.0, 0.0]
|
|
for _ in range(5):
|
|
self._wait_for_data_ready()
|
|
b = self.read(self.LSM6DS3_GYRO_I2C_REG_OUTX_L_G, 6)
|
|
for j in range(3):
|
|
val = self.parse_16bit(b[j*2], b[j*2+1]) * 70.0 # mdps/LSB for 2000 dps
|
|
out_buf[j] += val
|
|
return [x / 5.0 for x in out_buf]
|
|
|
|
def self_test(self, test_type: int):
|
|
# Set ODR to 208Hz, FS to 2000dps
|
|
self.write(self.LSM6DS3_GYRO_I2C_REG_CTRL2_G, self.LSM6DS3_GYRO_ODR_208HZ | self.LSM6DS3_GYRO_FS_2000dps)
|
|
|
|
# Wait for stable output
|
|
time.sleep(0.15)
|
|
self._wait_for_data_ready()
|
|
val_st_off = self._read_and_avg_data()
|
|
|
|
# Enable self-test
|
|
self.write(self.LSM6DS3_GYRO_I2C_REG_CTRL5_C, test_type)
|
|
|
|
# Wait for stable output
|
|
time.sleep(0.05)
|
|
self._wait_for_data_ready()
|
|
val_st_on = self._read_and_avg_data()
|
|
|
|
# Disable sensor and self-test
|
|
self.write(self.LSM6DS3_GYRO_I2C_REG_CTRL2_G, 0)
|
|
self.write(self.LSM6DS3_GYRO_I2C_REG_CTRL5_C, 0)
|
|
|
|
# Calculate differences and check limits
|
|
test_val = [abs(on - off) for on, off in zip(val_st_on, val_st_off, strict=False)]
|
|
for val in test_val:
|
|
if val < self.LSM6DS3_GYRO_MIN_ST_LIMIT_mdps or val > self.LSM6DS3_GYRO_MAX_ST_LIMIT_mdps:
|
|
raise Exception(f"Gyroscope self-test failed for test type {test_type}")
|
|
|
|
if __name__ == "__main__":
|
|
s = LSM6DS3_Gyro(1)
|
|
s.init()
|
|
time.sleep(0.1)
|
|
print(s.get_event(0))
|
|
s.shutdown()
|
|
|