stm32i2cmaster-slavehal

Can you change the slave i2c address during execution on a stm32 board?


It’s all in the title

I want to be able to change the device's i2c slave address upon reception of a certain i2c command. I'm currently using a nucleo 144 f207gz.

I've been trying to achieve this without success by calling HAL_I2C_Init after changing the OwnAddress1 field of the i2c handle.Init, I tried to call DeInit then Init, but all without success...

So is there a way to achieve this ? If so, how?


Solution

  • I finally managed to get something working. I was using interrupts and doing it wrong : the DeInit - Init has to be done in the RXCallback function. Here is a minimal working example:

    Using interrupts

    // [ CubeMX generated code ... ]
    uint8_t i2c_slave_address = 0x40;  
    uint8_t received_data [2]= {0};
    HAL_StatusTypeDef Change_I2C_Address(uint8_t new_address)
    {
        HAL_StatusTypeDef ret;
        HAL_I2C_DeInit(m_i2c_handle);
        hi2c1.Init.OwnAddress1 = addr << 1;
        ret = HAL_I2C_Init(m_i2c_handle);
        return ret;
    }
    int main(void)
    {
    
      HAL_Init();
      SystemClock_Config();
    
      MX_GPIO_Init();
      MX_I2C1_Init();
    
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      HAL_I2C_Slave_Receive_IT(&hi2c1, received_data, 2);
      while (1)
      {
    
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
      }
    }
    
    // [ CubeMX generated code ... ]
    
    void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)  {
        if (received_data[0] == 0xA5)
          { // Command to change address
            if(Change_I2C_Address(received_data[1]) != HAL_OK)
            {
                Error_Handler();
            }
            received_data[0] = 0;
          }
          else if (received_data[0] == 0x05)
          { // Command to blink
            HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, 1);
            HAL_Delay(5000);
            HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, 0);
            received_data[0] = 0;
          }
        HAL_I2C_Slave_Receive_IT(&hi2c1, received_data, 2);
    }
    

    Using blocking mode

    The main should look like this:

    HAL_StatusTypeDef Change_I2C_Address(uint8_t new_address) {
        HAL_StatusTypeDef ret;
        HAL_I2C_DeInit(m_i2c_handle);
        hi2c1.Init.OwnAddress1 = addr << 1;
        ret = HAL_I2C_Init(m_i2c_handle);
        return ret;
    }
    
    /* Main Function */
    int main(void) {
        HAL_Init();
        SystemClock_Config();
        MX_GPIO_Init();
        MX_I2C1_Init();
    
        while (1) {
            if (HAL_I2C_Slave_Receive(&hi2c1, received_data, 2, HAL_MAX_DELAY) == HAL_OK) {
                if (received_data[0] == 0xA5) {  // Command to change address
                    if(Change_I2C_Address(received_data[1]) != HAL_OK)
                    {
                        Error_Handler();
                    }
                }
            }
        }
    }