-
Notifications
You must be signed in to change notification settings - Fork 13
/
adafruit_fxas21002c.py
200 lines (162 loc) · 7.04 KB
/
adafruit_fxas21002c.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# SPDX-FileCopyrightText: 2017 Tony DiCola for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
`adafruit_fxas21002c`
====================================================
CircuitPython module for the NXP FXAS21002C gyroscope. Based on the driver
from: https://github.com/adafruit/Adafruit_FXAS21002C
See examples/simpletest.py for a demo of the usage.
* Author(s): Tony DiCola
Implementation Notes
--------------------
**Hardware:**
* Adafruit `Precision NXP 9-DOF Breakout Board - FXOS8700 + FXAS21002
<https://www.adafruit.com/product/3463>`_ (Product ID: 3463)
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://circuitpython.org/downloads
* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
"""
import struct
import time
try:
from typing import List, Tuple
from busio import I2C
except ImportError:
pass
__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_FXAS21002C.git"
import adafruit_bus_device.i2c_device as i2c_dev
from micropython import const
# Internal constants and register values:
_FXAS21002C_ADDRESS = const(0x21) # 0100001
_FXAS21002C_ID = const(0xD7) # 1101 0111
_GYRO_REGISTER_STATUS = const(0x00)
_GYRO_REGISTER_OUT_X_MSB = const(0x01)
_GYRO_REGISTER_OUT_X_LSB = const(0x02)
_GYRO_REGISTER_OUT_Y_MSB = const(0x03)
_GYRO_REGISTER_OUT_Y_LSB = const(0x04)
_GYRO_REGISTER_OUT_Z_MSB = const(0x05)
_GYRO_REGISTER_OUT_Z_LSB = const(0x06)
_GYRO_REGISTER_WHO_AM_I = const(0x0C) # 11010111 r
_GYRO_REGISTER_CTRL_REG0 = const(0x0D) # 00000000 r/w
_GYRO_REGISTER_CTRL_REG1 = const(0x13) # 00000000 r/w
_GYRO_REGISTER_CTRL_REG2 = const(0x14) # 00000000 r/w
_GYRO_SENSITIVITY_250DPS = 0.0078125 # Table 35 of datasheet
_GYRO_SENSITIVITY_500DPS = 0.015625 # ..
_GYRO_SENSITIVITY_1000DPS = 0.03125 # ..
_GYRO_SENSITIVITY_2000DPS = 0.0625 # ..
# User facing constants/module globals:
GYRO_RANGE_250DPS = 250
GYRO_RANGE_500DPS = 500
GYRO_RANGE_1000DPS = 1000
GYRO_RANGE_2000DPS = 2000
# Unit conversion:
DEGREE_TO_RAD = 3.141592653589793 / 180
class FXAS21002C:
"""Driver for the NXP FXAS21002C gyroscope.
:param ~busio.I2C i2c: The I2C bus the device is connected to
:param int address: The I2C device address. Defaults to :const:`0x21`
:param int gyro_range: Device range. Defaults to :const:`250`.
**Quickstart: Importing and using the device**
Here is an example of using the :class:`FXAS21002C` class.
First you will need to import the libraries to use the sensor
.. code-block:: python
import board
import adafruit_fxas21002c
Once this is done you can define your `board.I2C` object and define your sensor object
.. code-block:: python
i2c = board.I2C() # uses board.SCL and board.SDA
sensor = adafruit_fxas21002c.FXAS21002C(i2c)
Now you have access to the :attr:`gyroscope` attribute
.. code-block:: python
gyro_x, gyro_y, gyro_z = sensor.gyroscope
"""
# Class-level buffer for reading and writing data with the sensor.
# This reduces memory allocations but means the code is not re-entrant or
# thread safe!
_BUFFER = bytearray(7)
def __init__(
self,
i2c: I2C,
address: int = _FXAS21002C_ADDRESS,
gyro_range: int = GYRO_RANGE_250DPS,
) -> None:
if gyro_range not in (
GYRO_RANGE_250DPS,
GYRO_RANGE_500DPS,
GYRO_RANGE_1000DPS,
GYRO_RANGE_2000DPS,
):
raise ValueError("gyro_range option selected is not a valid option")
self._gyro_range = gyro_range
self._device = i2c_dev.I2CDevice(i2c, address)
# Check for chip ID value.
if self._read_u8(_GYRO_REGISTER_WHO_AM_I) != _FXAS21002C_ID:
raise RuntimeError("Failed to find FXAS21002C, check wiring!")
ctrl_reg0 = 0x00
if gyro_range == GYRO_RANGE_250DPS:
ctrl_reg0 = 0x03
elif gyro_range == GYRO_RANGE_500DPS:
ctrl_reg0 = 0x02
elif gyro_range == GYRO_RANGE_1000DPS:
ctrl_reg0 = 0x01
elif gyro_range == GYRO_RANGE_2000DPS:
ctrl_reg0 = 0x00
# Reset then switch to active mode with 100Hz output
# Putting into standby doesn't work as the chip becomes instantly
# unresponsive. Perhaps CircuitPython is too slow to go into standby
# and send reset? Keep these two commented for now:
# self._write_u8(_GYRO_REGISTER_CTRL_REG1, 0x00) # Standby)
# self._write_u8(_GYRO_REGISTER_CTRL_REG1, (1<<6)) # Reset
self._write_u8(_GYRO_REGISTER_CTRL_REG0, ctrl_reg0) # Set sensitivity
self._write_u8(_GYRO_REGISTER_CTRL_REG1, 0x0E) # Active
time.sleep(0.1) # 60 ms + 1/ODR
def _read_u8(self, address: int) -> int:
# Read an 8-bit unsigned value from the specified 8-bit address.
with self._device as i2c:
self._BUFFER[0] = address & 0xFF
i2c.write_then_readinto(self._BUFFER, self._BUFFER, out_end=1, in_end=1)
return self._BUFFER[0]
def _write_u8(self, address: int, val: int) -> None:
# Write an 8-bit unsigned value to the specified 8-bit address.
with self._device as i2c:
self._BUFFER[0] = address & 0xFF
self._BUFFER[1] = val & 0xFF
i2c.write(self._BUFFER, end=2)
def read_raw(self) -> Tuple[int, int, int]:
"""Read the raw gyroscope readings. Returns a 3-tuple of X, Y, Z axis
16-bit signed values. If you want the gyroscope values in friendly
units consider using the gyroscope property!
"""
# Read gyro data from the sensor.
with self._device as i2c:
self._BUFFER[0] = _GYRO_REGISTER_OUT_X_MSB
i2c.write_then_readinto(self._BUFFER, self._BUFFER, out_end=1)
# Parse out the gyroscope data as 16-bit signed data.
raw_x = struct.unpack_from(">h", self._BUFFER[0:2])[0]
raw_y = struct.unpack_from(">h", self._BUFFER[2:4])[0]
raw_z = struct.unpack_from(">h", self._BUFFER[4:6])[0]
return (raw_x, raw_y, raw_z)
# pylint is confused and incorrectly marking this function as bad return
# types. Perhaps it doesn't understand map returns an iterable value.
# Disable the warning.
@property
def gyroscope(self) -> List[float]:
"""Read the gyroscope value and return its X, Y, Z axis values as a
3-tuple in radians/second.
"""
raw = self.read_raw()
# Compensate values depending on the resolution
factor = 0
if self._gyro_range == GYRO_RANGE_250DPS:
factor = _GYRO_SENSITIVITY_250DPS
elif self._gyro_range == GYRO_RANGE_500DPS:
factor = _GYRO_SENSITIVITY_500DPS
elif self._gyro_range == GYRO_RANGE_1000DPS:
factor = _GYRO_SENSITIVITY_1000DPS
elif self._gyro_range == GYRO_RANGE_2000DPS:
factor = _GYRO_SENSITIVITY_2000DPS
factor *= DEGREE_TO_RAD
return [x * factor for x in raw]