I2C Communication Issues - Repeated Start Command Issue?

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

I am currently trying to communicate to an I2C sensor but I'm having a few issues getting it to work with the ATmega2560.  

 

Below is the spec for I2C Communication:

 

I have tested this using some code I created using the USB-ISS (http://www.robot-electronics.co....).  Below is a screenshot of a working pressure cycle reading:

 

MSB:  

 

LSB:

 

I've tried to get the code working and it appears I'm pretty close but when I use a resend bit, it seems to cause an issue, either with the stop bit, the next start bit, or a combination of the two.  Here is my TWI library code (taken from http://www.embedds.com/programmi..., verified against the suggested code in the datasheet):

 

/*
 * twi.c
 *
 * Created: 1/7/2012 23:06:28
 *  Author: embedds.com
*/
#include "twi.h"

//SDA = PD1
//SCK = PD0

#define DDR_TWI DDRD
#define DD_TWI_SDA PD1
#define DD_TWI_SCK PD0

uint8_t tempstatus = 0;

void TWIInit(void)
{
	//DDRD = (1<<PD1)|(1<<PD0);
	DDR_TWI = (1<<DD_TWI_SDA)|(1<<DD_TWI_SCK);
	
	// SCL CLK Freq = (CPU CLK Freq / (16 + (2 * TWBR) * (4^TWPS)))
	TWSR = 0x00;
//	TWBR = 0x38;	//set SCL to 62.5kHz
	TWBR = 0x0C;	//set SCL to 200kHz
//	TWBR = 0x02;	//set SCL to 400kHz
	//enable TWI
	TWCR = (1<<TWEN);
}
//send start signal
void TWIStart(void)
{
	TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
	while ((TWCR & (1<<TWINT)) == 0);
}
//send stop signal
void TWIStop(void)
{
	TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
}
void TWIWrite(uint8_t u8data)
{
	TWDR = u8data;
	TWCR = (1<<TWINT)|(1<<TWEN);	
	while ((TWCR & (1<<TWINT)) == 0);
}
//read byte with ACK
uint8_t TWIReadACK(void)
{
	TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
	while ((TWCR & (1<<TWINT)) == 0);
	return TWDR;
}
//read byte with NACK
uint8_t TWIReadNACK(void)
{
	TWCR = (1<<TWINT)|(1<<TWEN);
	while ((TWCR & (1<<TWINT)) == 0);
	return TWDR;
}
uint8_t TWIGetStatus(void)
{
	uint8_t status;
	//mask status
	status = TWSR & 0xF8;
	tempstatus = status;
	return status;
}

I've added a few extra lines (different communication speeds, and a redundant tempstatus bit as it won't seem to let me look at the value stored in status unless I add that.  No idea why but I'll take it), but for the most part, it's untouched and all from that other site (though, as I said, I did verify the code to ensure it's doing what it's supposed to.  I'm actually using it to communicate with another chip and haven't had any problems).  

 

Below is my implementation of the TWI library for this application:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include "twi.h"
#include "spi.h"

#define D4SENSORADDRESS 0x20
#define DACADDRESS 0x30
#define ERROR 1
#define SUCCESS (!ERROR)
#define DACCOMMANDMODEBYTE 0x00

#define I2C_SENSOR_COMPILE
//#define I2C_DAC_COMPILE
//#define SPI_SENSOR_COMPILE
//#define SPI_DAC_COMPILE

void D4I2CSensorIn(void);
void I2CDACOut(void);
void SPISensorIn(void);
void SPIDACOut(void);

#ifdef I2C_SENSOR_COMPILE
    #define D4I2C
#endif

uint8_t ReadData[2] = {0,0};
uint16_t ADCValue = 0;
void D4I2CSensorIn(void)
{
    uint8_t currentstatus = 0;
    
    // Request MSB
    TWIStart();
    if (TWIGetStatus() != 0x08)
    {
        // Error - Start condition has not been transmitted
    }
    else
    {
        TWIWrite((uint8_t)(D4SENSORADDRESS << 1));
        currentstatus = TWIGetStatus();
        
        if(currentstatus == 0x18)
        {
            TWIWrite((uint8_t)(0x04));
            currentstatus = TWIGetStatus();
            
            if(currentstatus == 0x28)
            {
                TWIWrite((uint8_t)(0x61));
                currentstatus = TWIGetStatus();
                
                if(currentstatus == 0x28)
                {
                    // MSB Request Complete
                }
            }
        }
    }
    TWIStop();
    
    
    //Request Read MSB
    TWIStart();
    if (TWIGetStatus() != 0x08)
    {
        // Error - Start condition has not been transmitted
    }
    else
    {
    
        TWIWrite((uint8_t)(D4SENSORADDRESS << 1));
        currentstatus = TWIGetStatus();
    
        if(currentstatus == 0x18)
        {
            TWIWrite((uint8_t)(0x04));
            currentstatus = TWIGetStatus();
        
            if(currentstatus == 0x28)
            {
                TWIWrite((uint8_t)((D4SENSORADDRESS << 1)+1));
                currentstatus = TWIGetStatus();
                
                if(currentstatus == 0x28)
                {
                    ReadData[0]	= (0x3F &TWIReadACK());
                }	
            }
        }
    }
    TWIStop();
}

 

 

When I run this, it runs continually (as this function is within an infinite loop) but never feeds me the MSB data (which makes sense as the example above doesn't have the additional start command like it should).  Below is a screenshot of what I get:

 

 

I added back in the start command (shown below):

 

void D4I2CSensorIn(void)
{
	uint8_t currentstatus = 0;
	
	// Request MSB
	TWIStart();
	if (TWIGetStatus() != 0x08)
	{
		// Error - Start condition has not been transmitted
	}
	else
	{
		TWIWrite((uint8_t)(D4SENSORADDRESS << 1));
		currentstatus = TWIGetStatus();
		
		if(currentstatus == 0x18)
		{
			TWIWrite((uint8_t)(0x04));
			currentstatus = TWIGetStatus();
			
			if(currentstatus == 0x28)
			{
				TWIWrite((uint8_t)(0x61));
				currentstatus = TWIGetStatus();
				
				if(currentstatus == 0x28)
				{
					// MSB Request Complete
				}
			}
		}
	}
	TWIStop();
	
	
	//Request Read MSB
	TWIStart();
	if (TWIGetStatus() != 0x08)
	{
		// Error - Start condition has not been transmitted
	}
	else
	{
	
		TWIWrite((uint8_t)(D4SENSORADDRESS << 1));
		currentstatus = TWIGetStatus();
	
		if(currentstatus == 0x18)
		{
			TWIWrite((uint8_t)(0x04));
			currentstatus = TWIGetStatus();
		
			if(currentstatus == 0x28)
			{
				TWIStart();
				currentstatus = TWIGetStatus();
				if(currentstatus == 0x10)
				{
					TWIWrite((uint8_t)((D4SENSORADDRESS << 1)+1));
					currentstatus = TWIGetStatus();
					
					if(currentstatus == 0x40)
					{
						ReadData[0]	= (0x3F &TWIReadACK());
					}	
				}
			}
		}
	}
	TWIStop();
}

I traced through the code and, after it calls the TWIStop() function at the bottom, it repeats and goes back to the top to the TWIStart() function.  It gets stuck on the second line of code:

 

void TWIStart(void)
{
	TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
	while ((TWCR & (1<<TWINT)) == 0);  <-------- The code is stuck here
}

Unfortunately, I'm not really sure what I did differently that would cause the code to get stuck.  I assume, based on the above results, it has something to do with the resent start command (which was received correctly, as it showed up on the scope and I received the status code 0x10 rather than 0x08 (which is the initial start condition transmit code).  Regardless, something funny is going on here and I'm kind of at a loss to know where to look.  

 

Any help you can give me would be appreciated!  If you need any additional screenshots or I left out key functionality of the code, let me know.  Thanks guys!

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

Well, that's unfortunate . . .

 

 

Did I post at a bad time and missed everyone, did I do something stupid and obvious that I should know better, or does no one have any insight as to what the issue could be?

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

To be honest,    I could not understand what you were trying to do.

 

You appear to have a PC dongle that can control the I2C bus.    i.e. nothing to do with an AVR.

 

Either you read your Sensors with the PC or you throw away the dongle and read your Sensors with the AVR.

 

The dongle does look as if it is a very handy tool for implementing a CDC device for the AVR's USART to talk to.    But I would not want to go down the route of Multi-Master-I2C unless you know what you are doing.

 

Returning to the case of "AVR as I2C Master" controlling several I2C slave devices:

1.   download a respected library.

2.   use it.    Especially the return values from the primitive functions.

3.   when you have your application working perfectly,   invent your own I2C code.

 

I am quite happy to help you debug (3) providing that you have got (1) and (2) working first.

 

I am fascinated by the "USB-ISS" device.    I would use a genuine hardware I2C Slave e.g. 24Cxxx for debugging rather than some "Universal PIC".    I am sure that the PIC can manage the I2C and USB in software but I would not put money on it.

 

My apologies if I have got the wrong end of the stick.    (I have made ZERO attempt at deciphering your scope traces.    it is easier to use a Logic Analyser)

 

David.

Last Edited: Thu. Sep 3, 2015 - 03:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

David,

 

Thanks for the reply!

 

I think I might have confused everyone with the USB-ISS module.  I only used that to verify issues I was seeing with the AVR aren't an issue with a bad sensor.  The USB-ISS is a good tool but it doesn't allow for some of the additional functionality of AVR devices so I'm trying to replace that, starting with the I2C communication.  

 

Are you suggesting I should replace the library I'm currently using as it's not considered a respected library or is that a general recommendation?  The reason I ask is I have been able to use this library with another sensor I have (that doesn't require the additional repeated start command) and haven't had any issues (and it does appear to be consistent with the sample code provided by Atmel in the datasheet).  If there's a better library, I don't have a problem moving towards it but I want to make sure I'm understanding what you're suggesting.  

 

In my particular case, the sensor does appear to be working as expected prior to adding the additional repeated start command and since then, my chip seems to halt at the following line on the start command after my stop command:

 

void TWIStart(void)
{
	TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
	while ((TWCR & (1<<TWINT)) == 0);  <-------- The code is stuck here
}

I'm assuming that the repeated start command is potentially causing an issue with the TWINT bit but I'm not sure why that is.  So that's where I'm at.  I'm able to send the MSB request (0x61), I'm able to request to receive the data and actually DO receive what I expect (after the 0x41 bit, I am receiving 0x01, which I would expect) but if you look at my scope picture, there is no stop bit shown and it gets caught in the next start command.  

 

Do you see anything obvious that I'm missing?  Or should I find a new library?

 

Let me know when you get the chance and thanks again for the reply! My only other option was to go to Atmel directly for support and I haven't had great luck with that (in fact I've purposely gone to an older 8 bit chip to avoid having to do with their newer framework as it's easier to get support in here).  

 

Thanks again!

 

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

I common error I see with I2C is the master must NAK the last data byte when reading data from the slave in order for the slave to release the bus so the master can send the stop and be ready to start again.   Could that be your problem.

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
https://www.onegold.com/join/7134f67c2b814c5ca8144a458eccfd61

 

 

 

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

ki0bk wrote:

I common error I see with I2C is the master must NAK the last data byte when reading data from the slave in order for the slave to release the bus so the master can send the stop and be ready to start again.   Could that be your problem.

 

Jim

 

 

Well, you were definitely onto something!  I changed the ACK to a NACK command and it stopped freezing up!  Unfortunately, it still appeared to not recognize the stop command from being recognized (you'll note on the scope pic there is no stop bit after 0x61 is transmitted):

 

 

I didn't worry about this until I added the second section where I request the LSB as well and now it doesn't appear to make it to the second request:

 

 

I ended up fixing this by adding a 20 us delay to the TWI code so the stop command now looks like this:

 

void TWIStop(void)
{
	TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
	_delay_us(20);
}

I'm not entirely sure why this fixes it (and I haven't found the limits of the delay that make it work) but it appears to have fixed the issue on the MSB.  However, I'm now looking at the data on the LSB coming in incorrectly (0x40 for some reason, possibly the status bit?):

 

 

 

I fixed this by setting the last command in the LSB to be an ACK (despite the fact the MSB required a NACK):

 

 

While I am happy this works, this leaves me with two questions:

 

1)  Why is it adding the delay is necessary for a stop bit?  I'm only running at 200khz so I wouldn't anticipate the AVR chip to be moving too fast for that command, according to their datasheet.  I'm assuming I'm better off fixing this issue with a while statement but I don't know what it is that's causing the issue in the first place to know what to poll for.  Any idea what might be causing the stop bit issue?

2)  Why is it the MSB requires a NACK and the LSB requires an ACK?  This doesn't really make sense to me.  

 

Any help you can give me to understand these two questions would be greatly appreciated.  Thanks again, guys!

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

Oh, and here's the final code for reference.  Sorry about that!

 

void D4I2CSensorIn(void)
{
	uint8_t currentstatus = 0;
	
	// Request MSB
	TWIStart();
	if (TWIGetStatus() != 0x08)
	{
		// Error - Start condition has not been transmitted
	}
	else
	{
		TWIWrite((uint8_t)(D4SENSORADDRESS << 1));
		currentstatus = TWIGetStatus();
		
		if(currentstatus == 0x18)
		{
			TWIWrite((uint8_t)(0x04));
			currentstatus = TWIGetStatus();
			
			if(currentstatus == 0x28)
			{
				TWIWrite((uint8_t)(0x61));
				currentstatus = TWIGetStatus();
				
				if(currentstatus == 0x28)
				{
					// MSB Request Complete
				}
			}
		}
	}
	TWIStop();
	
	//Request Read MSB
	TWIStart();
	if (TWIGetStatus() != 0x08)
	{
		// Error - Start condition has not been transmitted
	}
	else
	{
	
		TWIWrite((uint8_t)(D4SENSORADDRESS << 1));
		currentstatus = TWIGetStatus();
	
		if(currentstatus == 0x18)
		{
			TWIWrite((uint8_t)(0x04));
			currentstatus = TWIGetStatus();
		
			if(currentstatus == 0x28)
			{
				TWIStart();
				currentstatus = TWIGetStatus();
				if(currentstatus == 0x10)
				{
					TWIWrite((uint8_t)((D4SENSORADDRESS << 1)+1));
					currentstatus = TWIGetStatus();
					
					if(currentstatus == 0x40)
					{
//						ReadData[0]	= (0x3F &TWIReadACK());
						ReadData[0]	= (0x3F &TWIReadNACK());
					}	
				}
			}
		}
	}
	TWIStop();
	
	
	// Request LSB
	TWIStart();
	if (TWIGetStatus() != 0x08)
	{
		// Error - Start condition has not been transmitted
	}
	else
	{
		TWIWrite((uint8_t)(D4SENSORADDRESS << 1));
		currentstatus = TWIGetStatus();
			
		if(currentstatus == 0x18)
		{
			TWIWrite((uint8_t)(0x04));
			currentstatus = TWIGetStatus();
				
			if(currentstatus == 0x28)
			{
				TWIWrite((uint8_t)(0x62));
				currentstatus = TWIGetStatus();
					
				if(currentstatus == 0x28)
				{
					// MSB Request Complete
				}
			}
		}
	}
	TWIStop();
	
	//Request Read LSB
	TWIStart();
	if (TWIGetStatus() != 0x08)
	{
		// Error - Start condition has not been transmitted
	}
	else
	{
		
		TWIWrite((uint8_t)(D4SENSORADDRESS << 1));
		currentstatus = TWIGetStatus();
		
		if(currentstatus == 0x18)
		{
			TWIWrite((uint8_t)(0x04));
			currentstatus = TWIGetStatus();
			
			if(currentstatus == 0x28)
			{
				TWIStart();
				currentstatus = TWIGetStatus();
				if(currentstatus == 0x10)
				{
					TWIWrite((uint8_t)((D4SENSORADDRESS << 1)+1));
					currentstatus = TWIGetStatus();
					
					if(currentstatus == 0x40)
					//					if(currentstatus == 0x28)
					{
						ReadData[1]	= TWIReadACK();
//						ReadData[1]	= TWIReadNACK;
					}
				}
			}
		}
	}
	TWIStop();
	
}