Created
March 3, 2019 17:23
-
-
Save aprovecharLab/d971fba4ba0d440464004166f4b30d2e to your computer and use it in GitHub Desktop.
Reading the NXP 9-DOF (fxos8700+fxas21002c) Sensors
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Reading the NXP 9-DOF (fxos8700+fxas21002c) Sensors\n", | |
"### with FTDI USB/i2c\n", | |
"- Adafruit Precision NXP 9-DOF Breakout Board - FXOS8700 + FXAS21002C: https://www.adafruit.com/product/3463\n", | |
"- AdaFruit FT232H USB to i2c interface: https://www.adafruit.com/product/2264\n", | |
"- PyFtdi library: http://eblot.github.io/pyftdi/index.html" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"from pyftdi.i2c import I2cController\n", | |
"from os import environ\n", | |
"\n", | |
"import math\n", | |
"import time\n", | |
"\n", | |
"import struct" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"###############################################################################\n", | |
"def recast14to16(byte0,byte1):\n", | |
" # pack 14 bits (1.5 bytes) into 16 bits byte-type\n", | |
" b14 = struct.pack('BB',byte0,byte1)\n", | |
"\n", | |
" # unpack to unsigned 16 bit integer\n", | |
" # note this device pads byte1 on the right with two empty bits\n", | |
" uL = struct.unpack('>H',b14)[0] >> 2\n", | |
" \n", | |
" # this is for 2's complement signed numbers - \n", | |
" # if negative assign sign bits for 16 bit case\n", | |
" if (uL & 0x2000):\n", | |
" uL = uL | 0xe000\n", | |
" \n", | |
" # repack as 16 bit unsigned byte-type\n", | |
" packed = struct.pack('>H', uL)\n", | |
" # unpack as 16 bit signed integer\n", | |
" unpacked = struct.unpack('>h', packed)[0]\n", | |
"\n", | |
" return unpacked" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def twos_comp(val, bits):\n", | |
" # Convert an unsigned integer in 2's compliment form of the specified bit\n", | |
" # length to its signed integer value\n", | |
" if val & (1 << (bits - 1)) != 0:\n", | |
" return val - (1 << bits)\n", | |
" return val" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class FTDIi2c:\n", | |
" def __init__(self):\n", | |
" self.i2c = I2cController()\n", | |
" url = environ.get('FTDI_DEVICE', 'ftdi://ftdi:232h/1')\n", | |
" self.i2c.configure(url, clockstretching=False)\n", | |
"\n", | |
" def connect(self,address):\n", | |
" port = self.i2c.get_port(address)\n", | |
" return port\n", | |
"\n", | |
" def close(self):\n", | |
" # Close the I2C connection\n", | |
" self.i2c.terminate()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"###############################################################################\n", | |
"class i2cMagAccel:\n", | |
"\n", | |
" fxos8700_ADDRESS = 0x1F\n", | |
" fxos8700_ID = 0xC7\n", | |
" fxos8700_STATUS = 0x00\n", | |
" fxos8700_OUT_X_MSB = 0x01\n", | |
" fxos8700_OUT_X_LSB = 0x02\n", | |
" fxos8700_OUT_Y_MSB = 0x03\n", | |
" fxos8700_OUT_Y_LSB = 0x04\n", | |
" fxos8700_OUT_Z_MSB = 0x05\n", | |
" fxos8700_OUT_Z_LSB = 0x06\n", | |
" fxos8700_WHO_AM_I = 0x0D\n", | |
" fxos8700_XYZ_DATA_CFG = 0x0E\n", | |
" fxos8700_CTRL_REG1 = 0x2A\n", | |
" fxos8700_CTRL_REG2 = 0x2B\n", | |
" fxos8700_CTRL_REG3 = 0x2C\n", | |
" fxos8700_CTRL_REG4 = 0x2D\n", | |
" fxos8700_CTRL_REG5 = 0x2E\n", | |
" fxos8700_MSTATUS = 0x32\n", | |
" fxos8700_MOUT_X_MSB = 0x33\n", | |
" fxos8700_MOUT_X_LSB = 0x34\n", | |
" fxos8700_MOUT_Y_MSB = 0x35\n", | |
" fxos8700_MOUT_Y_LSB = 0x36\n", | |
" fxos8700_MOUT_Z_MSB = 0x37\n", | |
" fxos8700_MOUT_Z_LSB = 0x38\n", | |
" fxos8700_MCTRL_REG1 = 0x5B\n", | |
" fxos8700_MCTRL_REG2 = 0x5C\n", | |
" fxos8700_MCTRL_REG3 = 0x5D\n", | |
"\n", | |
" ACCEL_MG_LSB_2G = 0.000244\n", | |
" ACCEL_MG_LSB_4G = 0.000488\n", | |
" ACCEL_MG_LSB_8G = 0.000976\n", | |
" MAG_UT_LSB = 0.1 # uT/lsb\n", | |
"\n", | |
" GRAVITY_STANDARD = 9.80665\n", | |
" ACCEL_RANGE_2G = 0x00\n", | |
" ACCEL_RANGE_4G = 0x01\n", | |
" ACCEL_RANGE_8G = 0x02\n", | |
"\n", | |
" # standby mode for configuration\n", | |
" # write 0000 0000 = 0x00 to accelerometer control register 1 to place FXOS8700CQ into standby\n", | |
" # [7-1] = 0000 000\n", | |
" # [0]: active=0\n", | |
" standby_mode = 0x00\n", | |
"\n", | |
" # configure magnetometer\n", | |
" # write 0001 1111 = 0x1F to magnetometer control register 1\n", | |
" # [7]: m_acal=0: auto calibration disabled\n", | |
" # [6]: m_rst=0: no one-shot magnetic reset\n", | |
" # [5]: m_ost=0: no one-shot magnetic measurement\n", | |
" # [4-2]: m_os=111=7: 8x oversampling (for 200Hz) to reduce magnetometer noise\n", | |
" # [1-0]: m_hms=11=3: select hybrid mode with accel and magnetometer active \n", | |
" mag_settings_1 = 0x1F\n", | |
" \n", | |
" # write 0010 0000 = 0x20 to magnetometer control register 2\n", | |
" # [7]: reserved\n", | |
" # [6]: reserved\n", | |
" # [5]: hyb_autoinc_mode=1 to map the magnetometer registers to follow the accelerometer registers\n", | |
" # [4]: m_maxmin_dis=0 to retain default min/max latching even though not used\n", | |
" # [3]: m_maxmin_dis_ths=0\n", | |
" # [2]: m_maxmin_rst=0\n", | |
" # [1-0]: m_rst_cnt=00 to enable magnetic reset each cycle\n", | |
" mag_settings_2 = 0x20\n", | |
" \n", | |
" # configure accelerometer\n", | |
" # write 0000 1101 = 0x0D to accelerometer control register 1\n", | |
" # [7-6]: aslp_rate=00\n", | |
" # [5-3]: dr=001 for 200Hz data rate (when in hybrid mode)\n", | |
" # [2]: lnoise=1 for low noise mode\n", | |
" # [1]: f_read=0 for normal 16 bit reads\n", | |
" # [0]: active=1 to take the part out of standby and enable sampling\n", | |
" accel_settings_1 = 0x0D\n", | |
"\n", | |
" # use accelerometer high resolution mode instead of low power mode\n", | |
" accel_settings_2 = 0x02\n", | |
"\n", | |
" # accelerometer range\n", | |
" # write 0000 0001= 0x01 to XYZ_DATA_CFG register\n", | |
" # [7]: reserved\n", | |
" # [6]: reserved\n", | |
" # [5]: reserved\n", | |
" # [4]: hpf_out=0\n", | |
" # [3]: reserved\n", | |
" # [2]: reserved\n", | |
" # [1-0]: fs=01 for accelerometer range of +/-4g range with 0.488mg/LSB\n", | |
" accel_range = ACCEL_RANGE_4G\n", | |
"\n", | |
"# accel_gain = ACCEL_MG_LSB_4G * GRAVITY_STANDARD\n", | |
" accel_gain = ACCEL_MG_LSB_4G # g\n", | |
" mag_gain = MAG_UT_LSB * 1000.0 # nT/lsb\n", | |
" \n", | |
" \n", | |
" ###########################################################################\n", | |
" def __init__(self,port):\n", | |
" self.port = port\n", | |
" \n", | |
" def configure(self):\n", | |
" # Configure the accelerometer\n", | |
" self.port.write_to(self.fxos8700_CTRL_REG1, self.standby_mode) # standby on\n", | |
" self.port.write_to(self.fxos8700_XYZ_DATA_CFG, [self.accel_range])\n", | |
" self.port.write_to(self.fxos8700_CTRL_REG2, [self.accel_settings_2])\n", | |
" self.port.write_to(self.fxos8700_CTRL_REG1, [self.accel_settings_1]) # standby off\n", | |
"\n", | |
" # Configure the magnetometer\n", | |
" self.port.write_to(self.fxos8700_MCTRL_REG1, [self.mag_settings_1])\n", | |
" self.port.write_to(self.fxos8700_MCTRL_REG2, [self.mag_settings_2])\n", | |
" \n", | |
" deviceConfig = [self.accel_range, self.accel_settings_1, self.accel_settings_2, \\\n", | |
" self.mag_settings_1, self.mag_settings_2]\n", | |
"\n", | |
" return deviceConfig\n", | |
"\n", | |
" def device_ID(self):\n", | |
" device_id = self.port.exchange([self.fxos8700_WHO_AM_I], 1)[0]\n", | |
" return int(device_id)\n", | |
"\n", | |
"\n", | |
" ###########################################################################\n", | |
" # Read accelerometer and magnetometer data\n", | |
" def read(self):\n", | |
" raw = self.port.read_from(self.fxos8700_OUT_X_MSB, 13)\n", | |
"# print(' '.join('{:02x}'.format(x) for x in raw))\n", | |
"\n", | |
" ## accelerometer\n", | |
" # data are 14 bit unsigned integers\n", | |
" accel_raw_x = struct.unpack_from('>H', raw[0:2])[0]\n", | |
" accel_raw_y = struct.unpack_from('>H', raw[2:4])[0]\n", | |
" accel_raw_z = struct.unpack_from('>H', raw[4:6])[0]\n", | |
" \n", | |
" # convert 14 bit two's complement to 16 bit signed integers\n", | |
" # note: this device pads LSB with two empty bits on right, \n", | |
" # so need to shift everything right 2 places\n", | |
" accel_raw_x = twos_comp(accel_raw_x >> 2, 14)\n", | |
" accel_raw_y = twos_comp(accel_raw_y >> 2, 14)\n", | |
" accel_raw_z = twos_comp(accel_raw_z >> 2, 14)\n", | |
" \n", | |
"# # alternative to above for 14 bit to 16 bit conversion\n", | |
"# accel_raw_x = recast14to16(raw[0],raw[1])\n", | |
"# accel_raw_y = recast14to16(raw[2],raw[3])\n", | |
"# accel_raw_z = recast14to16(raw[4],raw[5])\n", | |
"\n", | |
" aX = float(accel_raw_x) * self.accel_gain\n", | |
" aY = float(accel_raw_y) * self.accel_gain\n", | |
" aZ = float(accel_raw_z) * self.accel_gain\n", | |
"\n", | |
" ## magnetometer\n", | |
" # already 16 bit signed integers\n", | |
" mag_raw_x = struct.unpack_from('>h', raw[6:8])[0]\n", | |
" mag_raw_y = struct.unpack_from('>h', raw[8:10])[0]\n", | |
" mag_raw_z = struct.unpack_from('>h', raw[10:12])[0]\n", | |
" \n", | |
" mX = float(mag_raw_x) * self.mag_gain\n", | |
" mY = float(mag_raw_y) * self.mag_gain\n", | |
" mZ = float(mag_raw_z) * self.mag_gain\n", | |
"\n", | |
" mag = [mX, mY, mZ]\n", | |
" accel = [aX, aY, aZ]\n", | |
"\n", | |
" return mag, accel" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"###############################################################################\n", | |
"class i2cGyro:\n", | |
" \n", | |
" fxas21002c_ADDRESS = 0x21\n", | |
" fxas21002c_ID = 0xD7\n", | |
" fxas21002c_STATUS = 0x00\n", | |
" fxas21002c_OUT_X_MSB = 0x01\n", | |
" fxas21002c_OUT_X_LSB = 0x02\n", | |
" fxas21002c_OUT_Y_MSB = 0x03\n", | |
" fxas21002c_OUT_Y_LSB = 0x04\n", | |
" fxas21002c_OUT_Z_MSB = 0x05\n", | |
" fxas21002c_OUT_Z_LSB = 0x06\n", | |
" fxas21002c_WHO_AM_I = 0x0C\n", | |
" fxas21002c_CTRL_REG0 = 0x0D\n", | |
" fxas21002c_CTRL_REG1 = 0x13\n", | |
" fxas21002c_CTRL_REG2 = 0x14\n", | |
" \n", | |
" fxas21002c_SENSITIVITY_250DPS = 0.0078125 # radians/second/lsb\n", | |
" fxas21002c_SENSITIVITY_500DPS = 0.015625\n", | |
" fxas21002c_SENSITIVITY_1000DPS = 0.03125\n", | |
" fxas21002c_SENSITIVITY_2000DPS = 0.0625\n", | |
"\n", | |
" GYRO_RANGE_250DPS = 250\n", | |
" GYRO_RANGE_500DPS = 500\n", | |
" GYRO_RANGE_1000DPS = 1000\n", | |
" GYRO_RANGE_2000DPS = 2000\n", | |
" \n", | |
" reset_mode = 0x40\n", | |
" standby_mode = 0x00\n", | |
" active_mode = 0x0e\n", | |
"\n", | |
" # user settings\n", | |
" gyro_range = GYRO_RANGE_500DPS \n", | |
" gyro_gain = fxas21002c_SENSITIVITY_500DPS # radians/second/lsb\n", | |
"\n", | |
" if gyro_range == GYRO_RANGE_250DPS:\n", | |
" sensitivity_mode = 0x03\n", | |
" elif gyro_range == GYRO_RANGE_500DPS:\n", | |
" sensitivity_mode = 0x02\n", | |
" elif gyro_range == GYRO_RANGE_1000DPS:\n", | |
" sensitivity_mode = 0x01\n", | |
" elif gyro_range == GYRO_RANGE_2000DPS:\n", | |
" sensitivity_mode = 0x00\n", | |
"\n", | |
" \n", | |
" ###########################################################################\n", | |
" def __init__(self,port):\n", | |
" self.port = port\n", | |
" \n", | |
" def configure(self):\n", | |
" # Reset then switch to active mode with 100Hz output\n", | |
" self.port.write_to(self.fxas21002c_CTRL_REG1, [self.standby_mode]) # Standby\n", | |
" self.port.exchange([self.fxas21002c_CTRL_REG1], self.reset_mode) # Reset\n", | |
" self.port.write_to(self.fxas21002c_CTRL_REG0, [self.sensitivity_mode]) # Set sensitivity\n", | |
" self.port.write_to(self.fxas21002c_CTRL_REG1, [self.active_mode]) # Active\n", | |
" time.sleep(0.1) # 60 ms + 1/ODR\n", | |
"\n", | |
" deviceConfig = [self.sensitivity_mode]\n", | |
" return deviceConfig\n", | |
"\n", | |
" def device_ID(self):\n", | |
" device_id = self.port.exchange([self.fxas21002c_WHO_AM_I], 1)[0]\n", | |
" return int(device_id)\n", | |
"\n", | |
" ###########################################################################\n", | |
" # Read gyroscope\n", | |
" def read(self):\n", | |
" raw = self.port.read_from(self.fxas21002c_OUT_X_MSB, 7)\n", | |
"# print(' '.join('{:02x}'.format(x) for x in raw))\n", | |
" \n", | |
" ## gyroscope\n", | |
" # already 16 bit signed integers\n", | |
" gyro_raw_x = struct.unpack_from('>h', raw[0:2])[0]\n", | |
" gyro_raw_y = struct.unpack_from('>h', raw[2:4])[0]\n", | |
" gyro_raw_z = struct.unpack_from('>h', raw[4:6])[0]\n", | |
" \n", | |
" gX = float(gyro_raw_x) * self.gyro_gain\n", | |
" gY = float(gyro_raw_y) * self.gyro_gain\n", | |
" gZ = float(gyro_raw_z) * self.gyro_gain\n", | |
"\n", | |
" gyro = [gX, gY, gZ]\n", | |
"\n", | |
" return gyro" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"metadata": { | |
"scrolled": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Device Config: 01 0d 02 1f 20\n", | |
"Device ID: 0xc7\n", | |
" \n", | |
"Device Config: 02\n", | |
"Device ID: 0xd7\n", | |
" \n", | |
"magXYZ: \t [33800.0, 44800.0, 22000.0] \t |M|: 60278.35432391963\n", | |
"accelXYZ: \t [0.019032, -0.033672, 1.042368]\n", | |
"gyroXYZ: \t [1.234375, 0.0, -0.0625]\n", | |
"\n", | |
"magXYZ: \t [34500.0, 45300.0, 20100.0] \t |M|: 60385.014697356826\n", | |
"accelXYZ: \t [0.017079999999999998, -0.034648, 1.043344]\n", | |
"gyroXYZ: \t [0.96875, -0.40625, 0.1875]\n", | |
"\n", | |
"magXYZ: \t [33400.0, 45100.0, 20200.0] \t |M|: 59645.703952589916\n", | |
"accelXYZ: \t [0.019032, -0.034648, 1.0389519999999999]\n", | |
"gyroXYZ: \t [0.703125, -0.171875, 0.234375]\n", | |
"\n", | |
"magXYZ: \t [33300.0, 43600.0, 20900.0] \t |M|: 58708.26176953292\n", | |
"accelXYZ: \t [0.020007999999999998, -0.034648, 1.04188]\n", | |
"gyroXYZ: \t [0.671875, -0.03125, 0.296875]\n", | |
"\n", | |
"magXYZ: \t [34300.0, 45600.0, 20700.0] \t |M|: 60698.76440258072\n", | |
"accelXYZ: \t [0.01952, -0.033672, 1.039928]\n", | |
"gyroXYZ: \t [0.859375, 0.078125, 0.140625]\n", | |
"\n", | |
"magXYZ: \t [33800.0, 43300.0, 20500.0] \t |M|: 58630.87923611584\n", | |
"accelXYZ: \t [0.021471999999999998, -0.033672, 1.045296]\n", | |
"gyroXYZ: \t [1.28125, 0.0625, 0.171875]\n", | |
"\n", | |
"magXYZ: \t [32700.0, 44000.0, 19400.0] \t |M|: 58151.95611499238\n", | |
"accelXYZ: \t [0.017568, -0.033672, 1.040904]\n", | |
"gyroXYZ: \t [0.9375, -0.109375, 0.390625]\n", | |
"\n", | |
"magXYZ: \t [33800.0, 45000.0, 21500.0] \t |M|: 60246.90863438555\n", | |
"accelXYZ: \t [0.015616, -0.032695999999999996, 1.04432]\n", | |
"gyroXYZ: \t [0.5625, -0.125, 0.234375]\n", | |
"\n", | |
"magXYZ: \t [34400.0, 44600.0, 19600.0] \t |M|: 59637.90740795656\n", | |
"accelXYZ: \t [0.020007999999999998, -0.036112, 1.03944]\n", | |
"gyroXYZ: \t [0.9375, 0.109375, 0.296875]\n", | |
"\n", | |
"magXYZ: \t [32900.0, 42100.0, 20100.0] \t |M|: 57086.162946899836\n", | |
"accelXYZ: \t [0.018056, -0.036112, 1.039928]\n", | |
"gyroXYZ: \t [0.90625, 0.0, -0.140625]\n", | |
"\n" | |
] | |
} | |
], | |
"source": [ | |
"i2c = FTDIi2c()\n", | |
"\n", | |
"port_nxpMA = i2c.connect(0x1F)\n", | |
"MagAccel = i2cMagAccel(port_nxpMA)\n", | |
"deviceConfig = MagAccel.configure()\n", | |
"print ('Device Config: ',' '.join('{:02x}'.format(x) for x in deviceConfig))\n", | |
"id = MagAccel.device_ID()\n", | |
"print ('Device ID: ',hex(id))\n", | |
"print(' ')\n", | |
"\n", | |
"port_nxpGY = i2c.connect(0x21)\n", | |
"Gyro = i2cGyro(port_nxpGY)\n", | |
"deviceConfig = Gyro.configure()\n", | |
"print ('Device Config: ',' '.join('{:02x}'.format(x) for x in deviceConfig))\n", | |
"id = Gyro.device_ID()\n", | |
"print ('Device ID: ',hex(id))\n", | |
"print(' ')\n", | |
"\n", | |
"for i in range(10):\n", | |
" magXYZ, accelXYZ = MagAccel.read()\n", | |
" gyroXYZ = Gyro.read()\n", | |
"\n", | |
" M = math.sqrt(magXYZ[0]**2 + magXYZ[1]**2 + magXYZ[2]**2)\n", | |
"\n", | |
" print ('magXYZ: \\t', magXYZ, '\\t |M|: ', M)\n", | |
" print ('accelXYZ: \\t',accelXYZ)\n", | |
" print ('gyroXYZ: \\t', gyroXYZ)\n", | |
" print ('')\n", | |
"\n", | |
" time.sleep(0.1)\n", | |
" \n", | |
"i2c.close()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.7.1" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment