Another AVR TWI Problem (or Mistake)?

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

Okay, so the last issue was definitely my fault. This one may be too, but it's not exactly obvious to me where I'm going wrong. afaik, what I'm doing is legal.

So I'm trying to test the equivalent of an SMBus QuickCommand() read/write.

First I send Start, SLA+W, Stop. It proceeds correctly.

Nexxt I send Start, SLA+R, Stop, It proceeds correctly.

Then I try to send a Start on the next test and TWINT is never set, neither is the write collision bit.

On the interrupt receiver side, when SLA+R comes in without a preceeding SLA+W, valid command, it is supposed to reset it's receiver (because it received invalid data). This is working.

On the receiver side I set this:

	TWCR = _BV(TWINT)|_BV(TWEN)|_BV(TWIE)|_BV(TWSTO)|_BV(TWEA);

If you look in the docs this doesn't actually send a Stop bit, it just resets the TWI receiver to a known state.

So events happen like this

Master sends Start bit.
Master send SLA+R
Receiver responds with ACK
Receiver resets its TWI Module.
Master sends stop bit.

(It's not clear the order between the master sending its stop bit or the receiver resetting its TWI module).

Here's what the test code looks like (the master).

	//  Send SLA+W then Stop  //
		////////////////////////////

		waitForUser();

		PORTB = 6^0xFF;

		TWCR = _BV(TWINT)|_BV(TWSTA)|_BV(TWEN);
		loop_until_twi_frees();
		TWDR = SLA;
		TWCR = _BV(TWINT)|_BV(TWEN);
		loop_until_twi_frees();
		TWCR = _BV(TWINT)|_BV(TWSTO)|_BV(TWEN);

		////////////////////////////
		//  Send SLA+R then Stop  //
		////////////////////////////

		waitForUser();

		PORTB = 7^0xFF;
	
		sendDataINT("\r\nA: 0x",7);
		sendHex(TW_STATUS);
		TWCR = _BV(TWINT)|_BV(TWSTA)|_BV(TWEN);
		loop_until_twi_frees();
		sendDataINT("\r\nB: 0x",7);
		sendHex(TW_STATUS);
		TWDR = SLA|TW_READ;
		TWCR = _BV(TWINT)|_BV(TWEN);
		loop_until_twi_frees();
		sendDataINT("\r\nC: 0x",7);
		sendHex(TW_STATUS);
		TWCR = _BV(TWINT)|_BV(TWSTO)|_BV(TWEN);

		/*
			Stage 2 - Failed Write Byte Types
		*/

		/////////////////////////
		//  Send SLA+W, CMD, P //
		/////////////////////////

		waitForUser();

		PORTB = 8^0xFF;
		sendDataINT("\r\nD: 0x",7);
		sendHex(TW_STATUS);
		TWCR = _BV(TWINT)|_BV(TWSTA)|_BV(TWEN);
		while (!(TWCR & _BV(TWINT)))
		{
			sendDataINT("\r\nE: 0x",7);
			sendHex(TW_STATUS);
			sendDataINT("\r\nF: 0x",7);
			sendHex(TWCR);
			for (i=0;i<50;i++)
				_delay_ms(20);
		}

sendDataINT()/sendHex() send data over the UART so I can debug on my PC.

Sending SLA+W, and sending SLA+R, those 'blocks' of code execute fully. It's the next section down where it sends a start bit and waits for TWINT to go high that never happens.

The debug info on the serial port looks like this:

A: 0xF8  //No status, TWINT=0 (valid after stop bit)
B: 0x08  //Start bit sent
C: 0x40  //SLA+R Rcvd, Ack Sent
D: 0xF8  //No status
E: 0xF8  //No Status
F: 0x24  //TWSTA=1, TWEN=1
E: 0xF8  //No Status
F: 0x24  //TWSTA=1, TWEN=1
...
etc.

I don't have an oscilloscope but I can see on my voltmeter that when the master 'locks up' in this case, that the data line is pulled low (by the master, as a reset of the master releases the bus).
The clock line remains high.

It's like a start condition that never finishes.

Note: I've also tried modifying the receiver to just reply with NACK instead of resetting the bus. It didn't appear to fix anything.

Note2: Sending Start, SLA+R, Stop is afaik legal on I2C. It's definitely legal in SMBus.

-Brad

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

The operation is certainly legal on the bus, and in fact in some cases necessary. For example, think about doing an I2C transaction on bus, while microcontroller resets. The bus can be in any state, and must safely be reset to known idle state.

The suggested sequence is according to most I2C EEPROM datasheets, start, 9 clocks with data high, start, stop.

Now, I have no idea why it fails on the hardware I2C controller. Maybe you could try to transmit another byte, since it seems the controller does understand SLA+W or SLA+R differently than regular bytes.

I should note here that mostly I have dedicated the hardware I2C controller to being a slave only in AVRs, firstly because they only have one controller and I needed two buses, and because in the process I noticed software I2C implementation gives more flexibility and debuggability.

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

Most I2C slaves work by you selecting a register with a write operation.
A subsequent write will write to the selected register.
Or a repeated_start and subsequent read will read from the selected register.

Many devices will auto-increment the register address on every read or write. This is obviously device dependent.

IME, the Atmel TWI expects you to do a write after the SLA_W. Your slave may also be expecting the write too.

So I would not do a Start / SLA_W / Stop in the first place. Even if this means writing a dummy value.

David.