SAMC21N SERCOM1 I2C Master hangs when SDA and SCL linea are shorted

Go To Last Post
9 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm writing a simple polling (no interrupt) driver for SERCOM1 (PA16 and PA17) in I2C Master Mode for a SAMC21N device. I can read and write data without big issues.

 

I usually want to write high reliable code, so I made a simple test: what happens when I make a short between the two I2C lines SDA and SCL. Of course, data can't be exchanged correctly, but luckily everything will come back normal when I remove the short.

 

What I don't like is what happens DURING the short circuit. My code hangs forever immediately after writing ADDR register, where I have a loop to wait for INTFLAG bits.

 

i2c->hw->I2CM.ADDR.reg = (i2c_addr7 << 1) + I2C_WRITE_BIT;
while(!i2c->hw->I2CM.INTFLAG.reg) {
    ;
}

Indeed the debugger shows that INTFLAG bits stay all cleared. Unfortunately even STATUS.BUSSTATE doesn't show anything special, it is 1 (IDLE). Other STATUS bits (BUSERR, ARBLOST, ...) are cleared too.

I tried enabling CTRLA bits MEXTTOEN, SEXTTOEN, INACTOUT, LOWTOUTEN, without success.

 

As soon as the short is removed, the INTFLAG bit is set and the execution goes on.

 

Should I implement a timeout at this very low level driver?

 

I will check what happens with ASF code, in the meantime anyone could make a suggestion?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I just noted that ASF code implement a timeout mechanism during waiting loop:

 

enum status_code _i2c_master_wait_for_bus(
		struct i2c_master_module *const module)
{
	/* Sanity check arguments */
	Assert(module);
	Assert(module->hw);

	SercomI2cm *const i2c_module = &(module->hw->I2CM);

	/* Wait for reply. */
	uint16_t timeout_counter = 0;
	while (!(i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) &&
			!(i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)) {

		/* Check timeout condition. */
		if (++timeout_counter >= module->buffer_timeout) {
			return STATUS_ERR_TIMEOUT;
		}
	}
	return STATUS_OK;
}

It's a simple counter that is incremented in the loop. I suspect Atmel knew the issue and tried to solve it adding this sub-optimal mechanism. I don't know why other "hardware" timeout mechanisms don't work.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sounds like standard I2C - lockup is part of the specification. Even more fun when you implement an I2C slave.

 

My usual test for I2C robustness is to short the signals like you do. If the system recovers, that is good. Try the same thing on just about any micro with I2C - you'll observe much the same behaviour.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do you think this is a good behaviour? If it's a superloop (cooperative multitasking), every tasks is stopped waiting for I2C. Unless you write the driver without waiting loops.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You'd have to question my sanoity if I said it was good behaviour. It is expected behaviour however. 

 

That's why you code must have timeouts. Even a transient can upset I2C and cause it to lock up. The NXP/Philips spec on I2C suggest recovery procedures.

 

If you're writing co-operative code, why are you coding wait loops? I'd be using a finite state machine and ensuring each state has a finite execution time.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Every UART drivers I saw in the past trust the hardware they manage. They write DATA register to send a byte and trust an interrupt will fire later. If the hardware hangs and no interrupt is fired in certain circumstances, the driver is completely locked. I never seen a timeout in UART driver.

 

Regarding SERCOM in I2C Master, we can't trust our hardware so we have to add a timeout in case the hw hangs. Even the datasheet says the internal state-machine can hang and a reset/reinitialization must be done. Nonsense to me. Note that SERCOM is filled with many timeouts flag (see CTRLA and STATUS register), but they doesn't work when SDA and SCL are shorted. I don't know why the internal state-machine isn't capable to exit from this situation with an error.

 

I'm using wait loops because I2C transaction is very short in time, so I can wait for the end of transaction... when it goes normally. If I need a timeout, this isn't acceptable anymore and I need to implement my driver as a non-blocking state-machine. I will have an internal hw state-machine that could hang, so I need another software state-machine.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As already noted, I2C is fundamentally different from UART: it is inherently prone lo lock up - this is defined in the original Philips (now NXP) specification.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As already noted, I2C is fundamentally different from UART: it is inherently prone lo lock up - this is defined in the original Philips (now NXP) specification.

This is why I hate I2C and, whenever possible, use SPI (EEPROM, DAC, ADCs).

 

Anyway SERCOM peripheral has timeouts (see STATUS and CTRLA register). I don't know why there are situations that aren't catched by those timeouts implemented in the peripheral and I need an additional software timeout.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No use complaining to us! Try just about any I2C peripheral from any manufacturer and you'll see much the same operation. In fact most of them have no concept of timeout - that's done in software.

 

All I can suggest is to bit-bash the I2C to get around the 'statefullness' of the I2C peripheral. You still have the issue of polling the ACK bit and if that never comes - but your code can manage a loop count and/or timeout to cope with this.

 

 

I'm battling with SPI at the moment - it too has its own set of challenges especially when implementing a slave.