Hi all,
Firstly I would like to state that this is one of the first projects I have done using Zephyr RTOS, so please consider me a novice (this is almost certainly an issue with my code, not software or hardware issues). I am using the nRF52DK (nRF52832) with the nRF Connect SDK v2.5.0.
I am currently trying to get readings from the magnetometer on the ICM20948 9-axis IMU (Sparkfun board) via I2C. I have read the gyroscope and accelerometer readings successfully using i2c_write_dt() and i2c_burst_read_dt() from the device.h API. However, reading the magnetometer readings is not so straightforward. The magnetometer is controlled as a slave by the IMU. I am having trouble getting the magnetometer to write the internal register values to the EXT_SLV_SENS_DATA_XX registers.
To begin, I am just trying to read the magnetometer ID (0x09) from the internal "who am I" register (main.c lines 55-182). After this, I will worry about reading the actual magnetometer measurements, and status registers.
I am having a very strange issue when I try to use i2c_write_dt() to modify the I2C_SLV0_ADDR, I2C_SLV0_REG, I2C_SLV0_DO and I2C_SLV0_CTRL registers. If I write the bit definition 0x00 to any of these registers, using i2c_burst_read_dt() I can verify they are written correctly. However, if I write any other value, i2c_burst_read_dt() reveals that a completely different bit definition has been written. Even more strangely, I can write any value I like to the I2C_SLV0_DO register and this works successfully (output in bold below), the same cannot be said for the other registers.
I have attached my code below in its entirety. Some parts are commented out - ignore these parts as they either don't work or were used for debugging. I have also attached an image of the serial output which includes printed statements of the expected register bit definitions after each write command. Any questions please ask and I'll do my best to respond. Thanks in advance for any help - it is greatly appreciated!
Kind regards,
Guy
main.c:
//////////////////////////////////////////////////////////// // Main C Program for ICM-20948 9-axis IMU //////////////////////////////////////////////////////////// #include <stdio.h> #include <stdint.h> #include <zephyr/kernel.h> #include <zephyr/device.h> #include <zephyr/devicetree.h> #include <zephyr/drivers/i2c.h> #include <zephyr/drivers/gpio.h> #include <zephyr/logging/log.h> #include <zephyr/sys/printk.h> LOG_MODULE_REGISTER(ICM20948,LOG_LEVEL_DBG); #define SLEEP_TIME_MS 1000 // Defining I2C device via node on device tree overlay file #define I2C0_NODE DT_NODELABEL(icm20948) static const struct i2c_dt_spec i2c_dev = I2C_DT_SPEC_GET(I2C0_NODE); #include "icm20948.c" int main(void) { icm20948_init(); //initialises icm20948 and tests who am i? k_msleep(100); icm20948_reset(); //resets internal registers k_msleep(100); icm20948_init(); //initialises icm20948 and tests who am i? k_msleep(100); icm20948_wake(); k_msleep(100); //icm20948_configure(); // configure icm20948 here, eg, accelerometer sensetivity icm20948_mag_init(); // initialise magnetometer k_msleep(100); printk("\nstart of mag whoami \n"); //4. Write to I2C_SLV0_ADDR to write mode, and slave address to the magnetometer (0x0C) //5. Write to I2C_SLV0_REG, setting the address to be written as the address of CTRL2 in the magnetometer //6. Populate ICM20948_I2C_SLV0_DO with the desired control setting //7. Write to ICM20948_I2C_SLV0_CTRL, enabling the mag and setting the grouping and bytes to be read //8. Switch I2C_SLV0_ADDR to read mode // and I2C_SLV0_REG to HXL //9. EXT_SLV_SENS_DATA_(00 to 06) will now be populated with HXL to ST2. It should be noted that reading ST2 is necessary as this automatically triggers the data to update in the axis registers. icm20948_reg_bank_select(3); int ret; printk("\r\r\rpost zeroes checks:\n"); //reset regs to 0x00 //specifying address of slave device we want to write to (0x0C for magnetometer), and write bit (MSB low ie 0x00) uint8_t buff19[] = {ICM20948_I2C_SLV0_ADDR , 0x00}; ret = i2c_write_dt(&i2c_dev,buff19,sizeof(buff19)); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_I2C_SLV0_ADDR); } icm20948_read_single_reg(3, ICM20948_I2C_SLV0_ADDR); printk("ADDR expect\t\t\t\t 0x00\n\n"); // internal mag address we want to write to uint8_t buff20[] = {ICM20948_I2C_SLV0_REG , 0x00}; ret = i2c_write_dt(&i2c_dev,buff20,sizeof(buff20)); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_I2C_SLV0_REG); } icm20948_read_single_reg(3, ICM20948_I2C_SLV0_REG); printk("REG expect\t\t\t\t 0x00\n\n"); // disabling power down mode and setting continous measurement mode uint8_t buff30[] = {ICM20948_I2C_SLV0_DO , 0x00};// THIS IS WORKING?!?! ret = i2c_write_dt(&i2c_dev,buff30,sizeof(buff30)); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_I2C_SLV0_DO); } icm20948_read_single_reg(3, ICM20948_I2C_SLV0_DO); printk("DO expect\t\t\t\t 0x00\n\n"); // enabling read of 1 byte uint8_t buff40[] = {ICM20948_I2C_SLV0_CTRL , 0x00}; ret = i2c_write_dt(&i2c_dev,buff40,sizeof(buff40)); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_I2C_SLV0_CTRL); } icm20948_read_single_reg(3, ICM20948_I2C_SLV0_CTRL); printk("CTRL expect\t\t\t\t 0x00\n\n\n\n"); k_msleep(100); printk("\r\r\n\npost setup checks:\n"); // Disabling power down mode and setting sample rate to continuous: //specifying address of slave device we want to write to (0x0C for magnetometer), and write bit (MSB low ie 0x00) uint8_t buff1[] = {ICM20948_I2C_SLV0_ADDR , ICM20948_MAG_I2C_ADDR}; ret = i2c_write_dt(&i2c_dev,buff1,sizeof(buff1)); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_I2C_SLV0_ADDR); } k_msleep(100); icm20948_read_single_reg(3, ICM20948_I2C_SLV0_ADDR); printk("ADDR expect\t\t\t\t 0x0C\n\n"); // internal mag address we want to write to uint8_t buff2[] = {ICM20948_I2C_SLV0_REG , ICM20948_MAG_INTERNAL_REG_CTRL_2}; ret = i2c_write_dt(&i2c_dev,buff2,sizeof(buff2)); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_I2C_SLV0_REG); } icm20948_read_single_reg(3, ICM20948_I2C_SLV0_REG); printk("REG expect\t\t\t\t 0x31\n\n"); // disabling power down mode and setting continous measurement mode uint8_t buff3[] = {ICM20948_I2C_SLV0_DO , ICM20948_MAG_INTERNAL_REG_CTRL_2_BIT_100KHZ};// THIS IS WORKING?!?! ret = i2c_write_dt(&i2c_dev,buff3,sizeof(buff3)); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_I2C_SLV0_DO); } icm20948_read_single_reg(3, ICM20948_I2C_SLV0_DO); printk("DO expect\t\t\t\t 0x08\n\n"); // enabling read of 1 byte uint8_t buff4[] = {ICM20948_I2C_SLV0_CTRL , 0x80|0x01}; ret = i2c_write_dt(&i2c_dev,buff4,sizeof(buff4)); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_I2C_SLV0_CTRL); } icm20948_read_single_reg(3, ICM20948_I2C_SLV0_CTRL); printk("CTRL expect\t\t\t\t 0x81\n\n\n\n"); printk("\r\rstart of read whoami:\n"); //specifying address of slave device we want to read from (0x0C for magnetometer), and read bit (MSB high ie 0x80) uint8_t buff5[] = {ICM20948_I2C_SLV0_ADDR , 0x80|ICM20948_MAG_I2C_ADDR}; ret = i2c_write_dt(&i2c_dev,buff5,sizeof(buff5)); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_I2C_SLV0_ADDR); } icm20948_read_single_reg(3, ICM20948_I2C_SLV0_ADDR); printk("ADDR expect\t\t\t\t 0x8C\n\n"); // internal mag address we want to read from uint8_t buff6[] = {ICM20948_I2C_SLV0_REG , ICM20948_MAG_INTERNAL_REG_WHO_AM_I}; ret = i2c_write_dt(&i2c_dev,buff6,sizeof(buff6)); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_I2C_SLV0_REG); } icm20948_read_single_reg(3, ICM20948_I2C_SLV0_REG); printk("REG expect\t\t\t\t 0x01\n\n\n\n"); uint8_t mag_id[1] = {0}; ret = i2c_burst_read_dt(&i2c_dev, ICM20948_MAG_REG_1, mag_id ,sizeof(mag_id)); if(ret != 0){ printk("Failed to read to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_MAG_REG_1); } if (mag_id[0] == 0x09) { printk("Verified who am i, (mag):\t 0x%.2X \n", mag_id[0]); } else { printk("WHO AM I?!?!?! (mag):\t 0x%.2X\n...(expected ID 0x09)\n", mag_id[0]); } printk("end of mag whoami \n"); k_msleep(100); k_msleep(100); printk("\nEntered main loop.\r");//for debug // main loop // while(true){ // // //icm20948_wake(); // k_msleep(100); // takes about 50 microseconds to wake sensors // // icm20948_mag_init(); // initialise magnetometer // k_msleep(100); // // //Read gyro, accelerometer, and temperature // icm20948_get_data(); // // //Read magnetometer // icm20948_mag_get_data(); // // // // // icm20948_sleep(); // // k_msleep(800); // } printk("\nEnd of program.\n_______________________________\n"); return 0; }
icm30948.c:
//////////////////////////////////////////////////////////// // Function Definitions for ICM-20948 9-axis IMU //////////////////////////////////////////////////////////// #include <stdio.h> #include <stdint.h> #include "icm20948.h" /*************************************************************************************/ // User functions: /*************************************************************************************/ // Initialise icm20948 and verify with icm20948 "who am i" void icm20948_init(void) { if (!device_is_ready(i2c_dev.bus)){ printk("I2C bus %s is not ready!\n\r",i2c_dev.bus->name); return; } else { uint8_t icm20948_whoami[1] = {0}; // verifing with who am i - expect to read 0xEA icm20948_reg_bank_select(0); //select user bank 0 //Do a burst read of 1 byte int ret = i2c_burst_read_dt(&i2c_dev, ICM20948_WHO_AM_I,icm20948_whoami,sizeof(icm20948_whoami)); if(ret != 0){ printk("Failed to read to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_WHO_AM_I); return; } printk("_______________________________\n"); printk("I2C bus %s is ready!\n\r",i2c_dev.bus->name); printk("Who am I:\t 0x%X\n", (icm20948_whoami[0])); printk("_______________________________\n"); if (icm20948_whoami[0] != 0xEA) { printk("WHO AM I?!?!?\n"); } } return; } // Reset IMU internal registers void icm20948_reset(void) { icm20948_reg_bank_select(0); char buff[] = {ICM20948_POWER_MGMT_1 , ICM20948_POWER_MGMT_1_BIT_RESET_DEVICE}; int ret = i2c_write_dt(&i2c_dev,buff,sizeof(buff)); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_POWER_MGMT_1); return; } printk("ICM-20948 reset successful.\n"); return; } // select user register bank (input 0 1 2 or 3) static void icm20948_reg_bank_select(int bank) { int ret; if (bank == 0) { char buff[] = {ICM20948_REG_BANK_SEL , ICM20948_REG_BANK_SEL_BIT_SEL_BANK_0}; ret = i2c_write_dt(&i2c_dev,buff,sizeof(buff)); k_msleep(20); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_REG_BANK_SEL); return; } //else {printk("Reg bank %d selected.\n", bank);} } else if (bank == 1) { char buff[] = {ICM20948_REG_BANK_SEL , ICM20948_REG_BANK_SEL_BIT_SEL_BANK_1}; ret = i2c_write_dt(&i2c_dev,buff,sizeof(buff)); k_msleep(20); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_REG_BANK_SEL); return; } //else {printk("Reg bank %d selected.\n", bank);} } else if (bank == 2) { char buff[] = {ICM20948_REG_BANK_SEL , ICM20948_REG_BANK_SEL_BIT_SEL_BANK_2}; ret = i2c_write_dt(&i2c_dev,buff,sizeof(buff)); k_msleep(20); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_REG_BANK_SEL); return; } //else {printk("Reg bank %d selected.\n", bank);} } else if (bank == 3) { char buff[] = {ICM20948_REG_BANK_SEL , ICM20948_REG_BANK_SEL_BIT_SEL_BANK_3}; ret = i2c_write_dt(&i2c_dev,buff,sizeof(buff)); k_msleep(20); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_REG_BANK_SEL); return; } //else {printk("Reg bank %d selected.\n", bank);} } else { printk("Invalid user bank register selection: %d\n", bank);} return; } // Wake icm20948 from sleep mode void icm20948_wake(void) { // wake device from sleep mode by disabling "sleep mode enabled" bit // = (reg to write to , value to write to reg) char buff[] = {ICM20948_POWER_MGMT_1 , ICM20948_POWER_MGMT_1_BIT_WAKE}; icm20948_reg_bank_select(0); //select user bank 0 int ret = i2c_write_dt(&i2c_dev,buff,sizeof(buff)); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_POWER_MGMT_1); } return; } // Put icm20948 in sleep mode void icm20948_sleep(void) { char buff[] = {ICM20948_POWER_MGMT_1 , ICM20948_POWER_MGMT_1_BIT_SLEEP}; icm20948_reg_bank_select(0); //select user bank 0 int ret = i2c_write_dt(&i2c_dev,buff,sizeof(buff)); if(ret != 0){ printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_POWER_MGMT_1); } return; } // Configure icm20948 void icm20948_configure(void) { return; } // Get and store accelerometer, gyro, and temperature measurements void icm20948_get_data(void) { icm20948_reg_bank_select(0); //select user bank 0 //Do a burst read of 14 bytes int8_t icm20948_reading[14] = {0}; // 0x2D to 0x3A int ret = i2c_burst_read_dt(&i2c_dev, ICM20948_ACCEL_XOUT_H,icm20948_reading,sizeof(icm20948_reading)); if(ret != 0){ printk("Failed to read to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_ACCEL_XOUT_H); } //Print reading to console //printk("_______________________________\n"); // High Byte | Low Byte //printk("x Accel Raw Value:\t %d\n", (int16_t)((icm20948_reading[ 0] << 8) | icm20948_reading[ 1]) ); // 0x2D to 0x2E //printk("y Accel Raw Value:\t %d\n", (int16_t)((icm20948_reading[ 2] << 8) | icm20948_reading[ 3]) ); // 0x2F to 0x30 //printk("z Accel Raw Value:\t %d\n", (int16_t)((icm20948_reading[ 4] << 8) | icm20948_reading[ 5]) ); // 0x31 to 0x32 //printk("x Gyro Raw Value:\t %d\n", (int16_t)((icm20948_reading[ 6] << 8) | icm20948_reading[ 7]) ); // 0x33 to 0x34 //printk("y Gyro Raw Value:\t %d\n", (int16_t)((icm20948_reading[ 8] << 8) | icm20948_reading[ 9]) ); // 0x35 to 0x36 //printk("z Gyro Raw Value:\t %d\n", (int16_t)((icm20948_reading[10] << 8) | icm20948_reading[11]) ); // 0x37 to 0x38 //printk("Temp Raw Value:\t\t %d\n", (int16_t)((icm20948_reading[12] << 8) | icm20948_reading[13]) ); // 0x39 to 0x3A printk("_______________________________\n"); printk("x Accel (g):\t %f\n", (float)((icm20948_reading[ 0] << 8) | icm20948_reading[ 1])/16384); // 0x2D to 0x2E printk("y Accel (g):\t %f\n", (float)((icm20948_reading[ 2] << 8) | icm20948_reading[ 3])/16384); // 0x2F to 0x30 printk("z Accel (g):\t %f\n", (float)((icm20948_reading[ 4] << 8) | icm20948_reading[ 5])/16384); // 0x31 to 0x32 printk("x Gyro (dps):\t %f\n", (float)((icm20948_reading[ 6] << 8) | icm20948_reading[ 7])/131); // 0x33 to 0x34 printk("y Gyro (dps):\t %f\n", (float)((icm20948_reading[ 8] << 8) | icm20948_reading[ 9])/131); // 0x35 to 0x36 printk("z Gyro (dps):\t %f\n", (float)((icm20948_reading[10] << 8) | icm20948_reading[11])/131); // 0x37 to 0x38 printk("Temp (degC):\t %f\n", (float)(((icm20948_reading[12] << 8) | icm20948_reading[13])/333.87 + 21)); // 0x39 to 0x3A printk("_______________________________\n"); //printk("x Accel m/s^2:\t %f\n", (float)((icm20948_reading[ 0] << 8) | icm20948_reading[ 1])*9.80665/16384); // 0x2D to 0x2E //printk("y Accel m/s^2:\t %f\n", (float)((icm20948_reading[ 2] << 8) | icm20948_reading[ 3])*9.80665/16384); // 0x2F to 0x30 //printk("z Accel m/s^2:\t %f\n", (float)((icm20948_reading[ 4] << 8) | icm20948_reading[ 5])*9.80665/16384); // 0x31 to 0x32 //printk("x Gyro dps:\t %f\n", (float)((icm20948_reading[ 6] << 8) | icm20948_reading[ 7])/131); // 0x33 to 0x34 //printk("y Gyro dps:\t %f\n", (float)((icm20948_reading[ 8] << 8) | icm20948_reading[ 9])/131); // 0x35 to 0x36 //printk("z Gyro dps:\t %f\n", (float)((icm20948_reading[10] << 8) | icm20948_reading[11])/131); // 0x37 to 0x38 //printk("Temp *C:\t %f\n", (float)(((icm20948_reading[12] << 8) | icm20948_reading[13])/333.87 + 21)); // 0x39 to 0x3A //printk("_______________________________\n"); return; } // function to read single byte from IMU registers void icm20948_read_single_reg(int bank, uint8_t start_addr) { icm20948_reg_bank_select(bank); uint8_t icm20948_reading[1] = {0}; int ret = i2c_burst_read_dt(&i2c_dev, start_addr, icm20948_reading, sizeof(icm20948_reading)); if(ret != 0){ printk("Failed to read Magnetometer readings.\n"); return; } printk("ICM-20948 bank %d register 0x%.2X value :\t 0x%.2X\n", bank, start_addr, (uint8_t)(icm20948_reading[0])); icm20948_reg_bank_select(0); return; } /*************************************************************************************/ // Magnetometer user functions: /*************************************************************************************/ // function to write to magnetometer internal registers //static void icm20948_mag_write_reg(uint8_t reg, uint8_t data) { // int ret; // icm20948_reg_bank_select(3); // // // //specifying address of slave device we want to write to (0x0C for magnetometer) // uint8_t buff1[] = {ICM20948_I2C_SLV0_ADDR , ICM20948_MAG_I2C_ADDR}; // ret = i2c_write_dt(&i2c_dev,buff1,sizeof(buff1)); // if(ret != 0){ // printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_I2C_SLV0_ADDR); // } // // We then write "reg" to i2c_slv0_reg ("reg" is internal magnetometer register want to write to) // uint8_t buff2[] = {ICM20948_I2C_SLV0_REG , reg}; // ret = i2c_write_dt(&i2c_dev,buff2,sizeof(buff2)); // if(ret != 0){ // printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_I2C_SLV0_REG); // } // // We then write "data" to i2c_slv0_do (data is the 8-bit value we want to write to "reg") // uint8_t buff3[] = {ICM20948_I2C_SLV0_DO , data}; // ret = i2c_write_dt(&i2c_dev,buff3,sizeof(buff3)); // if(ret != 0){ // printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_I2C_SLV0_DO); // } // // // Then we write 0x80 to i2c_slv0_ctrl to initiate write command // uint8_t buff4[] = {ICM20948_I2C_SLV0_CTRL , 0x80|0x01}; // ret = i2c_write_dt(&i2c_dev,buff4,sizeof(buff4)); // if(ret != 0){ // printk("Failed to write to Magnetometer register.\n"); // } // //// printk("_______________________________\n"); //// icm20948_read_single_reg(3,ICM20948_I2C_SLV0_ADDR);printk("ICM20948_I2C_SLV0_ADDR (exp \n"); //// icm20948_read_single_reg(3,ICM20948_I2C_SLV0_REG);printk("ICM20948_I2C_SLV0_REG (exp 0x%.2X)\n", reg); //// icm20948_read_single_reg(3,ICM20948_I2C_SLV0_CTRL);printk("ICM20948_I2C_SLV0_CTRL (exp 0x81)\n"); //enable reading from device of length 1 byte //// icm20948_read_single_reg(3,ICM20948_I2C_SLV0_DO);printk("ICM20948_I2C_SLV0_DO (exp 0x%.2X)\n", data); //// printk("_______________________________\n"); // k_msleep(100); // icm20948_reg_bank_select(0); // return; //} // function to read magnetometer internal registers //static void icm20948_mag_read_reg(uint8_t start_addr, uint8_t len) { // int ret; // // icm20948_reg_bank_select(3); // // k_msleep(20); // //specifying address of slave device we want to read from (0x8C for magnetometer) // uint8_t buff1[] = {ICM20948_I2C_SLV0_ADDR , 0x80|ICM20948_MAG_I2C_ADDR}; // ret = i2c_write_dt(&i2c_dev,buff1,sizeof(buff1)); // if(ret != 0){ // printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_I2C_SLV0_ADDR); // } // // We then write "start_addr" to i2c_slv0_reg (start_addr is register we want to initiate read from) // uint8_t buff2[] = {ICM20948_I2C_SLV0_REG , start_addr}; // ret = i2c_write_dt(&i2c_dev,buff2,sizeof(buff2)); // if(ret != 0){ // printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_I2C_SLV0_REG); // } // // Then we write 0x80 to i2c_slv0_ctrl to initiate read command (len is number of registers (BYTES) to read) // uint8_t buff3[] = {ICM20948_I2C_SLV0_CTRL , 0x80|len}; // ret = i2c_write_dt(&i2c_dev,buff3,sizeof(buff3)); // if(ret != 0){ // printk("Failed to read from Magnetometer.\n"); // } // // k_msleep(100); // // icm20948_reg_bank_select(0); // return; //} // initialises magnetometer void icm20948_mag_init(void) { int ret; icm20948_reg_bank_select(0); // resetting IMU i2c master module uint8_t buff0[] = {ICM20948_USER_CTRL , ICM20948_USER_CTRL_BIT_I2C_MST_RESET}; ret = i2c_write_dt(&i2c_dev,buff0,sizeof(buff0)); if(ret != 0){ printk("Failed to reset magnetometer I2C master module!\n"); } else { printk("Reset magnetometer I2C master module!\n"); } // enabling IMU as a i2c master uint8_t buff1[] = {ICM20948_USER_CTRL , ICM20948_USER_CTRL_BIT_I2C_MST_EN}; ret = i2c_write_dt(&i2c_dev,buff1,sizeof(buff1)); if(ret != 0){ printk("Failed to enable magnetometer!\n"); } uint8_t buff2[1] = {0}; ret = i2c_burst_read_dt(&i2c_dev, ICM20948_USER_CTRL, buff2 ,sizeof(buff2)); if(ret != 0){ printk("Failed to read to I2C device address 0x%c at Reg. 0x%c\n",i2c_dev.addr,ICM20948_USER_CTRL); } if(buff2[0] == 0x20) { printk("IMU enabled as I2C master.\n"); } else { printk("Failed to enable IMU as I2C master. ICM20948_USER_CTRL set to 0x%.2X (expect 0x20).\n", buff2[0]); } k_msleep(50); return; }
icm20948.h:
//////////////////////////////////////////////////////////// // Header file for ICM-20948 9-axis IMU //////////////////// //////////////////////////////////////////////////////////// #include <stdio.h> #include <stdint.h> //////////////////////////////////////////////////////////// // ICM-20948 Functions: /////////////////////////////////// //in "icm20948.c" void icm20948_init(void); // initialise and verify icm20948 "who am i" static void icm20948_reg_bank_select(int bank); // select register bank void icm20948_reset(void); //resets IMU void icm20948_configure(void); // configure icm20948 void icm20948_get_data(void); // gets accelerometer, gyro, and temperature measurments, prints to terminal void icm20948_wake(void); // wakes from sleep mode void icm20948_sleep(void); // put IMU in sleep mode void icm20948_read_single_reg(int bank, uint8_t start_addr); // read value of single register //static void icm20948_mag_write_reg(uint8_t reg, uint8_t data); // writes to magnetometer internal registers //static void icm20948_mag_read_reg(uint8_t start_addr, uint8_t len); // reads magnetometer internal registers void icm20948_mag_init(void); // initialises magnetometer //////////////////////////////////////////////////////////// // Registers and associated bit definitions: ////////////// // Magnetometer I2C address: #define ICM20948_MAG_I2C_ADDR (0x0C) #define ICM20948_MAG_I2C_ID 0x09 // expected who am I reg value // Reg bank select (common to all user banks): #define ICM20948_REG_BANK_SEL (0x7F) // Bit definitions: #define ICM20948_REG_BANK_SEL_BIT_SEL_BANK_0 0x00 /**< Register bank 0 */ #define ICM20948_REG_BANK_SEL_BIT_SEL_BANK_1 0x10 /**< Register bank 1 */ #define ICM20948_REG_BANK_SEL_BIT_SEL_BANK_2 0x20 /**< Register bank 2 */ #define ICM20948_REG_BANK_SEL_BIT_SEL_BANK_3 0x40 /**< Register bank 3 */ /*********************************************/ /* Bank 0 register map with bit definitions: */ /*********************************************/ #define ICM20948_WHO_AM_I (0x00) // Device ID register expected 0xEA #define ICM20948_USER_CTRL (0x03) #define ICM20948_USER_CTRL_RESET 0x00 #define ICM20948_USER_CTRL_BIT_I2C_MST_EN 0x20 // enable icm20948 as master I2C controller #define ICM20948_USER_CTRL_BIT_I2C_MST_RESET 0x02 // reset icm20948 master I2C controller #define ICM20948_POWER_MGMT_1 (0x06) // DEVICE IS IN SLEEP MODE BY DEFAULT #define ICM20948_POWER_MGMT_1_RESET 0x41 #define ICM20948_POWER_MGMT_1_BIT_WAKE 0x01 // enable accelerometer and gyro #define ICM20948_POWER_MGMT_1_BIT_RESET_DEVICE 0x80 // RESET ALL REGISTERS -> bit autoclears when set high #define ICM20948_POWER_MGMT_1_BIT_SLEEP 0x41 // enable sleep mode(BIT(6)) #define ICM20948_POWER_MGMT_2 (0x07) #define ICM20948_POWER_MGMT_2_RESET 0x00 #define ICM20948_POWER_MGMT_2_BIT_DISABLE_ 0x80 // disables accelerometer and gyro #define ICM20948_ACCEL_XOUT_H (0x2D) #define ICM20948_ACCEL_XOUT_L (0x2E) #define ICM20948_ACCEL_YOUT_H (0x2F) #define ICM20948_ACCEL_YOUT_L (0x30) #define ICM20948_ACCEL_ZOUT_H (0x31) #define ICM20948_ACCEL_ZOUT_L (0x32) #define ICM20948_GYRO_XOUT_H (0x33) #define ICM20948_GYRO_XOUT_L (0x34) #define ICM20948_GYRO_YOUT_H (0x35) #define ICM20948_GYRO_YOUT_L (0x36) #define ICM20948_GYRO_ZOUT_H (0x37) #define ICM20948_GYRO_ZOUT_L (0x38) #define ICM20948_TEMP_OUT_H (0x39) #define ICM20948_TEMP_OUT_L (0x3A) #define ICM20948_MAG_REG_1 (0x3B) //// Magnetometer readings //// #define ICM20948_MAG_REG_2 (0x3E) /////////////////////////////// #define ICM20948_MAG_REG_3 (0x3F) // #define ICM20948_MAG_REG_4 (0x40) // #define ICM20948_MAG_REG_5 (0x41) // #define ICM20948_MAG_REG_6 (0x42) // #define ICM20948_MAG_REG_7 (0x43) // #define ICM20948_MAG_REG_8 (0x44) // #define ICM20948_MAG_REG_9 (0x45) // #define ICM20948_MAG_REG_10 (0x46) // #define ICM20948_MAG_REG_11 (0x47) // #define ICM20948_MAG_REG_12 (0x48) // these will all be signed #define ICM20948_MAG_REG_13 (0x49) // #define ICM20948_MAG_REG_14 (0x4A) // #define ICM20948_MAG_REG_15 (0x4B) // #define ICM20948_MAG_REG_16 (0x4C) // #define ICM20948_MAG_REG_17 (0x4D) // #define ICM20948_MAG_REG_18 (0x4E) // #define ICM20948_MAG_REG_19 (0x4F) // #define ICM20948_MAG_REG_20 (0x50) // #define ICM20948_MAG_REG_21 (0x51) /////////////////////////////// #define ICM20948_MAG_REG_22 (0x52) /////////////////////////////// // banks 1 & 2 currently not used /*********************************************/ /* Bank 3 register map with bit definitions: */ /*********************************************/ #define ICM20948_I2C_MST_CTRL (0x01) #define ICM20948_I2C_SLV0_ADDR (0x03) #define ICM20948_I2C_SLV0_REG (0x04) #define ICM20948_I2C_SLV0_CTRL (0x05) #define ICM20948_I2C_SLV0_CTRL_BIT_ENABLE_MAG 0x80 #define ICM20948_I2C_SLV0_DO (0x06) /*********************************************/ /* Magnetometer internal register map: */ /*********************************************/ // these are not directly accessible #define ICM20948_MAG_INTERNAL_REG_WHO_AM_I (0x01) // expected 0x09 #define ICM20948_MAG_INTERNAL_REG_STATUS_1 (0x10) #define ICM20948_MAG_INTERNAL_REG_X_DATA_L (0x11) #define ICM20948_MAG_INTERNAL_REG_X_DATA_H (0x12) #define ICM20948_MAG_INTERNAL_REG_Y_DATA_L (0x13) #define ICM20948_MAG_INTERNAL_REG_Y_DATA_H (0x14) #define ICM20948_MAG_INTERNAL_REG_Z_DATA_L (0x15) #define ICM20948_MAG_INTERNAL_REG_Z_DATA_H (0x16) #define ICM20948_MAG_INTERNAL_REG_STATUS_2 (0x18) #define ICM20948_MAG_INTERNAL_REG_CTRL_2 (0x31) #define ICM20948_MAG_INTERNAL_REG_CTRL_2_RESET 0x00 // power down mode #define ICM20948_MAG_INTERNAL_REG_CTRL_2_BIT_SINGLE_MEASURE 0x01 // single measurement mode #define ICM20948_MAG_INTERNAL_REG_CTRL_2_BIT_100KHZ 0x08 // 100kHz continuous measurement mode #define ICM20948_MAG_INTERNAL_REG_CTRL_3 (0x32) #define ICM20948_MAG_INTERNAL_REG_CTRL_3_MAG_SOFT_RESET 0x01 // delay 100 ms after RESET MAGNETOMETER INTERNAL REGISTERS
prj.conf:
CONFIG_I2C=y CONFIG_SENSOR=y CONFIG_CBPRINTF_FP_SUPPORT=y CONFIG_LOG=y CONFIG_RESET_ON_FATAL_ERROR=n
DT overlay:
&pinctrl { // pinctrl node is used to specify I/O pin assignment and properties i2c0_default: i2c0_default { group1 { psels = <NRF_PSEL(TWIM_SDA, 0, 26)>, <NRF_PSEL(TWIM_SCL, 0, 27)>; bias-pull-up; }; }; i2c0_sleep: i2c0_sleep { group1 { psels = <NRF_PSEL(TWIM_SDA, 0, 26)>, <NRF_PSEL(TWIM_SCL, 0, 27)>; low-power-enable; }; }; }; &i2c0 { compatible = "nordic,nrf-twim"; status = "okay"; clock-frequency = <(I2C_BITRATE_STANDARD)>; pinctrl-0 = <&i2c0_default>; pinctrl-1 = <&i2c0_sleep>; pinctrl-names = "default", "sleep"; icm20948: icm20948@69 { reg = < 0x69 >; sda-pin = < 26 (GPIO_PULL_UP) >; scl-pin = < 27 (GPIO_PULL_UP) >; }; };
VCOM output:
_______________________________
*** Booting nRF Connect SDK v2.5.0 ***
_______________________________
I2C bus i2c@40003000 is ready!
Who am I: 0xEA
_______________________________
ICM-20948 reset successful.
_______________________________
I2C bus i2c@40003000 is ready!
Who am I: 0xEA
_______________________________
Reset magnetometer I2C master module!
IMU enabled as I2C master.
start of mag whoami
post zeroes checks:
ICM-20948 bank 3 register 0x03 value : 0x00
ADDR expect 0x00
ICM-20948 bank 3 register 0x04 value : 0x00
REG expect 0x00
ICM-20948 bank 3 register 0x06 value : 0x00
DO expect 0x00
ICM-20948 bank 3 register 0x05 value : 0x00
CTRL expect 0x00
post setup checks:
ICM-20948 bank 3 register 0x03 value : 0x00
ADDR expect 0x0C
ICM-20948 bank 3 register 0x04 value : 0x00
REG expect 0x31
ICM-20948 bank 3 register 0x06 value : 0x08
DO expect 0x08
ICM-20948 bank 3 register 0x05 value : 0x01
CTRL expect 0x81
start of read whoami:
ICM-20948 bank 3 register 0x03 value : 0x80
ADDR expect 0x8C
ICM-20948 bank 3 register 0x04 value : 0x00
REG expect 0x01
WHO AM I?!?!?! (mag): 0x00
...(expected ID 0x09)
end of mag whoami
Entered main loop.
End of program.
----------------------------------------