Hello everyone,
I'm currently working on a driver for the relatively new ICM20948 by InvenSense for Arduino. ( Accelerometer, Gyroscope and Magnetometer on a single chip)
I communicate via SPI and had no problems with neither the Accel nor the Gyroscope.
The Magnetometer (AK09916) on the other hand does not work with SPI directly.
To read and write from/to the Magnetometer Registers we have to use the I2C_Slave and the I2C_Master Registers.
Since the Datasheet doesn't provide a clear answer of how exactly to do that, (Or at least I can't figure it out) I'm hoping someone here can help me.
Even if it is not the same chip, it is very similar to the mpu9250 in a lot of ways. So if any of you figured out how to use the slave registers to acces the mpu9250 aux. magnetometer, it might be just what i'm looking for.
I still haven't managed to receive any data from the Magnetometer, so the first goal is simply reading the WHO_AM_I Register.
My Init function:
uint8_t ICM20948::init() {
uint8_t okByte = 0;
set_SPI(icm20948_ss_pin, icm20948_spi_settings, ICM20948_spi_read_mask);
delay(100);
if(get_WHO_AM_I_Reg() == 0xEA) {
reset();
set_CLKSEL_Bits(get_CLKSEL_value());
set_I2C_IF_DIS_Bit(true);
if(is_GYRO_SETTING_VALID()) setup_gyro();
else okByte |= 0b00000010;
if(is_ACCEL_SETTING_VALID()) setup_accel();
else okByte |= 0b00000100;
if(is_TEMP_SETTING_VALID()) setup_temp();
else okByte |= 0b00001000;
if(is_FIFO_SETTING_VALID()) setup_Fifo();
else okByte |= 0b00010000;
setup_i2c_slave0(ICM20948_MAG_ADDRESS); // THE PROBLEM
Serial.println(read_Mag_Register(0x01)); // <= Trying to read WHO_AM_I
}
else {
okByte |= 0b00000001;
}
return okByte;
}
Setting up the I2C_Mst and slave0 Registers
void ICM20948::setup_i2c_slave0(uint8_t slave_addr) {
select_user_bank(0);
// Settings in Usr Ctrl Register
set_I2C_MST_EN_Bit(true);
// Settings in INT_PIN_CFG Register
set_INT1_OPEN_Bit(false);
set_INT1_LATCH_EN_Bit(true);
set_INT_ANYRD_2CLEAR_Bit(false);
set_BYPASS_EN_Bit(true);
// Settings in INT_ENABLE Register
set_I2C_MST_INT_EN_Bit(true);
// Changes in INT_ENABLE_1 Register
set_RAW_DATA_0_RDY_EN_Bit(true);
// Changes in LP_CONFIG Register
set_I2C_MST_CYCLE_Bit(false);
select_user_bank(2);
set_ODR_ALIGN_EN_Bit(false);
select_user_bank(3);
// Setup I2C_MST_ODR_CONFIG Register
set_I2C_MST_ODR_CONFIG_Bits(0x00); // Shouldn't matter since gyro is enabled
// Setup I2C_MST_CTRL Register // Datasheet page 68
set_MULT_MST_EN_Bit(false); // Only for Multiple SLV Devices?
set_I2C_MST_P_NSR_Bit(true); // Not sure ?! Restart/Stop after read
set_I2C_MST_CLK_Bits(0x7); // recommended I2C_MST_CLK (Datasheet page 81);
// Setup I2C_MST_DELAY_CTRL Register // Datasheet page 69
set_DELAY_ES_SHADOW_Bit(false); // Not sure ?!
set_I2C_SLV0_DELAY_EN_Bit(false); // ODR Div. Register = 0 anyway.Sort of Smplrt_Div on/off (set at I2C_MST_ODR_CONFIG)
// Setup I2C_SLV0_ADDR Register // Datasheet page 69
set_I2C_ID_0_Bits(slave_addr); // Bits [6:0] of I2C_SLV0_ADDR set to Mag_Addr (0x0C)
// Setup I2C_SLV0_CTRL Register // Datasheet page 70
set_I2C_SLV0_BYTE_SW_Bit(false); // swap receiving Bytes (Check if needed once any data is received)
set_I2C_SLV0_GRP_Bit(false); // f.e. 2 Byte Group always ends with odd/even Reg. Addr (Check if needed once any data is received)
set_I2C_SLV0_REG_DIS_Bit(false); // Not sure ?!
set_I2C_SLV0_EN_Bit(true); // Enable reading data from slv 0. Stored at EXT_SENS_DATA_00
select_user_bank(0);
}
Read/Write from/to Magnetometer
uint8_t ICM20948::read_Mag_Register(uint8_t _mag_register) {
select_user_bank(3);
set_I2C_SLV0_RNW_Bit(true); // Set Bit [7] of I2C_SLV0_ADDR to 1 => Transfer is a read
set_I2C_SLV0_REG_Reg(_mag_register); // set i2c slv 0 register addr. from where to begin data transfer (0x01 => mag whoAmI)
set_I2C_SLV0_LENG_Bits(0x01); // Number of Bytes to be read from I2C slave 0
select_user_bank(0);
return read_Byte(ICM20948_EXT_SLV_SENS_DATA_00);
}
void ICM20948::write_Mag_Register(uint8_t _mag_register, uint8_t _value) {
select_user_bank(3);
set_I2C_SLV0_RNW_Bit(false); // Set Bit [7] of I2C_SLV0_ADDR to 0 => Transfer is a write
set_I2C_SLV0_REG_Reg(_mag_register); // set i2c slv 0 register addr. from where to begin data transfer (0x01 => mag whoAmI)
set_I2C_SLV0_LENG_Bits(0x01); // Number of Bytes to be read from I2C slave 0
set_I2C_SLV0_DO_Reg(_value); // Value beeing stored at mag_register
select_user_bank(0);
}
I've build a small function printing the registers for debug purposes which gives me the following console log after init:
|------------------------+---------------------|
| ICM20948 - Register Overview |
|------------------------+---------------------|
|||||||||||||||| [USER_BANK_0] |||||||||||||||||
|------------------------+---------------------|
| WHO_AM_I | 11101010 (0xEA) |
| USER_CTRL | 01110000 (0x70) |
| LP_CONFIG | 00000000 (0x00) |
| PWR_MGMT_1 | 00001001 (0x09) |
| PWR_MGMT_2 | 00000000 (0x00) |
| INT_PIN_CFG | 00100010 (0x22) |
| INT_ENABLE | 00000001 (0x01) |
| INT_ENABLE_1 | 00000001 (0x01) |
| INT_ENABLE_2 | 00000000 (0x00) |
| INT_ENABLE_3 | 00000000 (0x00) |
| FIFO_EN_1 | 00000000 (0x00) |
| FIFO_EN_2 | 00011110 (0x1E) |
|------------------------+---------------------|
|||||||||||||||| [USER_BANK_2] |||||||||||||||||
|------------------------+---------------------|
| GYRO_CONFIG_1 | 00110011 (0x33) |
| GYRO_CONFIG_2 | 00000000 (0x00) |
| GYRO_SMPLRT_DIV | 00000000 (0x00) |
| ACCEL_CONFIG | 00001001 (0x09) |
| ACCEL_CONFIG_2 | 00000000 (0x00) |
| ACCEL_SMPLRT_DIV | 00001010 (0x0A) |
| TEMP_CONFIG | 00000000 (0x00) |
| ODR_ALIGN_EN | 00000000 (0x00) |
|------------------------+---------------------|
|||||||||||||||| [USER_BANK_3] |||||||||||||||||
|------------------------+---------------------|
| I2C_MST_ODR_CONFIG | 00000000 (0x00) |
| I2C_MST_CTRL | 00010111 (0x17) |
| I2C_MST_DELAY_CTRL | 00000000 (0x00) |
| I2C_SLV0_ADDR | 10001100 (0x8C) |
| I2C_SLV0_REG | 00000001 (0x01) |
| I2C_SLV0_CTRL | 10000001 (0x81) |
| I2C_SLV0_DO | 00000000 (0x00) |
|------------------------+---------------------|
OK
The more I tested and failed the less I felt I know what i'm doing
Am i just missing one Bit? Does the different ODR mess with the communications? Do I somehow forget to trigger the transmit? You see, I'm kind of lost right now.
If you need some other information or have questions about my setup, plz let me know!