This is a device driver of AK8963, which is 3-axis magnetometer manufactured by AKM.
Dependents: SimpleSample combination combination
ak8963.cpp
- Committer:
- coisme
- Date:
- 2015-09-04
- Revision:
- 2:9dff393e5e86
- Parent:
- 1:a362b9e3aac2
File content as of revision 2:9dff393e5e86:
#include "ak8963.h"
// REGISTER MAP
#define AK8963_REG_WIA 0x00
#define AK8963_REG_INFO 0x01
#define AK8963_REG_ST1 0x02
#define AK8963_REG_HXL 0x03
#define AK8963_REG_HXH 0x04
#define AK8963_REG_HYL 0x05
#define AK8963_REG_HYH 0x06
#define AK8963_REG_HZL 0x07
#define AK8963_REG_HZH 0x08
#define AK8963_REG_ST2 0x09
#define AK8963_REG_CNTL1 0x0A
#define AK8963_REG_CNTL2 0x0B
#define AK8963_REG_ASTC 0x0C
#define AK8963_REG_TS1 0x0D
#define AK8963_REG_TS2 0x0E
#define AK8963_REG_I2CDIS 0x0F
#define AK8963_REG_ASAX 0x10
#define AK8963_REG_ASAY 0x11
#define AK8963_REG_ASAZ 0x12
#define AK8963_REG_RSV 0x13
// BITS in CNTL1 register
#define AK8963_CNTL1_MODE_PDN 0x00 // POWER DOWN MODE
#define AK8963_CNTL1_MODE_SNG 0x01 // SINGLE MEASUREMENT MODE
#define AK8963_CNTL1_MODE_CNT1 0x02 // CONTINUOUS MEASUREMENT MODE 1
#define AK8963_CNTL1_MODE_CNT2 0x06 // CONTINUOUS MEASUREMENT MODE 2
#define AK8963_CNTL1_MODE_EXT 0x04 // EXTERNAL TRIGGER MEASUREMENT MODE
#define AK8963_CNTL1_MODE_TEST 0x08 // SELF-TEST MODE
#define AK8963_CNTL1_MODE_FUSE_ROM 0x0F // FUSE ROM ACCESS MODE
#define AK8963_CNTL1_OUTPUT_14BIT 0x00
#define AK8963_CNTL1_OUTPUT_16BIT 0x10
#define AK8963_BIT_MASK_DRDY 0x01
#define AK8963_BIT_MASK_HOFL 0x08
#define AK8963_REG_WIA_VAL 0x48
#define AK8963_BUF_LENGTH_ASA 3
#define AK8963_BUF_LENGTH_BDATA 8
#define AK8963_WAIT_MODE_TRANSITION 100
#define AK8963_DEFAULT_OUTPUT_BIT AK8963_CNTL1_OUTPUT_16BIT
#define AK8963_SENSITIVITY (0.15) // uT/LSB
#define AK8963_WAIT_POWER_DOWN_US 100
#define LEN_ONE_BYTE 1
AK8963::AK8963(I2C *conn, SlaveAddress addr) {
slaveAddress = addr;
connection = conn;
getSensitivityAdjustment(&sensitivityAdjustment);
}
AK8963::Status AK8963::checkConnection() {
AK8963::Status status = AK8963::SUCCESS;
// Gets the WIA register value.
char wiaValue = 0;
if ((status=AK8963::read(AK8963_REG_WIA, &wiaValue, LEN_ONE_BYTE)) != SUCCESS) {
return status;
}
// Checks the obtained value equals to the supposed value.
if (wiaValue != AK8963_REG_WIA_VAL) {
return AK8963::ERROR;
}
return status;
}
AK8963::Status AK8963::read(char registerAddress, char *buf, int length) {
// Writes a start address
if (connection->write((slaveAddress << 1), ®isterAddress, LEN_ONE_BYTE) != 0) {
// I2C write failed.
return AK8963::ERROR_I2C_WRITE;
}
// Reads register data
if (connection->read((slaveAddress << 1), buf, length) != 0) {
// I2C read failed.
return AK8963::ERROR_I2C_READ;
}
return AK8963::SUCCESS;
}
AK8963::Status AK8963::write(char registerAddress, const char *buf, int length) {
int bufLength = length + 1;
char data[bufLength];
// Creates data to be sent.
data[0] = registerAddress;
for (int i=0; i < length; i++) {
data[1+i] = buf[i];
}
// Writes data
if (connection->write((slaveAddress << 1), data, bufLength) != 0) {
// I2C write failed.
return AK8963::ERROR_I2C_WRITE;
}
return AK8963::SUCCESS;
}
AK8963::Status AK8963::isDataReady() {
AK8963::Status status = AK8963::ERROR;
// Gets the ST1 register value.
char st1Value = 0;
if ((status=AK8963::read(AK8963_REG_ST1, &st1Value, LEN_ONE_BYTE)) != AK8963::SUCCESS) {
// I2C read failed.
return status;
}
// Sets a return status corresponds to the obtained value.
if ((st1Value & AK8963_BIT_MASK_DRDY) > 0) {
status = AK8963::DATA_READY;
} else {
status = AK8963::NOT_DATA_READY;
}
return status;
}
AK8963::Status AK8963::getData(char *buf) {
AK8963::Status status = AK8963::ERROR;
if ((status=AK8963::read(AK8963_REG_ST1, buf, AK8963_BUF_LENGTH_BDATA)) != AK8963::SUCCESS) {
// I2C read failed.
return status;
}
return status;
}
AK8963::Status AK8963::setOperationMode(AK8963::OperationMode mode) {
AK8963::Status status = AK8963::ERROR;
// The device has to be put into power-down mode first before switching mode.
char buf = AK8963::MODE_POWER_DOWN;
if (mode != AK8963::MODE_POWER_DOWN) {
if ((status=AK8963::write(AK8963_REG_CNTL1, &buf, LEN_ONE_BYTE)) != AK8963::SUCCESS) {
// I2C write failed.
return status;
}
wait_us(AK8963_WAIT_POWER_DOWN_US);
}
// Switch to the specified mode.
buf = (mode | AK8963_CNTL1_OUTPUT_16BIT);
if ((status=AK8963::write(AK8963_REG_CNTL1, &buf, LEN_ONE_BYTE)) != AK8963::SUCCESS) {
// I2C write failed.
return status;
}
wait_us(AK8963_WAIT_POWER_DOWN_US);
return AK8963::SUCCESS;
}
AK8963::Status AK8963::getMagneticVector(MagneticVector *vec) {
AK8963::Status status = AK8963::ERROR;
char buf[AK8963_BUF_LENGTH_BDATA];
if ((status=AK8963::getData(buf)) != AK8963::SUCCESS) {
// Failed to get data.
return status;
}
// Checks sensor overflow
if ((buf[7] & AK8963_BIT_MASK_HOFL) > 0) {
// Magnetic sensor overflow
vec->isOverflow = true;
} else {
// Magnetic sensor didn't overflow.
vec->isOverflow = false;
// Calculates magnetic field value
int16_t xi = (int16_t)((buf[2] << 8) | buf[1]);
int16_t yi = (int16_t)((buf[4] << 8) | buf[3]);
int16_t zi = (int16_t)((buf[6] << 8) | buf[5]);
vec->mx = (float)(xi * ((sensitivityAdjustment.x + 128)/256.0) * AK8963_SENSITIVITY);
vec->my = (float)(yi * ((sensitivityAdjustment.y + 128)/256.0) * AK8963_SENSITIVITY);
vec->mz = (float)(zi * ((sensitivityAdjustment.z + 128)/256.0) * AK8963_SENSITIVITY);
}
return AK8963::SUCCESS;
}
AK8963::Status AK8963::getSensitivityAdjustment(SensitivityAdjustment *sns) {
AK8963::Status status = AK8963::ERROR;
// Set the device into the fuse ROM access mode.
char data = AK8963_CNTL1_MODE_FUSE_ROM;
if ((status=AK8963::write(AK8963_REG_CNTL1, &data, LEN_ONE_BYTE)) != AK8963::SUCCESS) {
// I2C write failed.
return status;
}
// Wait
wait_us(AK8963_WAIT_POWER_DOWN_US);
// Read the fuse ROM
char buf[AK8963_BUF_LENGTH_ASA];
if ((status=AK8963::read(AK8963_REG_ASAX, buf, AK8963_BUF_LENGTH_ASA)) != AK8963::SUCCESS) {
// I2C read failed.
return status;
}
sns->x = buf[0];
sns->y = buf[1];
sns->z = buf[2];
// Set the device into the power-down mode
data = AK8963_CNTL1_MODE_PDN;
if ((status=AK8963::write(AK8963_REG_CNTL1, &data, LEN_ONE_BYTE)) != AK8963::SUCCESS) {
// I2C write failed.
return status;
}
// Wait
wait_us(AK8963_WAIT_POWER_DOWN_US);
return AK8963::SUCCESS;
}
Osamu Koizumi