cancel
Showing results for 
Search instead for 
Did you mean: 

I2C Working Inconsistently

AlexanderKibarov
Associate III

I am trying to read data from an MPU6050 sensor using I2C with the STM32F413ZH board. However, I am encountering errors, likely because the levels of the SCL and SDA initialization and termination bits are incorrect. This causes the system to sometimes work correctly and other times result in errors. When the HAL_I2C_IsDeviceReady function returns an error value, I tried to deinitialize and reinitialize the I2C unit, but it did not work. Upon investigating the error, I found that it returns HAL_BUSY and enters the I2C_WaitOnFlagUntilTimeout function, where the I2C error code indicates a timeout issue. Specifically, in these sections:

 

 

if (hi2c->State == HAL_I2C_STATE_READY)
  {
    /* Wait until BUSY flag is reset */
    if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG, tickstart) != HAL_OK)
    {
      return HAL_BUSY;
    }

 

 

 

while (__HAL_I2C_GET_FLAG(hi2c, Flag) == Status)
  {
    /* Check for the Timeout */
    if (Timeout != HAL_MAX_DELAY)
    {
      if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U))
      {
        if ((__HAL_I2C_GET_FLAG(hi2c, Flag) == Status))
        {
          hi2c->PreviousState     = I2C_STATE_NONE;
          hi2c->State             = HAL_I2C_STATE_READY;
          hi2c->Mode              = HAL_I2C_MODE_NONE;
          hi2c->ErrorCode         |= HAL_I2C_ERROR_TIMEOUT;

          /* Process Unlocked */
          __HAL_UNLOCK(hi2c);

          return HAL_ERROR;
        }
      }
    }
  }

 

 

Later, as described in section 30.4.5 of RM0285, I attempted to release the SCL and SDA lines by setting the I2C_CR1_PE bit to 0 for 3 time cycles and then back to 1, but that also did not work. Here is that code:

 

 

void I2C_Software_Reset(I2C_HandleTypeDef *hi2c)
{
    hi2c->Instance->CR1 &= ~I2C_CR1_PE;

    while (hi2c->Instance->CR1 & I2C_CR1_PE);

    hi2c->Instance->CR1 |= I2C_CR1_PE;
}

 

 

I haven't fully understood the exact problem. Could you please help?

Here is the complete code:

 

 

#include "main.h"

I2C_HandleTypeDef hi2c1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);

HAL_StatusTypeDef hal_status_one;

uint8_t data_mpu[1] = {0x00};

void I2C_Software_Reset(I2C_HandleTypeDef *hi2c)
{
    hi2c->Instance->CR1 &= ~I2C_CR1_PE;

    while (hi2c->Instance->CR1 & I2C_CR1_PE);

    hi2c->Instance->CR1 |= I2C_CR1_PE;
}

int main(void)
{
  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  MX_I2C1_Init();
	uint8_t data_data[1] = {0x00};
	HAL_I2C_Mem_Write(&hi2c1, (0x68 << 1), 0x6B, 1, data_data, 1, 100);
  while (1)
  {
	  if(!HAL_I2C_IsDeviceReady(&hi2c1, (0x68 << 1), 4, 100)) {
		  hal_status_one = HAL_I2C_Mem_Read(&hi2c1, (0x68 << 1), 0x3C, 1, data_mpu, 1, 100);
	  }
	  else {
		  I2C_Software_Reset(&hi2c1);
	  }
	}
}
static void MX_I2C1_Init(void)
{
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 100000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOG_CLK_ENABLE();

  HAL_GPIO_WritePin(GPIOB, LD1_Pin|LD3_Pin|LD2_Pin, GPIO_PIN_RESET);

  HAL_GPIO_WritePin(USB_PowerSwitchOn_GPIO_Port, USB_PowerSwitchOn_Pin, GPIO_PIN_RESET);

  GPIO_InitStruct.Pin = USER_Btn_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(USER_Btn_GPIO_Port, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = LD1_Pin|LD3_Pin|LD2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = USB_PowerSwitchOn_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(USB_PowerSwitchOn_GPIO_Port, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = USB_OverCurrent_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(USB_OverCurrent_GPIO_Port, &GPIO_InitStruct);
}

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
Andrew Neil
Evangelist III

You can't fix a hardware problem in software.

If your unreliability problem is due to unreliable connections - which is quite likely with breadboards & long flying leads - there's not a lot you can do about that in software!

Your hardware needs to be reliable for the software to work on it reliably.

This is why I keep asking you to  observe what's happening on the I2C lines - with an oscilloscope !

View solution in original post

6 REPLIES 6
AlexanderKibarov
Associate III

NEW STEP:

void GenerateNineClockPulses(void) {
    HAL_StatusTypeDef status;
    uint32_t timeout = 1000;
    uint32_t clock_pulses = 0;

    HAL_I2C_DeInit(&hi2c1);

    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
    HAL_Delay(1);

    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
    HAL_Delay(1);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);

    while (clock_pulses < 9) {
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
        HAL_Delay(1);

        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
        HAL_Delay(1);

        clock_pulses++;
    }

    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);

    HAL_I2C_Init(&hi2c1);
}

 

I observed that it worked correctly when I added such a code, but I do not know whether the reason for this correct operation is random as before or whether it is really related to this code. 

AlexanderKibarov
Associate III

The problem is probably related to this, but I don't understand why the process I did here is not done within the state library itself.

Andrew Neil
Evangelist III

This seems like a continuation of this thread:

https://community.st.com/t5/stm32-mcus-embedded-software/stm32-i2c-not-working-with-other-interrupts/td-p/703781

 

Did you ever observe what's happening on the I2C lines - with an oscilloscope ?

As @TDK said in that thread, the HAL_BUSY return likely means that one/both line(s) is being held low - you would be able to see that on an oscilloscope.

I can't see that you ever confirmed what board you're using - was @Dazai correct in assuming NUCLEO-F413ZH ?

How is your I2C slave connected to the board? long trailing wires? breadboard? other??

Some good, clear photos of your setup would help.

Hi Andrew, first of all, thank you. Yes, the problem continued, but I think I have solved the problem now. I am leaving the code below that I left above. When I added this code, everything got better. Probably the reason for the error was that, as you said, the scl and sda lines did not start as they should. I will leave the code and picture below and the card I use is stm32f413zh.

 

void GenerateNineClockPulses(void) {
    HAL_StatusTypeDef status;
    uint32_t timeout = 1000;
    uint32_t clock_pulses = 0;

    HAL_I2C_DeInit(&hi2c1);

    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
    HAL_Delay(1);

    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
    HAL_Delay(1);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);

    while (clock_pulses < 9) {
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
        HAL_Delay(1);

        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
        HAL_Delay(1);

        clock_pulses++;
    }

    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);

    HAL_I2C_Init(&hi2c1);
}

 

 

WhatsApp Image 2024-08-01 at 5.15.28 PM.jpegWhatsApp Image 2024-08-01 at 5.15.29 PM.jpegWhatsApp Image 2024-08-01 at 5.15.f28 PM.jpeg

To be honest, what I don't understand is that I have seen many people who have encountered this problem before and solved it in this way. Although this is the case, why are no precautions taken in the state library beforehand?

And, Why did the code I made here not work? Because it was written in section 30.4.5 of RM0385 that it should work.

void I2C_Software_Reset(I2C_HandleTypeDef *hi2c)
{
    hi2c->Instance->CR1 &= ~I2C_CR1_PE;

    while (hi2c->Instance->CR1 & I2C_CR1_PE);

    hi2c->Instance->CR1 |= I2C_CR1_PE;
}

 

Andrew Neil
Evangelist III

You can't fix a hardware problem in software.

If your unreliability problem is due to unreliable connections - which is quite likely with breadboards & long flying leads - there's not a lot you can do about that in software!

Your hardware needs to be reliable for the software to work on it reliably.

This is why I keep asking you to  observe what's happening on the I2C lines - with an oscilloscope !

Yes, I observed the lines and as I mentioned, scl and sda pins are not set correctly in start and spot condition. For this I added this function:

And when I delete this function it usually doesn't work but if it is added it always works.

void GenerateNineClockPulses(void) {
    HAL_StatusTypeDef status;
    uint32_t timeout = 1000;
    uint32_t clock_pulses = 0;

    HAL_I2C_DeInit(&hi2c1);

    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
    HAL_Delay(1);

    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
    HAL_Delay(1);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);

    while (clock_pulses < 9) {
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
        HAL_Delay(1);

        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);
        HAL_Delay(1);

        clock_pulses++;
    }

    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);

    HAL_I2C_Init(&hi2c1);
}