cancel
Showing results for 
Search instead for 
Did you mean: 

I2C communication with multiple slaves

RSy.1
Associate III

Hi guys may I ask for your inputs on the I2C communication. I am using STM32F103C8 MCU with 2 slaves on the I2C bus. 1st slave is PCF8574 IO expander and 2nd slave is MCP4725 DAC. I'm using CubeMX to generate my codes.

What I wanted to do is use 2 buttons. The first button is connected to STM32 and when toggled this enables/disables one of the PCF8574 IO. The second button connected to the STM32 is used to enable/disable the MCP4725 DAC. Both slaves are connected to the same I2C line.

Basically if I put both slaves together and place them in the while(1) loop everything works fine. However when I use the callback the 2 slaves tend to work intermittently and sometimes hang.

 MX_GPIO_Init();
 MX_DMA_Init();
 MX_I2C1_Init();  
 /* USER CODE BEGIN 2 */
HAL_I2C_Master_Transmit_IT(&hi2c1, 0xC0, buffer_I2C, 3);							
HAL_I2C_Master_Transmit_IT(&hi2c1, 0x40, buffer_PCF8574Write, 1);	
/* USER CODE END 2 */	
 
//call Master transfer complete callback
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)                  
{
	//define the pin combinations for the 2 buttons
     {
		if((HAL_GPIO_ReadPin(BUTTON1_LED_GPIO_Port, BUTTON1_LED_Pin)) && (!HAL_GPIO_ReadPin(BUTTON2_LED_GPIO_Port, BUTTON2_LED_Pin)))
		{
			buttonstate = 0xFF;		//PCF8574 out only
		}
		else if((!HAL_GPIO_ReadPin(BUTTON1_LED_GPIO_Port, BUTTON1_LED_Pin)) && (HAL_GPIO_ReadPin(BUTTON2_LED_GPIO_Port, BUTTON2_LED_Pin)))
		{
			buttonstate = 0xF0;		//MCP4725 out only
		}	
		else if((HAL_GPIO_ReadPin(BUTTON1_LED_GPIO_Port, BUTTON1_LED_Pin)) && (HAL_GPIO_ReadPin(BUTTON2_LED_GPIO_Port, BUTTON2_LED_Pin)))
		{
			buttonstate = 0x0F;		//PCF8574 and MCP4725  out
		}	
		else if((!HAL_GPIO_ReadPin(BUTTON1_LED_GPIO_Port, BUTTON1_LED_Pin)) && (!HAL_GPIO_ReadPin(BUTTON2_LED_GPIO_Port, BUTTON2_LED_Pin)))
		{
			buttonstate = 0x00;		//no PCF8574  and MCP4725  out
		}	
	}
	
	{
		switch(buttonstate)
		{
			//write to slave address 0x40 of PCF8574.  Configured active low.
                        case 0xFF:
			buffer_PCF8574Write[0] = 0xFE;																					
			HAL_I2C_Master_Transmit_IT(&hi2c1, 0x40, buffer_PCF8574Write, 1); 	
			
			//write to slave address 0xC0 of MCP4725
                        buffer_I2C[0] = 0x40;																					
			buffer_I2C[1] = 0x00;
			buffer_I2C[2] = 0x00;
			HAL_I2C_Master_Transmit_IT(&hi2c1, 0xC0, buffer_I2C, 3);
			break;
			
			//write to slave address 0xC0 of MCP4725
                        case 0xF0:
			buffer_I2C[0] = 0x40;																					
			buffer_I2C[1] = 0xFF;
			buffer_I2C[2] = 0xF0;
			HAL_I2C_Master_Transmit_IT(&hi2c1, 0xC0, buffer_I2C, 3); 	
			
			//write to slave address 0x40 of PCF8574.  Configured active low.
                        buffer_PCF8574Write[0] = 0xFF;	
			HAL_I2C_Master_Transmit_IT(&hi2c1, 0x40, buffer_PCF8574Write, 1);
			break;
			
			
                       //write to slave address 0x40 of PCF8574.  Configured active low.
                        case 0x0F:
			buffer_PCF8574Write[0] = 0xFE;	
			HAL_I2C_Master_Transmit_IT(&hi2c1, 0x40, buffer_PCF8574Write, 1);
			
			//write to slave address 0xC0 of MCP4725
                        buffer_I2C[0] = 0x40;																					
			buffer_I2C[1] = 0xFF;
			buffer_I2C[2] = 0xF0;
			HAL_I2C_Master_Transmit_IT(&hi2c1, 0xC0, buffer_I2C, 3);
			break;
			
			//write to slave address 0x40 of PCF8574.  Configured active low.
                        case 0x00:
			buffer_PCF8574Write[0] = 0xFF;	
			HAL_I2C_Master_Transmit_IT(&hi2c1, 0x40, buffer_PCF8574Write, 1);
			
			//write to slave address 0xC0 of MCP4725
                        buffer_I2C[0] = 0x40;																					
			buffer_I2C[1] = 0x00;
			buffer_I2C[2] = 0x00;
			HAL_I2C_Master_Transmit_IT(&hi2c1, 0xC0, buffer_I2C, 3);
			break;
			
			
			default:
			break;
		}
	}
}

5 REPLIES 5
TDK
Guru

> HAL_I2C_Master_Transmit_IT(&hi2c1, 0xC0, buffer_I2C, 3);

> HAL_I2C_Master_Transmit_IT(&hi2c1, 0x40, buffer_PCF8574Write, 1);

HAL_I2C_Master_Transmit_IT is a nonblocking call that returns immediately even though the work is going on in the background. You can't just call it whenever you want and expect it to work, you can only call it when it's ready, otherwise it's doing to ignore your call and return HAL_BUSY. Check for hi2c1.State == HAL_I2C_STATE_READY before you call it.

If you want to use polling mode, use HAL_I2C_Master_Transmit instead.

You can also consider using the HAL_I2C_Mem_Write function instead which are a little easier to understand when skimming code, but functionally identical to what you're doing.

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

Thanks TDK. Is this hi2c1.State == HAL_I2C_STATE_READY not generated by CubeMX automatically? Where do you insert this code?

Just use blocking mode. Or see examples in the cubemx repository.
If you feel a post has answered your question, please click "Accept as Solution".
RSy.1
Associate III

Thank you for the hint Sir.

> You can also consider using the HAL_I2C_Mem_Write function instead which are a little easier to understand when skimming code, but functionally identical to what you're doing.

No, they are not identical because two HAL_I2C_Master_Transmit_IT() makes two separate transactions with STOP condition after each. HAL_I2C_Mem_Write() is not only easier to understand but a more robust and compatible solution overall.

And, if using HAL, the driver state must be read using the respective API - HAL_I2C_GetState().