0

I have the following code for an accelerometer sensor, MPU6050.

#include "stm32f407xx.h"
#define MPU6050_ADDR 0xD0
#define SMPLRT_DIV_REG 0x19
#define ACCEL_CONFIG_REG 0x1C
#define ACCEL_XOUT_H_REG 0x3B
#define PWR_MGMT_1_REG 0x6B
#define WHO_AM_I_REG 0x75
int16_t x_read=0,y_read=0,z_read=0;
float x_accel,y_accel,z_accel;
uint8_t check,temp;
void GPIO_Config(void)
{
    RCC->AHB1ENR |=(1UL<<1);//Enable clock for port B
    GPIOB->MODER |=(2UL<<12);//PB6 to alternate function pin
    GPIOB->MODER |=(2UL<<14);//PB7 to alternate function pin
    GPIOB->PUPDR |=(0x5UL<<12);//set PB6 and 7 as pull up pins
    GPIOB->OTYPER |=(0x3UL<<6);//Set PB6 and 7 as open drain
    GPIOB->OSPEEDR |=(0xAUL<<12);//Set PB6 and 7 as high speed
    GPIOB->AFR[0] |= (0x44<<24);//Set PB6 and 7 to alternate function 4
}
void I2C_Config(void)
{
    RCC->APB1ENR |=(1UL<<21);//Enable I2C clock
    I2C1->CR1 |= (1UL<<15);//Reset I2C
    I2C1->CR1 &= ~(1UL<<15);
    I2C1->CR2 |=(16UL<<0);//Set peripheral clock at 16MHz
    I2C1->OAR1 |=(1UL<<14);//Should be set high
    I2C1->CCR |=(0x50UL<<0);//Set SCL as 100KHz
    I2C1->TRISE |=(17UL<<0);//Configure maximum rise time
    I2C1->CR1 |= (1UL<<0);//Enable I2C
}
void I2C_Start (void)
{
    I2C1->CR1 |= (1<<10);//Enable the ACK Bit
    I2C1->CR1 |= (1<<8);//Send the   start bit
    while (!(I2C1->SR1 & (1<<0)));//Wait for SB bit to set
}
void I2C_Write(uint8_t data)
{
    while (!(I2C1->SR1 & (1<<7)));//Wait till TX buffer is empty
    I2C1->DR = data;//Write data to I2C slave
    while (!(I2C1->SR1 & (1<<2)));//Wait till Byte transfer is completed
}
void I2C_Address (uint8_t Address)
{
    I2C1->DR = Address;  //  send the slave address
    while (!(I2C1->SR1 & (1<<1)));  // wait for ADDR bit to set
    temp = I2C1->SR1 | I2C1->SR2;  // read SR1 and SR2 to clear the ADDR bit
}
void I2C_Read (uint8_t Address, uint8_t *buffer, uint8_t size)
{
    int remaining = size;
    if (size == 1)
    {
        I2C1->DR = Address;  //  send the address
        while (!(I2C1->SR1 & (1<<1)));  // wait for ADDR bit to set
        I2C1->CR1 &= ~(1<<10);  // clear the ACK bit
        temp = I2C1->SR1 | I2C1->SR2;  // read SR1 and SR2 to clear the ADDR bit.... EV6 condition
        while (!(I2C1->SR1 & (1<<6)));  // wait for RxNE to set
        buffer[size-remaining] = I2C1->DR;  // Read the data from the DATA REGISTER
    }
    else
    {
        I2C1->DR = Address;  //  send the address
        while (!(I2C1->SR1 & (1<<1)));  // wait for ADDR bit to set
        temp = I2C1->SR1 | I2C1->SR2;  // read SR1 and SR2 to clear the ADDR bit
        while (remaining>2)
        {
            while (!(I2C1->SR1 & (1<<6)));  // wait for RxNE to set
            buffer[size-remaining] = I2C1->DR;  // copy the data into the buffer
            I2C1->CR1 |= 1<<10;  // Set the ACK bit to Acknowledge the data received
            remaining--;
        }
        while (!(I2C1->SR1 & (1<<6)));  // wait for RxNE to set
        buffer[size-remaining] = I2C1->DR;
        I2C1->CR1 &= ~(1<<10);  // clear the ACK bit
        I2C1->CR1 |= (1<<9);  // Stop I2C
        remaining--;
        while (!(I2C1->SR1 & (1<<6)));  // wait for RxNE to set
        buffer[size-remaining] = I2C1->DR;  // copy the data into the buffer
    }
}
void I2C_Stop (void)
{
    I2C1->CR1 |= (1<<9);  // Stop I2C
}
void configureLED(void)
{
    RCC->AHB1ENR |=(1UL<<3);  //Enable GPIOD clock
    GPIOD->MODER &= ~(0xFFUL<<12*2);
    GPIOD->MODER |= (0x55UL<<12*2);//Set Pins PD12-15 as output
}
void MPU_Write (uint8_t Address, uint8_t Reg, uint8_t Data)
{
    I2C_Start ();
    I2C_Address (Address);
    I2C_Write (Reg);
    I2C_Write (Data);
    I2C_Stop ();
}
void MPU_Read (uint8_t Address, uint8_t Reg, uint8_t *buffer, uint8_t size)
{
    I2C_Start ();
    I2C_Address (Address);
    I2C_Write (Reg);
    I2C_Start ();
    I2C_Read (Address+0x01, buffer, size);//To read, set LSB to 1
    I2C_Stop ();
}
void MPU6050_Init (void)
{
    uint8_t check;
    uint8_t Data;
    MPU_Read (MPU6050_ADDR,WHO_AM_I_REG, &check, 1);//Check device ID
if (check == 104)  // 0x68 will be returned by the sensor
{
    Data = 0;
    MPU_Write (MPU6050_ADDR, PWR_MGMT_1_REG, Data);//Power up the sensor
    Data = 0x07;
    MPU_Write(MPU6050_ADDR, SMPLRT_DIV_REG, Data);//Sampling rate of 1KHz
    Data = 0x00;
    MPU_Write(MPU6050_ADDR, ACCEL_CONFIG_REG, Data);//accelerometer range=+/- 2g
}

} void MPU6050_Read_Accel (void) { uint8_t Buf[6]; // Read 6 BYTES of data starting from ACCEL_XOUT_H register MPU_Read (MPU6050_ADDR, ACCEL_XOUT_H_REG, Buf, 6); x_read = (int16_t)(Buf[0] << 8 | Buf[1]); y_read = (int16_t)(Buf[2] << 8 | Buf[3]); z_read = (int16_t)(Buf[4] << 8 | Buf[5]); x_accel = x_read/16384.0; y_accel = y_read/16384.0; z_accel = z_read/16384.0; } int main () { configureLED(); GPIO_Config(); I2C_Config (); MPU6050_Init (); while (1) { MPU6050_Read_Accel (); } }

Initially STM32CubeIDE was able to show live expressions as dynamically changing, basically saying that circuit was working. It happened twice and now it doesn't work anymore, and I am unable to figure out why. A quick pause in debug mode showed me that it is stuck at this line:

while (!(I2C1->SR1 & (1<<1))); // wait for ADDR bit to set

The above line is in I2C_Address function. Is there a problem with the code ? Or connections ?

enter image description here

I made the above connection, except with STM32F4.

Edit: This is what STM32CubeIDE shows:

enter image description here

Edit: I have not included pullup resistors in the schematic, but I have used them. 4.7K for 5V

DaveFenner
  • 79
  • 7
  • The problem could be anywhere. Please investigate why the ADDR bit is not set, what other bits indicating any errors are set instead? How do you know the address byte was even transmitted, or the start condition? The code assumes no errors happen so it does not check if an operation was successful and why if it was unsuccessful. The reason could be the hardware though, we don't know the schematics of any of the modules so it is impossible to know if they can be directly wired together. The connections may be glitchy too. Lack of comments prevent verifying if the code does as expected. – Justme Nov 26 '23 at 13:48
  • Please use your oscilloscope or logic analyzer to check the actual signals on SCL and SDA. – the busybee Nov 27 '23 at 06:52
  • 1
    By not stating you had pull-ups to begin with, you invalidated the answers. Also, why on a 3.3V system, you put the pull-ups to 5V? Also you never said which modules those are so it's not even known if the hardware is built correctly. Please note, you simply don't have an STM32F407 and an MPU6050 as ICs with your own design, you are using mystery modules with unknown design, only containing those chips as part of the design. – Justme Nov 27 '23 at 07:07
  • Are the pull-ups really to 5V, rather than 3.3V? The STM32F407 datasheet shows the PB6 and PB7 pins are 5 V-tolerant I/Os, but internal pull-up and pull-down resistors must be disabled (the code enables pull-ups). However, the MPU6050 datasheet says the absolute maximum input voltage on the SCL and SDA pins is -0.5V to VDD + 0.5V. I.e. pull-ups to 5 V might have damaged the MPU6050. – Chester Gillon Nov 27 '23 at 23:21

2 Answers2

1

I²C needs pullup resistors on the bus lines, I see none in the schematic.

Otherwise you may need an oscilloscope or logic analyser...

Turbo J
  • 10,094
  • 1
  • 21
  • 28
1

It happened twice and now it doesn't work anymore, and I am unable to figure out why.

The other answer notes there are no external pull-up resistors shown on the bus lines nor on the schematics for the modules

The code contains in the question contains:

GPIOB->PUPDR |=(0x5UL<<12);//set PB6 and 7 as pull up pins

I.e. enables the internal pull-ups on the I2C pins. The STM32F407 datasheet shows the internal pull-ups are 40 kΩ (typical) / 50 kΩ (maximum).

Based upon Is there a correct resistance value for I2C pull-up resistors? the internal pull-ups are likely too weak for operation at 100 kHz, which could explain why the I2C communication initially worked but then stopped working.

Suggest try adding 4K7 or 10K external pull-ups to the I2C signals.

Chester Gillon
  • 3,690
  • 1
  • 11
  • 15
  • I did use 4.7K pullup resistors earlier, which is when the code worked, for a couple of seconds. I forgot to show in the schematic. – DaveFenner Nov 27 '23 at 06:42