You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

431 lines
17 KiB

  1. # The MIT License (MIT)
  2. #
  3. # Copyright (c) 2017 Tony DiCola 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_lsm9ds1`
  24. ====================================================
  25. CircuitPython module for the LSM9DS1 accelerometer, magnetometer, gyroscope.
  26. Based on the driver from:
  27. https://github.com/adafruit/Adafruit_LSM9DS1
  28. See examples/simpletest.py for a demo of the usage.
  29. * Author(s): Tony DiCola
  30. """
  31. __version__ = "1.0.1"
  32. __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LSM9DS1.git"
  33. import time
  34. try:
  35. import struct
  36. except ImportError:
  37. import ustruct as struct
  38. import adafruit_bus_device.i2c_device as i2c_device
  39. import adafruit_bus_device.spi_device as spi_device
  40. from micropython import const
  41. # Internal constants and register values:
  42. # pylint: disable=bad-whitespace
  43. _LSM9DS1_ADDRESS_ACCELGYRO = const(0x6B)
  44. _LSM9DS1_ADDRESS_MAG = const(0x1E)
  45. _LSM9DS1_XG_ID = const(0b01101000)
  46. _LSM9DS1_MAG_ID = const(0b00111101)
  47. _LSM9DS1_ACCEL_MG_LSB_2G = 0.061
  48. _LSM9DS1_ACCEL_MG_LSB_4G = 0.122
  49. _LSM9DS1_ACCEL_MG_LSB_8G = 0.244
  50. _LSM9DS1_ACCEL_MG_LSB_16G = 0.732
  51. _LSM9DS1_MAG_MGAUSS_4GAUSS = 0.14
  52. _LSM9DS1_MAG_MGAUSS_8GAUSS = 0.29
  53. _LSM9DS1_MAG_MGAUSS_12GAUSS = 0.43
  54. _LSM9DS1_MAG_MGAUSS_16GAUSS = 0.58
  55. _LSM9DS1_GYRO_DPS_DIGIT_245DPS = 0.00875
  56. _LSM9DS1_GYRO_DPS_DIGIT_500DPS = 0.01750
  57. _LSM9DS1_GYRO_DPS_DIGIT_2000DPS = 0.07000
  58. _LSM9DS1_TEMP_LSB_DEGREE_CELSIUS = 8 # 1°C = 8, 25° = 200, etc.
  59. _LSM9DS1_REGISTER_WHO_AM_I_XG = const(0x0F)
  60. _LSM9DS1_REGISTER_CTRL_REG1_G = const(0x10)
  61. _LSM9DS1_REGISTER_CTRL_REG2_G = const(0x11)
  62. _LSM9DS1_REGISTER_CTRL_REG3_G = const(0x12)
  63. _LSM9DS1_REGISTER_TEMP_OUT_L = const(0x15)
  64. _LSM9DS1_REGISTER_TEMP_OUT_H = const(0x16)
  65. _LSM9DS1_REGISTER_STATUS_REG = const(0x17)
  66. _LSM9DS1_REGISTER_OUT_X_L_G = const(0x18)
  67. _LSM9DS1_REGISTER_OUT_X_H_G = const(0x19)
  68. _LSM9DS1_REGISTER_OUT_Y_L_G = const(0x1A)
  69. _LSM9DS1_REGISTER_OUT_Y_H_G = const(0x1B)
  70. _LSM9DS1_REGISTER_OUT_Z_L_G = const(0x1C)
  71. _LSM9DS1_REGISTER_OUT_Z_H_G = const(0x1D)
  72. _LSM9DS1_REGISTER_CTRL_REG4 = const(0x1E)
  73. _LSM9DS1_REGISTER_CTRL_REG5_XL = const(0x1F)
  74. _LSM9DS1_REGISTER_CTRL_REG6_XL = const(0x20)
  75. _LSM9DS1_REGISTER_CTRL_REG7_XL = const(0x21)
  76. _LSM9DS1_REGISTER_CTRL_REG8 = const(0x22)
  77. _LSM9DS1_REGISTER_CTRL_REG9 = const(0x23)
  78. _LSM9DS1_REGISTER_CTRL_REG10 = const(0x24)
  79. _LSM9DS1_REGISTER_OUT_X_L_XL = const(0x28)
  80. _LSM9DS1_REGISTER_OUT_X_H_XL = const(0x29)
  81. _LSM9DS1_REGISTER_OUT_Y_L_XL = const(0x2A)
  82. _LSM9DS1_REGISTER_OUT_Y_H_XL = const(0x2B)
  83. _LSM9DS1_REGISTER_OUT_Z_L_XL = const(0x2C)
  84. _LSM9DS1_REGISTER_OUT_Z_H_XL = const(0x2D)
  85. _LSM9DS1_REGISTER_WHO_AM_I_M = const(0x0F)
  86. _LSM9DS1_REGISTER_CTRL_REG1_M = const(0x20)
  87. _LSM9DS1_REGISTER_CTRL_REG2_M = const(0x21)
  88. _LSM9DS1_REGISTER_CTRL_REG3_M = const(0x22)
  89. _LSM9DS1_REGISTER_CTRL_REG4_M = const(0x23)
  90. _LSM9DS1_REGISTER_CTRL_REG5_M = const(0x24)
  91. _LSM9DS1_REGISTER_STATUS_REG_M = const(0x27)
  92. _LSM9DS1_REGISTER_OUT_X_L_M = const(0x28)
  93. _LSM9DS1_REGISTER_OUT_X_H_M = const(0x29)
  94. _LSM9DS1_REGISTER_OUT_Y_L_M = const(0x2A)
  95. _LSM9DS1_REGISTER_OUT_Y_H_M = const(0x2B)
  96. _LSM9DS1_REGISTER_OUT_Z_L_M = const(0x2C)
  97. _LSM9DS1_REGISTER_OUT_Z_H_M = const(0x2D)
  98. _LSM9DS1_REGISTER_CFG_M = const(0x30)
  99. _LSM9DS1_REGISTER_INT_SRC_M = const(0x31)
  100. _MAGTYPE = True
  101. _XGTYPE = False
  102. _SENSORS_GRAVITY_STANDARD = 9.80665
  103. # User facing constants/module globals.
  104. ACCELRANGE_2G = (0b00 << 3)
  105. ACCELRANGE_16G = (0b01 << 3)
  106. ACCELRANGE_4G = (0b10 << 3)
  107. ACCELRANGE_8G = (0b11 << 3)
  108. MAGGAIN_4GAUSS = (0b00 << 5) # +/- 4 gauss
  109. MAGGAIN_8GAUSS = (0b01 << 5) # +/- 8 gauss
  110. MAGGAIN_12GAUSS = (0b10 << 5) # +/- 12 gauss
  111. MAGGAIN_16GAUSS = (0b11 << 5) # +/- 16 gauss
  112. GYROSCALE_245DPS = (0b00 << 4) # +/- 245 degrees/s rotation
  113. GYROSCALE_500DPS = (0b01 << 4) # +/- 500 degrees/s rotation
  114. GYROSCALE_2000DPS = (0b11 << 4) # +/- 2000 degrees/s rotation
  115. # pylint: enable=bad-whitespace
  116. def _twos_comp(val, bits):
  117. # Convert an unsigned integer in 2's compliment form of the specified bit
  118. # length to its signed integer value and return it.
  119. if val & (1 << (bits - 1)) != 0:
  120. return val - (1 << bits)
  121. return val
  122. class LSM9DS1:
  123. """Driver for the LSM9DS1 accelerometer, magnetometer, gyroscope."""
  124. # Class-level buffer for reading and writing data with the sensor.
  125. # This reduces memory allocations but means the code is not re-entrant or
  126. # thread safe!
  127. _BUFFER = bytearray(6)
  128. def __init__(self):
  129. # soft reset & reboot accel/gyro
  130. self._write_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG8, 0x05)
  131. # soft reset & reboot magnetometer
  132. self._write_u8(_MAGTYPE, _LSM9DS1_REGISTER_CTRL_REG2_M, 0x0C)
  133. time.sleep(0.01)
  134. # Check ID registers.
  135. if self._read_u8(_XGTYPE, _LSM9DS1_REGISTER_WHO_AM_I_XG) != _LSM9DS1_XG_ID or \
  136. self._read_u8(_MAGTYPE, _LSM9DS1_REGISTER_WHO_AM_I_M) != _LSM9DS1_MAG_ID:
  137. raise RuntimeError('Could not find LSM9DS1, check wiring!')
  138. # enable gyro continuous
  139. self._write_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG1_G, 0xC0) # on XYZ
  140. # Enable the accelerometer continous
  141. self._write_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG5_XL, 0x38)
  142. self._write_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG6_XL, 0xC0)
  143. # enable mag continuous
  144. self._write_u8(_MAGTYPE, _LSM9DS1_REGISTER_CTRL_REG3_M, 0x00)
  145. # Set default ranges for the various sensors
  146. self._accel_mg_lsb = None
  147. self._mag_mgauss_lsb = None
  148. self._gyro_dps_digit = None
  149. self.accel_range = ACCELRANGE_2G
  150. self.mag_gain = MAGGAIN_4GAUSS
  151. self.gyro_scale = GYROSCALE_245DPS
  152. @property
  153. def accel_range(self):
  154. """Get and set the accelerometer range. Must be a value of:
  155. - ACCELRANGE_2G
  156. - ACCELRANGE_4G
  157. - ACCELRANGE_8G
  158. - ACCELRANGE_16G
  159. """
  160. reg = self._read_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG6_XL)
  161. return (reg & 0b00011000) & 0xFF
  162. @accel_range.setter
  163. def accel_range(self, val):
  164. assert val in (ACCELRANGE_2G, ACCELRANGE_4G, ACCELRANGE_8G,
  165. ACCELRANGE_16G)
  166. reg = self._read_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG6_XL)
  167. reg = (reg & ~(0b00011000)) & 0xFF
  168. reg |= val
  169. self._write_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG6_XL, reg)
  170. if val == ACCELRANGE_2G:
  171. self._accel_mg_lsb = _LSM9DS1_ACCEL_MG_LSB_2G
  172. elif val == ACCELRANGE_4G:
  173. self._accel_mg_lsb = _LSM9DS1_ACCEL_MG_LSB_4G
  174. elif val == ACCELRANGE_8G:
  175. self._accel_mg_lsb = _LSM9DS1_ACCEL_MG_LSB_8G
  176. elif val == ACCELRANGE_16G:
  177. self._accel_mg_lsb = _LSM9DS1_ACCEL_MG_LSB_16G
  178. @property
  179. def mag_gain(self):
  180. """Get and set the magnetometer gain. Must be a value of:
  181. - MAGGAIN_4GAUSS
  182. - MAGGAIN_8GAUSS
  183. - MAGGAIN_12GAUSS
  184. - MAGGAIN_16GAUSS
  185. """
  186. reg = self._read_u8(_MAGTYPE, _LSM9DS1_REGISTER_CTRL_REG2_M)
  187. return (reg & 0b01100000) & 0xFF
  188. @mag_gain.setter
  189. def mag_gain(self, val):
  190. assert val in (MAGGAIN_4GAUSS, MAGGAIN_8GAUSS, MAGGAIN_12GAUSS,
  191. MAGGAIN_16GAUSS)
  192. reg = self._read_u8(_MAGTYPE, _LSM9DS1_REGISTER_CTRL_REG2_M)
  193. reg = (reg & ~(0b01100000)) & 0xFF
  194. reg |= val
  195. self._write_u8(_MAGTYPE, _LSM9DS1_REGISTER_CTRL_REG2_M, reg)
  196. if val == MAGGAIN_4GAUSS:
  197. self._mag_mgauss_lsb = _LSM9DS1_MAG_MGAUSS_4GAUSS
  198. elif val == MAGGAIN_8GAUSS:
  199. self._mag_mgauss_lsb = _LSM9DS1_MAG_MGAUSS_8GAUSS
  200. elif val == MAGGAIN_12GAUSS:
  201. self._mag_mgauss_lsb = _LSM9DS1_MAG_MGAUSS_12GAUSS
  202. elif val == MAGGAIN_16GAUSS:
  203. self._mag_mgauss_lsb = _LSM9DS1_MAG_MGAUSS_16GAUSS
  204. @property
  205. def gyro_scale(self):
  206. """Get and set the gyroscope scale. Must be a value of:
  207. - GYROSCALE_245DPS
  208. - GYROSCALE_500DPS
  209. - GYROSCALE_2000DPS
  210. """
  211. reg = self._read_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG1_G)
  212. return (reg & 0b00110000) & 0xFF
  213. @gyro_scale.setter
  214. def gyro_scale(self, val):
  215. assert val in (GYROSCALE_245DPS, GYROSCALE_500DPS, GYROSCALE_2000DPS)
  216. reg = self._read_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG1_G)
  217. reg = (reg & ~(0b00110000)) & 0xFF
  218. reg |= val
  219. self._write_u8(_XGTYPE, _LSM9DS1_REGISTER_CTRL_REG1_G, reg)
  220. if val == GYROSCALE_245DPS:
  221. self._gyro_dps_digit = _LSM9DS1_GYRO_DPS_DIGIT_245DPS
  222. elif val == GYROSCALE_500DPS:
  223. self._gyro_dps_digit = _LSM9DS1_GYRO_DPS_DIGIT_500DPS
  224. elif val == GYROSCALE_2000DPS:
  225. self._gyro_dps_digit = _LSM9DS1_GYRO_DPS_DIGIT_2000DPS
  226. def read_accel_raw(self):
  227. """Read the raw accelerometer sensor values and return it as a
  228. 3-tuple of X, Y, Z axis values that are 16-bit unsigned values. If you
  229. want the acceleration in nice units you probably want to use the
  230. accelerometer property!
  231. """
  232. # Read the accelerometer
  233. self._read_bytes(_XGTYPE, 0x80 | _LSM9DS1_REGISTER_OUT_X_L_XL, 6,
  234. self._BUFFER)
  235. raw_x, raw_y, raw_z = struct.unpack_from('<hhh', self._BUFFER[0:6])
  236. return (raw_x, raw_y, raw_z)
  237. @property
  238. def accelerometer(self):
  239. """Get the accelerometer X, Y, Z axis values as a 3-tuple of
  240. m/s^2 values.
  241. """
  242. raw = self.read_accel_raw()
  243. return map(lambda x: x * self._accel_mg_lsb / 1000.0 * _SENSORS_GRAVITY_STANDARD,
  244. raw)
  245. def read_mag_raw(self):
  246. """Read the raw magnetometer sensor values and return it as a
  247. 3-tuple of X, Y, Z axis values that are 16-bit unsigned values. If you
  248. want the magnetometer in nice units you probably want to use the
  249. magnetometer property!
  250. """
  251. # Read the magnetometer
  252. self._read_bytes(_MAGTYPE, 0x80 | _LSM9DS1_REGISTER_OUT_X_L_M, 6,
  253. self._BUFFER)
  254. raw_x, raw_y, raw_z = struct.unpack_from('<hhh', self._BUFFER[0:6])
  255. return (raw_x, raw_y, raw_z)
  256. @property
  257. def magnetometer(self):
  258. """Get the magnetometer X, Y, Z axis values as a 3-tuple of
  259. gauss values.
  260. """
  261. raw = self.read_mag_raw()
  262. return map(lambda x: x * self._mag_mgauss_lsb / 1000.0, raw)
  263. def read_gyro_raw(self):
  264. """Read the raw gyroscope sensor values and return it as a
  265. 3-tuple of X, Y, Z axis values that are 16-bit unsigned values. If you
  266. want the gyroscope in nice units you probably want to use the
  267. gyroscope property!
  268. """
  269. # Read the gyroscope
  270. self._read_bytes(_XGTYPE, 0x80 | _LSM9DS1_REGISTER_OUT_X_L_G, 6,
  271. self._BUFFER)
  272. raw_x, raw_y, raw_z = struct.unpack_from('<hhh', self._BUFFER[0:6])
  273. return (raw_x, raw_y, raw_z)
  274. @property
  275. def gyroscope(self):
  276. """Get the gyroscope X, Y, Z axis values as a 3-tuple of
  277. degrees/second values.
  278. """
  279. raw = self.read_mag_raw()
  280. return map(lambda x: x * self._gyro_dps_digit, raw)
  281. def read_temp_raw(self):
  282. """Read the raw temperature sensor value and return it as a 12-bit
  283. signed value. If you want the temperature in nice units you probably
  284. want to use the temperature property!
  285. """
  286. # Read temp sensor
  287. self._read_bytes(_XGTYPE, 0x80 | _LSM9DS1_REGISTER_TEMP_OUT_L, 2,
  288. self._BUFFER)
  289. temp = ((self._BUFFER[1] << 8) | self._BUFFER[0]) >> 4
  290. return _twos_comp(temp, 12)
  291. @property
  292. def temperature(self):
  293. """Get the temperature of the sensor in degrees Celsius."""
  294. # This is just a guess since the starting point (21C here) isn't documented :(
  295. # See discussion from:
  296. # https://github.com/kriswiner/LSM9DS1/issues/3
  297. temp = self.read_temp_raw()
  298. temp = 27.5 + temp/16
  299. return temp
  300. def _read_u8(self, sensor_type, address):
  301. # Read an 8-bit unsigned value from the specified 8-bit address.
  302. # The sensor_type boolean should be _MAGTYPE when talking to the
  303. # magnetometer, or _XGTYPE when talking to the accel or gyro.
  304. # MUST be implemented by subclasses!
  305. raise NotImplementedError()
  306. def _read_bytes(self, sensor_type, address, count, buf):
  307. # Read a count number of bytes into buffer from the provided 8-bit
  308. # register address. The sensor_type boolean should be _MAGTYPE when
  309. # talking to the magnetometer, or _XGTYPE when talking to the accel or
  310. # gyro. MUST be implemented by subclasses!
  311. raise NotImplementedError()
  312. def _write_u8(self, sensor_type, address, val):
  313. # Write an 8-bit unsigned value to the specified 8-bit address.
  314. # The sensor_type boolean should be _MAGTYPE when talking to the
  315. # magnetometer, or _XGTYPE when talking to the accel or gyro.
  316. # MUST be implemented by subclasses!
  317. raise NotImplementedError()
  318. class LSM9DS1_I2C(LSM9DS1):
  319. """Driver for the LSM9DS1 connect over I2C."""
  320. def __init__(self, i2c):
  321. self._mag_device = i2c_device.I2CDevice(i2c, _LSM9DS1_ADDRESS_MAG)
  322. self._xg_device = i2c_device.I2CDevice(i2c, _LSM9DS1_ADDRESS_ACCELGYRO)
  323. super().__init__()
  324. def _read_u8(self, sensor_type, address):
  325. if sensor_type == _MAGTYPE:
  326. device = self._mag_device
  327. else:
  328. device = self._xg_device
  329. with device as i2c:
  330. self._BUFFER[0] = address & 0xFF
  331. i2c.write(self._BUFFER, end=1, stop=False)
  332. i2c.readinto(self._BUFFER, end=1)
  333. return self._BUFFER[0]
  334. def _read_bytes(self, sensor_type, address, count, buf):
  335. if sensor_type == _MAGTYPE:
  336. device = self._mag_device
  337. else:
  338. device = self._xg_device
  339. with device as i2c:
  340. buf[0] = address & 0xFF
  341. i2c.write(buf, end=1, stop=False)
  342. i2c.readinto(buf, end=count)
  343. def _write_u8(self, sensor_type, address, val):
  344. if sensor_type == _MAGTYPE:
  345. device = self._mag_device
  346. else:
  347. device = self._xg_device
  348. with device as i2c:
  349. self._BUFFER[0] = address & 0xFF
  350. self._BUFFER[1] = val & 0xFF
  351. i2c.write(self._BUFFER, end=2)
  352. class LSM9DS1_SPI(LSM9DS1):
  353. """Driver for the LSM9DS1 connect over SPI."""
  354. def __init__(self, spi, xgcs, mcs):
  355. self._mag_device = spi_device.I2CDevice(spi, mcs)
  356. self._xg_device = spi_device.I2CDevice(spi, xgcs)
  357. super().__init__()
  358. def _read_u8(self, sensor_type, address):
  359. if sensor_type == _MAGTYPE:
  360. device = self._mag_device
  361. else:
  362. device = self._xg_device
  363. with device as spi:
  364. spi.configure(baudrate=200000, phase=0, polarity=0)
  365. self._BUFFER[0] = (address | 0x80) & 0xFF
  366. spi.write(self._BUFFER, end=1)
  367. spi.readinto(self._BUFFER, end=1)
  368. return self._BUFFER[0]
  369. def _read_bytes(self, sensor_type, address, count, buf):
  370. if sensor_type == _MAGTYPE:
  371. device = self._mag_device
  372. else:
  373. device = self._xg_device
  374. with device as spi:
  375. spi.configure(baudrate=200000, phase=0, polarity=0)
  376. buf[0] = (address | 0x80) & 0xFF
  377. spi.write(buf, end=1)
  378. spi.readinto(buf, end=count)
  379. def _write_u8(self, sensor_type, address, val):
  380. if sensor_type == _MAGTYPE:
  381. device = self._mag_device
  382. else:
  383. device = self._xg_device
  384. with device as spi:
  385. spi.configure(baudrate=200000, phase=0, polarity=0)
  386. self._BUFFER[0] = (address & 0x7F) & 0xFF
  387. self._BUFFER[1] = val & 0xFF
  388. spi.write(self._BUFFER, end=2)