Skip to content

Commit 3559ce7

Browse files
committed
micropython/drivers/imu/BMM150: Add Magnetometer driver.
* BOSCH BMM150 magnetometer.
1 parent a2e8382 commit 3559ce7

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
"""
2+
The MIT License (MIT)
3+
4+
Copyright (c) 2023 Arduino SA
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in
14+
all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
THE SOFTWARE.
23+
24+
Basic example usage:
25+
26+
import time
27+
from bmm150 import BMM150
28+
from machine import Pin, SPI, I2C
29+
30+
# Init in I2C mode.
31+
imu = BMM150(I2C(1, scl=Pin(15), sda=Pin(14)))
32+
33+
# Or init in SPI mode.
34+
# TODO: Not supported yet.
35+
# imu = BMM150(SPI(5), cs_pin=Pin(10))
36+
37+
while (True):
38+
print('magnetometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*imu.read_magnet()))
39+
time.sleep_ms(100)
40+
"""
41+
42+
import array
43+
import time
44+
from micropython import const
45+
46+
47+
class BMM150:
48+
_DEFAULT_ADDR = const(0x10)
49+
_CHIP_ID = const(0x40)
50+
_DATA = const(0x42)
51+
_POWER = const(0x4B)
52+
_OPMODE = const(0x4C)
53+
_INT_STATUS = const(0x4A)
54+
_TRIM_X1 = const(0x5D)
55+
_TRIM_Y1 = const(0x5E)
56+
_TRIM_Z4_LSB = const(0x62)
57+
_TRIM_Z2_LSB = const(0x68)
58+
_XYAXES_FLIP = const(-4096)
59+
_ZHAXES_FLIP = const(-16384)
60+
61+
def __init__(
62+
self,
63+
bus,
64+
cs_pin=None,
65+
address=_DEFAULT_ADDR,
66+
magnet_odr=30,
67+
):
68+
"""Initalizes the Magnetometer.
69+
bus: IMU bus
70+
address: I2C address (in I2C mode).
71+
cs_pin: SPI CS pin (in SPI mode).
72+
magnet_odr: (2, 6, 8, 10, 15, 20, 25, 30)
73+
"""
74+
self.bus = bus
75+
self.cs_pin = cs_pin
76+
self.address = address
77+
self._use_i2c = hasattr(self.bus, "readfrom_mem")
78+
79+
ODR = (10, 2, 6, 8, 15, 20, 25, 30)
80+
81+
# Sanity checks
82+
if not self._use_i2c:
83+
raise ValueError("SPI mode is not supported")
84+
if not magnet_odr in ODR:
85+
raise ValueError("Invalid sampling rate: %d" % magnet_odr)
86+
87+
# Perform soft reset, and power on.
88+
self.__write_reg(_POWER, 0x83)
89+
time.sleep_ms(10)
90+
91+
if self.__read_reg(_CHIP_ID) != 0x32:
92+
raise OSError("No BMM150 device was found at address 0x%x" % (self.address))
93+
94+
# Configure the device.
95+
# ODR | OP: Normal mode
96+
self.__write_reg(_OPMODE, ODR.index(magnet_odr) << 3)
97+
98+
# Read trim registers.
99+
trim_x1y1 = self.__read_reg(_TRIM_X1, 2)
100+
trim_xyz_data = self.__read_reg(_TRIM_Z4_LSB, 4)
101+
trim_xy1xy2 = self.__read_reg(_TRIM_Z2_LSB, 10)
102+
103+
self.trim_x1 = trim_x1y1[0]
104+
self.trim_y1 = trim_x1y1[1]
105+
self.trim_x2 = trim_xyz_data[2]
106+
self.trim_y2 = trim_xyz_data[3]
107+
self.trim_z1 = (trim_xy1xy2[3] << 8) | trim_xy1xy2[2]
108+
self.trim_z2 = (trim_xy1xy2[1] << 8) | trim_xy1xy2[0]
109+
self.trim_z3 = (trim_xy1xy2[7] << 8) | trim_xy1xy2[6]
110+
self.trim_z4 = (trim_xyz_data[1] << 8) | trim_xyz_data[0]
111+
self.trim_xy1 = trim_xy1xy2[9]
112+
self.trim_xy2 = trim_xy1xy2[8]
113+
self.trim_xyz1 = ((trim_xy1xy2[5] & 0x7F) << 8) | trim_xy1xy2[4]
114+
115+
# Allocate scratch buffer.
116+
self.scratch = memoryview(array.array("h", [0, 0, 0, 0]))
117+
118+
def __read_reg(self, reg, size=1):
119+
buf = self.bus.readfrom_mem(self.address, reg, size)
120+
if size == 1:
121+
return int(buf[0])
122+
return buf
123+
124+
def __read_reg_into(self, reg, buf):
125+
self.bus.readfrom_mem_into(self.address, reg, buf)
126+
127+
def __write_reg(self, reg, val):
128+
self.bus.writeto_mem(self.address, reg, bytes([val]))
129+
130+
def __compensate_x(self, raw, hall):
131+
"""Compensation equation ported from C driver"""
132+
x = 0.0
133+
if raw != _XYAXES_FLIP:
134+
x0 = self.trim_xyz1 * 16384.0 / hall
135+
x = x0 - 16384.0
136+
x1 = (self.trim_xy2) * (x**2 / 268435456.0)
137+
x2 = x1 + x * (self.trim_xy1) / 16384.0
138+
x3 = (self.trim_x2) + 160.0
139+
x4 = raw * ((x2 + 256.0) * x3)
140+
x = ((x4 / 8192.0) + (self.trim_x1 * 8.0)) / 16.0
141+
return x
142+
143+
def __compensate_y(self, raw, hall):
144+
"""Compensation equation ported from C driver"""
145+
y = 0.0
146+
if raw != _XYAXES_FLIP:
147+
y0 = self.trim_xyz1 * 16384 / hall
148+
y = y0 - 16384
149+
y1 = self.trim_xy2 * (y**2 / 268435456)
150+
y2 = y1 + y * self.trim_xy1 / 16384
151+
y3 = self.trim_y2 + 160
152+
y4 = raw * ((y2 + 256) * y3)
153+
y = ((y4 / 8192) + (self.trim_y1 * 8.0)) / 16.0
154+
return y
155+
156+
def __compensate_z(self, raw, hall):
157+
"""Compensation equation ported from C driver"""
158+
z = 0.0
159+
if raw != _ZHAXES_FLIP:
160+
z0 = raw - self.trim_z4
161+
z1 = hall - self.trim_xyz1
162+
z2 = self.trim_z3 * z1
163+
z3 = (self.trim_z1 * hall) / 32768.0
164+
z4 = self.trim_z2 + z3
165+
z5 = (z0 * 131072.0) - z2
166+
z = (z5 / (z4 * 4.0)) / 16.0
167+
return z
168+
169+
def reset(self):
170+
self.__write_reg(_CMD, 0xB6)
171+
172+
def read_magnet_raw(self):
173+
for i in range(0, 10):
174+
self.__read_reg_into(_DATA, self.scratch)
175+
if self.scratch[3] & 0x1:
176+
return (
177+
self.scratch[0] >> 3,
178+
self.scratch[1] >> 3,
179+
self.scratch[2] >> 1,
180+
self.scratch[3] >> 2,
181+
)
182+
time.sleep_ms(30)
183+
raise OSError("Data not ready")
184+
185+
def read_magnet(self):
186+
"""Returns magnetometer vector."""
187+
x, y, z, h = self.read_magnet_raw()
188+
return (self.__compensate_x(x, h), self.__compensate_y(y, h), self.__compensate_z(z, h))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module("bmm150.py", opt=3)
2+
metadata(description="BOSCH BMM150 magnetometer driver.", version="1.0.0")

0 commit comments

Comments
 (0)