2024-08-29 06:27 AM
Hi,I am working with stm32g491re.I am communicating with TCA9537(Ioexpander) from stm32 through I2C. It will control a realy. For my product EFT(Electrical Fast Transient) testing, After starting EFT teat relay getting turned off and not recovering.
Through oscilloscope we observed, after EFT test,sclk and sda lines not recovering
Io expander it self resetting.master and slave both side issues are there.so I tried below code.
So i modified the code.
void ioexpandercheck(){
uint8_t check[1];
HAL_StatusTypeDef status=tca9537_read(&hmcp, TCA9537_REGISTER_INPUT,check);
HAL_Delay(10);
if(status!=HAL_OK){
HAL_I2C_DeInit(&hi2c3);
HAL_Delay(10);
HAL_I2C_Init(&hi2c3);
HAL_Delay(50);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, 0);
HAL_Delay(5);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, 1);
HAL_Delay(5);
tca9537_init(&hmcp, &hi2c3, TCA9537_I2C_ADDR);
tca9537_iodir(&hmcp, TCA9537_CONFIG_IO12_OUTPUT);
}
}
Here continuously I am reading the expander pin if read is not ok I am resetting both I2C and ioexpander and reinitializing the ioexpander. With this i can able to recover the relay operation in EFT but its not stable.
In above ioexpander reset i done using reset pin of ioexpander.Which is not part of our present h/w set up.We made it now gor testing is there any other way can we reset instead of using reset pin.
In datasheet also they mentioned how to do with software.but i am conused about implementation.
Since I am reinitializing the i2c in above code,i2c lines are active so,can I do softreset
Can anyone suggest.
uint8_t reset_command = 0x06;
HAL_StatusTypeDef status = HAL_I2C_Master_Transmit(&hi2c3, 0x00, &reset_command, 1, 100);
Thanks
2024-08-29 09:41 AM
Sounds like what you're doing is a band aid.
Unless there is a HW issue, it's very rare that an I2C bus would lock up and need resetting.
2024-08-29 01:59 PM - edited 2024-08-29 01:59 PM
Agree with what @Karl Yamashita has to say except that this is during EFT testing, so errors due to spikes on the line are somewhat more expected.
The following situations can happen:
To recover from a situation in which SDA is stuck low:
You are not doing step 2 or step 3 here. You cannot send pulses while the pin is still initialized in I2C mode.
The software reset mechanism involves a normal write transaction to the chip, which is not possible when SDA is held low, so it can't help you here. However, if you bus is functioning normally, send a single 0x06 byte to the 0x00 slave address to reset the chip.
uint8_t data = 0x06;
HAL_I2C_Master_Transmit(&hi2cx, 0x00 << 1, &data, 1, HAL_MAX_DELAY);
2024-08-30 02:06 AM
Hi @Karl Yamashita ,
@Karl Yamashita wrote:Unless there is a HW issue, it's very rare that an I2C bus would lock up and need resetting.
I checked with Hardware team ,they are saying no hardware issue. So, that's why I am trying to
- Are there other devices on the same I2C bus? It would help to see a schematic of your connections, including pull-ups.
No other devices on the I2C bus, only one slave (Ioexpander).
- Are you using polling or interrupt when reading the I/O expander? How often are you reading the I/O expander to check the GPIO status?
I am using polling only when reading the expander. Aroung 100msec delay I am reading the ioexpander pin.
- is ioexpandercheckIs() the actual function to check the I/O status or is it just to check if the I/O expander is communicating? I can't tell what tca9537_read() is doing. If not, we need to see the rest of the code.
ioexpandercheckIs() function is to check ioexpander is communicating with stm32 or not .This I implemented for EFT.
tca9537_read(): function reads the input pin value.
Thanks
2024-08-30 02:14 AM
Hi @TDK ,
Thanks for suggestion.
To recover from a situation in which SDA is stuck low:
- Deinitialize the I2C peripheral.
- Re-initialize the SDA pin as a GPIO open-drain output.
- Send 9 negative-going pulses on this line with a frequency of 100 kHz or less.
- Re-initialize the SDA pin as an I2C pin and re-intiailize the I2C peripheral.
void I2C_Recovery_SDA_Stuck() {
// 1. Deinitialize the I2C peripheral
HAL_I2C_DeInit(&hi2c3);
// 2. Re-initialize the SDA pin as a GPIO open-drain output
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
// 3. Send 9 clock pulses on SDA with a frequency of 100 kHz or less
for (int i = 0; i < 9; i++) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, GPIO_PIN_RESET);
HAL_Delay(1); // Pulse LOW (1 us for ~100 kHz)
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, GPIO_PIN_SET);
HAL_Delay(1); // Pulse HIGH (1 us for ~100 kHz)
}
// 4. Re-initialize the SDA pin as an I2C pin
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; // Alternate function open-drain mode for I2C
GPIO_InitStruct.Pull = GPIO_PULLUP; // Pull-up resistor for I2C
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // High speed for I2C
GPIO_InitStruct.Alternate = GPIO_AF4_I2C3; // Set alternate function for I2C3
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
// 5. Reinitialize the I2C peripheral
HAL_I2C_Init(&hi2c3);
}
I implemented this for testing. Is this fine??
I will test it when i go for EFT test again.
- And potentially: Master I2C state is misbehaving (but slaves are fine) due to inadequate code and/or complicated and misunderstood peripheral details. One solution is to de-initialize and re-initialize the peripheral and hi2cx software state machine.
While EFT what I observed through oscilloscope is, sometimes SCLK(no clock) and SDA (HIGH value) after the test.
Sometimes both clock and data lines are normally working.
So, that's why i thought slave and master sides both needs to be reset.
Thanks for response.
2024-08-30 04:34 AM
That code looks good to me, should work.
2024-09-03 07:29 AM