Sensor Fusion Library 0.6.1
Orientation sensing for Espressif (ESP32, ESP8266) processors
Loading...
Searching...
No Matches
driver_fxos8700.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2015, Freescale Semiconductor, Inc.
3 * Copyright (c) 2016-2017 NXP
4 * Copyright (c) 2020 Bjarne Hansen
5 * All rights reserved.
6 *
7 * SPDX-License-Identifier: BSD-3-Clause
8 */
9
16#include "sensor_fusion.h" // Sensor fusion structures and types
17#include "driver_fxos8700.h" // FXOS8700 hardware interface
18#include "driver_fxos8700_registers.h" // describes the FXOS8700 register definitions and bit masks
19#include "driver_sensors.h" // prototypes for *_Init() and *_Read() methods
20#include "hal_i2c.h" // I2C interface methods
21
22
23// Command definition to read the WHO_AM_I value.
24const registerReadlist_t FXOS8700_WHO_AM_I_READ[] =
25{
26 { .readFrom = FXOS8700_WHO_AM_I, .numBytes = 1 }, __END_READ_DATA__
27};
28
29// Command definition to read the number of entries in the accel FIFO.
30const registerReadlist_t FXOS8700_F_STATUS_READ[] =
31{
32 { .readFrom = FXOS8700_STATUS, .numBytes = 1 }, __END_READ_DATA__
33};
34
35// Command definition to read the number of entries in the accel FIFO.
36registerReadlist_t FXOS8700_DATA_READ[] =
37{
38 { .readFrom = FXOS8700_OUT_X_MSB, .numBytes = 6 }, __END_READ_DATA__
39};
40
41// Each entry in a RegisterWriteList is composed of: register address, value to write, bit-mask to apply to write (0 enables)
42const registerwritelist_t FXOS8700_Initialization[] =
43{
44 // write 0000 0000 = 0x00 to CTRL_REG1 to place FXOS8700 into standby
45 // [7-1] = 0000 000
46 // [0]: active=0
47 { FXOS8700_CTRL_REG1, 0x00, 0x00 },
48
49 // write 0100 0000 = 0x40 to F_SETUP to enable FIFO in continuous (circular) mode
50 // [7-6]: f_mode[1-0]=01 for FIFO continuous mode
51 // [5-0]: f_wmrk[5-0]=000000 for no FIFO watermark
52 { FXOS8700_F_SETUP, 0x40, 0x00 },
53
54 // write 0001 1111 = 0x1F to M_CTRL_REG1
55 // [7]: m_acal=0: auto calibration disabled
56 // [6]: m_rst=0: one-shot magnetic reset disabled
57 // [5]: m_ost=0: one-shot magnetic measurement disabled
58 // [4-2]: m_os=111=7: maximum oversampling (8X at 200 Hz) to reduce magnetometer noise
59 // [1-0]: m_hms=11=3: select hybrid mode with accel and magnetometer active
60 { FXOS8700_M_CTRL_REG1, 0x1F, 0x00 },
61
62 // write 0000 0000 = 0x00 to M_CTRL_REG2
63 // [7]: reserved
64 // [6]: reserved
65 // [5]: hyb_autoinc_mode=0 to ensure address wraparound to 0x00 to clear accelerometer FIFO in one read
66 // [4]: m_maxmin_dis=0 to retain default min/max latching even though not used
67 // [3]: m_maxmin_dis_ths=0
68 // [2]: m_maxmin_rst=0
69 // [1-0]: m_rst_cnt=00 to enable magnetic reset each cycle
70 { FXOS8700_M_CTRL_REG2, 0x00, 0x00 },
71
72 // write 0000 0001= 0x01 to XYZ_DATA_CFG register
73 // [7]: reserved
74 // [6]: reserved
75 // [5]: reserved
76 // [4]: hpf_out=0
77 // [3]: reserved
78 // [2]: reserved
79 // [1-0]: fs=01 for 4g mode: 2048 counts / g = 8192 counts / g after 2 bit left shift
80 { FXOS8700_XYZ_DATA_CFG, 0x01, 0x00 },
81
82 // write 0000 0010 = 0x02 to CTRL_REG2 to set MODS bits
83 // [7]: st=0: self test disabled
84 // [6]: rst=0: reset disabled
85 // [5]: unused
86 // [4-3]: smods=00
87 // [2]: slpe=0: auto sleep disabled
88 // [1-0]: mods=10 for high resolution (maximum over sampling)
89 { FXOS8700_CTRL_REG2, 0x02, 0x00 },
90
91 // write 00XX X101 = 0x0D to accelerometer control register 1
92 // since this is a hybrid sensor with accelerometer and magnetometer sharing an ADC,
93 // the actual ODR is one-half of the the individual ODRs. E.g. ask for 400 Hz, get 200 Hz
94 // The values listed below are the actual realized ODRs.
95 // [7-6]: aslp_rate=00
96 // [5-3]: dr=111 for 0.78Hz data rate giving 0x3D
97 // [5-3]: dr=110 for 3.125Hz data rate giving 0x35
98 // [5-3]: dr=101 for 6.25Hz data rate giving 0x2D
99 // [5-3]: dr=100 for 25Hz data rate giving 0x25
100 // [5-3]: dr=011 for 50Hz data rate giving 0x1D
101 // [5-3]: dr=010 for 100Hz data rate giving 0x15
102 // [5-3]: dr=001 for 200Hz data rate giving 0x0D
103 // [5-3]: dr=000 for 400Hz data rate giving 0x05
104 // [2]: lnoise=1 for low noise mode (only works in 2g and 4g mode)
105 // [1]: f_read=0 for normal 16 bit reads
106 // [0]: active=1 to take the part out of standby and enable sampling
107#if (ACCEL_ODR_HZ <= 1) // select 0.78Hz ODR
108 { FXOS8700_CTRL_REG1, 0x3D, 0x00 },
109#elif (ACCEL_ODR_HZ <= 3) // select 3.125Hz ODR
110 { FXOS8700_CTRL_REG1, 0x35, 0x00 },
111#elif (ACCEL_ODR_HZ <= 6) // select 6.25Hz ODR
112 { FXOS8700_CTRL_REG1, 0x2D, 0x00 },
113#elif (ACCEL_ODR_HZ <= 30) // select 25Hz ODR
114 { FXOS8700_CTRL_REG1, 0x25, 0x00 },
115#elif (ACCEL_ODR_HZ <= 50) // select 50Hz ODR
116 { FXOS8700_CTRL_REG1, 0x1D, 0x00 },
117#elif (ACCEL_ODR_HZ <= 100) // select 100Hz ODR
118 { FXOS8700_CTRL_REG1, 0x15, 0x00 },
119#elif (ACCEL_ODR_HZ <= 200) // select 200Hz ODR
120 { FXOS8700_CTRL_REG1, 0x0D, 0x00 },
121#else // select 400Hz ODR
122 { FXOS8700_CTRL_REG1, 0x05, 0x00 },
123#endif
124 __END_WRITE_DATA__
125};
126
127#define FXOS8700_COUNTSPERG 8192 //assumes +/-4 g range on accelerometer
128#define FXOS8700_COUNTSPERUT 10
129
130// All sensor drivers and initialization functions have a similar prototype
131// sensor = pointer to linked list element used by the sensor fusion subsystem to specify required sensors
132// sfg = pointer to top level data structure for sensor fusion
133
134int8_t FXOS8700_Accel_Init(struct PhysicalSensor *sensor, SensorFusionGlobals *sfg) {
135 //Use the same init function for thermometer, magnetometer and accelererometer - it will
136 //end up being called three times, but that's OK
137 //TODO - can move the accel stuff in here, and mag stuff following...
138 return FXOS8700_Init(sensor, sfg);
139}
140
141int8_t FXOS8700_Mag_Init(struct PhysicalSensor *sensor, SensorFusionGlobals *sfg) {
142 //Use the same init function for thermometer, magnetometer and accelererometer - it will
143 //end up being called three times, but that's OK
144 return FXOS8700_Init(sensor, sfg);
145}
146
147int8_t FXOS8700_Therm_Init(struct PhysicalSensor *sensor, SensorFusionGlobals *sfg) {
148 //Use the same init function for thermometer, magnetometer and accelererometer - it will
149 //end up being called three times, but that's OK
150 return FXOS8700_Init(sensor, sfg);
151}
152
153int8_t FXOS8700_Init(struct PhysicalSensor *sensor, SensorFusionGlobals *sfg) {
154 int32_t status;
155 uint8_t reg;
156
157 status = Sensor_I2C_Read_Register(&sensor->deviceInfo, sensor->addr, FXOS8700_WHO_AM_I, 1, &reg);
158
159 if (status==SENSOR_ERROR_NONE) {
160#if F_USING_ACCEL
161 sfg->Accel.iWhoAmI = reg;
162 sfg->Accel.iCountsPerg = FXOS8700_COUNTSPERG;
163 sfg->Accel.fgPerCount = 1.0F / FXOS8700_COUNTSPERG;
164#endif
165#if F_USING_MAG
166 sfg->Mag.iWhoAmI = reg;
167 sfg->Mag.iCountsPeruT = FXOS8700_COUNTSPERUT;
168 sfg->Mag.fCountsPeruT = (float) FXOS8700_COUNTSPERUT;
169 sfg->Mag.fuTPerCount = 1.0F / FXOS8700_COUNTSPERUT;
170#endif
171 if (reg != FXOS8700_WHO_AM_I_PROD_VALUE) {
172 return SENSOR_ERROR_INIT; // The whoAmI did not match
173 }
174 } else {
175 // whoAmI will retain default value of zero
176 // return with error
177 return status;
178 }
179
180 // Configure and start the fxos8700 sensor. This does multiple register writes
181 // (see FXOS8700_Initialization definition above)
182 status = Sensor_I2C_Write_List(&sensor->deviceInfo, sensor->addr, FXOS8700_Initialization );
184#if F_USING_ACCEL
185 sfg->Accel.isEnabled = true;
186#endif
187#if F_USING_MAG
188 sfg->Mag.isEnabled = true;
189#endif
190
191 return (status);
192} // end FXOS8700_Init()
193
194#if F_USING_ACCEL
195int8_t FXOS8700_Accel_Read(struct PhysicalSensor *sensor, SensorFusionGlobals *sfg) {
196 uint8_t I2C_Buffer[6 * ACCEL_FIFO_SIZE]; // I2C read buffer
197 int32_t status; // I2C transaction status
198 int8_t j; // scratch
199 uint8_t fifo_packet_count;
200 int16_t sample[3];
201
202 if(!(sensor->isInitialized & F_USING_ACCEL)) {
203 return SENSOR_ERROR_INIT;
204 }
205
206 // read the F_STATUS register (mapped to STATUS) and extract number of
207 // measurements available (lower 6 bits)
208 status = Sensor_I2C_Read(&sensor->deviceInfo,
209 sensor->addr, FXOS8700_F_STATUS_READ, I2C_Buffer);
210 if (status == SENSOR_ERROR_NONE) {
211#ifdef SIMULATOR_MODE
212 fifo_packet_count = 1;
213#else
214 fifo_packet_count = I2C_Buffer[0] & FXOS8700_F_STATUS_F_CNT_MASK;
215#endif
216 // return if there are no measurements in the sensor FIFO.
217 // this will only occur when the calling frequency equals or exceeds
218 // ACCEL_ODR_HZ
219 if (fifo_packet_count == 0) {
220 return (SENSOR_ERROR_READ);
221 }
222 } else {
223 return (status);
224 }
225
226 // Steady state when fusing at 40 Hz is 5 packets per cycle to read (accel
227 // updates at 200 Hz). Noticed that I2C reads > 126 bytes don't work, so
228 // limit the number of FIFO packets per burst read. With the address
229 // auto-increment and wrap turned on, the registers are read
230 // 0x01,0x02,...0x05,0x06,0x01,0x02,... So we read 6 bytes per packet.
231#define MAX_FIFO_PACKETS_PER_READ 15 // for max of 90 bytes per I2C xaction.
232 FXOS8700_DATA_READ[0].readFrom = FXOS8700_OUT_X_MSB;
233 while ((fifo_packet_count > 0) && (status == SENSOR_ERROR_NONE)) {
234 if (MAX_FIFO_PACKETS_PER_READ < fifo_packet_count) {
235 FXOS8700_DATA_READ[0].numBytes = 6 * MAX_FIFO_PACKETS_PER_READ;
236 fifo_packet_count -= MAX_FIFO_PACKETS_PER_READ;
237 } else {
238 FXOS8700_DATA_READ[0].numBytes = 6 * fifo_packet_count;
239 fifo_packet_count = 0;
240 }
241 status = Sensor_I2C_Read(&sensor->deviceInfo,
242 sensor->addr, FXOS8700_DATA_READ, I2C_Buffer);
243 if (status == SENSOR_ERROR_NONE) {
244 for (j = 0; j < FXOS8700_DATA_READ[0].numBytes; j+=6) {
245 // place the measurements read into the accelerometer buffer structure
246 sample[CHX] = (I2C_Buffer[j + 0] << 8) | (I2C_Buffer[j + 1]);
247 sample[CHY] = (I2C_Buffer[j + 2] << 8) | (I2C_Buffer[j + 3]);
248 sample[CHZ] = (I2C_Buffer[j + 4] << 8) | (I2C_Buffer[j + 5]);
249 conditionSample(sample); //truncate negative values to -32767
250 // place the 6 bytes read into the 16 bit accelerometer structure
251 addToFifo((union FifoSensor*) &(sfg->Accel), ACCEL_FIFO_SIZE, sample);
252 } // end transfer all bytes from each packet
253 } // end processing a burst read
254 } // end emptying all packets from FIFO
255 return (status);
256} // end FXOS8700_ReadAccData()
257#endif
258
259#if F_USING_MAG
260// read FXOS8700 magnetometer over I2C
261int8_t FXOS8700_Mag_Read(struct PhysicalSensor *sensor, SensorFusionGlobals *sfg) {
262 uint8_t I2C_Buffer[6]; // I2C read buffer
263 int32_t status; // I2C transaction status
264 int16_t sample[3];
265
266 if(!(sensor->isInitialized & F_USING_MAG))
267 {
268 return SENSOR_ERROR_INIT;
269 }
270
271 // read the six sequential magnetometer output bytes
272 FXOS8700_DATA_READ[0].readFrom = FXOS8700_M_OUT_X_MSB;
273 FXOS8700_DATA_READ[0].numBytes = 6;
274 status = Sensor_I2C_Read(&sensor->deviceInfo, sensor->addr, FXOS8700_DATA_READ, I2C_Buffer );
275 if (status==SENSOR_ERROR_NONE) {
276 // place the 6 bytes read into the magnetometer structure
277 sample[CHX] = (I2C_Buffer[0] << 8) | I2C_Buffer[1];
278 sample[CHY] = (I2C_Buffer[2] << 8) | I2C_Buffer[3];
279 sample[CHZ] = (I2C_Buffer[4] << 8) | I2C_Buffer[5];
280 conditionSample(sample); // truncate negative values to -32767
281 addToFifo((union FifoSensor*) &(sfg->Mag), MAG_FIFO_SIZE, sample);
282 }
283 return status;
284}//end FXOS8700_ReadMagData()
285#endif //F_USING_MAG
286
287// read temperature register over I2C
288int8_t FXOS8700_Therm_Read(struct PhysicalSensor *sensor, SensorFusionGlobals *sfg) {
289 int8_t I2C_Buffer; // I2C read buffer
290 int32_t status; // I2C transaction status
291
292 if(!(sensor->isInitialized)) {
293 return SENSOR_ERROR_INIT;
294 }
295
296 // read the Temperature register 0x51
297 FXOS8700_DATA_READ[0].readFrom = FXOS8700_TEMP;
298 FXOS8700_DATA_READ[0].numBytes = 1;
299 status = Sensor_I2C_Read(&sensor->deviceInfo, sensor->addr, FXOS8700_DATA_READ, (uint8_t*)(&I2C_Buffer) );
300 if (status==SENSOR_ERROR_NONE) {
301 // convert the byte to temperature and place in sfg structure
302 sfg->Temp.temperatureC = (float)I2C_Buffer * 0.96; //section 14.3 of manual says 0.96 degC/LSB
303 }
304 return status;
305}//end FXOS8700_Therm_Read()
306
307// This is the composite read function that handles both accel and mag portions of the FXOS8700
308// It returns the first failing status flag
309int8_t FXOS8700_Read(struct PhysicalSensor *sensor, SensorFusionGlobals *sfg) {
310 int8_t sts1 = 0;
311 int8_t sts2 = 0;
312 int8_t sts3 = 0;
313#if F_USING_ACCEL
314 sts1 = FXOS8700_Accel_Read(sensor, sfg);
315#endif
316
317#if F_USING_MAG
318 sts2 = FXOS8700_Mag_Read(sensor, sfg);
319 sts3 = FXOS8700_Therm_Read(sensor, sfg);
320#endif
321
322 return (sts1 + sts2 + sts3);
323} // end FXOS8700_Read()
324
325// Each entry in a RegisterWriteList is composed of: register address, value to write, bit-mask to apply to write (0 enables)
326const registerwritelist_t FXOS8700_FULL_IDLE[] =
327{
328 // Set ACTIVE = other bits unchanged
329 { FXOS8700_CTRL_REG1, 0x00, 0x01 },
330 __END_WRITE_DATA__
331};
332
333// FXOS8700_Idle places the entire sensor into STANDBY mode (wakeup time = 1/ODR+1ms)
334// This driver is all-on or all-off. It does not support mag or accel only.
335// If you want that functionality, you can write your own using the initialization
336// function in this file as a starting template. We've chosen not to cover all
337// permutations in the interest of simplicity.
338int8_t FXOS8700_Idle(struct PhysicalSensor *sensor, SensorFusionGlobals *sfg) {
339 int32_t status;
340 if(sensor->isInitialized == (F_USING_ACCEL|F_USING_MAG)) {
341 status = Sensor_I2C_Write_List(&sensor->deviceInfo, sensor->addr, FXOS8700_FULL_IDLE );
342 sensor->isInitialized = 0;
343#if F_USING_ACCEL
344 sfg->Accel.isEnabled = false;
345#endif
346#if F_USING_MAG
347 sfg->Mag.isEnabled = false;
348#endif
349 } else {
350 return SENSOR_ERROR_INIT;
351 }
352 return status;
353} // end FXOS8700_Idle()
#define ACCEL_FIFO_SIZE
FXOS8700 (accel), MMA8652, FXLS8952 all have 32 element FIFO.
Definition board.h:60
#define MAG_FIFO_SIZE
FXOS8700 (mag) and MAG3110 have no FIFO so equivalent to 1 element FIFO. For.
Definition board.h:61
#define F_USING_MAG
nominally 0x0002 if an magnetometer is to be used, 0x0000 otherwise
Definition build.h:49
#define F_USING_ACCEL
nominally 0x0001 if an accelerometer is to be used, 0x0000 otherwise
Definition build.h:48
Describes the fxos8700 driver interface and structures.
Contains the register definitions for FXOS8700.
@ FXOS8700_XYZ_DATA_CFG
Provides function prototypes for driver level interfaces It does not have a corresponding ....
int8_t Sensor_I2C_Write_List(registerDeviceInfo_t *devInfo, uint16_t peripheralAddress, const registerwritelist_t *pRegWriteList)
Write register data to a sensor.
Definition hal_i2c.cc:145
int32_t Sensor_I2C_Read(registerDeviceInfo_t *devInfo, uint16_t peripheralAddress, const registerReadlist_t *pReadList, uint8_t *pOutBuffer)
Read register data from a sensor.
Definition hal_i2c.cc:177
The hal_i2c.h file declares low-level interface functions for reading and writing sensor registers us...
void conditionSample(int16_t sample[3])
conditionSample ensures that we never encounter the maximum negative two's complement value for a 16-...
void addToFifo(union FifoSensor *sensor, uint16_t maxFifoSize, int16_t sample[3])
addToFifo is called from within sensor driver read functions
The sensor_fusion.h file implements the top level programming interface.
#define CHX
Used to access X-channel entries in various data data structures.
#define CHY
Used to access Y-channel entries in various data data structures.
#define CHZ
Used to access Z-channel entries in various data data structures.
float fCountsPeruT
counts per uT
float fuTPerCount
uT per count
uint8_t iWhoAmI
sensor whoami
bool isEnabled
true if the device is sampling
int16_t iCountsPeruT
counts per uT
An instance of PhysicalSensor structure type should be allocated for each physical sensors (combo dev...
registerDeviceInfo_t deviceInfo
I2C device context.
uint16_t addr
I2C address if applicable.
uint16_t isInitialized
Bitfields to indicate sensor is active (use SensorBitFields from build.h)
The top level fusion structure.
struct MagSensor Mag
magnetometer storage
This structure defines the Read command List.
This structure defines the Write command List.
The FifoSensor union allows us to use common pointers for Accel, Mag & Gyro logical sensor structures...