|
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