1 /*
2 * Copyright (c) 2015, Freescale Semiconductor, Inc.
3 * Copyright 2016-2017 NXP
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9 /*! \file sensor_fusion.c
10 \brief The sensor_fusion.c file implements the top level programming interface
11 */
12
13 /* Including needed modules to compile this module/procedure */
14 #include <stdio.h>
15 #include "sensor_fusion.h"
16 #include "magnetic.h"
17 #include "drivers.h"
18 #include "sensor_drv.h"
19 #include "status.h"
20 #include "control.h"
21 #include "fusion.h"
22 //#include "debug.h"
23
24 /// Poor man's inheritance for status subsystem setStatus command
25 /// This function is normally involved via the "sfg." global pointer.
setStatus(SensorFusionGlobals * sfg,fusion_status_t status)26 void setStatus(SensorFusionGlobals *sfg, fusion_status_t status)
27 {
28 sfg->pStatusSubsystem->set(sfg->pStatusSubsystem, status);
29 }
30
31 /// Poor man's inheritance for status subsystem queueStatus command.
32 /// This function is normally involved via the "sfg." global pointer.
queueStatus(SensorFusionGlobals * sfg,fusion_status_t status)33 void queueStatus(SensorFusionGlobals *sfg, fusion_status_t status)
34 {
35 sfg->pStatusSubsystem->queue(sfg->pStatusSubsystem, status);
36 }
37
38 /// Poor man's inheritance for status subsystem updateStatus command.
39 /// This function is normally involved via the "sfg." global pointer.
updateStatus(SensorFusionGlobals * sfg)40 void updateStatus(SensorFusionGlobals *sfg)
41 {
42 sfg->pStatusSubsystem->update(sfg->pStatusSubsystem);
43 }
44
testStatus(SensorFusionGlobals * sfg)45 void testStatus(SensorFusionGlobals *sfg)
46 {
47 sfg->pStatusSubsystem->test(sfg->pStatusSubsystem);
48 }
49
50 /// utility function to insert default values in the top level structure
initSensorFusionGlobals(SensorFusionGlobals * sfg,StatusSubsystem * pStatusSubsystem,ControlSubsystem * pControlSubsystem)51 void initSensorFusionGlobals(SensorFusionGlobals *sfg,
52 StatusSubsystem *pStatusSubsystem,
53 ControlSubsystem *pControlSubsystem)
54 {
55 sfg->iFlags = // all of the following defines are either 0x0000 or a 1-bit value (2, 4, 8 ...) and are defined in build.h
56 F_USING_ACCEL |
57 F_USING_MAG |
58 F_USING_GYRO |
59 F_USING_PRESSURE |
60 F_USING_TEMPERATURE |
61 F_ALL_SENSORS | // refers to all applicable sensor types for the given physical unit
62 F_1DOF_P_BASIC | // 1DOF pressure (altitude) and temperature: (pressure)
63 F_3DOF_G_BASIC | // 3DOF accel tilt: (accel)
64 F_3DOF_B_BASIC | // 3DOF mag eCompass (vehicle): (mag)
65 F_3DOF_Y_BASIC | // 3DOF gyro integration: (gyro)
66 F_6DOF_GB_BASIC | // 6DOF accel and mag eCompass)
67 F_6DOF_GY_KALMAN | // 6DOF accel and gyro (Kalman): (accel + gyro)
68 F_9DOF_GBY_KALMAN ; // 9DOF accel, mag and gyro (Kalman): (accel + mag + gyro)
69
70 sfg->pControlSubsystem = pControlSubsystem;
71 sfg->pStatusSubsystem = pStatusSubsystem;
72 sfg->loopcounter = 0; // counter incrementing each iteration of sensor fusion (typically 25Hz)
73 sfg->systick_I2C = 0; // systick counter to benchmark I2C reads
74 sfg->systick_Spare = 0; // systick counter for counts spare waiting for timing interrupt
75 sfg->iPerturbation = 0; // no perturbation to be applied
76 sfg->installSensor = installSensor; // function for installing a new sensor into the structures
77 sfg->initializeFusionEngine = initializeFusionEngine; // function for installing a new sensor into the structures
78 sfg->readSensors = readSensors; // function for installing a new sensor into the structures
79 sfg->runFusion = runFusion; // function for installing a new sensor into the structures
80 sfg->applyPerturbation = ApplyPerturbation; // function used for step function testing
81 sfg->conditionSensorReadings = conditionSensorReadings; // function does averaging, HAL adjustments, etc.
82 sfg->clearFIFOs = clearFIFOs; // function to clear FIFO flags sfg->applyPerturbation = ApplyPerturbation; // function used for step function testing
83 sfg->setStatus = setStatus; // function to immediately set status change
84 sfg->queueStatus = queueStatus; // function to queue status change
85 sfg->updateStatus = updateStatus; // function to promote queued status change
86 sfg->testStatus = testStatus; // function for unit testing the status subsystem
87 sfg->pSensors = NULL; // pointer to linked list of physical sensors
88 // put error value into whoAmI as initial value
89 #if F_USING_ACCEL
90 sfg->Accel.iWhoAmI = 0;
91 #endif
92 #if F_USING_MAG
93 sfg->Mag.iWhoAmI = 0;
94 #endif
95 #if F_USING_GYRO
96 sfg->Gyro.iWhoAmI = 0;
97 #endif
98 #if F_USING_PRESSURE
99 sfg->Pressure.iWhoAmI = 0;
100 #endif
101 }
102 /// installSensor is used to instantiate a physical sensor driver into the
103 /// sensor fusion system.
104 /// This function is normally involved via the "sfg." global pointer.
installSensor(SensorFusionGlobals * sfg,struct PhysicalSensor * pSensor,uint16_t addr,uint16_t schedule,void * bus_driver,registerDeviceInfo_t * busInfo,initializeSensor_t * initialize,readSensor_t * read)105 int8_t installSensor(
106 SensorFusionGlobals *sfg, ///< top level fusion structure
107 struct PhysicalSensor *pSensor, ///< pointer to structure describing physical sensor
108 uint16_t addr, ///< I2C address for sensor (if applicable)
109 uint16_t schedule, ///< Parameter to control sensor sampling rate
110 void *bus_driver, ///< ISSDK sensor bus driver (usually KSDK I2C bus)
111 registerDeviceInfo_t *busInfo, ///< information required for bus power management
112 initializeSensor_t *initialize, ///< pointer to sensor initialization function
113 readSensor_t *read) ///< pointer to sensor read function
114 {
115 if (sfg && pSensor && bus_driver && initialize && read)
116 {
117 pSensor->bus_driver = bus_driver;
118 pSensor->deviceInfo.deviceInstance = busInfo->deviceInstance;
119 pSensor->deviceInfo.functionParam = busInfo->functionParam;
120 pSensor->deviceInfo.idleFunction = busInfo->idleFunction;
121
122 pSensor->initialize = initialize; // The initialization function is responsible for putting the sensor
123 // into the proper mode for sensor fusion. It is normally KSDK-based.
124 pSensor->read = read; // The read function is responsible for taking sensor readings and
125 // loading them into the sensor fusion input structures. Also KDSK-based.
126 pSensor->addr = addr; // I2C address if applicable
127 pSensor->schedule = schedule;
128 pSensor->slaveParams.pReadPreprocessFN = NULL; // SPI-specific parameters get overwritten later if used
129 pSensor->slaveParams.pWritePreprocessFN = NULL;
130 pSensor->slaveParams.pTargetSlavePinID = NULL;
131 pSensor->slaveParams.spiCmdLen = 0;
132 pSensor->slaveParams.ssActiveValue = 0;
133 // Now add the new sensor at the head of the linked list
134 pSensor->next = sfg->pSensors;
135 sfg->pSensors = pSensor;
136 return (0);
137 }
138 else
139 {
140 return (1);
141 }
142 }
143 // The initializeSensors function traverses the linked list of physical sensor
144 // types and calls the initialization function for each one.
initializeSensors(SensorFusionGlobals * sfg)145 int8_t initializeSensors(SensorFusionGlobals *sfg)
146 {
147 struct PhysicalSensor *pSensor;
148 int8_t s;
149 int8_t status = 0;
150 for (pSensor = sfg->pSensors; pSensor != NULL; pSensor = pSensor->next)
151 {
152 s = pSensor->initialize(pSensor, sfg);
153 if (status == 0) status = s; // will return 1st error flag, but try all sensors
154 }
155 return (status);
156 }
157
158 // process<Sensor>Data routines do post processing for HAL and averaging. They
159 // are called from the readSensors() function below.
160 #if F_USING_ACCEL
processAccelData(SensorFusionGlobals * sfg)161 void processAccelData(SensorFusionGlobals *sfg)
162 {
163 int32 iSum[3]; // channel sums
164 int16 i, j; // counters
165 if (sfg->Accel.iFIFOExceeded > 0) {
166 sfg->setStatus(sfg, SOFT_FAULT);
167 }
168
169 ApplyAccelHAL(&(sfg->Accel)); // This function is board-dependent
170
171 // calculate the average HAL-corrected measurement
172 for (j = CHX; j <= CHZ; j++) iSum[j] = 0;
173 for (i = 0; i < sfg->Accel.iFIFOCount; i++)
174 for (j = CHX; j <= CHZ; j++) iSum[j] += sfg->Accel.iGsFIFO[i][j];
175 if (sfg->Accel.iFIFOCount > 0)
176 {
177 for (j = CHX; j <= CHZ; j++)
178 {
179 sfg->Accel.iGs[j] = (int16)(iSum[j] / (int32) sfg->Accel.iFIFOCount);
180 sfg->Accel.fGs[j] = (float)sfg->Accel.iGs[j] * sfg->Accel.fgPerCount;
181 }
182 }
183
184 // apply precision accelerometer calibration (offset V, inverse gain invW and rotation correction R^T)
185 // to map fGs onto fGc (g), iGc (counts)
186 fInvertAccelCal(&(sfg->Accel), &(sfg->AccelCal));
187
188 // update the precision accelerometer data buffer
189 fUpdateAccelBuffer(&(sfg->AccelCal),
190 &(sfg->AccelBuffer),
191 &(sfg->Accel),
192 &(sfg->pControlSubsystem->AccelCalPacketOn));
193 return;
194 }
195 #endif
196 #if F_USING_MAG
processMagData(SensorFusionGlobals * sfg)197 void processMagData(SensorFusionGlobals *sfg)
198 {
199 int32 iSum[3]; // channel sums
200 int16 i, j; // counters
201
202 // printf("ProcessingMagData()\n");
203 if (sfg->Mag.iFIFOExceeded > 0) {
204 sfg->setStatus(sfg, SOFT_FAULT);
205 }
206
207 ApplyMagHAL(&(sfg->Mag)); // This function is board-dependent
208
209 // calculate the average HAL-corrected measurement
210 for (j = CHX; j <= CHZ; j++) iSum[j] = 0;
211 for (i = 0; i < sfg->Mag.iFIFOCount; i++)
212 for (j = CHX; j <= CHZ; j++) iSum[j] += sfg->Mag.iBsFIFO[i][j];
213 if (sfg->Mag.iFIFOCount > 0)
214 {
215 for (j = CHX; j <= CHZ; j++)
216 {
217 sfg->Mag.iBs[j] = (int16)(iSum[j] / (int32) sfg->Mag.iFIFOCount);
218 sfg->Mag.fBs[j] = (float)sfg->Mag.iBs[j] * sfg->Mag.fuTPerCount;
219 }
220 }
221
222 // remove hard and soft iron terms from fBs (uT) to get calibrated data fBc (uT), iBc (counts) and
223 // update magnetic buffer avoiding a write while a magnetic calibration is in progress.
224 // run one iteration of the time sliced magnetic calibration
225 fInvertMagCal(&(sfg->Mag), &(sfg->MagCal));
226 if (!sfg->MagCal.iMagBufferReadOnly)
227 iUpdateMagBuffer(&(sfg->MagBuffer), &(sfg->Mag), sfg->loopcounter);
228 fRunMagCalibration(&(sfg->MagCal), &(sfg->MagBuffer), &(sfg->Mag),
229 sfg->loopcounter);
230
231 return;
232 }
233 #endif
234 #if F_USING_GYRO
processGyroData(SensorFusionGlobals * sfg)235 void processGyroData(SensorFusionGlobals *sfg)
236 {
237 int32 iSum[3]; // channel sums
238 int16 i, j; // counters
239 if (sfg->Gyro.iFIFOExceeded > 0) {
240 sfg->setStatus(sfg, SOFT_FAULT);
241 }
242
243 ApplyGyroHAL(&(sfg->Gyro)); // This function is board-dependent
244
245 // calculate the average HAL-corrected measurement. This is used for offset
246 // initialization, display purposes and in the 3-axis gyro-only algorithm.
247 // The Kalman filters both do the full incremental rotation integration
248 // right in the filters themselves.
249 for (j = CHX; j <= CHZ; j++) iSum[j] = 0;
250 for (i = 0; i < sfg->Gyro.iFIFOCount; i++)
251 for (j = CHX; j <= CHZ; j++)
252 iSum[j] += sfg->Gyro.iYsFIFO[i][j];
253 if (sfg->Gyro.iFIFOCount > 0)
254 {
255 for (j = CHX; j <= CHZ; j++)
256 {
257 sfg->Gyro.iYs[j] = (int16)(iSum[j] / (int32) sfg->Gyro.iFIFOCount);
258 sfg->Gyro.fYs[j] = (float)sfg->Gyro.iYs[j] * sfg->Gyro.fDegPerSecPerCount;
259 }
260 }
261 return;
262 }
263 #endif
264 /// readSensors traverses the linked list of physical sensors, calling the
265 /// individual read functions one by one.
266 /// This function is normally involved via the "sfg." global pointer.
readSensors(SensorFusionGlobals * sfg,uint16_t read_loop_counter)267 int8_t readSensors(
268 SensorFusionGlobals *sfg, ///< pointer to global sensor fusion data structure
269 uint16_t read_loop_counter ///< current loop counter (used for multirate processing)
270 )
271 {
272 struct PhysicalSensor *pSensor;
273 int8_t s;
274 int8_t status = 0;
275 float remainder;
276
277 pSensor = sfg->pSensors;
278
279 for (pSensor = sfg->pSensors; pSensor != NULL; pSensor = pSensor->next)
280 { if (pSensor->isInitialized) {
281 remainder = fmod(read_loop_counter, pSensor->schedule);
282 if (remainder==0) {
283 s = pSensor->read(pSensor, sfg);
284 if (status == 0) status = s; // will return 1st error flag, but try all sensors
285 }
286 }
287 }
288 if (status==SENSOR_ERROR_INIT) sfg->setStatus(sfg, HARD_FAULT); // Never returns
289 return (status);
290 }
291 /// conditionSensorReadings() transforms raw software FIFO readings into forms that
292 /// can be consumed by the sensor fusion engine. This include sample averaging
293 /// and (in the case of the gyro) integrations, applying hardware abstraction layers,
294 /// and calibration functions.
295 /// This function is normally involved via the "sfg." global pointer.
conditionSensorReadings(SensorFusionGlobals * sfg)296 void conditionSensorReadings(SensorFusionGlobals *sfg) {
297 #if F_USING_ACCEL
298 if (sfg->Accel.isEnabled) processAccelData(sfg);
299 #endif
300
301 #if F_USING_MAG
302 if (sfg->Mag.isEnabled) processMagData(sfg);
303 #endif
304
305 #if F_USING_GYRO
306 if (sfg->Gyro.isEnabled) processGyroData(sfg);
307 #endif
308 return;
309 }
310
zeroArray(StatusSubsystem * pStatus,void * data,uint16_t size,uint16_t numElements,uint8_t check)311 void zeroArray(StatusSubsystem *pStatus, void* data, uint16_t size, uint16_t numElements, uint8_t check) {
312 uint16_t i;
313 uint8_t *d8;
314 uint16_t *d16;
315 uint32_t *d32;
316 switch(size) {
317 case 8:
318 d8 = data;
319 for (i=0; i<numElements; i++) d8[i]=0;
320 break;
321 case 16:
322 d16 = data;
323 for (i=0; i<numElements; i++) d16[i]=0;
324 break;
325 case 32:
326 d32 = data;
327 for (i=0; i<numElements; i++) d32[i]=0;
328 break;
329 default:
330 pStatus->set(pStatus, HARD_FAULT);
331 }
332 if (check) {
333 switch(size) {
334 case 8:
335 d8 = data;
336 for (i=0; i<numElements; i++)
337 if (d8[i]!=0) pStatus->set(pStatus, HARD_FAULT);
338 break;
339 case 16:
340 d16 = data;
341 for (i=0; i<numElements; i++)
342 if (d16[i]!=0) pStatus->set(pStatus, HARD_FAULT);
343 break;
344 case 32:
345 d32 = data;
346 for (i=0; i<numElements; i++)
347 if (d32[i]!=0) pStatus->set(pStatus, HARD_FAULT);
348 break;
349 }
350 return;
351 }
352 }
353 /// Function to clear FIFO at the end of each fusion computation
clearFIFOs(SensorFusionGlobals * sfg)354 void clearFIFOs(SensorFusionGlobals *sfg) {
355 // We only clear FIFOs if the sensors are enabled. This allows us
356 // to continue to use these values when we've shut higher power consumption
357 // sensors down during periods of no activity.
358 #if F_USING_ACCEL
359 sfg->Accel.iFIFOCount=0;
360 sfg->Accel.iFIFOExceeded = false;
361 #endif
362 #if F_USING_MAG
363 sfg->Mag.iFIFOCount=0;
364 sfg->Mag.iFIFOExceeded = false;
365 #endif
366 #if F_USING_GYRO
367 sfg->Gyro.iFIFOCount=0;
368 sfg->Gyro.iFIFOExceeded = false;
369 #endif
370 }
371
372 /// runFusion the top level call that actually runs the sensor fusion.
373 /// This is a utility function which manages the various defines in build.h.
374 /// You should feel free to drop down a level and implement only those portions
375 /// of fFuseSensors() that your application needs.
376 /// This function is normally involved via the "sfg." global pointer.
runFusion(SensorFusionGlobals * sfg)377 void runFusion(SensorFusionGlobals *sfg)
378 {
379 struct SV_1DOF_P_BASIC *pSV_1DOF_P_BASIC;
380 struct SV_3DOF_G_BASIC *pSV_3DOF_G_BASIC;
381 struct SV_3DOF_B_BASIC *pSV_3DOF_B_BASIC;
382 struct SV_3DOF_Y_BASIC *pSV_3DOF_Y_BASIC;
383 struct SV_6DOF_GB_BASIC *pSV_6DOF_GB_BASIC;
384 struct SV_6DOF_GY_KALMAN *pSV_6DOF_GY_KALMAN;
385 struct SV_9DOF_GBY_KALMAN *pSV_9DOF_GBY_KALMAN;
386 struct AccelSensor *pAccel;
387 struct MagSensor *pMag;
388 struct GyroSensor *pGyro;
389 struct PressureSensor *pPressure;
390 struct MagCalibration *pMagCal;
391 #if F_1DOF_P_BASIC
392 pSV_1DOF_P_BASIC = &(sfg->SV_1DOF_P_BASIC);
393 #else
394 pSV_1DOF_P_BASIC = NULL;
395 #endif
396 #if F_3DOF_G_BASIC
397 pSV_3DOF_G_BASIC = &(sfg->SV_3DOF_G_BASIC) ;
398 #else
399 pSV_3DOF_G_BASIC = NULL;
400 #endif
401 #if F_3DOF_B_BASIC
402 pSV_3DOF_B_BASIC = &(sfg->SV_3DOF_B_BASIC);
403 #else
404 pSV_3DOF_B_BASIC = NULL;
405 #endif
406 #if F_3DOF_Y_BASIC
407 pSV_3DOF_Y_BASIC = &(sfg->SV_3DOF_Y_BASIC);
408 #else
409 pSV_3DOF_Y_BASIC = NULL;
410 #endif
411 #if F_6DOF_GB_BASIC
412 pSV_6DOF_GB_BASIC = &(sfg->SV_6DOF_GB_BASIC);
413 #else
414 pSV_6DOF_GB_BASIC = NULL;
415 #endif
416 #if F_6DOF_GY_KALMAN
417 pSV_6DOF_GY_KALMAN = &(sfg->SV_6DOF_GY_KALMAN);
418 #else
419 pSV_6DOF_GY_KALMAN = NULL;
420 #endif
421 #if F_9DOF_GBY_KALMAN
422 pSV_9DOF_GBY_KALMAN = &(sfg->SV_9DOF_GBY_KALMAN);
423 #else
424 pSV_9DOF_GBY_KALMAN = NULL;
425 #endif
426 #if F_USING_ACCEL
427 pAccel = &(sfg->Accel);
428 #else
429 pAccel = NULL;
430 #endif
431 #if F_USING_MAG
432 pMag = &(sfg->Mag);
433 pMagCal = &(sfg->MagCal);
434 #else
435 pMag = NULL;
436 pMagCal = NULL;
437 #endif
438 #if F_USING_GYRO
439 pGyro = &(sfg->Gyro);
440 #else
441 pGyro = NULL;
442 #endif
443 #if F_USING_PRESSURE
444 pPressure = &(sfg->Pressure);
445 #else
446 pPressure = NULL;
447 #endif
448
449 // conditionSensorReadings(sfg); must be called prior to this function
450 // fuse the sensor data
451 fFuseSensors(pSV_1DOF_P_BASIC, pSV_3DOF_G_BASIC,
452 pSV_3DOF_B_BASIC, pSV_3DOF_Y_BASIC,
453 pSV_6DOF_GB_BASIC, pSV_6DOF_GY_KALMAN,
454 pSV_9DOF_GBY_KALMAN, pAccel, pMag, pGyro,
455 pPressure, pMagCal);
456 clearFIFOs(sfg);
457 }
458
459 /// This function is responsible for initializing the system prior to starting
460 /// the main fusion loop.
461 /// This function is normally involved via the "sfg." global pointer.
initializeFusionEngine(SensorFusionGlobals * sfg)462 void initializeFusionEngine(SensorFusionGlobals *sfg)
463 {
464 int16_t status = 0;
465 struct ControlSubsystem *pComm;
466 pComm = sfg->pControlSubsystem;
467
468 // configure the 24 bit downwards ARM systick timer and wait 50ms=CORE_SYSTICK_HZ / 20 clock ticks
469 // to avoid a race condition between Kinetis and the sensors after power on.
470 ARM_systick_enable();
471 // wait 50ms to avoid a race condition with sensors at power on
472 ARM_systick_delay_ms(CORE_SYSTICK_HZ, 50);
473
474 sfg->setStatus(sfg, INITIALIZING);
475 status = initializeSensors(sfg);
476 if (status!=SENSOR_ERROR_NONE) { // fault condition found
477 sfg->setStatus(sfg, HARD_FAULT); // Never returns
478 }
479
480 // recall: typedef enum quaternion {Q3, Q3M, Q3G, Q6MA, Q6AG, Q9} quaternion_type;
481 // Set the default quaternion to the most sophisticated supported by this build
482 pComm->DefaultQuaternionPacketType = Q3;
483 if (sfg->iFlags & F_3DOF_B_BASIC) pComm->DefaultQuaternionPacketType = Q3M;
484 if (sfg->iFlags & F_3DOF_Y_BASIC) pComm->DefaultQuaternionPacketType = Q3G;
485 if (sfg->iFlags & F_6DOF_GB_BASIC) pComm->DefaultQuaternionPacketType = Q6MA;
486 if (sfg->iFlags & F_6DOF_GY_KALMAN) pComm->DefaultQuaternionPacketType = Q6AG;
487 if (sfg->iFlags & F_9DOF_GBY_KALMAN) pComm->DefaultQuaternionPacketType = Q9;
488 pComm->QuaternionPacketType = pComm->DefaultQuaternionPacketType ;
489
490 // initialize the sensor fusion algorithms
491 fInitializeFusion(sfg);
492
493 // reset the loop counter to zero for first iteration
494 sfg->loopcounter = 0;
495
496 // initialize the magnetic calibration and magnetometer data buffer
497 #if F_USING_MAG
498 fInitializeMagCalibration(&sfg->MagCal, &sfg->MagBuffer);
499 #endif
500
501 // initialize the precision accelerometer calibration and accelerometer data buffer
502 #if F_USING_ACCEL
503 fInitializeAccelCalibration(&sfg->AccelCal, &sfg->AccelBuffer, &sfg->pControlSubsystem->AccelCalPacketOn );
504 #endif
505 sfg->setStatus(sfg, NORMAL);
506
507 clearFIFOs(sfg);
508 }
509
conditionSample(int16_t sample[3])510 void conditionSample(int16_t sample[3])
511 {
512 // This function should be called for every 16 bit sample read from sensor hardware.
513 // It is responsible for making sure that we never pass on the value of -32768.
514 // That value cannot be properly negated using 16-bit twos complement math.
515 // The ability to be later negated is required for general compatibility
516 // with possible HAL (Hardware abstraction logic) which is run later in
517 // the processing pipeline.
518 if (sample[CHX] == -32768) sample[CHX]++;
519 if (sample[CHY] == -32768) sample[CHY]++;
520 if (sample[CHZ] == -32768) sample[CHZ]++;
521 }
addToFifo(union FifoSensor * sensor,uint16_t maxFifoSize,int16_t sample[3])522 void addToFifo(union FifoSensor *sensor, uint16_t maxFifoSize, int16_t sample[3])
523 {
524 // Note that FifoSensor is a union of GyroSensor, MagSensor and AccelSensor.
525 // All contain FIFO structures in the same location. We use the Accel
526 // structure to index here.
527
528 // example usage: if (status==SENSOR_ERROR_NONE) addToFifo((FifoSensor*) &(sfg->Mag), MAG_FIFO_SIZE, sample);
529 uint8_t fifoCount = sensor->Accel.iFIFOCount;
530 if (fifoCount < maxFifoSize) {
531 // we have room for the new sample
532 sensor->Accel.iGsFIFO[fifoCount][CHX] = sample[CHX];
533 sensor->Accel.iGsFIFO[fifoCount][CHY] = sample[CHY];
534 sensor->Accel.iGsFIFO[fifoCount][CHZ] = sample[CHZ];
535 sensor->Accel.iFIFOCount += 1;
536 sensor->Accel.iFIFOExceeded = 0;
537 } else {
538 //there is no room for a new sample
539 sensor->Accel.iFIFOExceeded += 1;
540 }
541 }
542
543