/ 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)