bolderflight / invensense-imu

Arduino and CMake library for communicating with the InvenSense MPU-6500, MPU-9250 and MPU-9255 nine-axis IMUs.
MIT License
509 stars 213 forks source link

Can't initialize I2C/SPI bus #120

Closed brightproject closed 1 year ago

brightproject commented 1 year ago

Microcontroller stm32f401ccu6 STM32-STM32F4-STM32F401-STM32F401CCU6-pinout-low-resolution

Connecting by I2c SDA-PB7, SCL-PB6 IMU MPU-9250

#include "mpu9250.h"

/* Mpu9250 object, I2C bus,  0x68 address */
bfs::Mpu9250 imu(&Wire, bfs::Mpu9250::I2C_ADDR_PRIM);

void wakeup() {
  Serial.println("Motion");
}

void setup() {
  /* Serial to display data */
  Serial.begin(115200);
  while(!Serial) {}
  /* Start the I2C bus */
  Wire.begin();
  Wire.setClock(400000);
  /* Initialize and configure IMU */
  if (!imu.Begin()) {
    Serial.println("Error initializing communication with IMU");
    while(1) {}
  }
  /* 
  * Enable wake on motion with a threshold of 40 mg and an accel data rate of
  * 15.63 Hz 
  */
  imu.EnableWom(40, bfs::Mpu9250::WOM_RATE_15_63HZ);
  /* Attach the interrupt to pin 9 */
  pinMode(9, INPUT);
  attachInterrupt(9, wakeup, RISING);
}

void loop() {}

I get the following in the port monitor:

PS C:\Users\Admin> arduino-cli monitor -p COM6 -c baudrate=115200
Monitor port settings:
baudrate=115200
Connected to COM6! Press CTRL-C to exit.
Error initializing communication with IMU

I check the presence of the module on the bus I2C using the code:

#include <Wire.h>

//UART1 TX - PA9, RX - PA10
#define SerialOne Serial1

void setup(){
    Wire.begin();

    SerialOne.begin(115200);
    while (!SerialOne);
    SerialOne.println("\nI2C Scanner");
} 

void loop(){
    byte error, address;
    int nDevices;

    SerialOne.println("Scanning...");

    nDevices = 0;
    for(address = 8; address < 127; address++ ){
        Wire.beginTransmission(address);
        SerialOne.print(address,HEX);
        SerialOne.println("+");
        error = Wire.endTransmission();

        if (error == 0){
            SerialOne.print("I2C device found at address 0x");
            if (address<16)
                SerialOne.print("0");
            SerialOne.print(address,HEX);
            SerialOne.println(" !");

            nDevices++;
        }
        else if (error==4) {
            SerialOne.print("Unknow error at address 0x");
            if (address<16)
                SerialOne.print("0");
            SerialOne.println(address,HEX);
        } 
    }
    if (nDevices == 0)
        SerialOne.println("No I2C devices found\n");
    else
        SerialOne.println("done\n");

    delay(1000);
}

I receive an address I2C device found at address 0x68 !

flybrianfly commented 1 year ago

Have you tried running an I2C scanner program on the microcontroller to verify that it can see the MPU-9250 on the I2C bus and check the I2C address?

brightproject commented 1 year ago

Have you tried running an I2C scanner program

Answered above

brightproject commented 1 year ago

Why in the example, the bus config I2Cis done like this imu.Config(&Wire, bfs::Mpu9250::I2C_ADDR_PRIM); And in the library in the class mpu9250.h like this Mpu9250(TwoWire *i2c, const I2cAddr addr ...&Wire...TwoWire

flybrianfly commented 1 year ago

TwoWire is the object type that Arduino defines for I2C. Somewhere in the core software for the board, there is some declaration like: TwoWire Wire(...); TwoWire Wire1(...);

For the library, we're just grabbing a pointer to the I2C bus in use, especially since many boards have multiple I2C buses.

I would also check the I2C address using the scanner to verify whether you should be using I2C_ADDR_PRIM or I2C_ADDR_SEC.

brightproject commented 1 year ago

Again checked the I2C address with a scanner. Скриншот 09-07-2023 15 19 30 I am using this core for adruino IDE. It has a wire.h library, and I had to remove the folder from the ARDUINO folder so that there is no conflict, using two libraries. Later, I returned the folder with the wire.h library to the ARDUINO folder, and prescribed the use of the library #include <Wire.h> and #include "Wire.h>" But the result is the same. It seems to me that the STM32 compiler does not understand instructions... static_cast I think problem in here

#if defined(ARDUINO)
#include <Arduino.h>
#include <Wire.h>
#include "SPI.h"
#else
#include <cstddef>
#include <cstdint>
#include "core/core.h"
#endif
#include "invensense_imu.h"  // NOLINT

Was this library tested on STM32? What core does this library use? Perhaps there is no correlation with initialization I2C? https://github.com/stm32duino/Arduino_Core_STM32/wiki/API#i2C

brightproject commented 1 year ago

@flybrianfly If commented section while in code

  if (!imu.Begin()) {
    Serial.println("Error initializing communication with IMU");
    //while (1) {}
  }
  /* Set the sample rate divider */
  if (!imu.ConfigSrd(19)) {
    Serial.println("Error configured SRD");
    //while (1) {}
  }

I see a partial execution of the library code

1       0       0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    32.58
1       0       0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    32.39
1       0       0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    32.39
1       0       0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    32.63
1       0       0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    32.63
1       0       0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    32.63
1       0       0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    32.58
1       0       0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    32.58
1       0       0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    32.73

Скриншот 09-07-2023 21 28 45 Unit, apparently Serial.print(imu.new_imu_data()); And the numbers changing at the end, this is apparently Serial.print(imu.die_temp_c()); If disconnect MPU-9250 from SDA/SCL. Then the output stops and lines appear.

Error initializing communication with IMU
Error configured SRD

I don't understand what is happening and why the library cannot configure i2c. Somehow, magically, the library reads the temperature values die_temp_c() and new data new_imu_data() These values are obtained from MPU-9250 by i2c or not?

brightproject commented 1 year ago

The only library that we managed to launch for STM32 and arduino IDE. I2C did not even specify contacts, the library itself determined them. Скриншот 09-07-2023 23 19 55

Accel: -0.01, 0.09, 1.01 g
Gyro: -4.57, 4.51, 1.65 dps
Mag: 0.00, 0.00, 0.00 uT
Time: 118769 ms

Accel: -0.02, 0.12, 1.01 g
Gyro: -4.70, 2.99, 2.01 dps
Mag: 0.00, 0.00, 0.00 uT
Time: 120776 ms

Accel: -0.02, 0.14, 1.01 g
Gyro: -4.45, 3.54, 2.01 dps
Mag: 0.00, 0.00, 0.00 uT
Time: 122785 ms

Accel: -0.04, 0.17, 1.00 g
Gyro: -4.76, 2.99, 2.01 dps
Mag: 0.00, 0.00, 0.00 uT
Time: 124796 ms

Questions only to your library - apparently it is not optimized for stm32f401ccu6. And in this library added compatibility Mode with a ESP32. Could you change the library to work with stm32 or tell me what could be the reason for initializing(problems with initialization) i2c? I also ger Quaternion from mpu9250 by using this sketch Скриншот 10-07-2023 15 48 13

Q: 0.9695, -0.0162, 0.1011, 0.2225
R/P/Y: 4.46, 338.23, 333.72
Time: 1852857 ms

Q: 0.9696, -0.0173, 0.1005, 0.2224
R/P/Y: 4.57, 338.42, 333.73
Time: 1852957 ms

I just don’t understand yet, it seems like the quaternion cannot be calculated without a magnetometer, but I couldn’t read it from the mpu9250 module ... or in the quaternion example, it somehow magically pulled out of the module.

brightproject commented 1 year ago

I connected the I2C bus to logic analyzer and began to see what was happening. When I uploaded the sketch to the stm32f401ccu6 board, I connect it to port com 6. Nothing happens until you hit reset on board. Packet signals appear on the I2C bus. Скриншот 10-07-2023 15 51 46 The first three messages begin with writing the address of the sensor 0x69 to the mpu9250 sensor and receiving two responses 0x6B and 0x01 Скриншот 10-07-2023 15 54 56 These are the registers of the mpu9250 sensor

PWR_MGMNT_1_ = 0x6B;
CLKSEL_PLL_ = 0x01;

Then 7 parcels follow. Скриншот 10-07-2023 16 00 31

Write [0x69] + ACK
0x6B + ACK

PWR_MGMNT_1_ = 0x6B;

Read [0x69] + ACK
0x01 + NAK

INT_RAW_RDY_EN_ = 0x01;

Write [0x69] + ACK
0x6A + ACK
0x20 + ACK

USER_CTRL_ = 0x6A; I2C_MST_EN_ = 0x20; This is also followed by parcels, as I understand it, setting up the mpu9250 registers for work. But everywhere the reading Write [0x69] from the sensor ends with NAK The package ends with such parcels. Скриншот 10-07-2023 16 07 00 Well, in the port monitor there is a traditional inscription that the sensor has not been initialized. Скриншот 10-07-2023 16 08 56 Each line is a separate pressing of the RESET button on the BlackPill board and, accordingly, sending those packets that I described above. Strange ... the library SparkFun is written generally for SAMD boards, even when compiling it, a warning appears that the library may not work ... but it works and works fine. WARNING: The SparkFun_MPU-9250-DMP_Arduino_Library-master library must run on SAMD architectures and may not be compatible with your stm32 board.

The library invensense-imu does not issue any warnings, it compiles perfectly, communicates with the mpu9250 sensor via I2C and does not work ... a paradox!

I want to deal with the problem, but so far my knowledge is not enough ...

brightproject commented 1 year ago

I continue experiments, since the author of the library does not want to figure it out. I tried to connect the MPU9260 module to the ESP32 via I2C. The situation is the same as with stm32f401ccu6. As I already wrote, the MPU9260 module works fine with the SparkFun library, and even gives out a quaternion - therefore, the library in some way can "get through" to the magnetometer, because, without a magnetometer signal, the quaternion cannot be calculated ... I could be wrong. After 3 days of torment with I2C, which for MPU9260 is generally an "experimental" interface, it was decided to connect via SPI. Connecting to stm32 did this:

PA7 - SDA(MOSI)
PA6 - AD0(MISO)
PA5 - SCL(SCK)
PA4 - NCS(CS)
BY RESISTOR 10 kOm TO GND - FSYNC

The SPI protocol starts from command 0x6B, 0x01 - WriteRegister(PWR_MGMNT_1_, CLKSEL_PLL_)...Further, there are also parcels on the SPI bus, but there is no normal operation. Скриншот 11-07-2023 15 25 51

bool Mpu9250::Begin() {
  imu_.Begin();
  /* 1 MHz for config */
  spi_clock_ = SPI_CFG_CLOCK_;
  /* Select clock source to gyro */
  if (!WriteRegister(PWR_MGMNT_1_, CLKSEL_PLL_)) {
    return false;
  }
  /* Enable I2C master mode */
  if (!WriteRegister(USER_CTRL_, I2C_MST_EN_)) {
    return false;
  }
  /* Set the I2C bus speed to 400 kHz */
  if (!WriteRegister(I2C_MST_CTRL_, I2C_MST_CLK_)) {
    return false;
  }
  /* Set AK8963 to power down */
  WriteAk8963Register(AK8963_CNTL1_, AK8963_PWR_DOWN_);
  /* Reset the MPU9250 */
  WriteRegister(PWR_MGMNT_1_, H_RESET_);
  /* Wait for MPU-9250 to come back up */
  delay(1);
  /* Reset the AK8963 */
  WriteAk8963Register(AK8963_CNTL2_, AK8963_RESET_);
  /* Select clock source to gyro */
  if (!WriteRegister(PWR_MGMNT_1_, CLKSEL_PLL_)) {
    return false;
  }
  /* Check the WHO AM I byte */
  if (!ReadRegisters(WHOAMI_, sizeof(who_am_i_), &who_am_i_)) {
    return false;
  }
  if ((who_am_i_ != WHOAMI_MPU9250_) && (who_am_i_ != WHOAMI_MPU9255_)) {
    return false;
  }
  /* Enable I2C master mode */
  if (!WriteRegister(USER_CTRL_, I2C_MST_EN_)) {
    return false;
  }
  /* Set the I2C bus speed to 400 kHz */
  if (!WriteRegister(I2C_MST_CTRL_, I2C_MST_CLK_)) {
    return false;
  }
  /* Check the AK8963 WHOAMI */
  if (!ReadAk8963Registers(AK8963_WHOAMI_, sizeof(who_am_i_), &who_am_i_)) {
    return false;
  }
  if (who_am_i_ != WHOAMI_AK8963_) {
    return false;
  }
  /* Get the magnetometer calibration */
  /* Set AK8963 to power down */
  if (!WriteAk8963Register(AK8963_CNTL1_, AK8963_PWR_DOWN_)) {
    return false;
  }
  delay(100);  // long wait between AK8963 mode changes
  /* Set AK8963 to FUSE ROM access */
  if (!WriteAk8963Register(AK8963_CNTL1_, AK8963_FUSE_ROM_)) {
    return false;
  }
  delay(100);  // long wait between AK8963 mode changes
  /* Read the AK8963 ASA registers and compute magnetometer scale factors */
  if (!ReadAk8963Registers(AK8963_ASA_, sizeof(asa_buff_), asa_buff_)) {
    return false;
  }
  mag_scale_[0] = ((static_cast<float>(asa_buff_[0]) - 128.0f)
    / 256.0f + 1.0f) * 4912.0f / 32760.0f;
  mag_scale_[1] = ((static_cast<float>(asa_buff_[1]) - 128.0f)
    / 256.0f + 1.0f) * 4912.0f / 32760.0f;
  mag_scale_[2] = ((static_cast<float>(asa_buff_[2]) - 128.0f)
    / 256.0f + 1.0f) * 4912.0f / 32760.0f;
  /* Set AK8963 to power down */
  if (!WriteAk8963Register(AK8963_CNTL1_, AK8963_PWR_DOWN_)) {
    return false;
  }
  /* Set AK8963 to 16 bit resolution, 100 Hz update rate */
  if (!WriteAk8963Register(AK8963_CNTL1_, AK8963_CNT_MEAS2_)) {
    return false;
  }
  delay(100);  // long wait between AK8963 mode changes
  /* Select clock source to gyro */
  if (!WriteRegister(PWR_MGMNT_1_, CLKSEL_PLL_)) {
    return false;
  }
  /* Set the accel range to 16G by default */
  if (!ConfigAccelRange(ACCEL_RANGE_16G)) {
    return false;
  }
  /* Set the gyro range to 2000DPS by default*/
  if (!ConfigGyroRange(GYRO_RANGE_2000DPS)) {
    return false;
  }
  /* Set the DLPF to 184HZ by default */
  if (!ConfigDlpfBandwidth(DLPF_BANDWIDTH_184HZ)) {
    return false;
  }
  /* Set the SRD to 0 by default */
  if (!ConfigSrd(0)) {
    return false;
  }
  return true;
}

Played around with SPI modes in file invensense_imu.cpp Скриншот 11-07-2023 15 29 18

spi_->beginTransaction(SPISettings(spi_clock, MSBFIRST, SPI_MODE3));
//spi_->beginTransaction(SPISettings(spi_clock, MSBFIRST, SPI_MODE2));
//spi_->beginTransaction(SPISettings(spi_clock, MSBFIRST, SPI_MODE1));
//spi_->beginTransaction(SPISettings(spi_clock, MSBFIRST, SPI_MODE0));

Скриншот 11-07-2023 15 27 41\ Changed CPOL and CPHAinto logic analizer also. I study the datasheet, but there are no results yet. Скриншот 11-07-2023 16 06 49

Commented out code while(1) {} in section SETUP the lines again

  if (!imu.Begin()) {
    Serial.println("Error initializing communication with IMU");
    //while(1) {}
  }
  /* Set the sample rate divider */
  if (!imu.ConfigSrd(19)) {
    Serial.println("Error configured SRD");
    //while(1) {}
  }

In order for the MPU9250 module to start outputting data and get zeros again, except for the temperature sensor.

1       0       0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    32.01
1       0       0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    31.91
1       0       0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    32.01
1       0       0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    31.91
1       0       0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    31.86
1       0       0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    32.10

For the sake of interest, to make sure that the temperature data is fake, I cooled the MPU9250 chip with cold spray, and the temperature dropped to minus 28 degrees Celsius and then returned to room temperature. Скриншот 11-07-2023 15 05 29 Now the thought does not leave me - why does the MPU9250 module transmit data on the temperature of its chip in the I2C bus and via SPI, but does not give out data on the accelerometer, gyroscope and magnetometer? It's all about the library and the @flybrianfly can't help. Or just write that the library was created only for the Tinsy microcontroller and will not work anywhere else... Although where does the microcontroller Tinsy? The library does not work on ESP32 either. It seems to me that the case is in incorrect registers and calls in writeRegister and readRegisters into library. And also in the bfs namespace. Perhaps in the library it is necessary to perform a bit shift for addresses? 0x69<<1 or 0x68<<1 I have seen this in other libraries. Confusion with the 7-bit address for I2C. My head is completely confused with these codes ... But I don't have enough knowledge in programming to finalize the library code. Can I somehow use this library for the sensor GY-LSM6DS3? 6_osevoy-datchik-polozheniya-GY_LSM6DS3-_2v1_-akselerometr_-giroskop_ Because these torments with the MPU9250 chip, which has already been discontinued, are not worth the time spent. Which IMU module is currently modern and can be adapted for this library?

flybrianfly commented 1 year ago

Hi, rudeness will not be tolerated. I was traveling over the weekend to visit family and, always, my order of priority is to satisfy family and work obligations before working on open-source projects. Currently, we have a lot of work projects that are due soon - I've already triaged this issue and it's not something that can be easily fixed, since we don't even know what's wrong yet, so it will have to wait until I have a chance to dive deeper into the potential issue. This will likely be later this fall. I welcome a PR if you are able to solve the issue before then.

We use Teensy boards for development because they are well supported and follow the Arduino core guidelines; so I know that if I use a TwoWire method, it is working as intended. We've been working with these IMUs since they were released and the libraries have been working well for years now. Most issues with the library are due to:

  1. Poor core implementations by board manufacturers. For example, ESP32 works, but required a modification to one of the TwoWire methods because the ESP32 core didn't implement endTransmission(false) correctly.
  2. Trying to use the library with an unsupported IMU - this library only works with the MPU-6500 and MPU-9250. Others may be added in the future, but these are the InvenSense IMUs that are currently supported.
  3. Counterfeit IMUs and clones.

In addition to the Teensy boards, this library has bee used on the STM32L4 boards, ESP32, and AVR. I'm sure there are others that I haven't personally tested, but in general it should work well across Arduino boards.

enoch-prince commented 6 months ago

Still doesn't work on the ESP32 😒. Arduino ESP32 Core: v2.0.13 ESP32 by Espressif Core: v2.0.11

flybrianfly commented 6 months ago

@enoch-prince, can you give more information on the exact processor you're trying to use, the sensor, and the communication method? It should work if it's wired correctly and using a support chip that isn't counterfeit.