2017-09-25 05:56 PM
Hi there,
I am attempting to get an STM32F411CE microcontroller to interface with a BNO055 IMU using the I2C peripheral. This particular IMU uses clock stretching which doesn't seem to work with the HAL_I2C library that was auto-generated from STM32CubeMX. See image below:
I attached a second I2C device to the bus and was able to communicate with the library so I'm pretty sure it is only the clock stretching that is causing an issue.
As a reference, here is a waveform of an Atmel microcontroller interfacing with the same BNO055 device and it appears to be working as intended:
Any ideas on what could be causing this issue?
Thanks,
Kyle
Solved! Go to Solution.
2017-10-01 10:26 AM
It seems that the STM32CubeMX created a HAL_I2C_MspInit function with the wrong pins. Modifying it to PB7 and PB8 seems to make everything work as intended:
void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{GPIO_InitTypeDef GPIO_InitStruct;
if(hi2c->Instance==I2C1) { /* USER CODE BEGIN I2C1_MspInit 0 *//* USER CODE END I2C1_MspInit 0 */
/**I2C1 GPIO Configuration PB7 ------> I2C1_SDA PB8 ------> I2C1_SCL */ GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);/* Peripheral clock enable */
__HAL_RCC_I2C1_CLK_ENABLE(); /* USER CODE BEGIN I2C1_MspInit 1 *//* USER CODE END I2C1_MspInit 1 */
}}
2017-09-26 12:59 AM
How did you post those attachments? I am unable to access them.
Nevertheless, in 'F4 I2C the master clock stretching sense is governed by a parameter in the I2C_TRISE register, see 'SCL master clock generation' subchapter in I2C chapter.
JW
2017-09-26 08:45 AM
Hi JW,
Thanks for the quick response. I've re-uploaded the images so you should be able to see them now.
I tried modifying TRISE but it had no effect on the waveform. I made sure to modify with I2C disabled as per the reference manual. It looks like the peripheral is waiting until the I2C clock is released by the slave before continuing to cycle SCL, however it appears to ignore releasing the SDA line. Here is the code snippet of the I2C transmit function.
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{ uint32_t tickstart = 0x00U;/* Init tickstart for timeout management*/
tickstart = HAL_GetTick(); // __HAL_I2C_DISABLE(hi2c); hi2c->Instance->TRISE = 0x3F; // 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; }/* Process Locked */
__HAL_LOCK(hi2c);/* Check if the I2C is already enabled */
if((hi2c->Instance->CR1 & I2C_CR1_PE) != I2C_CR1_PE) { /* Enable I2C peripheral */ __HAL_I2C_ENABLE(hi2c); }/* Disable Pos */
hi2c->Instance->CR1 &= ~I2C_CR1_POS;hi2c->State = HAL_I2C_STATE_BUSY_TX;
hi2c->Mode = HAL_I2C_MODE_MASTER; hi2c->ErrorCode = HAL_I2C_ERROR_NONE;/* Prepare transfer parameters */
hi2c->pBuffPtr = pData; hi2c->XferCount = Size; hi2c->XferOptions = I2C_NO_OPTION_FRAME; hi2c->XferSize = hi2c->XferCount;/* Send Slave Address */
if(I2C_MasterRequestWrite(hi2c, DevAddress, Timeout, tickstart) != HAL_OK) { if(hi2c->ErrorCode == HAL_I2C_ERROR_AF) { /* Process Unlocked */ __HAL_UNLOCK(hi2c); return HAL_ERROR; } else { /* Process Unlocked */ __HAL_UNLOCK(hi2c); return HAL_TIMEOUT; } }/* Clear ADDR flag */
__HAL_I2C_CLEAR_ADDRFLAG(hi2c);while(hi2c->XferSize > 0U)
{ /* Wait until TXE flag is set */ if(I2C_WaitOnTXEFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK) { if(hi2c->ErrorCode == HAL_I2C_ERROR_AF) { /* Generate Stop */ hi2c->Instance->CR1 |= I2C_CR1_STOP; return HAL_ERROR; } else { return HAL_TIMEOUT; } }/* Write data to DR */
hi2c->Instance->DR = (*hi2c->pBuffPtr++); hi2c->XferCount--; hi2c->XferSize--;if((__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BTF) == SET) && (hi2c->XferSize != 0U))
{ /* Write data to DR */ hi2c->Instance->DR = (*hi2c->pBuffPtr++); hi2c->XferCount--; hi2c->XferSize--; }/* Wait until BTF flag is set */
if(I2C_WaitOnBTFFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK) { if(hi2c->ErrorCode == HAL_I2C_ERROR_AF) { /* Generate Stop */ hi2c->Instance->CR1 |= I2C_CR1_STOP; return HAL_ERROR; } else { return HAL_TIMEOUT; } }}
/* Generate Stop */
hi2c->Instance->CR1 |= I2C_CR1_STOP;hi2c->State = HAL_I2C_STATE_READY;
hi2c->Mode = HAL_I2C_MODE_NONE; /* Process Unlocked */ __HAL_UNLOCK(hi2c);return HAL_OK;
} else { return HAL_BUSY;}}2017-09-28 08:18 AM
Just an update. It appears the SCL line is not dropping low to finalize the transfer. As a fix, I added a function at the end of each data write to manually pull the SCL line low which seems to get the peripheral working again. Here is the fix:
static void i2c_tx_scl_fix(void)
{ uint16_t SCL_PIN = GPIO_PIN_8;// Re-configure SCL as input
GPIO_InitTypeDef gpio; gpio.Pin = SCL_PIN; gpio.Mode = GPIO_MODE_INPUT; gpio.Pull = GPIO_PULLUP; gpio.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, &gpio);// Wait until SCL line goes high
while(HAL_GPIO_ReadPin(GPIOB, SCL_PIN)==GPIO_PIN_RESET){}// Now configure SCL to output and force low to
// regain control of SDA gpio.Pin = SCL_PIN; gpio.Mode = GPIO_MODE_OUTPUT_OD; gpio.Pull = GPIO_NOPULL; gpio.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_WritePin(GPIOB, SCL_PIN, GPIO_PIN_RESET); HAL_GPIO_Init(GPIOB, &gpio);// Reset back to I2C configuration
gpio.Pin = SCL_PIN; gpio.Mode = GPIO_MODE_AF_OD; gpio.Alternate = GPIO_AF4_I2C1; gpio.Pull = GPIO_PULLUP; HAL_GPIO_WritePin(GPIOB, SCL_PIN, GPIO_PIN_RESET); HAL_GPIO_Init(GPIOB, &gpio);}and here is the updated HAL_I2C_Master_Transmit function:
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint32_t tickstart = 0x00U;/* Init tickstart for timeout management*/
tickstart = HAL_GetTick();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; }/* Process Locked */
__HAL_LOCK(hi2c);/* Check if the I2C is already enabled */
if((hi2c->Instance->CR1 & I2C_CR1_PE) != I2C_CR1_PE) { /* Enable I2C peripheral */ __HAL_I2C_ENABLE(hi2c); }/* Disable Pos */
hi2c->Instance->CR1 &= ~I2C_CR1_POS;hi2c->State = HAL_I2C_STATE_BUSY_TX;
hi2c->Mode = HAL_I2C_MODE_MASTER; hi2c->ErrorCode = HAL_I2C_ERROR_NONE;/* Prepare transfer parameters */
hi2c->pBuffPtr = pData; hi2c->XferCount = Size; hi2c->XferOptions = I2C_NO_OPTION_FRAME; hi2c->XferSize = hi2c->XferCount;/* Send Slave Address */
if(I2C_MasterRequestWrite(hi2c, DevAddress, Timeout, tickstart) != HAL_OK) { if(hi2c->ErrorCode == HAL_I2C_ERROR_AF) { /* Process Unlocked */ __HAL_UNLOCK(hi2c); return HAL_ERROR; } else { /* Process Unlocked */ __HAL_UNLOCK(hi2c); return HAL_TIMEOUT; } }/* Clear ADDR flag */
__HAL_I2C_CLEAR_ADDRFLAG(hi2c);while(hi2c->XferSize > 0U)
{ i2c_tx_scl_fix();/* Wait until TXE flag is set */
if(I2C_WaitOnTXEFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK) { if(hi2c->ErrorCode == HAL_I2C_ERROR_AF) { /* Generate Stop */ hi2c->Instance->CR1 |= I2C_CR1_STOP; return HAL_ERROR; } else { return HAL_TIMEOUT; } } /* Write data to DR */ hi2c->Instance->DR = (*hi2c->pBuffPtr++); hi2c->XferCount--; hi2c->XferSize--;if((__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BTF) == SET) && (hi2c->XferSize != 0U))
{ /* Write data to DR */ hi2c->Instance->DR = (*hi2c->pBuffPtr++); hi2c->XferCount--; hi2c->XferSize--; } /* Wait until BTF flag is set */ if(I2C_WaitOnBTFFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK) { if(hi2c->ErrorCode == HAL_I2C_ERROR_AF) { /* Generate Stop */ hi2c->Instance->CR1 |= I2C_CR1_STOP; return HAL_ERROR; } else { return HAL_TIMEOUT; } } }/* Generate Stop */
hi2c->Instance->CR1 |= I2C_CR1_STOP;hi2c->State = HAL_I2C_STATE_READY;
hi2c->Mode = HAL_I2C_MODE_NONE; /* Process Unlocked */ __HAL_UNLOCK(hi2c);i2c_tx_scl_fix();
return HAL_OK;
} else {return HAL_BUSY; }}I am still having trouble with the HAL_I2C_Master_Receive function. See waveform below:
Again, the peripheral seems to get stuck in a state that is preventing it from sending the next byte.
2017-10-01 10:26 AM
It seems that the STM32CubeMX created a HAL_I2C_MspInit function with the wrong pins. Modifying it to PB7 and PB8 seems to make everything work as intended:
void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{GPIO_InitTypeDef GPIO_InitStruct;
if(hi2c->Instance==I2C1) { /* USER CODE BEGIN I2C1_MspInit 0 *//* USER CODE END I2C1_MspInit 0 */
/**I2C1 GPIO Configuration PB7 ------> I2C1_SDA PB8 ------> I2C1_SCL */ GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);/* Peripheral clock enable */
__HAL_RCC_I2C1_CLK_ENABLE(); /* USER CODE BEGIN I2C1_MspInit 1 *//* USER CODE END I2C1_MspInit 1 */
}}
2017-10-02 01:28 AM
Hi
kyle.klumper
,Even if you consider that the problem is already fixed, it is interesting to understand its root cause, and fix it if possible.
So, could you please share the .ioc file leading to generate a wrong configuration for the I2C pins?
-Amel
BTW, instead of updating the title and write 'Solved', please use the feature 'mark as answered' or select one post as the correct answer. Refer to
https://community.st.com/0D50X00009bMM5DSAW
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2020-09-13 11:46 PM
Hi Kyle,
Thanks for your update. did I2C clock strectching got solved just with pin changes in HAL_I2C_MspInit()? or with the previous changes you did? can you pls clarify?
I just want to know whether I2C clock stretching is straight away idea with cube mx? or needs some manual modification. thanks.
BR,
Anantha R
2023-04-11 12:28 AM
Hi everyone, I have exactly the same problem with the stm32f405 microcontroller and reading BNO055 module with i2c.
How should to resolve this problem?
2023-04-11 10:57 AM
Have you tried the suggestions in the old posts above?
Post your configuration and initialization functions. And any scope or logic analyzer screen shots that show the issue.
2023-04-12 04:36 AM
Yes I tried.
My i2c configuration is:
And this is my read function:
int16_tBNO055_read(void)
{
uint8_t angleData[2];
int16_t angle;
uint8_t regAddr = 0x1A;
HAL_I2C_Master_Transmit(&hI2C, BNO055_ADDRESS, ®Addr, 1, 100);
HAL_I2C_Master_Receive(&hI2C, BNO055_ADDRESS, angleData, 2, 100);
angle = ((int16_t)(angleData[0] | angleData[1] << 8) / 16);
return angle;
}
I saw the line with a logic analyzer and then I thought may BNO055 keeps the lines low.