#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "analog.h"
#include "attitude.h"
#include "sensors.h"
#include "printf_P.h"
#include "mk3mag.h"
// for Delay functions
#include "timer0.h"
// For reading and writing acc. meter offsets.
#include "eeprom.h"
// For debugOut
#include "output.h"
// set ADC enable & ADC Start Conversion & ADC Interrupt Enable bit
#define startADC() (ADCSRA |= (1<<ADEN)|(1<<ADSC)|(1<<ADIE))
const char* recal
= ", recalibration needed.";
/*
* For each A/D conversion cycle, each analog channel is sampled a number of times
* (see array channelsForStates), and the results for each channel are summed.
* Here are those for the gyros and the acc. meters. They are not zero-offset.
* They are exported in the analog.h file - but please do not use them! The only
* reason for the export is that the ENC-03_FC1.3 modules needs them for calibrating
* the offsets with the DAC.
*/
volatile uint16_t sensorInputs
[8];
int16_t acc
[3];
int16_t filteredAcc
[3] = { 0,0,0 };
/*
* These 4 exported variables are zero-offset. The "PID" ones are used
* in the attitude control as rotation rates. The "ATT" ones are for
* integration to angles.
*/
int16_t gyro_PID
[2];
int16_t gyro_ATT
[2];
int16_t gyroD
[2];
int16_t gyroDWindow
[2][GYRO_D_WINDOW_LENGTH
];
uint8_t gyroDWindowIdx
= 0;
int16_t yawGyro
;
int16_t magneticHeading
;
int32_t groundPressure
;
int16_t dHeight
;
uint32_t gyroActivity
;
/*
* Offset values. These are the raw gyro and acc. meter sums when the copter is
* standing still. They are used for adjusting the gyro and acc. meter values
* to be centered on zero.
*/
sensorOffset_t gyroOffset
;
sensorOffset_t accOffset
;
sensorOffset_t gyroAmplifierOffset
;
/*
* In the MK coordinate system, nose-down is positive and left-roll is positive.
* If a sensor is used in an orientation where one but not both of the axes has
* an opposite sign, PR_ORIENTATION_REVERSED is set to 1 (true).
* Transform:
* pitch <- pp*pitch + pr*roll
* roll <- rp*pitch + rr*roll
* Not reversed, GYRO_QUADRANT:
* 0: pp=1, pr=0, rp=0, rr=1 // 0 degrees
* 1: pp=1, pr=-1,rp=1, rr=1 // +45 degrees
* 2: pp=0, pr=-1,rp=1, rr=0 // +90 degrees
* 3: pp=-1,pr=-1,rp=1, rr=1 // +135 degrees
* 4: pp=-1,pr=0, rp=0, rr=-1 // +180 degrees
* 5: pp=-1,pr=1, rp=-1,rr=-1 // +225 degrees
* 6: pp=0, pr=1, rp=-1,rr=0 // +270 degrees
* 7: pp=1, pr=1, rp=-1,rr=1 // +315 degrees
* Reversed, GYRO_QUADRANT:
* 0: pp=-1,pr=0, rp=0, rr=1 // 0 degrees with pitch reversed
* 1: pp=-1,pr=-1,rp=-1,rr=1 // +45 degrees with pitch reversed
* 2: pp=0, pr=-1,rp=-1,rr=0 // +90 degrees with pitch reversed
* 3: pp=1, pr=-1,rp=-1,rr=1 // +135 degrees with pitch reversed
* 4: pp=1, pr=0, rp=0, rr=-1 // +180 degrees with pitch reversed
* 5: pp=1, pr=1, rp=1, rr=-1 // +225 degrees with pitch reversed
* 6: pp=0, pr=1, rp=1, rr=0 // +270 degrees with pitch reversed
* 7: pp=-1,pr=1, rp=1, rr=1 // +315 degrees with pitch reversed
*/
void rotate
(int16_t* result
, uint8_t quadrant
, uint8_t reverse
) {
static const int8_t rotationTab
[] = {1,1,0,-1,-1,-1,0,1};
// Pitch to Pitch part
int8_t xx
= reverse
? rotationTab
[(quadrant
+4)%8] : rotationTab
[quadrant
];
// Roll to Pitch part
int8_t xy
= rotationTab
[(quadrant
+2)%8];
// Pitch to Roll part
int8_t yx
= reverse
? rotationTab
[(quadrant
+2)%8] : rotationTab
[(quadrant
+6)%8];
// Roll to Roll part
int8_t yy
= rotationTab
[quadrant
];
int16_t xIn
= result
[0];
result
[0] = xx
*xIn
+ xy
*result
[1];
result
[1] = yx
*xIn
+ yy
*result
[1];
if (quadrant
& 1) {
// A rotation was used above, where the factors were too large by sqrt(2).
// So, we multiply by 2^n/sqt(2) and right shift n bits, as to divide by sqrt(2).
// A suitable value for n: Sample is 11 bits. After transformation it is the sum
// of 2 11 bit numbers, so 12 bits. We have 4 bits left...
result
[0] = (result
[0]*11) >> 4;
result
[1] = (result
[1]*11) >> 4;
}
}
/*
* Air pressure
*/
volatile uint8_t rangewidth
= 105;
// Direct from sensor, irrespective of range.
// volatile uint16_t rawAirPressure;
// Value of 2 samples, with range.
uint16_t simpleAirPressure
;
// Value of AIRPRESSURE_OVERSAMPLING samples, with range, filtered.
int32_t filteredAirPressure
;
#define MAX_D_AIRPRESSURE_WINDOW_LENGTH 32
//int32_t lastFilteredAirPressure;
int16_t dAirPressureWindow
[MAX_D_AIRPRESSURE_WINDOW_LENGTH
];
uint8_t dWindowPtr
= 0;
#define MAX_AIRPRESSURE_WINDOW_LENGTH 32
int16_t airPressureWindow
[MAX_AIRPRESSURE_WINDOW_LENGTH
];
int32_t windowedAirPressure
;
uint8_t windowPtr
= 0;
// Partial sum of AIRPRESSURE_SUMMATION_FACTOR samples.
int32_t airPressureSum
;
// The number of samples summed into airPressureSum so far.
uint8_t pressureMeasurementCount
;
/*
* Battery voltage, in units of: 1k/11k / 3V * 1024 = 31.03 per volt.
* That is divided by 3 below, for a final 10.34 per volt.
* So the initial value of 100 is for 9.7 volts.
*/
int16_t UBat
= 100;
/*
* Control and status.
*/
volatile uint16_t ADCycleCount
= 0;
volatile uint8_t analogDataReady
= 1;
/*
* Experiment: Measuring vibration-induced sensor noise.
*/
uint16_t gyroNoisePeak
[3];
uint16_t accNoisePeak
[3];
volatile uint8_t adState
;
volatile uint8_t adChannel
;
// ADC channels
#define AD_GYRO_YAW 0
#define AD_GYRO_ROLL 1
#define AD_GYRO_PITCH 2
#define AD_AIRPRESSURE 3
#define AD_UBAT 4
#define AD_ACC_Z 5
#define AD_ACC_ROLL 6
#define AD_ACC_PITCH 7
/*
* Table of AD converter inputs for each state.
* The number of samples summed for each channel is equal to
* the number of times the channel appears in the array.
* The max. number of samples that can be taken in 2 ms is:
* 20e6 / 128 / 13 / (1/2e-3) = 24. Since the main control
* loop needs a little time between reading AD values and
* re-enabling ADC, the real limit is (how much?) lower.
* The acc. sensor is sampled even if not used - or installed
* at all. The cost is not significant.
*/
const uint8_t channelsForStates
[] PROGMEM
= {
AD_GYRO_PITCH
, AD_GYRO_ROLL
, AD_GYRO_YAW
,
AD_ACC_PITCH
, AD_ACC_ROLL
, AD_AIRPRESSURE
,
AD_GYRO_PITCH
, AD_GYRO_ROLL
, AD_ACC_Z
, // at 8, measure Z acc.
AD_GYRO_PITCH
, AD_GYRO_ROLL
, AD_GYRO_YAW
, // at 11, finish yaw gyro
AD_ACC_PITCH
, // at 12, finish pitch axis acc.
AD_ACC_ROLL
, // at 13, finish roll axis acc.
AD_AIRPRESSURE
, // at 14, finish air pressure.
AD_GYRO_PITCH
, // at 15, finish pitch gyro
AD_GYRO_ROLL
, // at 16, finish roll gyro
AD_UBAT
// at 17, measure battery.
};
// Feature removed. Could be reintroduced later - but should work for all gyro types then.
// uint8_t GyroDefectPitch = 0, GyroDefectRoll = 0, GyroDefectYaw = 0;
void analog_init
(void) {
uint8_t sreg
= SREG
;
// disable all interrupts before reconfiguration
cli
();
//ADC0 ... ADC7 is connected to PortA pin 0 ... 7
DDRA
= 0x00;
PORTA
= 0x00;
// Digital Input Disable Register 0
// Disable digital input buffer for analog adc_channel pins
DIDR0
= 0xFF;
// external reference, adjust data to the right
ADMUX
&= ~
((1<<REFS1
)|(1<<REFS0
)|(1<<ADLAR
));
// set muxer to ADC adc_channel 0 (0 to 7 is a valid choice)
ADMUX
= (ADMUX
& 0xE0);
//Set ADC Control and Status Register A
//Auto Trigger Enable, Prescaler Select Bits to Division Factor 128, i.e. ADC clock = SYSCKL/128 = 156.25 kHz
ADCSRA
= (1<<ADPS2
)|(1<<ADPS1
)|(1<<ADPS0
);
//Set ADC Control and Status Register B
//Trigger Source to Free Running Mode
ADCSRB
&= ~
((1<<ADTS2
)|(1<<ADTS1
)|(1<<ADTS0
));
for (uint8_t i
=0; i
<MAX_AIRPRESSURE_WINDOW_LENGTH
; i
++) {
airPressureWindow
[i
] = 0;
}
windowedAirPressure
= 0;
startAnalogConversionCycle
();
// restore global interrupt flags
SREG
= sreg
;
}
uint16_t rawGyroValue
(uint8_t axis
) {
return sensorInputs
[AD_GYRO_PITCH
-axis
];
}
uint16_t rawAccValue
(uint8_t axis
) {
return sensorInputs
[AD_ACC_PITCH
-axis
];
}
void measureNoise
(const int16_t sensor
,
volatile uint16_t* const noiseMeasurement
, const uint8_t damping
) {
if (sensor
> (int16_t) (*noiseMeasurement
)) {
*noiseMeasurement
= sensor
;
} else if (-sensor
> (int16_t) (*noiseMeasurement
)) {
*noiseMeasurement
= -sensor
;
} else if (*noiseMeasurement
> damping
) {
*noiseMeasurement
-= damping
;
} else {
*noiseMeasurement
= 0;
}
}
/*
* Min.: 0
* Max: About 106 * 240 + 2047 = 27487; it is OK with just a 16 bit type.
*/
uint16_t getSimplePressure
(int advalue
) {
uint16_t result
= (uint16_t) OCR0A
* (uint16_t) rangewidth
+ advalue
;
result
+= (acc
[Z
] * (staticParams.
airpressureAccZCorrection-128)) >> 10;
return result
;
}
void startAnalogConversionCycle
(void) {
analogDataReady
= 0;
// Stop the sampling. Cycle is over.
for (uint8_t i
= 0; i
< 8; i
++) {
sensorInputs
[i
] = 0;
}
adState
= 0;
adChannel
= AD_GYRO_PITCH
;
ADMUX
= (ADMUX
& 0xE0) | adChannel
;
startADC
();
}
/*****************************************************
* Interrupt Service Routine for ADC
* Runs at 312.5 kHz or 3.2 �s. When all states are
* processed further conversions are stopped.
*****************************************************/
ISR
(ADC_vect
) {
sensorInputs
[adChannel
] += ADC
;
// set up for next state.
adState
++;
if (adState
< sizeof(channelsForStates
)) {
adChannel
= pgm_read_byte
(&channelsForStates
[adState
]);
// set adc muxer to next adChannel
ADMUX
= (ADMUX
& 0xE0) | adChannel
;
// after full cycle stop further interrupts
startADC
();
} else {
ADCycleCount
++;
analogDataReady
= 1;
// do not restart ADC converter.
}
}
void measureGyroActivity
(int16_t newValue
) {
gyroActivity
+= abs(newValue
);
}
#define GADAMPING 10
void dampenGyroActivity
(void) {
uint32_t tmp
= gyroActivity
;
tmp
*= ((1<<GADAMPING
)-1);
tmp
>>= GADAMPING
;
gyroActivity
= tmp
;
}
void analog_updateGyros
(void) {
// for various filters...
int16_t tempOffsetGyro
[2], tempGyro
;
debugOut.
digital[0] &= ~DEBUG_SENSORLIMIT
;
for (uint8_t axis
=0; axis
<2; axis
++) {
tempGyro
= rawGyroValue
(axis
);
/*
* Process the gyro data for the PID controller.
*/
// 1) Extrapolate: Near the ends of the range, we boost the input significantly. This simulates a
// gyro with a wider range, and helps counter saturation at full control.
if (staticParams.
bitConfig & CFG_GYRO_SATURATION_PREVENTION
) {
if (tempGyro
< SENSOR_MIN_PITCHROLL
) {
debugOut.
digital[0] |= DEBUG_SENSORLIMIT
;
tempGyro
= tempGyro
* EXTRAPOLATION_SLOPE
- EXTRAPOLATION_LIMIT
;
} else if (tempGyro
> SENSOR_MAX_PITCHROLL
) {
debugOut.
digital[0] |= DEBUG_SENSORLIMIT
;
tempGyro
= (tempGyro
- SENSOR_MAX_PITCHROLL
) * EXTRAPOLATION_SLOPE
+ SENSOR_MAX_PITCHROLL
;
}
}
// 2) Apply sign and offset, scale before filtering.
tempOffsetGyro
[axis
] = (tempGyro
- gyroOffset.
offsets[axis
]) * GYRO_FACTOR_PITCHROLL
;
}
// 2.1: Transform axes.
rotate
(tempOffsetGyro
, staticParams.
gyroQuadrant, staticParams.
imuReversedFlags & IMU_REVERSE_GYRO_PR
);
for (uint8_t axis
=0; axis
<2; axis
++) {
// 3) Filter.
tempOffsetGyro
[axis
] = (gyro_PID
[axis
] * (staticParams.
gyroPIDFilterConstant - 1) + tempOffsetGyro
[axis
]) / staticParams.
gyroPIDFilterConstant;
// 4) Measure noise.
measureNoise
(tempOffsetGyro
[axis
], &gyroNoisePeak
[axis
], GYRO_NOISE_MEASUREMENT_DAMPING
);
// 5) Differential measurement.
// gyroD[axis] = (gyroD[axis] * (staticParams.gyroDFilterConstant - 1) + (tempOffsetGyro[axis] - gyro_PID[axis])) / staticParams.gyroDFilterConstant;
int16_t diff
= tempOffsetGyro
[axis
] - gyro_PID
[axis
];
gyroD
[axis
] -= gyroDWindow
[axis
][gyroDWindowIdx
];
gyroD
[axis
] += diff
;
gyroDWindow
[axis
][gyroDWindowIdx
] = diff
;
// 6) Done.
gyro_PID
[axis
] = tempOffsetGyro
[axis
];
// Prepare tempOffsetGyro for next calculation below...
tempOffsetGyro
[axis
] = (rawGyroValue
(axis
) - gyroOffset.
offsets[axis
]) * GYRO_FACTOR_PITCHROLL
;
}
if (gyroDWindowIdx
>= GYRO_D_WINDOW_LENGTH
) {
gyroDWindowIdx
= 0;
}
/*
* Now process the data for attitude angles.
*/
rotate
(tempOffsetGyro
, staticParams.
gyroQuadrant, staticParams.
imuReversedFlags & IMU_REVERSE_GYRO_PR
);
dampenGyroActivity
();
gyro_ATT
[PITCH
] = tempOffsetGyro
[PITCH
];
gyro_ATT
[ROLL
] = tempOffsetGyro
[ROLL
];
measureGyroActivity
(tempOffsetGyro
[PITCH
]);
measureGyroActivity
(tempOffsetGyro
[ROLL
]);
// Yaw gyro.
if (staticParams.
imuReversedFlags & IMU_REVERSE_GYRO_YAW
)
yawGyro
= gyroOffset.
offsets[YAW
] - sensorInputs
[AD_GYRO_YAW
];
else
yawGyro
= sensorInputs
[AD_GYRO_YAW
] - gyroOffset.
offsets[YAW
];
measureGyroActivity
(yawGyro
*staticParams.
yawRateFactor);
}
void analog_updateAccelerometers
(void) {
// Pitch and roll axis accelerations.
for (uint8_t axis
=0; axis
<2; axis
++) {
acc
[axis
] = rawAccValue
(axis
) - accOffset.
offsets[axis
];
}
rotate
(acc
, staticParams.
accQuadrant, staticParams.
imuReversedFlags & IMU_REVERSE_ACC_XY
);
for(uint8_t axis
=0; axis
<3; axis
++) {
filteredAcc
[axis
] = (filteredAcc
[axis
] * (staticParams.
accFilterConstant - 1) + acc
[axis
]) / staticParams.
accFilterConstant;
measureNoise
(acc
[axis
], &accNoisePeak
[axis
], 1);
}
// Z acc.
if (staticParams.
imuReversedFlags & 8)
acc
[Z
] = accOffset.
offsets[Z
] - sensorInputs
[AD_ACC_Z
];
else
acc
[Z
] = sensorInputs
[AD_ACC_Z
] - accOffset.
offsets[Z
];
// debugOut.analog[29] = acc[Z];
}
void analog_updateAirPressure
(void) {
static uint16_t pressureAutorangingWait
= 25;
uint16_t rawAirPressure
;
int16_t newrange
;
// air pressure
if (pressureAutorangingWait
) {
//A range switch was done recently. Wait for steadying.
pressureAutorangingWait
--;
} else {
rawAirPressure
= sensorInputs
[AD_AIRPRESSURE
];
if (rawAirPressure
< MIN_RAWPRESSURE
) {
// value is too low, so decrease voltage on the op amp minus input, making the value higher.
newrange
= OCR0A
- (MAX_RAWPRESSURE
- MIN_RAWPRESSURE
) / (rangewidth
* 4); // 4; // (MAX_RAWPRESSURE - rawAirPressure) / (rangewidth * 2) + 1;
if (newrange
> MIN_RANGES_EXTRAPOLATION
) {
pressureAutorangingWait
= (OCR0A
- newrange
) * AUTORANGE_WAIT_FACTOR
; // = OCRA0 - OCRA0 +
OCR0A
= newrange
;
} else {
if (OCR0A
) {
OCR0A
--;
pressureAutorangingWait
= AUTORANGE_WAIT_FACTOR
;
}
}
} else if (rawAirPressure
> MAX_RAWPRESSURE
) {
// value is too high, so increase voltage on the op amp minus input, making the value lower.
// If near the end, make a limited increase
newrange
= OCR0A
+ (MAX_RAWPRESSURE
- MIN_RAWPRESSURE
) / (rangewidth
* 4); // 4; // (rawAirPressure - MIN_RAWPRESSURE) / (rangewidth * 2) - 1;
if (newrange
< MAX_RANGES_EXTRAPOLATION
) {
pressureAutorangingWait
= (newrange
- OCR0A
) * AUTORANGE_WAIT_FACTOR
;
OCR0A
= newrange
;
} else {
if (OCR0A
< 254) {
OCR0A
++;
pressureAutorangingWait
= AUTORANGE_WAIT_FACTOR
;
}
}
}
// Even if the sample is off-range, use it.
simpleAirPressure
= getSimplePressure
(rawAirPressure
);
debugOut.
analog[6] = rawAirPressure
;
debugOut.
analog[7] = simpleAirPressure
;
if (simpleAirPressure
< MIN_RANGES_EXTRAPOLATION
* rangewidth
) {
// Danger: pressure near lower end of range. If the measurement saturates, the
// copter may climb uncontrolledly... Simulate a drastic reduction in pressure.
debugOut.
digital[1] |= DEBUG_SENSORLIMIT
;
airPressureSum
+= (int16_t) MIN_RANGES_EXTRAPOLATION
* rangewidth
+ (simpleAirPressure
- (int16_t) MIN_RANGES_EXTRAPOLATION
* rangewidth
) * PRESSURE_EXTRAPOLATION_COEFF
;
} else if (simpleAirPressure
> MAX_RANGES_EXTRAPOLATION
* rangewidth
) {
// Danger: pressure near upper end of range. If the measurement saturates, the
// copter may descend uncontrolledly... Simulate a drastic increase in pressure.
debugOut.
digital[1] |= DEBUG_SENSORLIMIT
;
airPressureSum
+= (int16_t) MAX_RANGES_EXTRAPOLATION
* rangewidth
+ (simpleAirPressure
- (int16_t) MAX_RANGES_EXTRAPOLATION
* rangewidth
) * PRESSURE_EXTRAPOLATION_COEFF
;
} else {
// normal case.
// If AIRPRESSURE_OVERSAMPLING is an odd number we only want to add half the double sample.
// The 2 cases above (end of range) are ignored for this.
debugOut.
digital[1] &= ~DEBUG_SENSORLIMIT
;
airPressureSum
+= simpleAirPressure
;
}
// 2 samples were added.
pressureMeasurementCount
+= 2;
// Assumption here: AIRPRESSURE_OVERSAMPLING is even (well we all know it's 14 haha...)
if (pressureMeasurementCount
== AIRPRESSURE_OVERSAMPLING
) {
// The best oversampling count is 14.5. We add a quarter of the double ADC value to get the final half.
airPressureSum
+= simpleAirPressure
>> 2;
uint32_t lastFilteredAirPressure
= filteredAirPressure
;
if (!staticParams.
airpressureWindowLength) {
filteredAirPressure
= (filteredAirPressure
* (staticParams.
airpressureFilterConstant - 1)
+ airPressureSum
+ staticParams.
airpressureFilterConstant / 2) / staticParams.
airpressureFilterConstant;
} else {
// use windowed.
windowedAirPressure
+= simpleAirPressure
;
windowedAirPressure
-= airPressureWindow
[windowPtr
];
airPressureWindow
[windowPtr
++] = simpleAirPressure
;
if (windowPtr
>= staticParams.
airpressureWindowLength) windowPtr
= 0;
filteredAirPressure
= windowedAirPressure
/ staticParams.
airpressureWindowLength;
}
// positive diff of pressure
int16_t diff
= filteredAirPressure
- lastFilteredAirPressure
;
// is a negative diff of height.
dHeight
-= diff
;
// remove old sample (fifo) from window.
dHeight
+= dAirPressureWindow
[dWindowPtr
];
dAirPressureWindow
[dWindowPtr
++] = diff
;
if (dWindowPtr
>= staticParams.
airpressureDWindowLength) dWindowPtr
= 0;
pressureMeasurementCount
= airPressureSum
= 0;
}
}
}
void analog_updateBatteryVoltage
(void) {
// Battery. The measured value is: (V * 1k/11k)/3v * 1024 = 31.03 counts per volt (max. measurable is 33v).
// This is divided by 3 --> 10.34 counts per volt.
UBat
= (3 * UBat
+ sensorInputs
[AD_UBAT
] / 3) / 4;
}
void analog_update
(void) {
analog_updateGyros
();
analog_updateAccelerometers
();
analog_updateAirPressure
();
analog_updateBatteryVoltage
();
#ifdef USE_MK3MAG
magneticHeading
= volatileMagneticHeading
;
#endif
}
void analog_setNeutral
() {
gyro_init
();
if (gyroOffset_readFromEEProm
()) {
printf("gyro offsets invalid%s",recal
);
gyroOffset.
offsets[PITCH
] = gyroOffset.
offsets[ROLL
] = 512 * GYRO_OVERSAMPLING_PITCHROLL
;
gyroOffset.
offsets[YAW
] = 512 * GYRO_OVERSAMPLING_YAW
;
}
if (accOffset_readFromEEProm
()) {
printf("acc. meter offsets invalid%s",recal
);
accOffset.
offsets[PITCH
] = accOffset.
offsets[ROLL
] = 512 * ACC_OVERSAMPLING_XY
;
accOffset.
offsets[Z
] = 717 * ACC_OVERSAMPLING_Z
;
}
// Noise is relative to offset. So, reset noise measurements when changing offsets.
for (uint8_t i
=PITCH
; i
<=ROLL
; i
++) {
gyroNoisePeak
[i
] = 0;
accNoisePeak
[i
] = 0;
gyroD
[i
] = 0;
for (uint8_t j
=0; j
<GYRO_D_WINDOW_LENGTH
; j
++) {
gyroDWindow
[i
][j
] = 0;
}
}
// Setting offset values has an influence in the analog.c ISR
// Therefore run measurement for 100ms to achive stable readings
delay_ms_with_adc_measurement
(100, 0);
gyroActivity
= 0;
}
void analog_calibrateGyros
(void) {
#define GYRO_OFFSET_CYCLES 32
uint8_t i
, axis
;
int32_t offsets
[3] = { 0, 0, 0 };
gyro_calibrate
();
// determine gyro bias by averaging (requires that the copter does not rotate around any axis!)
for (i
= 0; i
< GYRO_OFFSET_CYCLES
; i
++) {
delay_ms_with_adc_measurement
(10, 1);
for (axis
= PITCH
; axis
<= YAW
; axis
++) {
offsets
[axis
] += rawGyroValue
(axis
);
}
}
for (axis
= PITCH
; axis
<= YAW
; axis
++) {
gyroOffset.
offsets[axis
] = (offsets
[axis
] + GYRO_OFFSET_CYCLES
/ 2) / GYRO_OFFSET_CYCLES
;
int16_t min
= (512-200) * (axis
==YAW
) ? GYRO_OVERSAMPLING_YAW
: GYRO_OVERSAMPLING_PITCHROLL
;
int16_t max
= (512+200) * (axis
==YAW
) ? GYRO_OVERSAMPLING_YAW
: GYRO_OVERSAMPLING_PITCHROLL
;
if(gyroOffset.
offsets[axis
] < min
|| gyroOffset.
offsets[axis
] > max
)
versionInfo.
hardwareErrors[0] |= FC_ERROR0_GYRO_PITCH
<< axis
;
}
gyroOffset_writeToEEProm
();
startAnalogConversionCycle
();
}
/*
* Find acc. offsets for a neutral reading, and write them to EEPROM.
* Does not (!} update the local variables. This must be done with a
* call to analog_calibrate() - this always (?) is done by the caller
* anyway. There would be nothing wrong with updating the variables
* directly from here, though.
*/
void analog_calibrateAcc
(void) {
#define ACC_OFFSET_CYCLES 32
uint8_t i
, axis
;
int32_t offsets
[3] = { 0, 0, 0 };
for (i
= 0; i
< ACC_OFFSET_CYCLES
; i
++) {
delay_ms_with_adc_measurement
(10, 1);
for (axis
= PITCH
; axis
<= YAW
; axis
++) {
offsets
[axis
] += rawAccValue
(axis
);
}
}
for (axis
= PITCH
; axis
<= YAW
; axis
++) {
accOffset.
offsets[axis
] = (offsets
[axis
] + ACC_OFFSET_CYCLES
/ 2) / ACC_OFFSET_CYCLES
;
int16_t min
,max
;
if (axis
==Z
) {
if (staticParams.
imuReversedFlags & IMU_REVERSE_ACC_Z
) {
// TODO: This assumes a sensitivity of +/- 2g.
min
= (256-200) * ACC_OVERSAMPLING_Z
;
max
= (256+200) * ACC_OVERSAMPLING_Z
;
} else {
// TODO: This assumes a sensitivity of +/- 2g.
min
= (768-200) * ACC_OVERSAMPLING_Z
;
max
= (768+200) * ACC_OVERSAMPLING_Z
;
}
} else {
min
= (512-200) * ACC_OVERSAMPLING_XY
;
max
= (512+200) * ACC_OVERSAMPLING_XY
;
}
if(gyroOffset.
offsets[axis
] < min
|| gyroOffset.
offsets[axis
] > max
) {
versionInfo.
hardwareErrors[0] |= FC_ERROR0_ACC_X
<< axis
;
}
}
accOffset_writeToEEProm
();
startAnalogConversionCycle
();
}
void analog_setGround
() {
groundPressure
= filteredAirPressure
;
}
int32_t analog_getHeight
(void) {
return groundPressure
- filteredAirPressure
;
}
int16_t analog_getDHeight
(void) {
/*
int16_t result = 0;
for (int i=0; i<staticParams.airpressureDWindowLength; i++) {
result -= dAirPressureWindow[i]; // minus pressure is plus height.
}
// dHeight = -dPressure, so here it is the old pressure minus the current, not opposite.
return result;
*/
return dHeight
;
}