// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Copyright (c) 04.2007 Holger Buss
// + Nur für den privaten Gebrauch
// + www.MikroKopter.com
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Es gilt für das gesamte Projekt (Hardware, Software, Binärfiles, Sourcecode und Dokumentation),
// + dass eine Nutzung (auch auszugsweise) nur f�r den privaten und nicht-kommerziellen Gebrauch zulässig ist.
// + Sollten direkte oder indirekte kommerzielle Absichten verfolgt werden, ist mit uns (info@mikrokopter.de) Kontakt
// + bzgl. der Nutzungsbedingungen aufzunehmen.
// + Eine kommerzielle Nutzung ist z.B.Verkauf von MikroKoptern, Bestückung und Verkauf von Platinen oder Bausätzen,
// + Verkauf von Luftbildaufnahmen, usw.
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Werden Teile des Quellcodes (mit oder ohne Modifikation) weiterverwendet oder veröffentlicht,
// + unterliegen sie auch diesen Nutzungsbedingungen und diese Nutzungsbedingungen incl. Copyright müssen dann beiliegen
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Sollte die Software (auch auszugesweise) oder sonstige Informationen des MikroKopter-Projekts
// + auf anderen Webseiten oder Medien veröffentlicht werden, muss unsere Webseite "http://www.mikrokopter.de"
// + eindeutig als Ursprung verlinkt und genannt werden
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Keine Gewähr auf Fehlerfreiheit, Vollständigkeit oder Funktion
// + Benutzung auf eigene Gefahr
// + Wir übernehmen keinerlei Haftung für direkte oder indirekte Personen- oder Sachschäden
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Die Portierung der Software (oder Teile davon) auf andere Systeme (ausser der Hardware von www.mikrokopter.de) ist nur
// + mit unserer Zustimmung zulässig
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Die Funktion printf_P() unterliegt ihrer eigenen Lizenz und ist hiervon nicht betroffen
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Redistributions of source code (with or without modifications) must retain the above copyright notice,
// + this list of conditions and the following disclaimer.
// + * Neither the name of the copyright holders nor the names of contributors may be used to endorse or promote products derived
// + from this software without specific prior written permission.
// + * The use of this project (hardware, software, binary files, sources and documentation) is only permittet
// + for non-commercial use (directly or indirectly)
// + Commercial use (for example: selling of MikroKopters, selling of PCBs, assembly, ...) is only permitted
// + with our written permission
// + * If sources or documentations are redistributet on other webpages, out webpage (http://www.MikroKopter.de) must be
// + clearly linked as origin
// + * porting to systems other than hardware from www.mikrokopter.de is not allowed
// + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// + POSSIBILITY OF SUCH DAMAGE.
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#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"
// for Delay functions
#include "timer0.h"
// For debugOut
#include "uart0.h"
// For reading and writing acc. meter offsets.
#include "eeprom.h"
// For debugOut.digital
#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];
volatile int16_t rawGyroSum
[3];
volatile int16_t acc
[3];
volatile int16_t filteredAcc
[3] = { 0,0,0 };
// volatile int32_t stronglyFilteredAcc[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.
*/
volatile int16_t gyro_PID
[2];
volatile int16_t gyro_ATT
[2];
volatile int16_t gyroD
[2];
volatile int16_t yawGyro
;
/*
* 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
;
/*
* This allows some experimentation with the gyro filters.
* Should be replaced by #define's later on...
*/
/*
* Air pressure
*/
volatile uint8_t rangewidth
= 105;
// Direct from sensor, irrespective of range.
// volatile uint16_t rawAirPressure;
// Value of 2 samples, with range.
volatile uint16_t simpleAirPressure
;
// Value of AIRPRESSURE_SUMMATION_FACTOR samples, with range, filtered.
volatile int32_t filteredAirPressure
;
// Partial sum of AIRPRESSURE_SUMMATION_FACTOR samples.
volatile int32_t airPressureSum
;
// The number of samples summed into airPressureSum so far.
volatile 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.
*/
volatile int16_t UBat
= 100;
/*
* Control and status.
*/
volatile uint16_t ADCycleCount
= 0;
volatile uint8_t analogDataReady
= 1;
/*
* Experiment: Measuring vibration-induced sensor noise.
*/
volatile uint16_t gyroNoisePeak
[3];
volatile 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
));
startAnalogConversionCycle
();
// restore global interrupt flags
SREG
= sreg
;
}
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
) {
return (uint16_t) OCR0A
* (uint16_t) rangewidth
+ advalue
;
}
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 analog_updateGyros
(void) {
// for various filters...
int16_t tempOffsetGyro
, tempGyro
;
debugOut.
digital[0] &= ~DEBUG_SENSORLIMIT
;
for (uint8_t axis
=0; axis
<2; axis
++) {
tempGyro
= rawGyroSum
[axis
] = sensorInputs
[AD_GYRO_PITCH
-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.
if (GYRO_REVERSED
[axis
]) {
tempOffsetGyro
= (gyroOffset.
offsets[axis
] - tempGyro
) * GYRO_FACTOR_PITCHROLL
;
} else {
tempOffsetGyro
= (tempGyro
- gyroOffset.
offsets[axis
]) * GYRO_FACTOR_PITCHROLL
;
}
// 3) Scale and filter.
tempOffsetGyro
= (gyro_PID
[axis
] * (staticParams.
gyroPIDFilterConstant - 1) + tempOffsetGyro
) / staticParams.
gyroPIDFilterConstant;
// 4) Measure noise.
measureNoise
(tempOffsetGyro
, &gyroNoisePeak
[axis
], GYRO_NOISE_MEASUREMENT_DAMPING
);
// 5) Differential measurement.
gyroD
[axis
] = (gyroD
[axis
] * (staticParams.
gyroDFilterConstant - 1) + (tempOffsetGyro
- gyro_PID
[axis
])) / staticParams.
gyroDFilterConstant;
// 6) Done.
gyro_PID
[axis
] = tempOffsetGyro
;
/*
* Now process the data for attitude angles.
*/
tempGyro
= rawGyroSum
[axis
];
// 1) Apply sign and offset, scale before filtering.
if (GYRO_REVERSED
[axis
]) {
tempOffsetGyro
= (gyroOffset.
offsets[axis
] - tempGyro
) * GYRO_FACTOR_PITCHROLL
;
} else {
tempOffsetGyro
= (tempGyro
- gyroOffset.
offsets[axis
]) * GYRO_FACTOR_PITCHROLL
;
}
// 2) Filter.
gyro_ATT
[axis
] = (gyro_ATT
[axis
] * (staticParams.
gyroATTFilterConstant - 1) + tempOffsetGyro
) / staticParams.
gyroATTFilterConstant;
}
// Yaw gyro.
rawGyroSum
[YAW
] = sensorInputs
[AD_GYRO_YAW
];
if (GYRO_REVERSED
[YAW
])
yawGyro
= gyroOffset.
offsets[YAW
] - sensorInputs
[AD_GYRO_YAW
];
else
yawGyro
= sensorInputs
[AD_GYRO_YAW
] - gyroOffset.
offsets[YAW
];
}
void analog_updateAccelerometers
(void) {
// Z acc.
if (ACC_REVERSED
[Z
])
acc
[Z
] = accOffset.
offsets[Z
] - sensorInputs
[AD_ACC_Z
];
else
acc
[Z
] = sensorInputs
[AD_ACC_Z
] - accOffset.
offsets[Z
];
// Pitch and roll axis accelerations.
for (uint8_t axis
=0; axis
<2; axis
++) {
if (ACC_REVERSED
[axis
])
acc
[axis
] = accOffset.
offsets[axis
] - sensorInputs
[AD_ACC_PITCH
-axis
];
else
acc
[axis
] = sensorInputs
[AD_ACC_PITCH
-axis
] - accOffset.
offsets[axis
];
}
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);
}
}
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
--;
debugOut.
analog[27] = (uint16_t) OCR0A
;
debugOut.
analog[31] = simpleAirPressure
;
} 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[27] = (uint16_t) OCR0A
;
debugOut.
analog[31] = 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_SUMMATION_FACTOR 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
;
if (pressureMeasurementCount
== AIRPRESSURE_SUMMATION_FACTOR
- 1)
airPressureSum
+= simpleAirPressure
/ 2;
else
airPressureSum
+= simpleAirPressure
;
}
// 2 samples were added.
pressureMeasurementCount
+= 2;
if (pressureMeasurementCount
>= AIRPRESSURE_SUMMATION_FACTOR
) {
filteredAirPressure
= (filteredAirPressure
* (AIRPRESSURE_FILTER
- 1)
+ airPressureSum
+ AIRPRESSURE_FILTER
/ 2) / AIRPRESSURE_FILTER
;
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;
debugOut.
analog[11] = UBat
;
debugOut.
analog[21] = sensorInputs
[AD_UBAT
];
}
void analog_update
(void) {
analog_updateGyros
();
analog_updateAccelerometers
();
analog_updateAirPressure
();
analog_updateBatteryVoltage
();
}
void analog_setNeutral
() {
if (gyroAmplifierOffset_readFromEEProm
()) {
printf("gyro amp invalid%s",recal
);
gyro_loadAmplifierOffsets
(1);
} else
gyro_loadAmplifierOffsets
(0);
if (gyroOffset_readFromEEProm
()) {
printf("gyro offsets invalid%s",recal
);
gyroOffset.
offsets[PITCH
] = gyroOffset.
offsets[ROLL
] = 512 * GYRO_SUMMATION_FACTOR_PITCHROLL
;
gyroOffset.
offsets[YAW
] = 512 * GYRO_SUMMATION_FACTOR_YAW
;
}
if (accOffset_readFromEEProm
()) {
printf("acc. meter offsets invalid%s",recal
);
accOffset.
offsets[PITCH
] = accOffset.
offsets[ROLL
] = 512 * ACC_SUMMATION_FACTOR_PITCHROLL
;
accOffset.
offsets[Z
] = 717 * ACC_SUMMATION_FACTOR_Z
;
}
// Noise is relative to offset. So, reset noise measurements when changing offsets.
gyroNoisePeak
[PITCH
] = gyroNoisePeak
[ROLL
] = 0;
accNoisePeak
[PITCH
] = accNoisePeak
[ROLL
] = 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);
// Rough estimate. Hmm no nothing happens at calibration anyway.
// airPressureSum = simpleAirPressure * (AIRPRESSURE_SUMMATION_FACTOR/2);
// pressureMeasurementCount = 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
(20);
for (axis
= PITCH
; axis
<= YAW
; axis
++) {
offsets
[axis
] += rawGyroSum
[axis
];
}
}
for (axis
= PITCH
; axis
<= YAW
; axis
++) {
gyroOffset.
offsets[axis
] = (offsets
[axis
] + GYRO_OFFSET_CYCLES
/ 2) / GYRO_OFFSET_CYCLES
;
}
gyroOffset_writeToEEProm
();
}
/*
* 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 10
uint8_t i
, axis
;
int32_t deltaOffset
[3] = { 0, 0, 0 };
int16_t filteredDelta
;
for (i
= 0; i
< ACC_OFFSET_CYCLES
; i
++) {
delay_ms_with_adc_measurement
(10);
for (axis
= PITCH
; axis
<= YAW
; axis
++) {
deltaOffset
[axis
] += acc
[axis
];
}
}
for (axis
= PITCH
; axis
<= YAW
; axis
++) {
filteredDelta
= (deltaOffset
[axis
] + ACC_OFFSET_CYCLES
/ 2)
/ ACC_OFFSET_CYCLES
;
accOffset.
offsets[axis
] += ACC_REVERSED
[axis
] ? -filteredDelta
: filteredDelta
;
}
accOffset_writeToEEProm
();
}