tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/mpu6886/mpu6886.go (about) 1 // Package mpu6886 provides a driver for the MPU6886 accelerometer and gyroscope 2 // made by InvenSense. 3 // 4 // Datasheet: 5 // https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/core/MPU-6886-000193%2Bv1.1_GHIC_en.pdf 6 package mpu6886 // import "tinygo.org/x/drivers/mpu6886" 7 8 import ( 9 "errors" 10 "time" 11 12 "tinygo.org/x/drivers" 13 ) 14 15 const WhoAmI = 0x19 16 17 var errNotConnected = errors.New("mpu6886: failed to communicate with a sensor") 18 19 // Device wraps an I2C connection to a MPU6886 device. 20 type Device struct { 21 bus drivers.I2C 22 Address uint16 23 aRange uint8 24 gRange uint8 25 } 26 27 // Config contains settings for filtering, sampling, and modes of operation 28 type Config struct { 29 AccelRange uint8 30 GyroRange uint8 31 } 32 33 // New creates a new MPU6886 connection. The I2C bus must already be 34 // configured. 35 // 36 // This function only creates the Device object, it does not touch the device. 37 func New(bus drivers.I2C) *Device { 38 return &Device{bus: bus, Address: DefaultAddress} 39 } 40 41 // Connected returns whether a MPU6886 has been found. 42 // It does a "who am I" request and checks the response. 43 func (d *Device) Connected() bool { 44 data := []byte{0} 45 d.bus.Tx(d.Address, []byte{WHO_AM_I}, data) 46 return data[0] == WhoAmI 47 } 48 49 // Configure sets up the device for communication. 50 func (d *Device) Configure(config Config) (err error) { 51 if config.AccelRange < 4 { 52 d.aRange = config.AccelRange 53 } 54 if config.GyroRange < 4 { 55 d.gRange = config.GyroRange 56 } 57 58 if !d.Connected() { 59 return errNotConnected 60 } 61 // This initialization sequence is borrowed from Arduino M5Stack library 62 // Zero register 63 if err = d.bus.Tx(d.Address, []byte{PWR_MGMT_1, 0x00}, nil); err != nil { 64 return 65 } 66 time.Sleep(10 * time.Millisecond) 67 // Set DEVICE_RESET bit 68 if err = d.bus.Tx(d.Address, []byte{PWR_MGMT_1, 0x80}, nil); err != nil { 69 return 70 } 71 time.Sleep(10 * time.Millisecond) 72 // Set CLKSEL to 1 - Auto selects the best available clock source 73 if err = d.bus.Tx(d.Address, []byte{PWR_MGMT_1, 0x01}, nil); err != nil { 74 return 75 } 76 time.Sleep(10 * time.Millisecond) 77 // Set ACCEL_FS_SEL 78 if err = d.bus.Tx(d.Address, []byte{ACCEL_CONFIG, d.aRange << 3}, nil); err != nil { 79 return 80 } 81 time.Sleep(time.Millisecond) 82 // Set FS_SEL 83 if err = d.bus.Tx(d.Address, []byte{GYRO_CONFIG, d.gRange << 3}, nil); err != nil { 84 return 85 } 86 time.Sleep(time.Millisecond) 87 // default: 0x80, set DLPF_CFG to 001 (Low Pass Filter) 88 if err = d.bus.Tx(d.Address, []byte{CONFIG, 0x01}, nil); err != nil { 89 return 90 } 91 time.Sleep(time.Millisecond) 92 // Set sample rate divisor, sample rate is ~ 170 Hz 93 if err = d.bus.Tx(d.Address, []byte{SMPLRT_DIV, 0x05}, nil); err != nil { 94 return 95 } 96 time.Sleep(time.Millisecond) 97 // Set Interupt pin 98 if err = d.bus.Tx(d.Address, []byte{INT_PIN_CFG, 0x22}, nil); err != nil { 99 return 100 } 101 time.Sleep(time.Millisecond) 102 // Enable DATA_RDY_INT_EN 103 if err = d.bus.Tx(d.Address, []byte{INT_ENABLE, 0x01}, nil); err != nil { 104 return 105 } 106 time.Sleep(100 * time.Millisecond) 107 return nil 108 } 109 110 // ReadTemperature returns the temperature in Celsius millidegrees (°C/1000). 111 func (d *Device) ReadTemperature() (t int32, err error) { 112 data := make([]byte, 2) 113 if err = d.bus.Tx(d.Address, []byte{TEMP_OUT_H}, data); err != nil { 114 return 115 } 116 rawTemperature := int32(int16((uint16(data[0]) << 8) | uint16(data[1]))) 117 // The formula to convert to degrre of Celsius is 118 // T_C = T_raw / 326.8 + 25.0 119 // This formula should not overflow 120 t = rawTemperature*10000/3268 + 25000 121 return 122 } 123 124 // ReadAcceleration reads the current acceleration from the device and returns 125 // it in µg (micro-gravity). When one of the axes is pointing straight to Earth 126 // and the sensor is not moving the returned value will be around 1000000 or 127 // -1000000. 128 func (d *Device) ReadAcceleration() (x int32, y int32, z int32, err error) { 129 data := make([]byte, 6) 130 if err = d.bus.Tx(d.Address, []byte{ACCEL_XOUT_H}, data); err != nil { 131 return 132 } 133 // Now do two things: 134 // 1. merge the two values to a 16-bit number (and cast to a 32-bit integer) 135 // 2. scale the value to bring it in the -1000000..1000000 range. 136 // This is done with a trick. What we do here is essentially multiply by 137 // 1000000 and divide by 16384 to get the original scale, but to avoid 138 // overflow we do it at 1/64 of the value: 139 // 1000000 / 64 = 15625 140 // 16384 / 64 = 256 141 divider := int32(1) 142 switch d.aRange { 143 case AFS_RANGE_2_G: 144 divider = 256 145 case AFS_RANGE_4_G: 146 divider = 128 147 case AFS_RANGE_8_G: 148 divider = 64 149 case AFS_RANGE_16_G: 150 divider = 32 151 } 152 x = int32(int16((uint16(data[0])<<8)|uint16(data[1]))) * 15625 / divider 153 y = int32(int16((uint16(data[2])<<8)|uint16(data[3]))) * 15625 / divider 154 z = int32(int16((uint16(data[4])<<8)|uint16(data[5]))) * 15625 / divider 155 return 156 } 157 158 // ReadRotation reads the current rotation from the device and returns it in 159 // µ°/s (micro-degrees/sec). This means that if you were to do a complete 160 // rotation along one axis and while doing so integrate all values over time, 161 // you would get a value close to 360000000. 162 func (d *Device) ReadRotation() (x int32, y int32, z int32, err error) { 163 data := make([]byte, 6) 164 if err = d.bus.Tx(d.Address, []byte{GYRO_XOUT_H}, data); err != nil { 165 return 166 } 167 // First the value is converted from a pair of bytes to a signed 16-bit 168 // value and then to a signed 32-bit value to avoid integer overflow. 169 // Then the value is scaled to µ°/s (micro-degrees per second). 170 // This is done in the following steps: 171 // 1. Multiply by 250 * 1000_000 172 // 2. Divide by 32768 173 // The following calculation (x * 15625 / 2048 * 1000) is essentially the 174 // same but avoids overflow. First both operations are divided by 16 leading 175 // to multiply by 15625000 and divide by 2048, and then part of the multiply 176 // is done after the divide instead of before. 177 divider := int32(1) 178 switch d.gRange { 179 case GFS_RANGE_250: 180 divider = 2048 181 case GFS_RANGE_500: 182 divider = 1024 183 case GFS_RANGE_1000: 184 divider = 512 185 case GFS_RANGE_2000: 186 divider = 256 187 } 188 x = int32(int16((uint16(data[0])<<8)|uint16(data[1]))) * 15625 / divider * 1000 189 y = int32(int16((uint16(data[2])<<8)|uint16(data[3]))) * 15625 / divider * 1000 190 z = int32(int16((uint16(data[4])<<8)|uint16(data[5]))) * 15625 / divider * 1000 191 return 192 }