A potential avr TWI bug?

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

This code is written in WinAVR/AVRGCC. It's short and could be easily ported to assembler so I'm posting it in the general forum.

Anyways. I've got some multimaster/slave twi code (SMBus) that seems to work good when it receives good data. So I thought I'd write some code to test/debug that code by sending malformed SMBus 'packets' to the slave.

Anyways, what I thought would be a dirt simple exercise to speed up debugging has turned into a wtf moment.

I'm not going to bore you with the actual code that lead to this, but it would appear that the AVR's hardware TWI module has a problem with sending a start, then a stop, then another start.

I'm hoping I'm just missing something painfully obvious, but I'm starting to wonder if this is an actual hardware state-machine issue.

I'm compiling for/testing this on an ATMega16 @ 8 Mhz.

int main(void)
{
	int i;

	DDRB = 0xFF;
	PORTB = 0xFF;

	//Start up delay, 1/4 seconnd
	for (i=0;i<12;i++)
		_delay_ms(20);
	_delay_ms(10);

	//Set TWI Speed to 100kHz
	TWBR = 32;
	//Initialize status register
	TWSR = 0xF8;
	//Initialize Data Register
	TWDR = 0xFF;
	while (1)
	{
		TWCR = _BV(TWINT)|_BV(TWEN)|_BV(TWSTA);
		PORTB = 1^0xFF;
		while (!(TWCR & _BV(TWINT)));
		TWCR = _BV(TWINT)|_BV(TWEN)|_BV(TWSTO);
		PORTB = 2^0xFF;
		while (!(TWCR & _BV(TWINT)));
	}
return 0;
}

Here's what happens (and I know this from using my own routine to wait for a button press while cycling, but I get the same behavior (though it's not as obvious) with the same code pasted.

It loops through the first time, sending a start bit, then a stop bit, then it executes the first line in the while loop and gets stuck in an infinite loop waiting for TWINT to go high.

Anyways, code compiled for an Atmega16, (actual chip labeled as Atmega16L), and I've had no problems running my own interrupt based code. So fuses/etc should be fine.

As far as I'm concerned, after a stop-bit is sent the master-transmitter should be reset? Anyone have any ideas on this. Did I really find a bug in the hardware?

Oh and on another note, I've had this same chip in the same setup communicating with an SMBus device/another AVR with the only difference being code. I'm purposely sending start/stop/start to try to test the slave code I've already written, but it seems to be bricking the hardware. Before anyone asks I've got external pull-ups on SCL/SDA.

-Brad

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

Against all common sense, sending a STOP does not result in TWINT bit set.

When STOP condition is sent, the TWSTOP bit seems to go back to zero. I really don't know, but this has bitten me a couple of times too. I remember that when I noticed TWSTOP does not cause TWINT, I also found out the next start is sent too fast if there is no delay, so I just put a suitable delay there. Later I read somewhere TWSTOP might go low again when stop is sent, but the exact timing or how to do it is not officially known in any datasheet I guess.

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

Ahh yes, you are right about that. Since there's no master-transmitter stop bit interrupt it doesn't set TWINT.

Last Edited: Sun. Apr 4, 2010 - 07:29 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have not tried your code yet. I will later.

Re the TWSTOP:
You do not get a TWINT but you can just wait for the TWSTO bit clearing in the TWCR register.

Re not always getting a TWINT:
Any polling for the TWINT to become set should also check the TWWC (write collision) bit.

Edit.
Yes. You definitely get hang-ups.

In practice, you are hardly likely to issue start/stop.

If you issue a Start, and the bus is unavailable, you just leave.

If you issue a Start on an available bus, you write the Slave address. Regardless of success or failure, a subsequent Stop command works.

I can see that you never want to get caught waiting for a TWINT that never comes, or even a TWSTO that never clears. But this is probably best solved by not instigating the condition in the first place.

David.