2019-09-04 04:41 AM
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.
2019-09-04 05:46 AM
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?
2019-09-04 08:06 AM
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
}
}
}
2019-09-04 10:43 PM
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.