2-wire behaves strangely

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

I'm trying to hook up a 2-wire connection using the method on page 183 in the mega 16 data sheet. (Setting/clearing and polling the TWINT bit.) I get some very strange results that I hope you can help me with. Anybody seen this kind of behaviour before?

I have to send a start condition, device address with r/w bit, a local address, and finally the data byte and stop condition. In my code, a failed packet results in a warning message, not a halted transmission.

I can see all clocks and signals when there is nothing connected to the mega16. There are no ACKs. Then, when I connect my slave 2-wire unit, it ACKs the device address like it should, but the MCU doesn't send the local address! There is no action on neither SDA or SCL. The MCU seems to be pulling them low.

The even weirder thing is that if I set up the slave to listen to another device address (i.e. present but not ACKing), the MCU is able to clock out all packets while reporting errors.

Greetings,

Børge

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

Can you post your code? It sounds vaguely as though the slave is stretching the
ACK pulse, which it's allowed to do I guess but it's supposed to let it go eventually.

What sort of pullups are you using? And what sort of slave device is it?

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

Are you using external pull-ups on the SDA/SCL lines? If not, the addition of the other devices will be enough to explain what you are seeing.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

My pullups are those in the mega16. So the voltage levels look okay whether or not the slave is connected. My slave is an evaluation board for a new chip from Nordic Semiconductor where I work as an FAE.

Here is the code for my 2-wire write. The chip requires the following sequence:
-start
-device address(w)
-internal address
-restart, device address(r)
-reading bytes with ACK
-reading last byte with NACK
-stop

Thanks,
Børge

#define CHIP2WDEVADR 0b00101001
#define START2W      0x08
#define RSTART2W     0x10
#define MTSLA2W      0x18
#define DATACK2W     0x28
#define MRSLA2W      0x40

// Set up MCU's master SPI interface
void mcu_2w_master_init(void) {
	TWBR = 0x0C;      // SCL speed at a little below 100kb/s
	TWSR = 0;         // See mega16 datasheet, p178
	TWAR = 0xAA;      // 2-w adr. of this unit is 1010101, no 
	TWCR = (1<<TWEN); // Enable two-wire interface
	PORTC = 0x03;     // AVR pull-up resistors SDA/SCL, 
	return;
}


// Read data from 2-wire slave interface of chip
void mcu_2wread(char startadr, char endadr) {
	extern char slaveinbuf[];
	int n=0;

	// Send internal register address
	// Send new start condition
	// Send device address with r/_w = 1
	// Read bytes, ACK if more bytes wanted, NACK when done
	
	if (endadr - startadr <= SLAVEBUFSIZE) {

		// Send start condition
		DB_PRINTF(PSTR ("\r\nS"));
		TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);	// Start condition
		loop_until_bit_is_set(TWCR,TWINT);
		if ((TWSR & 0xF8) != START2W)
			DB_PRINTF(PSTR ("-"));


		// 7-bit device address, LSB=0 for write
		// Start transmission of address
		// Wait until the device address has been sent
		// Test for valid Master Transmit mode
		DB_PRINTF(PSTR ("1"));
		TWDR = (CHIP2WDEVADR<<1) & 0xFE;
		TWCR = (1<<TWINT) | (1<<TWEN);
		loop_until_bit_is_set(TWCR,TWINT);
		if ((TWSR & 0xF8) != MTSLA2W)
			DB_PRINTF(PSTR ("-"));

		// Send internal address of chip
		// Wait until the internal address has been sent
		DB_PRINTF(PSTR ("2"));

//////// This is as far as it gets. When slave is connected properly, it
//////// prints the "2" but doesn't turn on the 2-wire clock. No slave or
//////// NACK displays the "-" error messages but goes through with
//////// the transfer.

		TWDR = startadr;                  
		TWCR = (1<<TWINT) | (1<<TWEN);
		loop_until_bit_is_set(TWCR,TWINT);
		if ((TWSR & 0xF8) != DATACK2W)
			DB_PRINTF(PSTR ("-"));

		// Send another start condition
		// Wait until the start condition has been sent
		// Test for valid restart condition sent
		DB_PRINTF(PSTR ("Rs"));
		TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);	
		loop_until_bit_is_set(TWCR,TWINT);			
		if ((TWSR & 0xF8) != RSTART2W)				
			DB_PRINTF(PSTR ("-"));

		// 7-bit device address, LSB=1 for read
		// Start transmission of address
		// Wait until the device address has been sent
		// Test for valid Master Read mode
		DB_PRINTF(PSTR ("3"));
		TWDR = (CHIP2WDEVADR<<1) | 0x01;				
		TWCR = (1<<TWINT) | (1<<TWEN);				
		loop_until_bit_is_set(TWCR,TWINT);			
		if ((TWSR & 0xF8) != MRSLA2W)				
			DB_PRINTF(PSTR ("-"));

		// Read bytes until done
		while (startadr++ <= endadr) {
			DB_PRINTF(PSTR ("D"));

			if (startadr == endadr)
				// Send NACK after receive
			TWCR = (1<<TWINT) | (1<<TWEN);
			else
				// Send ACK after receive
				TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);	
			loop_until_bit_is_set(TWCR,TWINT);
			if ((TWSR & 0xF8) != DATACK2W)
				DB_PRINTF(PSTR ("-"));

			slaveinbuf[n++] == TWDR;
		}
		
		DB_PRINTF(PSTR ("P\r\n"));
		TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);	// Send stop condition
	} // if valid read length
	return;
}

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

I don't think the AVR pullups are useable for I2C. Seems to me a 4.7K extrernal resistor is a widely recommended choice.

HTH,

Randy

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

The internal pullups are definitely not strong enough, to support external I2C devices.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

Quote:
The internal pullups are definitely not strong enough, to support external I2C devices.

I have succeeded with using the internal pullups for Some external devices, but I always knew
I was "living in sin" (:-)).

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

mckenney wrote:
Quote:
The internal pullups are definitely not strong enough, to support external I2C devices.

I have succeeded with using the internal pullups for Some external devices, but I always knew
I was "living in sin" (:-)).

Interesting, I would imagine that your communication rate had to be pretty slow to work. Since the driving factor for the pull-up is the capacitance of each device that it needs to charge to create a high. Incidentally, you will find that to increase speed, you will quite often have to use a stronger pull-up. (It's all about that RC curve)

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

Quote:

The internal pullups are definitely not strong enough, to support external I2C devices.

I have been using the internal PU,s on a M16 (OLIMEX PCBA). The M16 sends data to an I/O expander (PCF8574) which is mounted on the same Olimex board and also sends TWI data via a connector to a Olimex board for a Mega32. The Mega32 is configured as a Slave device.
The connector from the Mega16 to the Mega32 is about 6 inch's in length.
Both the I/O expander and the Mega32 are used as slave devices to the mega16 board.
The SDA/SCL lines both look OK using the o'scope.

I'll believe corporations
are people when Texas executes one.

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

tubecut wrote:

I have been using the internal PU,s on a M16 (OLIMEX PCBA). The M16 sends data to an I/O expander (PCF8574) which is mounted on the same Olimex board and also sends TWI data via a connector to a Olimex board for a Mega32. The Mega32 is configured as a Slave device.
The connector from the Mega16 to the Mega32 is about 6 inch's in length.
Both the I/O expander and the Mega32 are used as slave devices to the mega16 board.
The SDA/SCL lines both look OK using the o'scope.

And my lines look just fine too. I'll get the iron running to change to resistors closer to the slave device. But the main problem was that my code stopped when it got an ACK and continued when it got a NACK.

Some other code that I got from an Atmel app note polls the TWIE bit to see if the 2-wire is busy. The code in the data sheet polls the TWINT bit.

Greetings,
Børge

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

Quote:
But the main problem was that my code stopped when it got an ACK
and continued when it got a NACK.

This is sort of why the pullup theory seems like a good fit -- the failure (SCL/SDA
fall low permanently) occurs after the first time your slave device touches the bus,
as though the slave's driver is allowing it to float after driving it. If the slave
never touches the bus it works fine.

Keep in mind that a NACK isn't a bus event, it's the Absence of a bus event.

Quote:
Some other code that I got from an Atmel app note polls the TWIE bit to
see if the 2-wire is busy.

This is a bit odd, since TWIE is something the software sets, i.e. it might as well
be an SRAM variable. I'm not saying it's wrong, it's just not a general-purpose
solution.

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

Are there any know issues with the 2-wire port on atmega16? I have tried and tried, and there is no way I manage it to clock out the first data byte after it receives an ACK on the address and r/w bit.

I have tried the polling code from page 183 of the mega16 manual and TWI_Master.c from Atmel, dated 24. mai 2004 11:31:20.

Please, any working 2-wire code for the mega16 would greatly help me!

Greetings,
Børge

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

PEBKC - Problem exists between keyboard and chair. I was using the wrong firmware in my 2-wire client. Took me a while to see that it was pulling down the clock line forever.

Sorry to have waisted your time.

Thanks,
Børge