/ adafruit_l3gd20.py
adafruit_l3gd20.py
1 # The MIT License (MIT) 2 # 3 # Copyright (c) 2018 Michael McWethy for Adafruit Industries 4 # 5 # Permission is hereby granted, free of charge, to any person obtaining a copy 6 # of this software and associated documentation files (the "Software"), to deal 7 # in the Software without restriction, including without limitation the rights 8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 # copies of the Software, and to permit persons to whom the Software is 10 # furnished to do so, subject to the following conditions: 11 # 12 # The above copyright notice and this permission notice shall be included in 13 # all copies or substantial portions of the Software. 14 # 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 # THE SOFTWARE. 22 """ 23 `adafruit_l3gd20` 24 ==================================================== 25 26 Adafruit 9-DOF Absolute Orientation IMU Fusion Breakout - L3GD20 27 28 This is a CircuitPython driver for the Bosch L3GD20 nine degree of freedom 29 inertial measurement unit module with sensor fusion. 30 31 * Author(s): Michael McWethy 32 33 Implementation Notes 34 -------------------- 35 36 **Hardware:** 37 38 * `L3GD20H Triple-Axis Gyro Breakout Board <https://www.adafruit.com/product/1032>`_ 39 40 **Software and Dependencies:** 41 42 * Adafruit CircuitPython firmware for the supported boards: 43 https://github.com/adafruit/circuitpython/releases 44 45 46 * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register 47 """ 48 49 # imports 50 51 __version__ = "0.0.0-auto.0" 52 __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_l3gd20.git" 53 54 55 from micropython import const 56 from adafruit_register.i2c_struct import Struct 57 try: 58 from struct import unpack 59 except ImportError: 60 from ustruct import unpack 61 62 __version__ = "0.0.0-auto.0" 63 __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_L3GD20.git" 64 65 L3DS20_RANGE_250DPS = const(0) 66 L3DS20_RANGE_500DPS = const(1) 67 L3DS20_RANGE_2000DPS = const(2) 68 69 _L3GD20_REGISTER_CTRL_REG1 = const(0x20) 70 _L3GD20_REGISTER_CTRL_REG4 = const(0x23) 71 72 # _L3GD20_REGISTER_OUT_X_L = const(0x28) 73 _L3GD20_REGISTER_OUT_X_L_X80 = const(0xA8) 74 _L3GD20_REGISTER_OUT_X_L_X40 = const(0x68) 75 76 _ID_REGISTER = const(0x0F) 77 78 _L3GD20_CHIP_ID = const(0xD4) 79 _L3GD20H_CHIP_ID = const(0xD7) 80 81 _L3GD20_SENSITIVITY_250DPS = 0.00875 ## Roughly 22/256 for fixed point match 82 _L3GD20_SENSITIVITY_500DPS = 0.0175 ## Roughly 45/256 83 _L3GD20_SENSITIVITY_2000DPS = 0.070 ## Roughly 18/256 84 85 86 class L3GD20: # pylint: disable=no-member 87 """ 88 Driver for the L3GD20 3-axis Gyroscope sensor. 89 90 :param int rng: a range value one of L3DS20_RANGE_250DPS (default), L3DS20_RANGE_500DPS, or 91 L3DS20_RANGE_2000DPS 92 """ 93 94 def __init__(self, rng=L3DS20_RANGE_250DPS): 95 chip_id = self.read_register(_ID_REGISTER) 96 if chip_id != _L3GD20_CHIP_ID and chip_id != _L3GD20H_CHIP_ID: 97 raise RuntimeError("bad chip id (%x != %x or %x)" % 98 (chip_id, _L3GD20_CHIP_ID, _L3GD20H_CHIP_ID)) 99 100 if rng != L3DS20_RANGE_250DPS and \ 101 rng != L3DS20_RANGE_500DPS and \ 102 rng != L3DS20_RANGE_2000DPS: 103 raise ValueError("Range value must be one of L3DS20_RANGE_250DPS, " 104 "L3DS20_RANGE_500DPS, or L3DS20_RANGE_2000DPS") 105 106 # Set CTRL_REG1 (0x20) 107 # ==================================================================== 108 # BIT Symbol Description Default 109 # --- ------ --------------------------------------------- ------- 110 # 7-6 DR1#0 Output data rate 111 # 5-4 BW1#0 Bandwidth selection 112 # 3 PD 0 = Power-down mode, 1 = normal#sleep mode 113 # 2 ZEN Z-axis enable (0 = disabled, 1 = enabled) 114 # 1 YEN Y-axis enable (0 = disabled, 1 = enabled) 115 # 0 XEN X-axis enable (0 = disabled, 1 = enabled) 116 117 # Switch to normal mode and enable all three channels 118 self.write_register(_L3GD20_REGISTER_CTRL_REG1, 0x0F) 119 120 # Set CTRL_REG2 (0x21) 121 # ==================================================================== 122 # BIT Symbol Description Default 123 # --- ------ --------------------------------------------- ------- 124 # 5-4 HPM1#0 High-pass filter mode selection 125 # 3-0 HPCF3..0 High-pass filter cutoff frequency selection 126 127 # Nothing to do ... keep default values 128 # ------------------------------------------------------------------ 129 130 # Set CTRL_REG3 (0x22) 131 # ==================================================================== 132 # BIT Symbol Description Default 133 # --- ------ --------------------------------------------- ------- 134 # 7 I1_Int1 Interrupt enable on INT1 (0=disable,1=enable) 135 # 6 I1_Boot Boot status on INT1 (0=disable,1=enable) 136 # 5 H-Lactive Interrupt active config on INT1 (0=high,1=low) 137 # 4 PP_OD Push-Pull#Open-Drain (0=PP, 1=OD) 138 # 3 I2_DRDY Data ready on DRDY#INT2 (0=disable,1=enable) 139 # 2 I2_WTM FIFO wtrmrk int on DRDY#INT2 (0=dsbl,1=enbl) 140 # 1 I2_ORun FIFO overrun int on DRDY#INT2 (0=dsbl,1=enbl) 141 # 0 I2_Empty FIFI empty int on DRDY#INT2 (0=dsbl,1=enbl) 142 143 # Nothing to do ... keep default values 144 # ----------------------------------------------------------------- 145 146 # Set CTRL_REG4 (0x23) 147 # ==================================================================== 148 # BIT Symbol Description Default 149 # --- ------ --------------------------------------------- ------- 150 # 7 BDU Block Data Update (0=continuous, 1=LSB#MSB) 151 # 6 BLE Big#Little-Endian (0=Data LSB, 1=Data MSB) 152 # 5-4 FS1#0 Full scale selection 153 # 00 = 250 dps 154 # 01 = 500 dps 155 # 10 = 2000 dps 156 # 11 = 2000 dps 157 # 0 SIM SPI Mode (0=4-wire, 1=3-wire) 158 159 # Adjust resolution if requested 160 161 if rng == L3DS20_RANGE_250DPS: 162 self.scale = _L3GD20_SENSITIVITY_250DPS 163 self.write_register(_L3GD20_REGISTER_CTRL_REG4, 0x00) 164 165 if rng == L3DS20_RANGE_500DPS: 166 self.scale = _L3GD20_SENSITIVITY_500DPS 167 self.write_register(_L3GD20_REGISTER_CTRL_REG4, 0x10) 168 169 if rng == L3DS20_RANGE_2000DPS: 170 self.scale = _L3GD20_SENSITIVITY_2000DPS 171 self.write_register(_L3GD20_REGISTER_CTRL_REG4, 0x20) 172 173 # ------------------------------------------------------------------ 174 175 # Set CTRL_REG5 (0x24) 176 # ==================================================================== 177 # BIT Symbol Description Default 178 # --- ------ --------------------------------------------- ------- 179 # 7 BOOT Reboot memory content (0=normal, 1=reboot) 180 # 6 FIFO_EN FIFO enable (0=FIFO disable, 1=enable) 181 # 4 HPen High-pass filter enable (0=disable,1=enable) 182 # 3-2 INT1_SEL INT1 Selection config 183 # 1-0 OUT_SEL Out selection config 184 185 # Nothing to do ... keep default values 186 # ------------------------------------------------------------------ 187 188 189 @property 190 def acceleration(self): 191 """ 192 x, y, z acceleration tuple floats, rescaled appropriately for range selected 193 """ 194 raw = self.acceleration_raw 195 return tuple(self.scale*v for v in raw) 196 197 198 class L3GD20_I2C(L3GD20): 199 """ 200 Driver for L3GD20 Gyroscope using I2C communications 201 202 :param ~busio.I2C i2c: initialized busio I2C class 203 :param int rng: the optional range value: L3DS20_RANGE_250DPS(default), L3DS20_RANGE_500DPS, or 204 L3DS20_RANGE_2000DPS 205 :param address: the optional device address, 0x68 is the default address 206 """ 207 208 acceleration_raw = Struct(_L3GD20_REGISTER_OUT_X_L_X80, '<hhh') 209 """Gives the raw acceleration readings, in units of the scaled mdps.""" 210 211 def __init__(self, i2c, rng=L3DS20_RANGE_250DPS, address=0x6B): 212 import adafruit_bus_device.i2c_device as i2c_device 213 self.i2c_device = i2c_device.I2CDevice(i2c, address) 214 self.buffer = bytearray(2) 215 super().__init__(rng) 216 217 def write_register(self, register, value): 218 """ 219 Update a register with a byte value 220 221 :param int register: which device register to write 222 :param value: a byte to write 223 """ 224 self.buffer[0] = register 225 self.buffer[1] = value 226 with self.i2c_device as i2c: 227 i2c.write(self.buffer) 228 229 def read_register(self, register): 230 """ 231 Returns a byte value from a register 232 233 :param register: the register to read a byte 234 """ 235 self.buffer[0] = register 236 with self.i2c_device as i2c: 237 i2c.write(self.buffer, end=1, stop=False) 238 i2c.readinto(self.buffer, start=1) 239 return self.buffer[1] 240 241 class L3GD20_SPI(L3GD20): 242 """ 243 Driver for L3GD20 Gyroscope using SPI communications 244 245 :param ~busio.SPI spi_busio: initialized busio SPI class 246 :param ~digitalio.DigitalInOut cs: digital in/out to use as chip select signal 247 :param int rng: the optional range value: L3DS20_RANGE_250DPS(default), L3DS20_RANGE_500DPS, or 248 L3DS20_RANGE_2000DPS 249 :param baudrate: spi baud rate default is 100000 250 """ 251 def __init__(self, spi_busio, cs, rng=L3DS20_RANGE_250DPS, baudrate=100000): 252 import adafruit_bus_device.spi_device as spi_device 253 self._spi = spi_device.SPIDevice(spi_busio, cs, baudrate=baudrate) 254 self._spi_bytearray1 = bytearray(1) 255 self._spi_bytearray6 = bytearray(6) 256 super().__init__(rng) 257 258 def write_register(self, register, value): 259 """ 260 Low level register writing over SPI, writes one 8-bit value 261 262 :param int register: which device register to write 263 :param value: a byte to write 264 """ 265 register &= 0x7F # Write, bit 7 low. 266 with self._spi as spi: 267 spi.write(bytes([register, value & 0xFF])) 268 269 def read_register(self, register): 270 """ 271 Low level register reading over SPI, returns a list of values 272 273 :param register: the register to read a byte 274 """ 275 register = (register | 0x80) & 0xFF # Read single, bit 7 high. 276 with self._spi as spi: 277 self._spi_bytearray1[0] = register 278 spi.write(self._spi_bytearray1) 279 spi.readinto(self._spi_bytearray1) 280 print("$%02X => %s" % (register, [hex(i) for i in self._spi_bytearray1])) 281 return self._spi_bytearray1[0] 282 283 def read_bytes(self, register, buffer): 284 """ 285 Low level register streem reading over SPI, returns a list of values 286 287 :param register: the register to read bytes 288 :param bytearray buffer: buffer to fill with data from stream 289 """ 290 register = (register | 0x80) & 0xFF # Read single, bit 7 high. 291 with self._spi as spi: 292 self._spi_bytearray1[0] = register 293 spi.write(self._spi_bytearray1) 294 spi.readinto(buffer) 295 296 @property 297 def acceleration_raw(self): 298 """Gives the raw acceleration readings, in units of the scaled mdps.""" 299 buffer = self._spi_bytearray6 300 self.read_bytes(_L3GD20_REGISTER_OUT_X_L_X40, buffer) 301 return unpack('<hhh', buffer)