2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
28 #if defined(USE_MAG_AK8963) || defined(USE_MAG_SPI_AK8963)
30 #include "build/debug.h"
32 #include "common/axis.h"
33 #include "common/maths.h"
34 #include "common/utils.h"
36 #include "drivers/bus.h"
37 #include "drivers/bus_i2c.h"
38 #include "drivers/bus_i2c_busdev.h"
39 #include "drivers/bus_spi.h"
40 #include "drivers/io.h"
41 #include "drivers/sensor.h"
42 #include "drivers/time.h"
44 #include "drivers/compass/compass.h"
46 #include "drivers/accgyro/accgyro.h"
47 #include "drivers/accgyro/accgyro_mpu.h"
48 #include "drivers/accgyro/accgyro_mpu6500.h"
49 #include "drivers/accgyro/accgyro_spi_mpu6500.h"
50 #include "drivers/accgyro/accgyro_spi_mpu9250.h"
51 #include "drivers/compass/compass_ak8963.h"
53 #include "scheduler/scheduler.h"
55 // This sensor is also available also part of the MPU-9250 connected to the secondary I2C bus.
57 // 10 MHz max SPI frequency
58 #define AK8963_MAX_SPI_CLK_HZ 10000000
60 // AK8963, mag sensor address
61 #define AK8963_MAG_I2C_ADDRESS 0x0C
62 #define AK8963_DEVICE_ID 0x48
65 #define AK8963_MAG_REG_WIA 0x00
66 #define AK8963_MAG_REG_INFO 0x01
67 #define AK8963_MAG_REG_ST1 0x02
68 #define AK8963_MAG_REG_HXL 0x03
69 #define AK8963_MAG_REG_HXH 0x04
70 #define AK8963_MAG_REG_HYL 0x05
71 #define AK8963_MAG_REG_HYH 0x06
72 #define AK8963_MAG_REG_HZL 0x07
73 #define AK8963_MAG_REG_HZH 0x08
74 #define AK8963_MAG_REG_ST2 0x09
75 #define AK8963_MAG_REG_CNTL1 0x0A
76 #define AK8963_MAG_REG_CNTL2 0x0B
77 #define AK8963_MAG_REG_ASCT 0x0C // self test
78 #define AK8963_MAG_REG_I2CDIS 0x0F
79 #define AK8963_MAG_REG_ASAX 0x10 // Fuse ROM x-axis sensitivity adjustment value
80 #define AK8963_MAG_REG_ASAY 0x11 // Fuse ROM y-axis sensitivity adjustment value
81 #define AK8963_MAG_REG_ASAZ 0x12 // Fuse ROM z-axis sensitivity adjustment value
83 #define READ_FLAG 0x80
84 #define I2C_SLV0_EN 0x80
86 #define ST1_DATA_READY 0x01
87 #define ST1_DATA_OVERRUN 0x02
89 #define ST2_MAG_SENSOR_OVERFLOW 0x08
91 #define CNTL1_MODE_POWER_DOWN 0x00
92 #define CNTL1_MODE_ONCE 0x01
93 #define CNTL1_MODE_CONT1 0x02
94 #define CNTL1_MODE_CONT2 0x06
95 #define CNTL1_MODE_SELF_TEST 0x08
96 #define CNTL1_MODE_FUSE_ROM 0x0F
97 #define CNTL1_BIT_14_BIT 0x00
98 #define CNTL1_BIT_16_BIT 0x10
100 #define CNTL2_SOFT_RESET 0x01
102 #define I2CDIS_DISABLE_MASK 0x1D
104 #if defined(USE_MAG_AK8963) && (defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU9250))
106 static bool ak8963SpiWriteRegisterDelay(const extDevice_t
*dev
, uint8_t reg
, uint8_t data
)
108 spiWriteReg(dev
, reg
, data
);
109 delayMicroseconds(10);
113 static bool ak8963SlaveReadRegisterBuffer(const extDevice_t
*slaveDev
, uint8_t reg
, uint8_t *buf
, uint8_t len
)
115 extDevice_t
*dev
= slaveDev
->bus
->busType_u
.mpuSlave
.master
;
117 ak8963SpiWriteRegisterDelay(dev
, MPU_RA_I2C_SLV0_ADDR
, slaveDev
->busType_u
.mpuSlave
.address
| READ_FLAG
); // set I2C slave address for read
118 ak8963SpiWriteRegisterDelay(dev
, MPU_RA_I2C_SLV0_REG
, reg
); // set I2C slave register
119 ak8963SpiWriteRegisterDelay(dev
, MPU_RA_I2C_SLV0_CTRL
, (len
& 0x0F) | I2C_SLV0_EN
); // read number of bytes
122 bool ack
= spiReadRegMskBufRB(dev
, MPU_RA_EXT_SENS_DATA_00
, buf
, len
); // read I2C
127 static bool ak8963SlaveWriteRegister(const extDevice_t
*slaveDev
, uint8_t reg
, uint8_t data
)
129 extDevice_t
*dev
= slaveDev
->bus
->busType_u
.mpuSlave
.master
;
131 ak8963SpiWriteRegisterDelay(dev
, MPU_RA_I2C_SLV0_ADDR
, slaveDev
->busType_u
.mpuSlave
.address
); // set I2C slave address for write
132 ak8963SpiWriteRegisterDelay(dev
, MPU_RA_I2C_SLV0_REG
, reg
); // set I2C slave register
133 ak8963SpiWriteRegisterDelay(dev
, MPU_RA_I2C_SLV0_DO
, data
); // set I2C sLave value
134 ak8963SpiWriteRegisterDelay(dev
, MPU_RA_I2C_SLV0_CTRL
, (1 & 0x0F) | I2C_SLV0_EN
); // write 1 byte
138 typedef struct queuedReadState_s
{
141 uint32_t readStartedAt
; // time read was queued in micros.
144 static queuedReadState_t queuedRead
= { false, 0, 0};
146 static bool ak8963SlaveStartRead(const extDevice_t
*slaveDev
, uint8_t reg
, uint8_t len
)
148 if (queuedRead
.waiting
) {
152 extDevice_t
*dev
= slaveDev
->bus
->busType_u
.mpuSlave
.master
;
154 queuedRead
.len
= len
;
156 ak8963SpiWriteRegisterDelay(dev
, MPU_RA_I2C_SLV0_ADDR
, slaveDev
->busType_u
.mpuSlave
.address
| READ_FLAG
); // set I2C slave address for read
157 ak8963SpiWriteRegisterDelay(dev
, MPU_RA_I2C_SLV0_REG
, reg
); // set I2C slave register
158 ak8963SpiWriteRegisterDelay(dev
, MPU_RA_I2C_SLV0_CTRL
, (len
& 0x0F) | I2C_SLV0_EN
); // read number of bytes
160 queuedRead
.readStartedAt
= micros();
161 queuedRead
.waiting
= true;
166 static uint32_t ak8963SlaveQueuedReadTimeRemaining(void)
168 if (!queuedRead
.waiting
) {
172 int32_t timeSinceStarted
= micros() - queuedRead
.readStartedAt
;
174 int32_t timeRemaining
= 8000 - timeSinceStarted
;
176 if (timeRemaining
< 0) {
180 return timeRemaining
;
183 static bool ak8963SlaveCompleteRead(const extDevice_t
*slaveDev
, uint8_t *buf
)
185 uint32_t timeRemaining
= ak8963SlaveQueuedReadTimeRemaining();
187 extDevice_t
*dev
= slaveDev
->bus
->busType_u
.mpuSlave
.master
;
189 if (timeRemaining
> 0) {
190 delayMicroseconds(timeRemaining
);
193 queuedRead
.waiting
= false;
195 return spiReadRegMskBufRB(dev
, MPU_RA_EXT_SENS_DATA_00
, buf
, queuedRead
.len
); // read I2C buffer
198 static bool ak8963SlaveReadData(const extDevice_t
*dev
, uint8_t *buf
)
206 static ak8963ReadState_e state
= CHECK_STATUS
;
210 // we currently need a different approach for the MPU9250 connected via SPI.
211 // we cannot use the ak8963SlaveReadRegisterBuffer() method for SPI, it is to slow and blocks for far too long.
218 ak8963SlaveStartRead(dev
, AK8963_MAG_REG_ST1
, 1);
219 state
= WAITING_FOR_STATUS
;
223 case WAITING_FOR_STATUS
: {
224 uint32_t timeRemaining
= ak8963SlaveQueuedReadTimeRemaining();
229 ack
= ak8963SlaveCompleteRead(dev
, &buf
[0]);
231 uint8_t status
= buf
[0];
233 if (!ack
|| (status
& ST1_DATA_READY
) == 0) {
234 // too early. queue the status read again
235 state
= CHECK_STATUS
;
243 // read the 6 bytes of data and the status2 register
244 ak8963SlaveStartRead(dev
, AK8963_MAG_REG_HXL
, 7);
246 state
= WAITING_FOR_DATA
;
250 case WAITING_FOR_DATA
: {
251 uint32_t timeRemaining
= ak8963SlaveQueuedReadTimeRemaining();
256 ack
= ak8963SlaveCompleteRead(dev
, &buf
[0]);
257 state
= CHECK_STATUS
;
265 static bool ak8963ReadRegisterBuffer(const extDevice_t
*dev
, uint8_t reg
, uint8_t *buf
, uint8_t len
)
267 #if defined(USE_MAG_AK8963) && (defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU9250))
268 if (dev
->bus
->busType
== BUS_TYPE_MPU_SLAVE
) {
269 return ak8963SlaveReadRegisterBuffer(dev
, reg
, buf
, len
);
272 return busReadRegisterBuffer(dev
, reg
, buf
, len
);
275 static bool ak8963WriteRegister(const extDevice_t
*dev
, uint8_t reg
, uint8_t data
)
277 #if defined(USE_MAG_AK8963) && (defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU9250))
278 if (dev
->bus
->busType
== BUS_TYPE_MPU_SLAVE
) {
279 return ak8963SlaveWriteRegister(dev
, reg
, data
);
282 return busWriteRegister(dev
, reg
, data
);
285 static bool ak8963DirectReadData(const extDevice_t
*dev
, uint8_t *buf
)
289 bool ack
= ak8963ReadRegisterBuffer(dev
, AK8963_MAG_REG_ST1
, &status
, 1);
291 if (!ack
|| (status
& ST1_DATA_READY
) == 0) {
295 return ak8963ReadRegisterBuffer(dev
, AK8963_MAG_REG_HXL
, buf
, 7);
298 static int16_t parseMag(uint8_t *raw
, int16_t gain
) {
299 int ret
= (int16_t)(raw
[1] << 8 | raw
[0]) * gain
/ 256;
300 return constrain(ret
, INT16_MIN
, INT16_MAX
);
303 static bool ak8963Read(magDev_t
*mag
, int16_t *magData
)
308 extDevice_t
*dev
= &mag
->dev
;
310 switch (dev
->bus
->busType
) {
311 #if defined(USE_MAG_SPI_AK8963) || defined(USE_MAG_AK8963)
314 ack
= ak8963DirectReadData(dev
, buf
);
318 #if defined(USE_MAG_AK8963) && (defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU9250))
319 case BUS_TYPE_MPU_SLAVE
:
320 ack
= ak8963SlaveReadData(dev
, buf
);
327 uint8_t status2
= buf
[6];
332 ak8963WriteRegister(dev
, AK8963_MAG_REG_CNTL1
, CNTL1_BIT_16_BIT
| CNTL1_MODE_ONCE
); // start reading again uint8_t status2 = buf[6];
334 if (status2
& ST2_MAG_SENSOR_OVERFLOW
) {
338 magData
[X
] = parseMag(buf
+ 0, mag
->magGain
[X
]);
339 magData
[Y
] = parseMag(buf
+ 2, mag
->magGain
[Y
]);
340 magData
[Z
] = parseMag(buf
+ 4, mag
->magGain
[Z
]);
345 static bool ak8963Init(magDev_t
*mag
)
350 extDevice_t
*dev
= &mag
->dev
;
352 busDeviceRegister(dev
);
354 ak8963WriteRegister(dev
, AK8963_MAG_REG_CNTL1
, CNTL1_MODE_POWER_DOWN
); // power down before entering fuse mode
355 ak8963WriteRegister(dev
, AK8963_MAG_REG_CNTL1
, CNTL1_MODE_FUSE_ROM
); // Enter Fuse ROM access mode
356 ak8963ReadRegisterBuffer(dev
, AK8963_MAG_REG_ASAX
, asa
, sizeof(asa
)); // Read the x-, y-, and z-axis calibration values
358 mag
->magGain
[X
] = asa
[X
] + 128;
359 mag
->magGain
[Y
] = asa
[Y
] + 128;
360 mag
->magGain
[Z
] = asa
[Z
] + 128;
362 ak8963WriteRegister(dev
, AK8963_MAG_REG_CNTL1
, CNTL1_MODE_POWER_DOWN
); // power down after reading.
364 // Clear status registers
365 ak8963ReadRegisterBuffer(dev
, AK8963_MAG_REG_ST1
, &status
, 1);
366 ak8963ReadRegisterBuffer(dev
, AK8963_MAG_REG_ST2
, &status
, 1);
368 // Trigger first measurement
369 ak8963WriteRegister(dev
, AK8963_MAG_REG_CNTL1
, CNTL1_BIT_16_BIT
| CNTL1_MODE_ONCE
);
373 void ak8963BusInit(const extDevice_t
*dev
)
375 switch (dev
->bus
->busType
) {
376 #ifdef USE_MAG_AK8963
382 #ifdef USE_MAG_SPI_AK8963
384 IOHi(dev
->busType_u
.spi
.csnPin
); // Disable
385 IOInit(dev
->busType_u
.spi
.csnPin
, OWNER_COMPASS_CS
, 0);
386 IOConfigGPIO(dev
->busType_u
.spi
.csnPin
, IOCFG_OUT_PP
);
387 spiSetClkDivisor(dev
, spiCalculateDivider(AK8963_MAX_SPI_CLK_HZ
));
391 #if defined(USE_MAG_AK8963) && (defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU9250))
392 case BUS_TYPE_MPU_SLAVE
:
393 rescheduleTask(TASK_COMPASS
, TASK_PERIOD_HZ(40));
395 // Disable DMA on gyro as this upsets slave access timing
396 spiDmaEnable(dev
->bus
->busType_u
.mpuSlave
.master
, false);
398 // initialize I2C master via SPI bus
399 ak8963SpiWriteRegisterDelay(dev
->bus
->busType_u
.mpuSlave
.master
, MPU_RA_INT_PIN_CFG
, MPU6500_BIT_INT_ANYRD_2CLEAR
| MPU6500_BIT_BYPASS_EN
);
400 ak8963SpiWriteRegisterDelay(dev
->bus
->busType_u
.mpuSlave
.master
, MPU_RA_I2C_MST_CTRL
, 0x0D); // I2C multi-master / 400kHz
401 ak8963SpiWriteRegisterDelay(dev
->bus
->busType_u
.mpuSlave
.master
, MPU_RA_USER_CTRL
, 0x30); // I2C master mode, SPI mode only
409 void ak8963BusDeInit(const extDevice_t
*dev
)
411 switch (dev
->bus
->busType
) {
412 #ifdef USE_MAG_AK8963
418 #ifdef USE_MAG_SPI_AK8963
420 spiPreinitByIO(dev
->busType_u
.spi
.csnPin
);
424 #if defined(USE_MAG_AK8963) && (defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU9250))
425 case BUS_TYPE_MPU_SLAVE
:
426 ak8963SpiWriteRegisterDelay(dev
->bus
->busType_u
.mpuSlave
.master
, MPU_RA_INT_PIN_CFG
, MPU6500_BIT_INT_ANYRD_2CLEAR
);
434 bool ak8963Detect(magDev_t
*mag
)
438 extDevice_t
*dev
= &mag
->dev
;
440 if ((dev
->bus
->busType
== BUS_TYPE_I2C
|| dev
->bus
->busType
== BUS_TYPE_MPU_SLAVE
) && dev
->busType_u
.mpuSlave
.address
== 0) {
441 dev
->busType_u
.mpuSlave
.address
= AK8963_MAG_I2C_ADDRESS
;
446 ak8963WriteRegister(dev
, AK8963_MAG_REG_CNTL2
, CNTL2_SOFT_RESET
); // reset MAG
449 bool ack
= ak8963ReadRegisterBuffer(dev
, AK8963_MAG_REG_WIA
, &sig
, 1); // check for AK8963
451 if (ack
&& sig
== AK8963_DEVICE_ID
) // 0x48 / 01001000 / 'H'
453 mag
->init
= ak8963Init
;
454 mag
->read
= ak8963Read
;
459 ak8963BusDeInit(dev
);