1
\$\begingroup\$

I am using two STM32F303K8 Nucleos and hooked up the SDA/SCL (GPIO B PIN 6 & 7) pins to 3.3V across roughly 4.7K resistors to 3V3.

When I check the voltage on the pins, both are 3V3 so it's not locking up and it's sitting idle yet the I2C (or HAL at least) keeps telling me it's busy.

I took most of the code out of the CubeMX example using HAL 1.10.

This is the main loop for it

while(true)
{
    #ifdef MASTER_BOARD
    do
    {
        if(HAL_I2C_Master_Transmit_IT(&I2CHandle, (uint16_t)0x30F, (uint8_t*)aTxBuffer, 7) != HAL_OK)
        {
            Error_Handler();
        }

        while(HAL_I2C_GetState(&I2CHandle) != HAL_I2C_STATE_READY)
        {

        }
    }while(HAL_I2C_GetError(&I2CHandle) == HAL_I2C_ERROR_AF);
    HAL_Delay(1000);
    #else
    do
    {
        if(HAL_I2C_Slave_Receive_IT(&I2CHandle, (uint8_t*)aRxBuffer, 7) != HAL_OK)
        {
            Error_Handler();
        }

        while(HAL_I2C_GetState(&I2CHandle) != HAL_I2C_STATE_READY)
        {

        }
    }while(HAL_I2C_GetError(&I2CHandle) == HAL_I2C_ERROR_AF);
    #endif
}

This is the initialization (HAL_I2C_Init() calls the HAL_I2C_MspInit() function internally)

bool InitI2C()
{

I2CHandle.Instance = I2C1;
I2CHandle.Init.Timing          = 0x00400B27;
I2CHandle.Init.OwnAddress1     = 0x30F;
I2CHandle.Init.AddressingMode  = I2C_ADDRESSINGMODE_10BIT;
I2CHandle.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
I2CHandle.Init.OwnAddress2     = 0xFF;
I2CHandle.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
I2CHandle.Init.NoStretchMode   = I2C_NOSTRETCH_DISABLE;

if(HAL_I2C_Init(&I2CHandle) != HAL_OK)
    return false;

HAL_I2CEx_ConfigAnalogFilter(&I2CHandle, I2C_ANALOGFILTER_ENABLE);
return true;
}

The MspInit function along with the I2C interrupts

extern "C"
{
void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c)
{
    GPIO_InitTypeDef  GPIO_InitStruct;
    RCC_PeriphCLKInitTypeDef  RCC_PeriphCLKInitStruct;

    /*##-1- Configure the I2C clock source. The clock is derived from the SYSCLK #*/
    RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2C1;
    RCC_PeriphCLKInitStruct.I2c1ClockSelection = RCC_I2C1CLKSOURCE_SYSCLK;
    HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);

    /*##-2- Enable peripherals and GPIO Clocks #################################*/
    /* Enable GPIO TX/RX clock */
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /* Enable I2Cx clock */
    __HAL_RCC_I2C1_CLK_ENABLE(); 

    /*##-3- Configure peripheral GPIO ##########################################*/  
    /* I2C TX GPIO pin configuration  */
    GPIO_InitStruct.Pin       = GPIO_PIN_6;
    GPIO_InitStruct.Mode      = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull      = GPIO_NOPULL;
    GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* I2C RX GPIO pin configuration  */
    GPIO_InitStruct.Pin       = GPIO_PIN_7;
    GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /*##-4- Configure the NVIC for I2C ########################################*/   
    /* NVIC for I2Cx */
    HAL_NVIC_SetPriority(I2C1_ER_IRQn, 1, 1);
    HAL_NVIC_EnableIRQ(I2C1_ER_IRQn);
    HAL_NVIC_SetPriority(I2C1_EV_IRQn, 1, 2);
    HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
}

void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *I2cHandle)
{
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
}

void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *I2cHandle)
{
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
}

void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *I2cHandle)
{
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
}

void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *I2cHandle)
{
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
}
}

Yes the Msp function triggers because I tested it with an LED toggle. The issue is that the interrupts never fire off (which would toggle an LED upon complete transmission) and both the master and slave hang on this

while(HAL_I2C_GetState(&I2CHandle) != HAL_I2C_STATE_READY)

It doesn't matter which boots up first, or if at the same time. It just hangs saying it's busy.

Everywhere I am reading people are always having issues setting this up and nobody has any valid solution.

Edit 1:

So I got it to a state where the SDA pin is high and SCL is low. I am not sure what this means, did it get stuck trying to figure out the ACK? All that happens after the master transmits this is that it gets stuck in the busy loop and so does the receiver. But it only happens when the master boots up after the slave. If the slave boots up first both lines will remain high.

Edit 2:

Figured it out, read below.

\$\endgroup\$
1
\$\begingroup\$

Alright I found out the issue.

  1. I forgot to add the interrupt handlers for the I2C. I enabled the interrupts but never added the handlers, somehow missed it when reading through.

    //If you are using C++ like me remember to wrap these around extern "C" {}
    void I2C1_EV_IRQHandler(void)
    {
        HAL_I2C_EV_IRQHandler(&I2CHandle); //I2CHandle is your I2C_HandleTypeDef object
    }
    
    void I2C1_ER_IRQHandler(void)
    {
        HAL_I2C_ER_IRQHandler(&I2CHandle);
    }
    
  2. The GPIO pins need to be configured as pull-up

    GPIO_InitStruct.Pin       = GPIO_PIN_6;
    GPIO_InitStruct.Mode      = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull      = GPIO_PULLUP; //<------------- here
    GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
    GPIO_InitStruct.Pin       = GPIO_PIN_7;
    GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    

It seems GPIO_NOPULL doesn't work at all. The slave never recognizes the masters request and just chills around. But after changing it to GPIO_PULLUP it works however I remember reading that you shouldn't do that but whatever.

\$\endgroup\$
  • \$\begingroup\$ Why not? You can use internal pullup, as long as the bus is expected to be on 3V3 level, not 5V. Normally, on multi-slave bus you should add external pullups with value depending on amount of devices and their sink currents, but in single-slave configuration using MCU internal pullups should be fine \$\endgroup\$ – stiebrs Apr 21 at 19:08
0
\$\begingroup\$

I have bumped into this myself at some point. Can't remember the workaround I did, but this might help

\$\endgroup\$
  • \$\begingroup\$ I tried everything on that page, nothing works. The flag can clear and I can reset the I2C but then the error handler kicks in right after. \$\endgroup\$ – nurtul Apr 18 at 23:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.