tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/lsm9ds1/lsm9ds1.go (about)

     1  // LSM9DS1, 9 axis Inertial Measurement Unit (IMU)
     2  //
     3  // Datasheet: https://www.st.com/resource/en/datasheet/lsm6ds3.pdf
     4  package lsm9ds1 // import "tinygo.org/x/drivers/lsm9ds1"
     5  
     6  import (
     7  	"errors"
     8  
     9  	"tinygo.org/x/drivers"
    10  	"tinygo.org/x/drivers/internal/legacy"
    11  )
    12  
    13  type AccelRange uint8
    14  type AccelSampleRate uint8
    15  type AccelBandwidth uint8
    16  
    17  type GyroRange uint8
    18  type GyroSampleRate uint8
    19  
    20  type MagRange uint8
    21  type MagSampleRate uint8
    22  
    23  // Device wraps connection to a LSM9DS1 device.
    24  type Device struct {
    25  	bus             drivers.I2C
    26  	AccelAddress    uint8
    27  	MagAddress      uint8
    28  	accelMultiplier int32
    29  	gyroMultiplier  int32
    30  	magMultiplier   int32
    31  	buf             [6]uint8
    32  }
    33  
    34  // Configuration for LSM9DS1 device.
    35  type Configuration struct {
    36  	AccelRange      AccelRange
    37  	AccelSampleRate AccelSampleRate
    38  	AccelBandWidth  AccelBandwidth
    39  	GyroRange       GyroRange
    40  	GyroSampleRate  GyroSampleRate
    41  	MagRange        MagRange
    42  	MagSampleRate   MagSampleRate
    43  }
    44  
    45  var errNotConnected = errors.New("lsm9ds1: failed to communicate with either acel/gyro or magnet sensor")
    46  
    47  // New creates a new LSM9DS1 connection. The I2C bus must already be configured.
    48  //
    49  // This function only creates the Device object, it does not touch the device.
    50  func New(bus drivers.I2C) *Device {
    51  	return &Device{
    52  		bus:          bus,
    53  		AccelAddress: ACCEL_ADDRESS,
    54  		MagAddress:   MAG_ADDRESS,
    55  	}
    56  }
    57  
    58  // Connected returns whether both sensor on LSM9DS1 has been found.
    59  // It does two "who am I" requests and checks the responses.
    60  // In a rare case of an I2C bus issue, it can also return an error.
    61  // Case of boolean false and error nil means I2C is up,
    62  // but "who am I" responses have unexpected values.
    63  func (d *Device) Connected() bool {
    64  	data1, data2 := d.buf[:1], d.buf[1:2]
    65  	legacy.ReadRegister(d.bus, d.AccelAddress, WHO_AM_I, data1)
    66  	legacy.ReadRegister(d.bus, d.MagAddress, WHO_AM_I_M, data2)
    67  	return data1[0] == 0x68 && data2[0] == 0x3D
    68  }
    69  
    70  // ReadAcceleration reads the current acceleration from the device and returns
    71  // it in µg (micro-gravity). When one of the axes is pointing straight to Earth
    72  // and the sensor is not moving the returned value will be around 1000000 or
    73  // -1000000.
    74  func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
    75  	data := d.buf[:6]
    76  	err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), OUT_X_L_XL, data)
    77  	if err != nil {
    78  		return
    79  	}
    80  	x = int32(int16((uint16(data[1])<<8)|uint16(data[0]))) * d.accelMultiplier
    81  	y = int32(int16((uint16(data[3])<<8)|uint16(data[2]))) * d.accelMultiplier
    82  	z = int32(int16((uint16(data[5])<<8)|uint16(data[4]))) * d.accelMultiplier
    83  	return
    84  }
    85  
    86  // ReadRotation reads the current rotation from the device and returns it in
    87  // µ°/s (micro-degrees/sec). This means that if you were to do a complete
    88  // rotation along one axis and while doing so integrate all values over time,
    89  // you would get a value close to 360000000.
    90  func (d *Device) ReadRotation() (x, y, z int32, err error) {
    91  	data := d.buf[:6]
    92  	err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), OUT_X_L_G, data)
    93  	if err != nil {
    94  		return
    95  	}
    96  	x = int32(int16((uint16(data[1])<<8)|uint16(data[0]))) * d.gyroMultiplier
    97  	y = int32(int16((uint16(data[3])<<8)|uint16(data[2]))) * d.gyroMultiplier
    98  	z = int32(int16((uint16(data[5])<<8)|uint16(data[4]))) * d.gyroMultiplier
    99  	return
   100  }
   101  
   102  // ReadMagneticField reads the current magnetic field from the device and returns
   103  // it in nT (nanotesla). 1 G (gauss) = 100_000 nT (nanotesla).
   104  func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
   105  	data := d.buf[:6]
   106  	err = legacy.ReadRegister(d.bus, uint8(d.MagAddress), OUT_X_L_M, data)
   107  	if err != nil {
   108  		return
   109  	}
   110  	x = int32(int16((int16(data[1])<<8)|int16(data[0]))) * d.magMultiplier
   111  	y = int32(int16((int16(data[3])<<8)|int16(data[2]))) * d.magMultiplier
   112  	z = int32(int16((int16(data[5])<<8)|int16(data[4]))) * d.magMultiplier
   113  	return
   114  }
   115  
   116  // ReadTemperature returns the temperature in Celsius milli degrees (°C/1000)
   117  func (d *Device) ReadTemperature() (t int32, err error) {
   118  	data := d.buf[:2]
   119  	err = legacy.ReadRegister(d.bus, uint8(d.AccelAddress), OUT_TEMP_L, data)
   120  	if err != nil {
   121  		return
   122  	}
   123  	// From "Table 5. Temperature sensor characteristics"
   124  	// temp = value/16 + 25
   125  	t = 25000 + (int32(int16((int16(data[1])<<8)|int16(data[0])))*125)/2
   126  	return
   127  }
   128  
   129  // --- end of public methods --------------------------------------------------
   130  
   131  // doConfigure is called by public Configure methods after all
   132  // necessary board-specific initialisations are taken care of
   133  func (d *Device) doConfigure(cfg Configuration) (err error) {
   134  
   135  	// Verify unit communication
   136  	if !d.Connected() {
   137  		return errNotConnected
   138  	}
   139  
   140  	// Multipliers come from "Table 3. Sensor characteristics" of the datasheet * 1000
   141  	switch cfg.AccelRange {
   142  	case ACCEL_2G:
   143  		d.accelMultiplier = 61
   144  	case ACCEL_4G:
   145  		d.accelMultiplier = 122
   146  	case ACCEL_8G:
   147  		d.accelMultiplier = 244
   148  	case ACCEL_16G:
   149  		d.accelMultiplier = 732
   150  	}
   151  	switch cfg.GyroRange {
   152  	case GYRO_250DPS:
   153  		d.gyroMultiplier = 8750
   154  	case GYRO_500DPS:
   155  		d.gyroMultiplier = 17500
   156  	case GYRO_2000DPS:
   157  		d.gyroMultiplier = 70000
   158  	}
   159  	switch cfg.MagRange {
   160  	case MAG_4G:
   161  		d.magMultiplier = 14
   162  	case MAG_8G:
   163  		d.magMultiplier = 29
   164  	case MAG_12G:
   165  		d.magMultiplier = 43
   166  	case MAG_16G:
   167  		d.magMultiplier = 58
   168  	}
   169  
   170  	data := d.buf[:1]
   171  
   172  	// Configure accelerometer
   173  	// Sample rate & measurement range
   174  	data[0] = uint8(cfg.AccelSampleRate)<<5 | uint8(cfg.AccelRange)<<3
   175  	err = legacy.WriteRegister(d.bus, d.AccelAddress, CTRL_REG6_XL, data)
   176  	if err != nil {
   177  		return
   178  	}
   179  
   180  	// Configure gyroscope
   181  	// Sample rate & measurement range
   182  	data[0] = uint8(cfg.GyroSampleRate)<<5 | uint8(cfg.GyroRange)<<3
   183  	err = legacy.WriteRegister(d.bus, d.AccelAddress, CTRL_REG1_G, data)
   184  	if err != nil {
   185  		return
   186  	}
   187  
   188  	// Configure magnetometer
   189  
   190  	// Temperature compensation enabled
   191  	// High-performance mode XY axis
   192  	// Sample rate
   193  	data[0] = 0b10000000 | 0b01000000 | uint8(cfg.MagSampleRate)<<2
   194  	err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG1_M, data)
   195  	if err != nil {
   196  		return
   197  	}
   198  
   199  	// Measurement range
   200  	data[0] = uint8(cfg.MagRange) << 5
   201  	err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG2_M, data)
   202  	if err != nil {
   203  		return
   204  	}
   205  
   206  	// Continuous-conversion mode
   207  	// https://electronics.stackexchange.com/questions/237397/continuous-conversion-vs-single-conversion-mode
   208  	data[0] = 0b00000000
   209  	err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG3_M, data)
   210  	if err != nil {
   211  		return
   212  	}
   213  
   214  	// High-performance mode Z axis
   215  	data[0] = 0b00001000
   216  	err = legacy.WriteRegister(d.bus, d.MagAddress, CTRL_REG4_M, data)
   217  	if err != nil {
   218  		return
   219  	}
   220  
   221  	return nil
   222  }