cancel
Showing results for 
Search instead for 
Did you mean: 

I2C HAL_BUSY Debugging Help

Liferafter
Associate III

Currently I am trying to interface with a LM75A temperature sensor using the STM32F446RE nucleo board.

From the datasheet of the LM75A, I gathered that the sensor, returns a temperature measurement without any configuration.

So I tried to simply hook up the LM75A (with floating Address Pins) and connecting it to the I2C1 port on the STM32.

Addressing the slave on the given default address of 0x48h

 

The code below returns HAL_BUSY, and locks up. Since I do not have a Logic Analyzer at home, I cannot manually debug what is going on.

 

#include "COM_Driver.h"
#include "LM75A_Driver.h"
#include <string.h>
int main(){

  LM75_Init();
  COM_Init();

  uint8_t data[2];
  float temperature;
  char tes[8];
  while (1)
  {
    HAL_StatusTypeDef status = LM75_ReadReg(LM75A_ADDRESS, LM75_TEMP_REG, data, 2);
    if (status != HAL_OK)
    {
      // Handle I2C communication error
      continue; // Skip to the next iteration of the loop
    }

    temperature = (float)((data[0] << 😎 | data[1]) / 256;
    sprintf(tes, "%f\n", temperature);
    COM_printf(tes);
    COM_printf("Hi!");

    HAL_Delay(1000);

  }
}

LM75 Driver is simply an abstraction of HAL_MASTER_TRANSMIT()

#include "LM75A_Driver.h"

I2C_HandleTypeDef hi2c;

// Initialize I2C peripheral and GPIO pins
void LM75_Init(void)
{

    // Enable clock for I2C peripheral and GPIO port
    I2Cx_CLK_ENABLE();
    I2Cx_GPIO_CLK_ENABLE();

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // Configure SCL, SDA pins as alternate function
    GPIO_InitStruct.Pin = I2Cx_SCL_PIN | I2Cx_SDA_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = I2Cx_AF;

    __HAL_RCC_GPIOB_CLK_ENABLE();

    HAL_GPIO_Init(I2Cx_GPIO_PORT, &GPIO_InitStruct);

    __HAL_RCC_I2C1_CLK_ENABLE();

    // I2C peripheral configuration
    hi2c.Instance = I2Cx;
    hi2c.Init.ClockSpeed = 300000; // Adjust as needed
    hi2c.Init.DutyCycle = I2C_DUTYCYCLE_2;
    hi2c.Init.OwnAddress1 = 0;
    hi2c.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    hi2c.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
    hi2c.Init.OwnAddress2 = 0;
    hi2c.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
    hi2c.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;

    if (HAL_I2C_Init(&hi2c) != HAL_OK)
    {
        // Initialization Error
        Error_Handler();
    }
}

// Write data to an I2C device
HAL_StatusTypeDef LM75_WriteReg(uint8_t device_address, uint8_t register_address, uint8_t *data, uint16_t size)
{
    if (register_address == LM75_TEMP_REG || register_address == LM75_INFO_REG)
    {
        return HAL_ERROR;
    }

    return HAL_I2C_Master_Transmit(&hi2c, device_address, data, size, HAL_MAX_DELAY);
}

// Read data from an I2C device
HAL_StatusTypeDef LM75_ReadReg(uint8_t device_address, uint8_t register_address, uint8_t *data, uint16_t size)
{
    return HAL_I2C_Master_Receive(&hi2c, device_address, data, size, HAL_MAX_DELAY);
}

And COM_printf() is a implemented function to print the temperature data to my computers terminal! There are no bugs in that piece of code.

Also I have not handled the HAL Errors since this is a prototype code.

If you have any suggestions from my code code that would be much appreciated.


1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Guru

Use HAL_I2C_IsDeviceReady to verify your slave address is being acknowledged. It should return HAL_OK. If it does not, recheck your address and wiring. A 7-bit address should be left-shifted by one bit.

Note that your LM75_ReadReg function doesn't use register_address at all so is not likely to be implemented correctly.

Consider using HAL_I2C_Mem_Read and HAL_I2C_Mem_Write instead.

If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

8 REPLIES 8
TDK
Guru

Use HAL_I2C_IsDeviceReady to verify your slave address is being acknowledged. It should return HAL_OK. If it does not, recheck your address and wiring. A 7-bit address should be left-shifted by one bit.

Note that your LM75_ReadReg function doesn't use register_address at all so is not likely to be implemented correctly.

Consider using HAL_I2C_Mem_Read and HAL_I2C_Mem_Write instead.

If you feel a post has answered your question, please click "Accept as Solution".
TDK
Guru

> with floating Address Pins

This means the address is not defined. Pins should be grounded or tied to 3.3V to establish a 0 or 1 for those bits.

TDK_0-1706497501339.png

 

If you feel a post has answered your question, please click "Accept as Solution".

My five cents (as TDK has mentioned):

verify what the I2C Slave Address format is:
Sometimes datasheet, schematics... give you a slave address based on 8bit (where bit 0 is actually RD/nWR).

Sometimes they give you a 7bit Slave Addr, without the bit 0 considered.

My suggestion:
Try the given slave address, e.g. 0x48 and try also to shift this address <<1 or >>1. e.g. 0x90 vs. 0x24.
Sometimes, it is confusing to know if Slave Address is given as 8bit or 7bit address (with or without the bit 0).
But you should get a NACK if address is wrong. Verify which error code you get from I2C driver.

Hi!

Thank you for taking the time to look at my issue!

I just soldered the address pins to point to address 0x48 today (Soldered all Address pins directly to ground). I utilized the same code, as I was using before, and the problem has not gone away. 

I also looked at my implementation of LM75_ReadReg, as mentioned by @TDK , and the problem still stands. The address was defined in a .h file for the LM75A driver (passed through by the calling function), and the function call in LM75A .c file looked something like this:

 

HAL_StatusTypeDef LM75_ReadReg(uint8_t device_address, uint8_t register_address, uint8_t *data, uint16_t size)
{
    return HAL_I2C_Master_Receive(&hi2c, device_address, data, size, HAL_MAX_DELAY);
}

 

I know that some of the parameters in the LM75_ReadReg are unneeded, like the register address, the only reason it is there is to facilitate some future logic to handle reading different registers.

I tried multiple different addresses, tried shifting both left and right by 1, but never got anything other than HAL_BUSY.

I tried to debug it using the STLink on the nucleo board, but found that after the first iteration of the loop where the I2C port indicates a HAL_Busy, the second iteration completely freezes the program. There is no output to the stlink registers, and there nothing being printed.

Have you used breakpoints in order trace down where the first time the error happens (and why)?

Do you have pull-ups for I2C?
Can it be, that no pull-ups there (cannot work) and the I2C device sees always "I2C bus is busy" (allocated, SCK floating or permanent low, a short-cut on signals, ...).
Or the external I2C slave (your LM74A) is not powered, is hold in reset, does not have a clock...?

Do you have a scope picture? (is there anything coming out?) But if already "BUSY" from the beginning - nothing will be sent. Try to find the source code line where it returns the first time with "BUSY" (deep down in the function call tree). It should give you a clue.

Is it BUSY already the very first time when you launch the FW (after reset)?
(maybe the driver lock variables are in a wrong state, already BUSY from the beginning, had to say why: often stack size is too small, a corrupted memory...).

Does HAL_I2C_IsDeviceReady return HAL_OK?

Look at the code that returns HAL_BUSY and understand why that gets returned. If the state of the peripheral is not ready, figure out why and correct that. Look at hi2c->State and determine what state it's in and how it got there.

If you feel a post has answered your question, please click "Accept as Solution".

Thank you again for responding, and I really do appreciate your help!

 

1) The LM75A board I am using for testing does have pull up resistors connected to the I2C SDA and SCL lines. However I am not too sure on the ratings for said resistors.

2) I will conduct some hardware tests tomorrow when I have access to a logic analyzer and a multimeter, to make sure there is a clock and power going to the chip

3) The error occurs due to a HAL_TIMEOUT, in the function: I2C_WaitOnFlagUntilTimeout()

Here's is the call stack:

 

 

 

I2C_WaitOnFlagUntilTimeout@0x08000e10 (ROOTDIR\CASE\Drivers\STM32F4xx_HAL_Driver\Src\stm32f4xx_hal_i2c.c:7226)
HAL_I2C_IsDeviceReady@0x08001614 (ROOTDIR\Drivers\STM32F4xx_HAL_Driver\Src\stm32f4xx_hal_i2c.c:3443)
LM75_ReadReg@0x08000ae6 (ROOTDIR\Drivers\Peripherals\Src\LM75A_Driver.c:68)
vTelemetryLM75AReadTask@0x0800076a (ROOTDIR\App\Tasks\Src\vTelemetry.c:39)
main@0x08000728 (ROOTDIR\App\Src\main.c:52)

 

 

I2C_WaitOnFlagUntilTimeout():

 

 

 

static HAL_StatusTypeDef I2C_WaitOnFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Flag, FlagStatus Status, uint32_t Timeout, uint32_t Tickstart)
{
  /* Wait until flag is set */
  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;
        }
      }
    }
  }
  return HAL_OK;
}

 

Here on the second iteration of my main loop, when the function listed above is called a second time, it becomes stuck, never reaching timeout. HAL_GetTick() always returns 50. This, I think, isn't an issue due to the debugger, since the issue is still there when not using the stlink. 

the hi2c structure, always has HAL_I2C_State_Ready(). Even when going through and after I2C_WaitOnFlagUntilTimeout()

The HAL_I2C_IsDeviceReady like the HAL_I2C_Master_Receive() also returns a HAL_BUSY. As mentioned in my other reply the flag happens due to a failed reset on the Busy Flag in HAL_I2C_IsDeviceReady(). 

The Busy Flag isn't reset before reaching timeout.

if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG, tickstart) != HAL_OK)
    {
      return HAL_BUSY;
    }

Something similar happens in the HAL_I2C_Master_Receive().

I am increasingly suspicious of there being some issue in my hardware, and will verify this tomorrow.

Thank you again for taking the time to look at my problem.