1+ #include " F_AK8975.hpp"
2+
3+
4+ int AK8975::init (calData cal, uint8_t address)
5+ {
6+ // initialize address variable and calibration data.
7+ IMUAddress = address;
8+
9+ if (cal.valid == false )
10+ {
11+ calibration = { 0 };
12+ calibration.magScale [0 ] = 1 .f ;
13+ calibration.magScale [1 ] = 1 .f ;
14+ calibration.magScale [2 ] = 1 .f ;
15+ }
16+ else
17+ {
18+ calibration = cal;
19+ }
20+
21+ if (!(readByte (AK8975_ADDRESS , AK8975_WHO_AM_I ) == AK8975_WHOAMI_DEFAULT_VALUE )) {
22+ return -1 ;
23+ }
24+
25+ // First extract the factory calibration for each magnetometer axis
26+ uint8_t rawData[3 ]; // x/y/z gyro calibration data stored here
27+ writeByte (AK8975_ADDRESS , AK8975_CNTL , 0x00 ); // Power down magnetometer
28+ delay (10 );
29+ writeByte (AK8975_ADDRESS , AK8975_CNTL , 0x0F ); // Enter Fuse ROM access mode
30+ delay (10 );
31+ readBytes (AK8975_ADDRESS , AK8975_ASAX , 3 , &rawData[0 ]); // Read the x-, y-, and z-axis calibration values
32+ factoryMagCal[0 ] = (float )(rawData[0 ] - 128 ) / 256 . + 1 .; // Return x-axis sensitivity adjustment values, etc.
33+ factoryMagCal[1 ] = (float )(rawData[1 ] - 128 ) / 256 . + 1 .;
34+ factoryMagCal[2 ] = (float )(rawData[2 ] - 128 ) / 256 . + 1 .;
35+ writeByte (AK8975_ADDRESS , AK8975_CNTL , 0x00 ); // Power down magnetometer
36+ delay (10 );
37+ // Configure the magnetometer for continuous read and highest resolution
38+ // set Mscale bit 4 to 1 (0) to enable 16 (14) bit resolution in CNTL register,
39+ // and enable continuous mode data acquisition Mmode (bits [3:0]), 0010 for 8 Hz and 0110 for 100 Hz sample rates
40+ writeByte (AK8975_ADDRESS , AK8975_CNTL , 0x01 ); // Set magnetometer to single measure mode.
41+ delay (10 );
42+ return 0 ;
43+ }
44+
45+ void AK8975::update () {
46+ if (dataAvailable ())
47+ {
48+ int16_t magCount[3 ] = { 0 , 0 , 0 }; // Stores the 16-bit signed magnetometer sensor output
49+ uint8_t rawData[7 ]; // x/y/z gyro register data, ST2 register stored here, must read ST2 at end of data acquisition
50+ readBytes (AK8975_ADDRESS , AK8975_XOUT_L , 7 , &rawData[0 ]); // Read the six raw data and ST2 registers sequentially into data array
51+ uint8_t c = rawData[6 ]; // End data read by reading ST2 register
52+ if (!(c & 0x08 )) { // Check if magnetic sensor overflow set, if not then report data
53+ magCount[0 ] = ((int16_t )rawData[1 ] << 8 ) | rawData[0 ]; // Turn the MSB and LSB into a signed 16-bit value
54+ magCount[1 ] = ((int16_t )rawData[3 ] << 8 ) | rawData[2 ]; // Data stored as little Endian
55+ magCount[2 ] = ((int16_t )rawData[5 ] << 8 ) | rawData[4 ];
56+ }
57+
58+ float mx, my, mz;
59+
60+ mx = (float )(magCount[1 ] * mRes * factoryMagCal[1 ] - calibration.magBias [1 ]) * calibration.magScale [1 ];
61+ my = (float )(magCount[0 ] * mRes * factoryMagCal[0 ] - calibration.magBias [0 ]) * calibration.magScale [0 ]; // get actual magnetometer value, this depends on scale being set
62+ mz = -(float )(magCount[2 ] * mRes * factoryMagCal[2 ] - calibration.magBias [2 ]) * calibration.magScale [2 ];
63+
64+ switch (geometryIndex) {
65+ case 0 :
66+ mag.magX = mx;
67+ mag.magY = my;
68+ mag.magZ = mz;
69+ break ;
70+ case 1 :
71+ mag.magX = -my;
72+ mag.magY = mx;
73+ mag.magZ = mz;
74+ break ;
75+ case 2 :
76+ mag.magX = mx;
77+ mag.magY = my;
78+ mag.magZ = mz;
79+ break ;
80+ case 3 :
81+ mag.magX = my;
82+ mag.magY = -mx;
83+ mag.magZ = mz;
84+ break ;
85+ case 4 :
86+ mag.magX = -mz;
87+ mag.magY = -my;
88+ mag.magZ = -mx;
89+ break ;
90+ case 5 :
91+ mag.magX = -mz;
92+ mag.magY = mx;
93+ mag.magZ = -my;
94+ break ;
95+ case 6 :
96+ mag.magX = -mz;
97+ mag.magY = my;
98+ mag.magZ = mx;
99+ break ;
100+ case 7 :
101+ mag.magX = -mz;
102+ mag.magY = -mx;
103+ mag.magZ = my;
104+ break ;
105+ }
106+ }
107+ else
108+ {
109+ if (!readByte (AK8975_ADDRESS , AK8975_CNTL ) & 0x01 ){
110+ writeByte (AK8975_ADDRESS , AK8975_CNTL , 0x01 ); // Set magnetometer to single measure mode.
111+ }
112+ }
113+ // // Apply mag soft iron error compensation
114+ // mx = x * calibration.mag_softiron_matrix[0][0] + y * calibration.mag_softiron_matrix[0][1] + z * calibration.mag_softiron_matrix[0][2];
115+ // my = x * calibration.mag_softiron_matrix[1][0] + y * calibration.mag_softiron_matrix[1][1] + z * calibration.mag_softiron_matrix[1][2];
116+ // mz = x * calibration.mag_softiron_matrix[2][0] + y * calibration.mag_softiron_matrix[2][1] + z * calibration.mag_softiron_matrix[2][2];
117+ }
118+
119+ void AK8975::getAccel (AccelData* out)
120+ {
121+ return ;
122+ }
123+ void AK8975::getGyro (GyroData* out)
124+ {
125+ return ;
126+ }
127+ void AK8975::getMag (MagData* out)
128+ {
129+ memcpy (out, &mag, sizeof (mag));
130+ }
131+
132+ int AK8975::setAccelRange (int range) {
133+ return -1 ;
134+ }
135+
136+ int AK8975::setGyroRange (int range) {
137+ return -1 ;
138+ }
139+
140+ void AK8975::calibrateAccelGyro (calData* cal)
141+ {
142+ return ;
143+ }
144+
145+ void AK8975::calibrateMag (calData* cal)
146+ {
147+ uint16_t ii = 0 , sample_count = 0 ;
148+ int32_t mag_bias[3 ] = { 0 , 0 , 0 }, mag_scale[3 ] = { 0 , 0 , 0 };
149+ int16_t mag_max[3 ] = { -32767 , -32767 , -32767 }, mag_min[3 ] = { 32767 , 32767 , 32767 }, mag_temp[3 ] = { 0 , 0 , 0 };
150+
151+ // shoot for ~fifteen seconds of mag data
152+ sample_count = 1024 ; // at 100 Hz ODR, new mag data is available every 10 ms
153+
154+ for (ii = 0 ; ii < sample_count; ii++)
155+ {
156+ writeByte (AK8975_ADDRESS , AK8975_CNTL , 0x01 ); // Set magnetometer to single measure mode.
157+ delay (15 ); // at 100 Hz ODR, new mag data is available every 10 ms
158+ uint8_t rawData[7 ]; // x/y/z gyro register data, ST2 register stored here, must read ST2 at end of data acquisition
159+ if (readByte (AK8975_ADDRESS , AK8975_ST1 ) & 0x01 ) { // wait for magnetometer data ready bit to be set
160+ readBytes (AK8975_ADDRESS , AK8975_XOUT_L , 7 , &rawData[0 ]); // Read the six raw data and ST2 registers sequentially into data array
161+ uint8_t c = rawData[6 ]; // End data read by reading ST2 register
162+ if (!(c & 0x08 )) { // Check if magnetic sensor overflow set, if not then report data
163+ mag_temp[0 ] = ((int16_t )rawData[1 ] << 8 ) | rawData[0 ]; // Turn the MSB and LSB into a signed 16-bit value
164+ mag_temp[1 ] = ((int16_t )rawData[3 ] << 8 ) | rawData[2 ]; // Data stored as little Endian
165+ mag_temp[2 ] = ((int16_t )rawData[5 ] << 8 ) | rawData[4 ];
166+ }
167+ for (int jj = 0 ; jj < 3 ; jj++)
168+ {
169+ if (mag_temp[jj] > mag_max[jj]) mag_max[jj] = mag_temp[jj];
170+ if (mag_temp[jj] < mag_min[jj]) mag_min[jj] = mag_temp[jj];
171+ }
172+ }
173+ }
174+
175+ // Get hard iron correction
176+ mag_bias[0 ] = (mag_max[0 ] + mag_min[0 ]) / 2 ; // get average x mag bias in counts
177+ mag_bias[1 ] = (mag_max[1 ] + mag_min[1 ]) / 2 ; // get average y mag bias in counts
178+ mag_bias[2 ] = (mag_max[2 ] + mag_min[2 ]) / 2 ; // get average z mag bias in counts
179+
180+ cal->magBias [0 ] = (float )mag_bias[0 ] * mRes * factoryMagCal[0 ]; // save mag biases in G for main program
181+ cal->magBias [1 ] = (float )mag_bias[1 ] * mRes * factoryMagCal[1 ];
182+ cal->magBias [2 ] = (float )mag_bias[2 ] * mRes * factoryMagCal[2 ];
183+
184+ // Get soft iron correction estimate
185+ mag_scale[0 ] = (mag_max[0 ] - mag_min[0 ]) / 2 ; // get average x axis max chord length in counts
186+ mag_scale[1 ] = (mag_max[1 ] - mag_min[1 ]) / 2 ; // get average y axis max chord length in counts
187+ mag_scale[2 ] = (mag_max[2 ] - mag_min[2 ]) / 2 ; // get average z axis max chord length in counts
188+
189+ float avg_rad = mag_scale[0 ] + mag_scale[1 ] + mag_scale[2 ];
190+ avg_rad /= 3.0 ;
191+
192+ cal->magScale [0 ] = avg_rad / ((float )mag_scale[0 ]);
193+ cal->magScale [1 ] = avg_rad / ((float )mag_scale[1 ]);
194+ cal->magScale [2 ] = avg_rad / ((float )mag_scale[2 ]);
195+ }
0 commit comments