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