77
77
_CMD_REG_CONF3 = const (0x02 ) # Oversampling, Filter, Resolution
78
78
_CMD_REG_CONF4 = const (0x03 ) # Sensitivity drift
79
79
80
+ # Gain settings
80
81
GAIN_5X = 0x0
81
82
GAIN_4X = 0x1
82
83
GAIN_3X = 0x2
87
88
GAIN_1X = 0x7
88
89
_GAIN_SHIFT = const (4 )
89
90
90
- _RES_2_15 = const (0 ) # +/- 2^15
91
- _RES_2_15B = const (1 ) # +/- 2^15
92
- _RES_22000 = const (2 ) # +/- 22000
93
- _RES_11000 = const (3 ) # +/- 11000
94
- _RES_SHIFT = const (5 )
91
+ # Resolution settings
92
+ RESOLUTION_16 = 0x0
93
+ RESOLUTION_17 = 0x1
94
+ RESOLUTION_18 = 0x2
95
+ RESOLUTION_19 = 0x3
96
+
97
+ # Filter settings
98
+ FILTER_0 = 0x0
99
+ FILTER_1 = 0x1
100
+ FILTER_2 = 0x2
101
+ FILTER_3 = 0x3
102
+ FILTER_4 = 0x4
103
+ FILTER_5 = 0x5
104
+ FILTER_6 = 0x6
105
+ FILTER_7 = 0x7
106
+
107
+ # Oversampling settings
108
+ OSR_0 = 0x0
109
+ OSR_1 = 0x1
110
+ OSR_2 = 0x2
111
+ OSR_3 = 0x3
95
112
96
113
_HALLCONF = const (0x0C ) # Hall plate spinning rate adjust.
97
114
120
137
((0.150 , 0.242 ), (0.300 , 0.484 ), (0.601 , 0.968 ), (1.202 , 1.936 )),
121
138
)
122
139
140
+ # Lookup table for conversion times for different filter and
141
+ # oversampling settings. Values taken from datasheet.
142
+ _TCONV_LOOKUP = (
143
+ # OSR = 0 1 2 3
144
+ (1.27 , 1.84 , 3.00 , 5.30 ), # DIG_FILT = 0
145
+ (1.46 , 2.23 , 3.76 , 6.84 ), # DIG_FILT = 1
146
+ (1.84 , 3.00 , 5.30 , 9.91 ), # DIG_FILT = 2
147
+ (2.61 , 4.53 , 8.37 , 16.05 ), # DIG_FILT = 3
148
+ (4.15 , 7.60 , 14.52 , 28.34 ), # DIG_FILT = 4
149
+ (7.22 , 13.75 , 26.80 , 52.92 ), # DIG_FILT = 5
150
+ (13.36 , 26.04 , 51.38 , 102.07 ), # DIG_FILT = 6
151
+ (25.65 , 50.61 , 100.53 , 200.37 ), # DIF_FILT = 7
152
+ )
153
+
123
154
124
- class MLX90393 :
155
+ class MLX90393 : # pylint: disable=too-many-instance-attributes
125
156
"""
126
157
Driver for the MLX90393 magnetometer.
127
158
:param i2c_bus: The `busio.I2C` object to use. This is the only
@@ -131,16 +162,38 @@ class MLX90393:
131
162
:param bool debug: (optional) Enable debug output.
132
163
"""
133
164
134
- def __init__ (self , i2c_bus , address = 0x0C , gain = GAIN_1X , debug = False ):
165
+ def __init__ (
166
+ self ,
167
+ i2c_bus ,
168
+ address = 0x0C ,
169
+ gain = GAIN_1X ,
170
+ resolution = RESOLUTION_16 ,
171
+ filt = FILTER_7 ,
172
+ oversampling = OSR_3 ,
173
+ debug = False ,
174
+ ): # pylint: disable=too-many-arguments
135
175
self .i2c_device = I2CDevice (i2c_bus , address )
136
176
self ._debug = debug
137
177
self ._status_last = 0
138
- self ._res_current = _RES_2_15
178
+ self ._res_x = self ._res_y = self ._res_z = resolution
179
+ self ._filter = filt
180
+ self ._osr = oversampling
139
181
self ._gain_current = gain
140
182
141
183
# Put the device in a known state to start
142
184
self .reset ()
143
185
186
+ # Set resolution to the supplied level
187
+ self .resolution_x = self ._res_x
188
+ self .resolution_y = self ._res_y
189
+ self .resolution_z = self ._res_z
190
+
191
+ # Set filter to the supplied level
192
+ self .filter = self ._filter
193
+
194
+ # Set oversampling to the supplied level
195
+ self .oversampling = self ._osr
196
+
144
197
# Set gain to the supplied level
145
198
self .gain = self ._gain_current
146
199
@@ -162,6 +215,7 @@ def _transceive(self, payload, rxlen=0):
162
215
# Write 'value' to the specified register
163
216
# TODO: Check this. It's weird that the write is accepted but the read is naked.
164
217
with self .i2c_device as i2c :
218
+ # pylint: disable=unexpected-keyword-arg
165
219
i2c .write (payload , stop = False )
166
220
167
221
while True :
@@ -185,22 +239,19 @@ def _transceive(self, payload, rxlen=0):
185
239
@property
186
240
def last_status (self ):
187
241
"""
188
- Returns the last status byte received from the sensor.
242
+ The last status byte received from the sensor.
189
243
"""
190
244
return self ._status_last
191
245
192
246
@property
193
247
def gain (self ):
194
248
"""
195
- Gets the current gain setting for the device.
249
+ The gain setting for the device.
196
250
"""
197
251
return self ._gain_current
198
252
199
253
@gain .setter
200
254
def gain (self , value ):
201
- """
202
- Sets the gain for the device.
203
- """
204
255
if value > GAIN_1X or value < GAIN_5X :
205
256
raise ValueError ("Invalid GAIN setting" )
206
257
if self ._debug :
@@ -217,6 +268,81 @@ def gain(self, value):
217
268
)
218
269
)
219
270
271
+ @property
272
+ def resolution_x (self ):
273
+ """The X axis resolution."""
274
+ return self ._res_x
275
+
276
+ @resolution_x .setter
277
+ def resolution_x (self , resolution ):
278
+ self ._set_resolution (0 , resolution )
279
+ self ._res_x = resolution
280
+
281
+ @property
282
+ def resolution_y (self ):
283
+ """The Y axis resolution."""
284
+ return self ._res_y
285
+
286
+ @resolution_y .setter
287
+ def resolution_y (self , resolution ):
288
+ self ._set_resolution (1 , resolution )
289
+ self ._res_y = resolution
290
+
291
+ @property
292
+ def resolution_z (self ):
293
+ """The Z axis resolution."""
294
+ return self ._res_z
295
+
296
+ @resolution_z .setter
297
+ def resolution_z (self , resolution ):
298
+ self ._set_resolution (2 , resolution )
299
+ self ._res_z = resolution
300
+
301
+ def _set_resolution (self , axis , resolution ):
302
+ if resolution not in (
303
+ RESOLUTION_16 ,
304
+ RESOLUTION_17 ,
305
+ RESOLUTION_18 ,
306
+ RESOLUTION_19 ,
307
+ ):
308
+ raise ValueError ("Incorrect resolution setting." )
309
+ shift = (5 , 7 , 9 )[axis ]
310
+ mask = (0xFF9F , 0xFE7F , 0xF9FF )[axis ]
311
+ reg = self .read_reg (_CMD_REG_CONF3 )
312
+ reg &= mask
313
+ reg |= (resolution & 0x3 ) << shift
314
+ self .write_reg (_CMD_REG_CONF3 , reg )
315
+
316
+ @property
317
+ def filter (self ):
318
+ """The filter level."""
319
+ return self ._filter
320
+
321
+ @filter .setter
322
+ def filter (self , level ):
323
+ if level not in range (8 ):
324
+ raise ValueError ("Incorrect filter level." )
325
+ reg = self .read_reg (_CMD_REG_CONF3 )
326
+ reg &= 0xFFE3
327
+ reg |= (level & 0x7 ) << 2
328
+ self .write_reg (_CMD_REG_CONF3 , reg )
329
+ self ._filter = level
330
+
331
+ @property
332
+ def oversampling (self ):
333
+ """The oversampling level."""
334
+ return self ._osr
335
+
336
+ @oversampling .setter
337
+ def oversampling (self , level ):
338
+ if level not in range (4 ):
339
+ raise ValueError ("Incorrect oversampling level." )
340
+ reg = self .read_reg (_CMD_REG_CONF3 )
341
+ reg &= 0xFFFC
342
+ reg |= level & 0x3
343
+ self .write_reg (_CMD_REG_CONF3 , reg )
344
+ self ._osr = level
345
+
220
346
def display_status (self ):
221
347
"""
222
348
Prints out the content of the last status byte in a human-readble
@@ -248,14 +374,29 @@ def read_reg(self, reg):
248
374
with self .i2c_device as i2c :
249
375
i2c .readinto (data )
250
376
# Unpack data (status byte, big-endian 16-bit register value)
251
- self ._status_last , val = struct .unpack (">Bh " , data )
377
+ self ._status_last , val = struct .unpack (">BH " , data )
252
378
if self ._debug :
253
379
print ("\t [{}]" .format (time .monotonic ()))
254
380
print ("\t Writing :" , [hex (b ) for b in payload ])
255
381
print ("\t Response :" , [hex (b ) for b in data ])
256
382
print ("\t Status :" , hex (data [0 ]))
257
383
return val
258
384
385
+ def write_reg (self , reg , value ):
386
+ """
387
+ Writes the 16-bit value to the supplied register.
388
+ """
389
+ self ._transceive (
390
+ bytes (
391
+ [
392
+ _CMD_WR ,
393
+ value >> 8 , # high byte
394
+ value & 0xFF , # low byte
395
+ reg << 2 , # the register
396
+ ]
397
+ )
398
+ )
399
+
259
400
def reset (self ):
260
401
"""
261
402
Performs a software reset of the sensor.
@@ -276,20 +417,40 @@ def read_data(self):
276
417
"""
277
418
Reads a single X/Y/Z sample from the magnetometer.
278
419
"""
279
- delay = 0.01
420
+ # Set conversion delay based on filter and oversampling
421
+ delay = _TCONV_LOOKUP [self ._filter ][self ._osr ] / 1000 # per datasheet
422
+ delay *= 1.1 # plus a little
280
423
281
424
# Set the device to single measurement mode
282
425
self ._transceive (bytes ([_CMD_SM | _CMD_AXIS_ALL ]))
283
426
284
427
# Insert a delay since we aren't using INTs for DRDY
285
428
time .sleep (delay )
286
429
287
- # Read the 'XYZ' data as three signed 16-bit integers
430
+ # Read the 'XYZ' data
288
431
data = self ._transceive (bytes ([_CMD_RM | _CMD_AXIS_ALL ]), 6 )
289
- self ._status_last , m_x , m_y , m_z = struct .unpack (">Bhhh" , data )
432
+
433
+ # Unpack status and raw int values
434
+ self ._status_last = data [0 ]
435
+ m_x = self ._unpack_axis_data (self ._res_x , data [1 :3 ])
436
+ m_y = self ._unpack_axis_data (self ._res_y , data [3 :5 ])
437
+ m_z = self ._unpack_axis_data (self ._res_z , data [5 :7 ])
290
438
291
439
# Return the raw int values if requested
292
- return (m_x , m_y , m_z )
440
+ return m_x , m_y , m_z
441
+
442
+ # pylint: disable=no-self-use
443
+ def _unpack_axis_data (self , resolution , data ):
444
+ # see datasheet
445
+ if resolution == RESOLUTION_19 :
446
+ (value ,) = struct .unpack (">H" , data )
447
+ value -= 0x4000
448
+ elif resolution == RESOLUTION_18 :
449
+ (value ,) = struct .unpack (">H" , data )
450
+ value -= 0x8000
451
+ else :
452
+ value = struct .unpack (">h" , data )[0 ]
453
+ return value
293
454
294
455
@property
295
456
def magnetic (self ):
@@ -300,8 +461,8 @@ def magnetic(self):
300
461
x , y , z = self .read_data
301
462
302
463
# Convert the raw integer values to uT based on gain and resolution
303
- x *= _LSB_LOOKUP [self ._gain_current ][self ._res_current ][0 ]
304
- y *= _LSB_LOOKUP [self ._gain_current ][self ._res_current ][0 ]
305
- z *= _LSB_LOOKUP [self ._gain_current ][self ._res_current ][1 ]
464
+ x *= _LSB_LOOKUP [self ._gain_current ][self ._res_x ][0 ]
465
+ y *= _LSB_LOOKUP [self ._gain_current ][self ._res_y ][0 ]
466
+ z *= _LSB_LOOKUP [self ._gain_current ][self ._res_z ][1 ]
306
467
307
468
return x , y , z
0 commit comments