Prof Greg Egan
/
UAVXArm-GKE
UAVX Multicopter Flight Controller.
Diff: compass.c
- Revision:
- 0:62a1c91a859a
- Child:
- 1:1e3318a30ddd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compass.c Fri Feb 18 22:28:05 2011 +0000 @@ -0,0 +1,525 @@ +// =============================================================================================== +// = UAVXArm Quadrocopter Controller = +// = Copyright (c) 2008 by Prof. Greg Egan = +// = Original V3.15 Copyright (c) 2007 Ing. Wolfgang Mahringer = +// = http://code.google.com/p/uavp-mods/ http://uavp.ch = +// =============================================================================================== + +// This is part of UAVXArm. + +// UAVXArm is free software: you can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. + +// UAVXArm is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without +// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along with this program. +// If not, see http://www.gnu.org/licenses/ + +#include "UAVXArm.h" + +// Local magnetic declination not included +// http://www.ngdc.noaa.gov/geomagmodels/Declination.jsp + + +void ReadCompass(void); +void GetHeading(void); +void CalibrateCompass(void); +void ShowCompassType(void); +void DoCompassTest(void); +void InitCompass(void); + +MagStruct Mag[3] = {{ 0,0 },{ 0,0 },{ 0,0 }}; +real32 MagDeviation, CompassOffset; +real32 MagHeading, Heading, Headingp, FakeHeading; +real32 HeadingSin, HeadingCos; +uint8 CompassType; + +void ReadCompass(void) { + switch ( CompassType ) { + case HMC5843: + ReadHMC5843(); + break; + case HMC6352: + ReadHMC6352(); + break; + default: + Heading = 0; + break; + } // switch + +} // ReadCompass + +void CalibrateCompass(void) { + switch ( CompassType ) { + case HMC5843: + CalibrateHMC5843(); + break; + case HMC6352: + CalibrateHMC6352(); + break; + default: + break; + } // switch +} // CalibrateCompass + +void ShowCompassType(void) { + switch ( CompassType ) { + case HMC5843: + TxString("HMC5843"); + break; + case HMC6352: + TxString("HMC6352"); + break; + default: + break; + } + } // ShowCompassType + +void DoCompassTest(void) { + switch ( CompassType ) { + case HMC5843: + DoHMC5843Test(); + break; + case HMC6352: + DoHMC6352Test(); + break; + default: + TxString("\r\nCompass test\r\nCompass not detected?\r\n"); + break; + } // switch +} // DoCompassTest + +void GetHeading(void) { + + const real32 CompassA = COMPASS_UPDATE_S / ( OneOnTwoPiCompassF + COMPASS_UPDATE_S ); + + ReadCompass(); + + Heading = Make2Pi( MagHeading - MagDeviation - CompassOffset ); + if ( fabs(Heading - Headingp ) > PI ) + Headingp = Heading; + + Heading = Headingp + (Heading - Headingp) * CompassA; + Headingp = Heading; + +#ifdef SIMULATE +#if ( defined AILERON | defined ELEVON ) + if ( State == InFlight ) + FakeHeading -= FakeDesiredRoll/5 + FakeDesiredYaw/5; +#else + if ( State == InFlight ) { + if ( Abs(FakeDesiredYaw) > 5 ) + FakeHeading -= FakeDesiredYaw/5; + } + + FakeHeading = Make2Pi((int16)FakeHeading); + Heading = FakeHeading; +#endif // AILERON | ELEVON +#endif // SIMULATE +} // GetHeading + +void InitCompass(void) { + if ( IsHMC5843Active() ) + CompassType = HMC5843; + else + if ( HMC6352Active() ) + CompassType = HMC6352; + else { + CompassType = NoCompass; + F.CompassValid = false; + } + + switch ( CompassType ) { + case HMC5843: + InitHMC5843(); + break; + case HMC6352: + InitHMC6352(); + break; + default: + MagHeading = 0; + } // switch + + ReadCompass(); + mS[CompassUpdate] = mSClock(); + Heading = Headingp = Make2Pi( MagHeading - MagDeviation - CompassOffset ); + +} // InitCompass + +//________________________________________________________________________________________ + +// HMC5843 3 Axis Magnetometer + +void ReadHMC5843(void); +void GetHMC5843Parameters(void); +void DoHMC5843Test(void); +void CalibrateHMC5843(void); +void InitHMC5843(void); +boolean HMC5843Active(void); + +void ReadHMC5843(void) { + static char b[6]; + static i16u X, Y, Z; + static uint8 r; + static real32 mx, my; + static real32 CRoll, SRoll, CPitch, SPitch; + + I2CCOMPASS.start(); + r = I2CCOMPASS.write(HMC5843_ID); + r = I2CCOMPASS.write(0x03); // point to data + I2CCOMPASS.stop(); + + I2CCOMPASS.read(HMC5843_ID, b, 6); + + X.b1 = b[0]; + X.b0 = b[1]; + Y.b1 = b[2]; + Y.b0 =b[3]; + Z.b1 = b[4]; + Z.b0 = b[5]; + + if ( F.Using9DOF ) { // SparkFun/QuadroUFO 9DOF Breakout pins front edge components up + Mag[BF].V = X.i16; + Mag[LR].V = -Y.i16; + Mag[UD].V = -Z.i16; + } else { // SparkFun Magnetometer Breakout pins right edge components up + Mag[BF].V = -X.i16; + Mag[LR].V = Y.i16; + Mag[UD].V = -Z.i16; + } + + CRoll = cos(Angle[Roll]); + SRoll = sin(Angle[Roll]); + CPitch = cos(Angle[Pitch]); + SPitch = sin(Angle[Pitch]); + + mx = (Mag[BF].V-Mag[BF].Offset) * CPitch + (Mag[LR].V-Mag[LR].Offset) * SRoll * SPitch + (Mag[UD].V-Mag[UD].Offset) * CRoll * SPitch; + my = (Mag[LR].V-Mag[LR].Offset) * CRoll - (Mag[UD].V-Mag[UD].Offset) * SRoll; + + // Magnetic Heading + MagHeading = MakePi(atan2( -my, mx )); + + F.CompassValid = true; + return; + +} // ReadHMC5843 + +void CalibrateHMC5843(void) { + +} // DoHMC5843Test + +void DoHMC5843Test(void) { + TxString("\r\nCompass test (HMC5843)\r\n\r\n"); + + ReadHMC5843(); + + TxString("Mag:\t"); + TxVal32(Mag[LR].V, 0, HT); + TxVal32(Mag[BF].V, 0, HT); + TxVal32(Mag[UD].V, 0, HT); + TxNextLine(); + TxNextLine(); + + TxVal32(MagHeading * RADDEG * 10.0, 1, 0); + TxString(" deg (Magnetic)\r\n"); + + Heading = Headingp = Make2Pi( MagHeading - MagDeviation - CompassOffset ); + TxVal32(Heading * RADDEG * 10.0, 1, 0); + TxString(" deg (True)\r\n"); +} // DoHMC5843Test + +void InitHMC5843(void) { + static uint8 r; + + I2CCOMPASS.start(); + r = I2CCOMPASS.write(HMC5843_ID); + r = I2CCOMPASS.write(0x02); + r = I2CCOMPASS.write(0x00); // Set continuous mode (default to 10Hz) + I2CCOMPASS.stop(); + + Delay1mS(50); + +} // InitHMC5843Magnetometer + +boolean IsHMC5843Active(void) { + I2CCOMPASS.start(); + F.CompassValid = !(I2CCOMPASS.write(HMC5843_ID) != I2C_ACK); + I2CCOMPASS.stop(); + + if ( F.CompassValid ) + TrackMinI2CRate(400000); + + return ( F.CompassValid ); + +} // IsHMC5843Active + +//________________________________________________________________________________________ + +// HMC6352 Compass + +void ReadHMC6352(void); +uint8 WriteByteHMC6352(uint8); +void GetHMC6352Parameters(void); +void DoHMC6352Test(void); +void CalibrateHMC6352(void); +void InitHMC6352(void); +boolean IsHMC6352Active(void); + +void ReadHMC6352(void) { + static i16u v; + + I2CCOMPASS.start(); + F.CompassMissRead = I2CCOMPASS.write(HMC6352_ID + 1) != I2C_ACK; + v.b1 = I2CCOMPASS.read(I2C_ACK); + v.b0 = I2CCOMPASS.read(I2C_NACK); + I2CCOMPASS.stop(); + + MagHeading = Make2Pi( ((real32)v.i16 * PI) / 1800.0 - CompassOffset ); // Radians +} // ReadHMC6352 + +uint8 WriteByteHMC6352(uint8 d) { + I2CCOMPASS.start(); + if ( I2CCOMPASS.write(HMC6352_ID) != I2C_ACK ) goto WError; + if ( I2CCOMPASS.write(d) != I2C_ACK ) goto WError; + I2CCOMPASS.stop(); + + return( I2C_ACK ); +WError: + I2CCOMPASS.stop(); + return ( I2C_NACK ); +} // WriteByteHMC6352 + +char CP[9]; + +#define TEST_COMP_OPMODE 0x70 // standby mode to reliably read EEPROM + +void GetHMC6352Parameters(void) { + uint8 r; + + I2CCOMPASS.start(); + if ( I2CCOMPASS.write(HMC6352_ID) != I2C_ACK ) goto CTerror; + if ( I2CCOMPASS.write('G') != I2C_ACK ) goto CTerror; + if ( I2CCOMPASS.write(0x74) != I2C_ACK ) goto CTerror; + if ( I2CCOMPASS.write(TEST_COMP_OPMODE) != I2C_ACK ) goto CTerror; + I2CCOMPASS.stop(); + + Delay1mS(20); + + for (r = 0; r <= (uint8)8; r++) { // must have this timing - not block read! + + I2CCOMPASS.start(); + if ( I2CCOMPASS.write(HMC6352_ID) != I2C_ACK ) goto CTerror; + if ( I2CCOMPASS.write('r') != I2C_ACK ) goto CTerror; + if ( I2CCOMPASS.write(r) != I2C_ACK ) goto CTerror; + I2CCOMPASS.stop(); + + Delay1mS(10); + + I2CCOMPASS.start(); + if ( I2CCOMPASS.write(HMC6352_ID+1) != I2C_ACK ) goto CTerror; + CP[r] = I2CCOMPASS.read(I2C_NACK); + I2CCOMPASS.stop(); + + Delay1mS(10); + } + + return; + +CTerror: + I2CCOMPASS.stop(); + TxString("FAIL\r\n"); + +} // GetHMC6352Parameters + +void DoHMC6352Test(void) { + static real32 Temp; + + TxString("\r\nCompass test (HMC6352)\r\n"); + + I2CCOMPASS.start(); + if ( I2CCOMPASS.write(HMC6352_ID) != I2C_ACK ) goto CTerror; + if ( I2CCOMPASS.write('G') != I2C_ACK ) goto CTerror; + if ( I2CCOMPASS.write(0x74) != I2C_ACK ) goto CTerror; + if ( I2CCOMPASS.write(TEST_COMP_OPMODE) != I2C_ACK ) goto CTerror; + I2CCOMPASS.stop(); + + Delay1mS(1); + + // I2CCOMPASS.start(); // Do Set/Reset now + if ( WriteByteHMC6352('O') != I2C_ACK ) goto CTerror; + + Delay1mS(7); + + GetHMC6352Parameters(); + + TxString("\r\nRegisters\r\n"); + TxString("\t0:\tI2C"); + TxString("\t 0x"); + TxValH(CP[0]); + if ( CP[0] != (uint8)0x42 ) + TxString("\t Error expected 0x42 for HMC6352"); + TxNextLine(); + + Temp = (CP[1]*256)|CP[2]; + TxString("\t1:2:\tXOffset\t"); + TxVal32((int32)Temp, 0, 0); + TxNextLine(); + + Temp = (CP[3]*256)|CP[4]; + TxString("\t3:4:\tYOffset\t"); + TxVal32((int32)Temp, 0, 0); + TxNextLine(); + + TxString("\t5:\tDelay\t"); + TxVal32((int32)CP[5], 0, 0); + TxNextLine(); + + TxString("\t6:\tNSum\t"); + TxVal32((int32)CP[6], 0, 0); + TxNextLine(); + + TxString("\t7:\tSW Ver\t"); + TxString(" 0x"); + TxValH(CP[7]); + TxNextLine(); + + TxString("\t8:\tOpMode:"); + switch ( ( CP[8] >> 5 ) & 0x03 ) { + case 0: + TxString(" 1Hz"); + break; + case 1: + TxString(" 5Hz"); + break; + case 2: + TxString(" 10Hz"); + break; + case 3: + TxString(" 20Hz"); + break; + } + + if ( CP[8] & 0x10 ) TxString(" S/R"); + + switch ( CP[8] & 0x03 ) { + case 0: + TxString(" Standby"); + break; + case 1: + TxString(" Query"); + break; + case 2: + TxString(" Continuous"); + break; + case 3: + TxString(" Not-allowed"); + break; + } + TxNextLine(); + + InitCompass(); + if ( !F.CompassValid ) goto CTerror; + + Delay1mS(50); + + ReadHMC6352(); + if ( F.CompassMissRead ) goto CTerror; + + TxNextLine(); + TxVal32(MagHeading * RADDEG * 10.0, 1, 0); + TxString(" deg (Magnetic)\r\n"); + Heading = Headingp = Make2Pi( MagHeading - MagDeviation - CompassOffset ); + TxVal32(Heading * RADDEG * 10.0, 1, 0); + TxString(" deg (True)\r\n"); + + return; +CTerror: + I2CCOMPASS.stop(); + TxString("FAIL\r\n"); +} // DoHMC6352Test + +void CalibrateHMC6352(void) { // calibrate the compass by rotating the ufo through 720 deg smoothly + TxString("\r\nCalib. compass - Press CONTINUE button (x) to Start\r\n"); + while ( PollRxChar() != 'x' ); // UAVPSet uses 'x' for CONTINUE button + + // Do Set/Reset now + if ( WriteByteHMC6352('O') != I2C_ACK ) goto CCerror; + + Delay1mS(7); + + // set Compass device to Calibration mode + if ( WriteByteHMC6352('C') != I2C_ACK ) goto CCerror; + + TxString("\r\nRotate horizontally 720 deg in ~30 sec. - Press CONTINUE button (x) to Finish\r\n"); + while ( PollRxChar() != 'x' ); + + // set Compass device to End-Calibration mode + if ( WriteByteHMC6352('E') != I2C_ACK ) goto CCerror; + + TxString("\r\nCalibration complete\r\n"); + + Delay1mS(50); + + InitCompass(); + + return; + +CCerror: + TxString("Calibration FAILED\r\n"); +} // CalibrateHMC6352 + +void InitHMC6352(void) { + + // 20Hz continuous read with periodic reset. +#ifdef SUPPRESS_COMPASS_SR +#define COMP_OPMODE 0x62 +#else +#define COMP_OPMODE 0x72 +#endif // SUPPRESS_COMPASS_SR + + // Set device to Compass mode + I2CCOMPASS.start(); + if ( I2CCOMPASS.write(HMC6352_ID) != I2C_ACK ) goto CTerror; + if ( I2CCOMPASS.write('G') != I2C_ACK ) goto CTerror; + if ( I2CCOMPASS.write(0x74) != I2C_ACK ) goto CTerror; + if ( I2CCOMPASS.write(COMP_OPMODE) != I2C_ACK ) goto CTerror; + I2CCOMPASS.stop(); + + Delay1mS(1); + + // save operation mode in Flash + if ( WriteByteHMC6352('L') != I2C_ACK ) goto CTerror; + + Delay1mS(1); + + // Do Bridge Offset Set/Reset now + if ( WriteByteHMC6352('O') != I2C_ACK ) goto CTerror; + + Delay1mS(50); + + F.CompassValid = true; + + return; +CTerror: + F.CompassValid = false; + Stats[CompassFailS]++; + F.CompassFailure = true; + + I2CCOMPASS.stop(); +} // InitHMC6352 + +boolean HMC6352Active(void) { + + I2CCOMPASS.start(); + F.CompassValid = !(I2CCOMPASS.write(HMC6352_ID) != I2C_ACK); + I2CCOMPASS.stop(); + + if ( F.CompassValid ) + TrackMinI2CRate(100000); + + return ( F.CompassValid ); + +} // HMC6352Active