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 }