cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F103C8 I2C module enters to busy state and cannot get out of it

GunkutA
Senior

Hello, I am trying to communicate with an SMBus battery. That has 2 sections so I am using 2 I2C modules. There was this problem: After the first I2C module makes a successful communication second module gets stuck. And it gets stuck in Busy. So I checked the errata and added some errata function that is:

void I2C_Errate_Workaround(int module){
 
	if(module==1){
		//Disable The I2C's
		I2C1 -> CR1 &= ~(I2C_CR1_PE);
		//Pins are general purpose output open-drain
		GPIOB->CRL |= 0b01<<24; //I2C1 SCL Open Drain
		GPIOB->CRL |= 0b01<<28; //I2C1 SDA Open Drain
		//Make SCL/SDA High
		GPIOB-> ODR |= GPIO_ODR_ODR6;
		GPIOB -> ODR |= GPIO_ODR_ODR7;
		//Check whether pins are high
		if((GPIOB->IDR & GPIO_PIN_6) == GPIO_PIN_RESET || (GPIOB ->IDR & GPIO_PIN_7) == GPIO_PIN_RESET)
		{
			for(;;){}
		}
		//SDA Low Level
		GPIOB->ODR &= ~(GPIO_ODR_ODR7);
		// Check if SDA pins are really low
		if((GPIOB->IDR & GPIO_PIN_7)== GPIO_PIN_SET){
			for(;;){}
		}
		// SCL Pin to Low
		GPIOB -> ODR &= ~(GPIO_ODR_ODR6);
		// Check whether SCL pins are cleared
		if((GPIOB->IDR & GPIO_PIN_6)== GPIO_PIN_SET){
			for(;;){}
		}
		//Configure SCL to High
		GPIOB->ODR |= GPIO_ODR_ODR6;
		//Check if SCL High
		if((GPIOB->IDR & GPIO_PIN_6)== GPIO_PIN_RESET)
		{
			for(;;){}
		}
		//Set SDA High
		GPIOB->ODR |=GPIO_ODR_ODR7;
		//Check if SDA high
		if((GPIOB->IDR & GPIO_PIN_7)== GPIO_PIN_RESET)
		{
			for(;;){}
		}
		//Set all to alternate function open drain
		GPIOB->CRL |= 0b11<<24; //I2C1 SCL Open Drain
		GPIOB->CRL |= 0b11<<28; //I2C1 SDA Open Drain
		//Set Swrst bit
		I2C1->CR1 |= I2C_CR1_SWRST;
		//Clear Swrst bit
		I2C1->CR1 &= ~(I2C_CR1_SWRST);
		//Enable I2C modules
		I2C1->CR1 |= I2C_CR1_PE;
	} else if (module ==2){
		//Disable The I2C's
		I2C2 -> CR1 &= ~( I2C_CR1_PE);
		// Pins are general purpose output open-drain
		GPIOB->CRH |= 0b01<<8;
		GPIOB->CRH |= 0b01<<10;
		//Set pins as outputs
		GPIOB -> ODR |= GPIO_ODR_ODR10;
		GPIOB -> ODR |= GPIO_ODR_ODR11;
		//Check whether pins are high
		if((GPIOB ->IDR & GPIO_PIN_11) == GPIO_PIN_RESET || (GPIOB -> IDR & GPIO_PIN_10)== GPIO_PIN_RESET)
		{
			for(;;){}
		}
		//SDA Low Level
		GPIOB ->ODR &= ~(GPIO_ODR_ODR11);
		// Check if SDA pins are really low
		if((GPIOB->IDR & GPIO_PIN_11)== GPIO_PIN_SET)
		{
			for(;;){}
		}
		// SCL Pins to Low
		GPIOB -> ODR &= ~(GPIO_ODR_ODR10);
		// Check whether SCL pins are cleared
		if((GPIOB->IDR & GPIO_PIN_10)== GPIO_PIN_SET)
		{
			for(;;){}
		}
		//Configure SCL to High
		GPIOB->ODR |= GPIO_ODR_ODR10;
		//Check if SCL High
		if((GPIOB-> IDR & GPIO_PIN_10)== GPIO_PIN_RESET)
		{
			for(;;){}
		}
		//Set SDA High
		GPIOB->ODR |= GPIO_ODR_ODR11;
 
		//Check if SDA high
		if((GPIOB->IDR & GPIO_PIN_11)== GPIO_PIN_RESET)
		{
			for(;;){}
		}
		//Set all to alternate function open drain
		GPIOB->CRH |= 0b11<<8; // I2C2 SCL Open Drain
		GPIOB->CRH |= 0b11<<10; //I2C2 SDA Open Drain
		//Set Swrst bit
		I2C2->CR1 |= I2C_CR1_SWRST;
		//Clear Swrst bit
		I2C2->CR1 &= ~(I2C_CR1_SWRST);
		//Enable I2C modules
		I2C2->CR1 |= I2C_CR1_PE;
	}
}

And I call this errate in the beginning of my SMBus task. Then I DeInit and Init the module with HAL Library. That solved the problem but after some time, problem occurs again and this time nothing helps it to recover from lockup.

This is the main SMBus task:

	/* USER CODE BEGIN StartSMBUSTask */
	/* Infinite loop */
	for(;;)
	{
		int receivedSOC;
		do{		count1++;
 
 
		HAL_I2C_DeInit(&hi2c1);
		HAL_I2C_DeInit(&hi2c2);
		osDelay(50);
		__HAL_RCC_I2C2_CLK_ENABLE();
 
		SMBus_GPIO_Init();
	HAL_I2C_Init(&hi2c2);
 
		receivedSOC = getBatterySOC(&hi2c2);
		if (receivedSOC != -1 && receivedSOC<=100){
			socBattery2 = receivedSOC;
				memset (statusString,0x00,255);
				statusStringLength = sprintf(statusString,"Success (1) \r\n");
				CDC_Transmit_FS(statusString,statusStringLength);
				flag1=1;
		}
		else{
 
	
 
		}}
		while(count1<=10 && flag1 ==0);
 
 
 
		osDelay(300);
 
		do{			count2++;
 
		HAL_I2C_DeInit(&hi2c1);
		HAL_I2C_DeInit(&hi2c2);
		osDelay(50);
		__HAL_RCC_I2C1_CLK_ENABLE();
		SMBus_GPIO_Init();
 
		HAL_I2C_Init(&hi2c1);
 
		receivedSOC = getBatterySOC(&hi2c);
		if (receivedSOC != -1 && receivedSOC<=100){
			socBattery1 = receivedSOC;
			memset (statusString,0x00,255);
			statusStringLength = sprintf(statusString,"Success (2) \r\n");
			CDC_Transmit_FS(statusString,statusStringLength);
			flag2=1;
		}
		else{
			
		}}
		while(count2<=10 && flag2==0);
		if( count1==10 || count2 ==10){
			I2C_Errate_Workaround(2);
				I2C_Errate_Workaround(1);
 
		}
 
 
		count2=0;
		count1=0;
		flag1=0;
		flag2=0;
 
 
 
		osDelay(300);
	}
	/* USER CODE END StartSMBUSTask */
}

Any idea that how can I solve this issue? Thanks beforehand.

3 REPLIES 3
GunkutA
Senior

By the way I realized that calling errata function and then using HAL_I2C_Init(); function works but then after sometime it gets useless. And I need to call HAL_I2C_Init function eventhough I enable I2C module by register. Why is that?

T J
Lead

Yes, I had to call HAL_I2C_Init too;

this is my implementation:

static void ssd1306_WriteCommand(uint8_t command)
{
 
    HAL_StatusTypeDef status = 1;
    int checked = 0;
    while ((status != HAL_OK)&&(checked++ < 100))
    {
        status = HAL_I2C_Mem_Write(&hi2c1, SSD1306_I2C_ADDR,00,1, &command, 1, 100);                   
        extern char string[];
        if (status != HAL_OK)
        {
            static int IIC_FailedCounter = 0;
            setCursor1(40, 0);
            sprintf(string, "ssd1306_WriteCommand failed Count %d Status %d   \n", IIC_FailedCounter++, status);
            putsCDC(string);
            status = HAL_I2C_Init(&hi2c1);
            sprintf(string, "attempt reinit returned %d  \n", status);
            putsCDC(string);
            
            // reports:
            //ssd1306_WriteCommand failed Count 3 Status 1
            //attempt reinit returned 0
        
        }
    }
 
}

GunkutA
Senior

Hello TJ and thank you for your reply. I realised that calling Init function of I2C module byitself is not enough unfortunately in my case. I had to do a software reset from RCC->APB1RSTR and it looks like it is working for now. No lock up for 5 mins for now. I think these MCU's have some problems with I2C modules. Never had a problem with I2C in PIC before.