cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F767 I2C burst errors with Multiple slaves

giucanMT
Associate II

I've developed a board using an STM32F767ZI to manage a set of power supplies via PMBUS. I've connected 4 I2C buses, each with 5 slave devices (power supplies), managing a total of 20 slave devices.

My application continuously polls the slave devices to get their status. When I use a single I2C peripheral, I never get any read errors. However, when multiple I2C peripherals are running simultaneously, I get bursts of read errors on all 4 buses (though not all at the same time) approximately every 30 seconds to 1 minute.

Each I2C polling routine runs in a separate FreeRTOS task.

I've tried using the HAL drivers I2C in Polling, IRQ, and DMA modes. I've also tried using the I2C peripherals in PMBus mode (CubeMX Setup). The bus speed is 100KHz. I tested enabling one peripheral at a time and noticed that the frequency of errors increases as the number of enabled peripherals increases (no errors with only one I2C enabled, errors every 30s/1m with 4 I2C enabled).

Can anyone suggest what I should investigate?

Thanks

 

6 REPLIES 6
TDK
Super User

> noticed that the frequency of errors increases as the number of enabled peripherals increases

If the HAL_I2C functions return HAL_OK, the problem is likely elsewhere in your code. Look at the data flow between this function and where you observe it. Use blocking functions while debugging.

If you feel a post has answered your question, please click "Accept as Solution".
/**
 * @brief PMBus Command Read
 *  Addr: PMBus target address
 *  ComIdx: PMBus command index
 *  ReadData: Buffer to store read data
 * @return 0: OK, >1: Error
 */
uint8_t PMBusCommRead(uint8_t BusIdx, uint8_t Addr, uint8_t ComIdx, uint8_t *ReadData)
{

  HAL_StatusTypeDef HAL_Res;
  BaseType_t        SemaphoreFlag;
  uint8_t           DataI2C[PMBUS_MAX_BYTE];
  uint8_t           Comm;
  uint8_t           NumBytes;
  uint8_t           i;
  I2C_HandleTypeDef * I2C_Bus;

  switch(BusIdx)
  {
  	case PMBUS1:
  		I2C_Bus = &hi2c4;
		break;
  	case PMBUS2:
  		I2C_Bus = &hi2c3;
		break;
  	case PMBUS3:
  		I2C_Bus = &hi2c2;
		break;
  	case PMBUS4:
  		I2C_Bus = &hi2c1;
		break;
  }


  // Set I2C Semaphore
  if (I2C_Semaphore[BusIdx] != NULL)
  {
    SemaphoreFlag = xSemaphoreTake(I2C_Semaphore[BusIdx], 0);
    while (SemaphoreFlag != pdTRUE)
    {
      SemaphoreFlag = xSemaphoreTake(I2C_Semaphore[BusIdx], 0);
    }
  }

  Comm  = RCP1600Commands[ComIdx].Cmd;
  NumBytes = RCP1600Commands[ComIdx].Len;

  // Send SMBUS message
  HAL_Res=HAL_I2C_Master_Transmit(I2C_Bus, (uint16_t)(Addr), &Comm, 1,I2C_TO);

  if (HAL_Res != HAL_OK)
  {
	  //Release I2C Semaphore
	  if (I2C_Semaphore[BusIdx] != NULL)
	  {
	    xSemaphoreGive(I2C_Semaphore[BusIdx]);
	  }
	  return 1;
  }

  // Wait for SMBUS transfer end
  while (HAL_I2C_GetState(I2C_Bus) != HAL_I2C_STATE_READY)
  {

  }
  HAL_Res=HAL_I2C_Master_Receive(I2C_Bus, (uint16_t) Addr, DataI2C, (uint16_t) NumBytes,I2C_TO);

  if (HAL_Res != HAL_OK)
  {
	  //Release I2C Semaphore
	  if (I2C_Semaphore[BusIdx] != NULL)
	  {
	    xSemaphoreGive(I2C_Semaphore[BusIdx]);
	  }
	  return 1;
  }

  while (HAL_I2C_GetState(I2C_Bus) != HAL_I2C_STATE_READY)
  {
  }

  //Release I2C Semaphore
  if (I2C_Semaphore[BusIdx] != NULL)
  {
    xSemaphoreGive(I2C_Semaphore[BusIdx]);
  }

  for (i = 0; i < NumBytes; i++)
  {
  	ReadData[i] = DataI2C[i];
  }
  return 0;

}

 this is my code. I never get return different from HAL_OK. But sometimes the ReadDat a contains 0xFF or wrong value. Observing the I2C bus on oscilloscope sometimes the transmitted message is not correct but sometimes is right. I have no idea about the errors happen only when multiple tasks run the "PMBusCommRead" function.

> Observing the I2C bus on oscilloscope sometimes the transmitted message is not correct but sometimes is right.

In what way is it not correct? The chip can only read the data on the bus. Only one thread can use the I2C bus at a time. If multiple call it at the same, time, it won't work.

To me, it looks like your semaphore check is not sufficient. If the semaphore is already taken, it just ignores it.

If you feel a post has answered your question, please click "Accept as Solution".
uint8_t PMBusCommRead(uint8_t BusIdx, uint8_t Addr, uint8_t ComIdx, uint8_t *ReadData)
{

  HAL_StatusTypeDef HAL_Res;
  uint8_t           DataI2C[PMBUS_MAX_BYTE];
  uint8_t           Comm;
  uint8_t           NumBytes;
  uint8_t           i;
  I2C_HandleTypeDef * I2C_Bus;

  switch(BusIdx)
  {
		case PMBUS1:
			I2C_Bus = &hi2c4;
		break;
		case PMBUS2:
			I2C_Bus = &hi2c3;
		break;
		case PMBUS3:
			I2C_Bus = &hi2c2;
		break;
		case PMBUS4:
			I2C_Bus = &hi2c1;
		break;
		default:
			return 1;
		break;
  }


  // Set I2C Semaphore
  if (I2C_Semaphore != NULL)
  {
	if (xSemaphoreTake(I2C_Semaphore, pdMS_TO_TICKS(100)) != pdTRUE) {
		return 1;
	}
  }

  Comm  = RCP1600Commands[ComIdx].Cmd;
  NumBytes = RCP1600Commands[ComIdx].Len;

  // Send SMBUS message
  HAL_Res=HAL_I2C_Master_Transmit(I2C_Bus, (uint16_t)(Addr), &Comm, 1,I2C_TO);

  if (HAL_Res != HAL_OK)
  {
	  PMBus_ReleaseBus(BusIdx);  // Use cleanup function
	  return 1;
  }

  // Wait for ready
  if (WaitForI2CReady(I2C_Bus, BusIdx) != 0) {
      PMBus_ReleaseBus(BusIdx);  // Use cleanup function
      return 1;
  }

  HAL_Res=HAL_I2C_Master_Receive(I2C_Bus, (uint16_t) Addr, DataI2C, (uint16_t) NumBytes,I2C_TO);

  if (HAL_Res != HAL_OK)
  {
	  PMBus_ReleaseBus(BusIdx);  // Use cleanup function
	  return 1;
  }

  // Wait for receive complete
  if (WaitForI2CReady(I2C_Bus, BusIdx) != 0) {
      PMBus_ReleaseBus(BusIdx);  // Use cleanup function
      return 1;
  }

  // Copy data
  for (i = 0; i < NumBytes; i++)
  {
  	ReadData[i] = DataI2C[i];
  }

  PMBus_ReleaseBus(BusIdx);  // Success path

  return 0;

}

 I've changed to code to manage correctly the semaphore but, if different semaphores (one for every i2c peripherals) the code still doesn't work. I had wrongly supposed that calling the HAL_I2C with different peripheral doesn't need a "single" "global" I2C semaphore for all the I2C busses but for only the single bus.
Changing the code using single semaphore for all I2C peripherals (as attached) solves the issue.

Your code still has race conditions, and you've removed xSemaphoreGive entirely for some reason?

This isn't a HAL issue, it's a bug in your code.

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

Sorry, I've moved the xSemaphoreGive in the PMBus_ReleaseBus function. Please could you help me, are race conditions in my code? Where? Thanks in advance for your help.

void PMBus_ReleaseBus(uint8_t BusIdx) {
    if (BusIdx < PMBUS_NUM && I2C_Semaphore != NULL) {
        xSemaphoreGive(I2C_Semaphore);
    }
}

// Helper function to wait for I2C ready
uint8_t WaitForI2CReady(I2C_HandleTypeDef *I2C_Bus, uint8_t BusIdx) {
    TickType_t start_tick = xTaskGetTickCount();
    const TickType_t state_timeout = pdMS_TO_TICKS(50);

    while (HAL_I2C_GetState(I2C_Bus) != HAL_I2C_STATE_READY) {
        if ((xTaskGetTickCount() - start_tick) > state_timeout) {
            return 1;  // Timeout
        }
        taskYIELD();
    }
    return 0;
}