|
2 | 2 | The MIT License (MIT)
|
3 | 3 |
|
4 | 4 | Copyright (c) 2013, 2014 Damien P. George
|
| 5 | +Copyright (c) 2022-2023 Ibrahim Abdelkader <iabdalkader@openmv.io> |
5 | 6 |
|
6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 | 8 | of this software and associated documentation files (the "Software"), to deal
|
|
32 | 33 | from lsm9ds1 import LSM9DS1
|
33 | 34 | from machine import Pin, I2C
|
34 | 35 |
|
35 |
| -lsm = LSM9DS1(I2C(1, scl=Pin(15), sda=Pin(14))) |
| 36 | +imu = LSM9DS1(I2C(1, scl=Pin(15), sda=Pin(14))) |
36 | 37 |
|
37 | 38 | while (True):
|
38 |
| - #for g,a in lsm.iter_accel_gyro(): print(g,a) # using fifo |
39 |
| - print('Accelerometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*lsm.accel())) |
40 |
| - print('Magnetometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*lsm.magnet())) |
41 |
| - print('Gyroscope: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*lsm.gyro())) |
| 39 | + #for g,a in imu.iter_accel_gyro(): print(g,a) # using fifo |
| 40 | + print('Accelerometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*imu.accel())) |
| 41 | + print('Magnetometer: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*imu.magnet())) |
| 42 | + print('Gyroscope: x:{:>8.3f} y:{:>8.3f} z:{:>8.3f}'.format(*imu.gyro())) |
42 | 43 | print("")
|
43 | 44 | time.sleep_ms(100)
|
44 | 45 | """
|
|
58 | 59 | _OFFSET_REG_X_M = const(0x05)
|
59 | 60 | _CTRL_REG1_M = const(0x20)
|
60 | 61 | _OUT_M = const(0x28)
|
61 |
| -_SCALE_GYRO = const(((245, 0), (500, 1), (2000, 3))) |
62 |
| -_SCALE_ACCEL = const(((2, 0), (4, 2), (8, 3), (16, 1))) |
| 62 | +_ACCEL_SCALE = const((2, 16, 4, 8)) |
| 63 | +_GYRO_SCALE = const((245, 500, 2000)) |
| 64 | +_MAGNET_SCALE = const((4, 8, 12, 16)) |
| 65 | +_ODR_IMU = const((0, 14.9, 59.5, 119, 238, 476, 952)) |
| 66 | +_ODR_MAGNET = const((0.625, 1.25, 2.5, 5, 10, 20, 40, 80)) |
63 | 67 |
|
64 | 68 |
|
65 | 69 | class LSM9DS1:
|
66 |
| - def __init__(self, i2c, address_gyro=0x6B, address_magnet=0x1E): |
67 |
| - self.i2c = i2c |
68 |
| - self.address_gyro = address_gyro |
| 70 | + def __init__( |
| 71 | + self, |
| 72 | + bus, |
| 73 | + address_imu=0x6B, |
| 74 | + address_magnet=0x1E, |
| 75 | + gyro_odr=952, |
| 76 | + gyro_scale=245, |
| 77 | + accel_odr=952, |
| 78 | + accel_scale=4, |
| 79 | + magnet_odr=80, |
| 80 | + magnet_scale=4, |
| 81 | + ): |
| 82 | + """Initalizes Gyro, Accelerometer and Magnetometer. |
| 83 | + bus: IMU bus |
| 84 | + address_imu: IMU I2C address. |
| 85 | + address_magnet: Magnetometer I2C address. |
| 86 | + gyro_odr: (0, 14.9Hz, 59.5Hz, 119Hz, 238Hz, 476Hz, 952Hz) |
| 87 | + gyro_scale: (245dps, 500dps, 2000dps ) |
| 88 | + accel_odr: (0, 14.9Hz, 59.5Hz, 119Hz, 238Hz, 476Hz, 952Hz) |
| 89 | + accel_scale: (+/-2g, +/-4g, +/-8g, +-16g) |
| 90 | + magnet_odr: (0.625Hz, 1.25Hz, 2.5Hz, 5Hz, 10Hz, 20Hz, 40Hz, 80Hz) |
| 91 | + magnet_scale: (+/-4, +/-8, +/-12, +/-16) |
| 92 | + """ |
| 93 | + self.bus = bus |
| 94 | + self.address_imu = address_imu |
69 | 95 | self.address_magnet = address_magnet
|
70 |
| - # check id's of accelerometer/gyro and magnetometer |
| 96 | + |
| 97 | + # Sanity checks |
| 98 | + if not gyro_odr in _ODR_IMU: |
| 99 | + raise ValueError("Invalid gyro sampling rate: %d" % gyro_odr) |
| 100 | + if not gyro_scale in _GYRO_SCALE: |
| 101 | + raise ValueError("Invalid gyro scaling: %d" % gyro_scale) |
| 102 | + |
| 103 | + if not accel_odr in _ODR_IMU: |
| 104 | + raise ValueError("Invalid accelerometer sampling rate: %d" % accel_odr) |
| 105 | + if not accel_scale in _ACCEL_SCALE: |
| 106 | + raise ValueError("Invalid accelerometer scaling: %d" % accel_scale) |
| 107 | + |
| 108 | + if not magnet_odr in _ODR_MAGNET: |
| 109 | + raise ValueError("Invalid magnet sampling rate: %d" % magnet_odr) |
| 110 | + if not magnet_scale in _MAGNET_SCALE: |
| 111 | + raise ValueError("Invalid magnet scaling: %d" % magnet_scale) |
| 112 | + |
71 | 113 | if (self.magent_id() != b"=") or (self.gyro_id() != b"h"):
|
72 | 114 | raise OSError(
|
73 |
| - "Invalid LSM9DS1 device, using address {}/{}".format(address_gyro, address_magnet) |
| 115 | + "Invalid LSM9DS1 device, using address {}/{}".format(address_imu, address_magnet) |
74 | 116 | )
|
75 |
| - # allocate scratch buffer for efficient conversions and memread op's |
76 |
| - self.scratch = array.array("B", [0, 0, 0, 0, 0, 0]) |
77 |
| - self.scratch_int = array.array("h", [0, 0, 0]) |
78 |
| - self.init_gyro_accel() |
79 |
| - self.init_magnetometer() |
80 |
| - |
81 |
| - def init_gyro_accel(self, sample_rate=6, scale_gyro=0, scale_accel=0): |
82 |
| - """Initalizes Gyro and Accelerator. |
83 |
| - sample rate: 0-6 (off, 14.9Hz, 59.5Hz, 119Hz, 238Hz, 476Hz, 952Hz) |
84 |
| - scale_gyro: 0-2 (245dps, 500dps, 2000dps ) |
85 |
| - scale_accel: 0-3 (+/-2g, +/-4g, +/-8g, +-16g) |
86 |
| - """ |
87 |
| - assert sample_rate <= 6, "invalid sampling rate: %d" % sample_rate |
88 |
| - assert scale_gyro <= 2, "invalid gyro scaling: %d" % scale_gyro |
89 |
| - assert scale_accel <= 3, "invalid accelerometer scaling: %d" % scale_accel |
90 |
| - |
91 |
| - i2c = self.i2c |
92 |
| - addr = self.address_gyro |
93 |
| - mv = memoryview(self.scratch) |
94 |
| - # angular control registers 1-3 / Orientation |
95 |
| - mv[0] = ((sample_rate & 0x07) << 5) | ((_SCALE_GYRO[scale_gyro][1] & 0x3) << 3) |
| 117 | + |
| 118 | + mv = memoryview(bytearray(6)) |
| 119 | + |
| 120 | + # Configure Gyroscope. |
| 121 | + mv[0] = (_ODR_IMU.index(gyro_odr) << 5) | ((_GYRO_SCALE.index(gyro_scale)) << 3) |
96 | 122 | mv[1:4] = b"\x00\x00\x00"
|
97 |
| - i2c.writeto_mem(addr, _CTRL_REG1_G, mv[:5]) |
98 |
| - # ctrl4 - enable x,y,z, outputs, no irq latching, no 4D |
99 |
| - # ctrl5 - enable all axes, no decimation |
| 123 | + self.bus.writeto_mem(self.address_imu, _CTRL_REG1_G, mv[:5]) |
| 124 | + |
| 125 | + # Configure Accelerometer |
| 126 | + mv[0] = 0x38 # ctrl4 - enable x,y,z, outputs, no irq latching, no 4D |
| 127 | + mv[1] = 0x38 # ctrl5 - enable all axes, no decimation |
100 | 128 | # ctrl6 - set scaling and sample rate of accel
|
101 |
| - # ctrl7,8 - leave at default values |
102 |
| - # ctrl9 - FIFO enabled |
103 |
| - mv[0] = mv[1] = 0x38 |
104 |
| - mv[2] = ((sample_rate & 7) << 5) | ((_SCALE_ACCEL[scale_accel][1] & 0x3) << 3) |
105 |
| - mv[3] = 0x00 |
106 |
| - mv[4] = 0x4 |
107 |
| - mv[5] = 0x2 |
108 |
| - i2c.writeto_mem(addr, _CTRL_REG4_G, mv[:6]) |
| 129 | + mv[2] = (_ODR_IMU.index(accel_odr) << 5) | ((_ACCEL_SCALE.index(accel_scale)) << 3) |
| 130 | + mv[3] = 0x00 # ctrl7 - leave at default values |
| 131 | + mv[4] = 0x4 # ctrl8 - leave at default values |
| 132 | + mv[5] = 0x2 # ctrl9 - FIFO enabled |
| 133 | + self.bus.writeto_mem(self.address_imu, _CTRL_REG4_G, mv) |
109 | 134 |
|
110 | 135 | # fifo: use continous mode (overwrite old data if overflow)
|
111 |
| - i2c.writeto_mem(addr, _FIFO_CTRL_REG, b"\x00") |
112 |
| - i2c.writeto_mem(addr, _FIFO_CTRL_REG, b"\xc0") |
113 |
| - |
114 |
| - self.scale_gyro = 32768 / _SCALE_GYRO[scale_gyro][0] |
115 |
| - self.scale_accel = 32768 / _SCALE_ACCEL[scale_accel][0] |
| 136 | + self.bus.writeto_mem(self.address_imu, _FIFO_CTRL_REG, b"\x00") |
| 137 | + self.bus.writeto_mem(self.address_imu, _FIFO_CTRL_REG, b"\xc0") |
116 | 138 |
|
117 |
| - def init_magnetometer(self, sample_rate=7, scale_magnet=0): |
118 |
| - """ |
119 |
| - sample rates = 0-7 (0.625, 1.25, 2.5, 5, 10, 20, 40, 80Hz) |
120 |
| - scaling = 0-3 (+/-4, +/-8, +/-12, +/-16 Gauss) |
121 |
| - """ |
122 |
| - assert sample_rate < 8, "invalid sample rate: %d (0-7)" % sample_rate |
123 |
| - assert scale_magnet < 4, "invalid scaling: %d (0-3)" % scale_magnet |
124 |
| - i2c = self.i2c |
125 |
| - addr = self.address_magnet |
126 |
| - mv = memoryview(self.scratch) |
127 |
| - mv[0] = 0x40 | (sample_rate << 2) # ctrl1: high performance mode |
128 |
| - mv[1] = scale_magnet << 5 # ctrl2: scale, normal mode, no reset |
| 139 | + # Configure Magnetometer |
| 140 | + mv[0] = 0x40 | (magnet_odr << 2) # ctrl1: high performance mode |
| 141 | + mv[1] = _MAGNET_SCALE.index(magnet_scale) << 5 # ctrl2: scale, normal mode, no reset |
129 | 142 | mv[2] = 0x00 # ctrl3: continous conversion, no low power, I2C
|
130 | 143 | mv[3] = 0x08 # ctrl4: high performance z-axis
|
131 | 144 | mv[4] = 0x00 # ctr5: no fast read, no block update
|
132 |
| - i2c.writeto_mem(addr, _CTRL_REG1_M, mv[:5]) |
133 |
| - self.scale_factor_magnet = 32768 / ((scale_magnet + 1) * 4) |
| 145 | + self.bus.writeto_mem(self.address_magnet, _CTRL_REG1_M, mv[:5]) |
| 146 | + |
| 147 | + self.gyro_scale = 32768 / gyro_scale |
| 148 | + self.accel_scale = 32768 / accel_scale |
| 149 | + self.scale_factor_magnet = 32768 / ((_MAGNET_SCALE.index(magnet_scale) + 1) * 4) |
| 150 | + |
| 151 | + # Allocate scratch buffer for efficient conversions and memread op's |
| 152 | + self.scratch_int = array.array("h", [0, 0, 0]) |
134 | 153 |
|
135 | 154 | def calibrate_magnet(self, offset):
|
136 | 155 | """
|
137 |
| - offset is a magnet vecor that will be substracted by the magnetometer |
| 156 | + offset is a magnet vector that will be subtracted by the magnetometer |
138 | 157 | for each measurement. It is written to the magnetometer's offset register
|
139 | 158 | """
|
| 159 | + import struct |
| 160 | + |
140 | 161 | offset = [int(i * self.scale_factor_magnet) for i in offset]
|
141 |
| - mv = memoryview(self.scratch) |
142 |
| - mv[0] = offset[0] & 0xFF |
143 |
| - mv[1] = offset[0] >> 8 |
144 |
| - mv[2] = offset[1] & 0xFF |
145 |
| - mv[3] = offset[1] >> 8 |
146 |
| - mv[4] = offset[2] & 0xFF |
147 |
| - mv[5] = offset[2] >> 8 |
148 |
| - self.i2c.writeto_mem(self.address_magnet, _OFFSET_REG_X_M, mv[:6]) |
| 162 | + self.bus.writeto_mem(self.address_magnet, _OFFSET_REG_X_M, struct.pack("<HHH", offset)) |
149 | 163 |
|
150 | 164 | def gyro_id(self):
|
151 |
| - return self.i2c.readfrom_mem(self.address_gyro, _WHO_AM_I, 1) |
| 165 | + return self.bus.readfrom_mem(self.address_imu, _WHO_AM_I, 1) |
152 | 166 |
|
153 | 167 | def magent_id(self):
|
154 |
| - return self.i2c.readfrom_mem(self.address_magnet, _WHO_AM_I, 1) |
| 168 | + return self.bus.readfrom_mem(self.address_magnet, _WHO_AM_I, 1) |
155 | 169 |
|
156 | 170 | def magnet(self):
|
157 | 171 | """Returns magnetometer vector in gauss.
|
158 | 172 | raw_values: if True, the non-scaled adc values are returned
|
159 | 173 | """
|
160 | 174 | mv = memoryview(self.scratch_int)
|
161 | 175 | f = self.scale_factor_magnet
|
162 |
| - self.i2c.readfrom_mem_into(self.address_magnet, _OUT_M | 0x80, mv) |
| 176 | + self.bus.readfrom_mem_into(self.address_magnet, _OUT_M | 0x80, mv) |
163 | 177 | return (mv[0] / f, mv[1] / f, mv[2] / f)
|
164 | 178 |
|
165 | 179 | def gyro(self):
|
166 | 180 | """Returns gyroscope vector in degrees/sec."""
|
167 | 181 | mv = memoryview(self.scratch_int)
|
168 |
| - f = self.scale_gyro |
169 |
| - self.i2c.readfrom_mem_into(self.address_gyro, _OUT_G | 0x80, mv) |
| 182 | + f = self.gyro_scale |
| 183 | + self.bus.readfrom_mem_into(self.address_imu, _OUT_G | 0x80, mv) |
170 | 184 | return (mv[0] / f, mv[1] / f, mv[2] / f)
|
171 | 185 |
|
172 | 186 | def accel(self):
|
173 | 187 | """Returns acceleration vector in gravity units (9.81m/s^2)."""
|
174 | 188 | mv = memoryview(self.scratch_int)
|
175 |
| - f = self.scale_accel |
176 |
| - self.i2c.readfrom_mem_into(self.address_gyro, _OUT_XL | 0x80, mv) |
| 189 | + f = self.accel_scale |
| 190 | + self.bus.readfrom_mem_into(self.address_imu, _OUT_XL | 0x80, mv) |
177 | 191 | return (mv[0] / f, mv[1] / f, mv[2] / f)
|
178 | 192 |
|
179 | 193 | def iter_accel_gyro(self):
|
180 | 194 | """A generator that returns tuples of (gyro,accelerometer) data from the fifo."""
|
181 | 195 | while True:
|
182 | 196 | fifo_state = int.from_bytes(
|
183 |
| - self.i2c.readfrom_mem(self.address_gyro, _FIFO_SRC, 1), "big" |
| 197 | + self.bus.readfrom_mem(self.address_imu, _FIFO_SRC, 1), "big" |
184 | 198 | )
|
185 | 199 | if fifo_state & 0x3F:
|
186 | 200 | # print("Available samples=%d" % (fifo_state & 0x1f))
|
|
0 commit comments