Need help with I2C (Repeated Start)

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

Hi,

 

I am using I2C protocol (AtMega2560) to write certain registers of the camera module OV7670. Need to implement a repeated start condition for that purpose. I have written the code for that but not sure whether it will work practically or not (i.e. there is no compilation errors).

Actually I have learned the theory of the protocol but implementing it for the first time.

 

Thanks.

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>

/*I2C Functions Start*/

void twi_init()
{
	TWBR = 0xC6;					/*SCL would be of 10MHz with Pre-scale of 1*/
	TWCR = (1 << TWEN);
	TWSR = 0x00;					/*Pre-scale is set to 1*/
}

void twiStart (void)
{
	TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTA);		/*(TWI interrupt cleared) | (TWI enabled) | (TWI start condition)*/
	while (!(TWCR & (1 << TWINT)));							/*Wait for the start condition to be transmitted*/
	TWCR |= (0 << TWSTA);									/*Clear the start bit*/
}

void twiStop (void)
{
	TWCR |= (1 << TWSTO);									/*Set the Stop bit*/
}

void twiWrite (uint8_t deta)
{
	TWDR = deta;											/*Data to be sent*/
	TWCR = (1 << TWINT) | (1 << TWEN);
	while (!(TWCR & (1 << TWINT)));
}

/*I2C Functions End*/

/*Sart of the Main Function*/

int main()
{
	twi_init();
	twiStart();			/*Set the start condition*/
	twiWrite(0x42);		/*Slave address*/
	twiWrite(0x00);		/*Address of the first memory location*/
	
	twiWrite(0x02);		/*Gain setting*/
	_delay_us(500);
	twiStart();			/*Repeated Start*/
	twiWrite(0x84);		/*Blue setting*/
	_delay_us(500);
	twiStart();			/*Repeated Start*/
	twiWrite(0x84);		/*Red setting*/
	twiStop();			/*Set the Stop Condition*/
	while (1);
}

/*End of Main Function*/

 

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

Are you sure you need the repeated start? Usually when you write to consecutive addresses you do not need to send a restart, but this may be device dependend.
Other comments:
- You do not need to clear the start bit. This will happen on the next command.
- Also clear the interrupt and set the enable bit when you send the stop bit
- Check the status bit for errors. This may help to troubleshoot when it does not work
- Run the code to see if it works :-)

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

You should probably insert some delay at the start of main() to make sure that the camera is ready to receive I2C.

 

BTW I found this online:  https://circuitdigest.com/microcontroller-projects/how-to-use-ov7670-camera-module-with-arduino

 

Probably a good idea to use as a starting point for you (if that wasn't already the case?). I also like the way the code handles run-time errors (missing ACK).

/Jakob Selbing

Last Edited: Tue. Nov 19, 2019 - 06:55 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

If you use a Repeated Start you need to send the Slave Address e.g. 0x42 (W) (0x43 R)
Most I2C devices let you write data to contiguous registers i.e. the register self-increments.
Otherwise you have to ReStart address, set register, send data
You must study your datasheet.

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

thenuts wrote:
this may be device dependend

It certainly may be - therefore, you should assume that it is, and verify in the datasheet.

 

david.prentice wrote:
You must study your datasheet

That is the key!

 

jaksel wrote:
handles run-time errors (missing ACK).

Is also very important.

 

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...
Last Edited: Tue. Nov 19, 2019 - 11:02 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

A repeated start is the same as a STOP followed by a START when there is only one Master on the I2C system.  You should do this:

twi_init();
	twiStart();		/*Set the start condition*/
	twiWrite(0x42);		/*Slave address*/
	twiWrite(0x00);		/*Address of the I2C device's internal register for Gain setting */
	twiWrite(0x02);		/*Gain setting value? */
	twiStop();

	twiStart();		/*Repeated Start*/
        twiWrite(0x42)          /*  Slave address */
        twiWrite ( address of the I2C device's internal register that holds the Blue setting )
	twiWrite(0x84);		/*Blue setting value? */
	twiStop();

	twiStart();		/*Repeated Start*/
        twiWrite(0x42);         /* Slave address */
        twiWrite( address of the register that holds the Red setting )
	twiWrite(0x84);		/*Red setting value ? */
	twiStop();		/*Set the Stop Condition*/

 

Last Edited: Wed. Nov 20, 2019 - 01:13 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

Where to begin, I2C void functions, no status checks or ack/nak checks, blind sending of data, use of delays rather than status checking, no reason this should not work....

 

Any chance there are pull up resistors on the bus?

 

Can I suggest you use a proven I2C lib like peter flurry’s, and drop the use of the above.

jim

 

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

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

Last Edited: Wed. Nov 20, 2019 - 02:59 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have made changes as per the suggestions, please check for the possible mistakes that remain.

Here is the improvised version of the code:

#define F_CPU 16000000UL
#include <util/delay.h>
#include <avr/io.h>


void  twiError ()
{
	DDRB = 0x80;
	PORTB ^= 0x80;
	_delay_ms(500);
}

void twi_init()
{
	TWBR = 0xC6;					/*SCL would be of 10MHz with Pre-scale of 1*/
	TWCR = (1 << TWEN);
	TWSR = 0x00;					/*Pre-scale is set to 1*/
}

void twiStart (void)
{
	TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTA);		/*(TWI interrupt cleared) | (TWI enabled) | (TWI start condition)*/
	while (!(TWCR & (1 << TWINT)));							/*Wait for the start condition to be transmitted*/
	TWCR |= (0 << TWSTA);									/*Clear the start bit*/
	if ((TWSR & 0xF8) != 0x08)								/*Start acknowledgment check*/
	twiError();
}

void twiStop (void)
{
	TWCR |= (1 << TWSTO);									/*Set the Stop bit*/
}

void twiAddr (uint8_t addr)
{
	TWDR = addr;											/*Data to be sent*/
	TWCR = (1 << TWINT) | (1 << TWEN);
	while (!(TWCR & (1 << TWINT)));							/*Wait for the transmission*/
	if ((TWSR & 0xF8) != 0x18)								/*SLA+W acknowledgment check*/
	twiError();
}

void twiData (uint8_t deta)
{
	TWDR = deta;											/*Data to be sent*/
	TWCR = (1 << TWINT) | (1 << TWEN);
	while (!(TWCR & (1 << TWINT)));							/*Wait for the transmission*/
	if ((TWSR & 0xF8) != 0x28)								/*Data acknowledgment check*/
	twiError();
}




int main()
{
	twi_init();
	twiStart();			/*Set the start condition*/
	twiAddr(0x42);		/*Slave address*/
	twiData(0x00);		/*Address of the first memory location*/
	
	twiData(0x02);		/*Gain setting*/
	twiStop();
	twiStart();			/*Repeated Start*/
	twiData(0x84);		/*Blue setting*/
	twiStop();
	twiStart();			/*Repeated Start*/
	twiData(0x84);		/*Red setting*/
	twiStop();			/*Set the Stop Condition*/
	while (1);
		
}

 

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

Yes you can suggest whatever that is helpful. But I am trying to learn it all from the scratch and your help in that will be appreciated.

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

Simonetta wrote:

A repeated start is the same as a STOP followed by a START when there is only one Master on the I2C system.

 I have read somewhere about it, but the reasons of doing so are vague.

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

david.prentice wrote:
Most I2C devices let you write data to contiguous registers i.e. the register self-increments. Otherwise you have to ReStart address, set register, send data You must study your datasheet.

Absolutely friend, but I couldn't find anywhere in the datasheet of the module whether the registers self-increment or not.

So, this part would go with trial and error.

 

 

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

Thanks for it friend. Please check the new version of the code I have uploaded. :)

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

I am already rusty at this, so a refresh is probably due.

 

https://en.wikipedia.org/wiki/I%C2%B2C

 

A repeated start occurs if the active master does not release the bus (e.g., stop condition). If the i2c link has one master, it does not need to worry about transaction interrupts. The repeated start is a way to hold onto the bus and make multiple transactions; thus, it blocks the other masters. Some slave devices require it for complex matters (e.g., reading more extensive memory collections).  

 

The reference implementation I used for my AVR is

 

https://github.com/arduino/ArduinoCore-avr/blob/master/libraries/Wire/src/utility/twi.c

 

A few changes (e.g., digitalWrite can be changed to register manipulation), and it should work, at least it has for me so far (328p, 328pb, 1284p, 324pb).

 

I only looked at your code enough to see that you can probably understand the twi.c. I2C is not a good thing to reinvent, read the white papers (links on Wikipedia), and use a source that is known to (more or less) work.

 

For a few examples using twi.c, these were freshly brewed.

 

https://github.com/epccs/Gravimetric/blob/master/Applications/lib/rpu_mgr.c

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

Zarrar wrote:
I am trying to learn it all from scratch

 

EDIT: If your goal is really to learn I2C (aka "TWI") from scratch, then I would strongly suggest that you start with something a lot simpler than this camera!

 

Anyhow, to to learn I2C (aka "TWI") from scratch, you need to start here:

 

UM10204 I 2C-bus specification and user manualhttps://www.nxp.com/docs/en/user-guide/UM10204.pdf

 

This is the Official specification of the I2C protocol by NXP - who invented it, and "own" it.

 

It is a very well-established and widely-used standard - so there are plenty of references and tutorials available; eg,

 

https://en.wikipedia.org/wiki/I%C2%B2C

 

https://i2c.info/

 

https://www.i2c-bus.org/

 

Having understood the basics of the protocol itself, you then need to study the particular implementation of it on the devices you are using

 

This will be described in the datasheets for the devices you are using. Usually, manufacturers will also provide Application Notes, examples, etc to illustrate & further describe what the datasheet says.

 

For your ATmega2560, start by going to the Product Page:

 

https://www.microchip.com/wwwproducts/en/ATmega2560

 

There you can see links for the datasheet.

 

The I2C is described in section 24, "2-wire Serial Interface" (note that Atmel, among others, refer to it as "2-Wire" or "Two-Wire" or "TWI").

 

Also, go to the 'Documents' tab. There you will find a large number of application notes - including several relating to I2C; eg,

 

AN_1981 - AVR155: Accessing an I2C LCD Display using the AVR 2-Wire Serial Interface

 

AN_2565 - AVR311: Using the TWI module as I2C slave on tinyAVR and megaAVR devices

 

AN2480 - AVR315: Using the TWI Module as I2C Master

 

 

 

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...
Last Edited: Wed. Nov 20, 2019 - 09:31 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

In #1, Zarrar wrote:
camera module OV7670

In #11, Zarrar wrote:
I couldn't find anywhere in the datasheet of the module whether the registers self-increment or not

What datasheet are you using? Please give a link.

 

I didn't find it on the OmniVision site, but there are loads of 3rd-party sites with things they call an "OV7670 datasheet"; eg,

 

https://www.voti.nl/docs/OV7670.pdf

 

In that, it says it has an "SCCB" interface - which, it says, is "compatible with" I2C.

 

So it's not just plain, vanilla, straightforward I2C;  it's something "compatible with" I2C - whatever that means!

 

So you're going to need to do some more googling to find out about this "SCCB" interface - and exactly how "compatible" it is with I2C.

 

I found this:

SCCB seems to be similar to I2C but you will definitely have to learn a thing or two about it before making your camera work.

 

https://www.robotshop.com/commun...

 

Again, If your goal is really to learn I2C (aka "TWI") from scratch, then I would strongly suggest that you start with something a lot simpler than this camera!

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: 2


awneil wrote:
start with something a lot simpler than this camera!

eg, Microchip have this:

 

https://www.microchip.com/DevelopmentTools/ProductDetails/PKSERIAL-I2C1

 

It is a simple demo board with a few simple, standard, common I2C devices on it all ready assembled with LEDs, etc, to demonstrate their operation.

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: 1

This is the Official specification of the I2C protocol by NXP - who invented it, and "own" it.

I always think of Philips as being the inventor, since I was using their I2c books way back in the 80's...looks like they were bought out in 2006:

 

Aug 2006:  With a change of ownership from Royal Philips Electronics to a consortium of private equity investment companies, Philips Semiconductors will simply become NXP.

NXP stands for the consumer's "next experience," according to CEO Frans van Houten. NXP is also associated with Philips Semiconductors' Nexperia platform where audio and video processing technologies reside.

The NXP trademark will include a "Founded by Philips" tagline as part of its branding.

 

Yes, to learn I2C coding, work your way up from the simplest chips.  A good scope or logic analyzer is extremely useful (especially analyzers that include I2C decoders).

 

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

avrcandies wrote:
I always think of Philips as being the inventor

Yes, historically that's true.

 

But Philips became NXP - as you noted.

 

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

Simonetta wrote:
A repeated start is the same as a STOP followed by a START

No, it's not.

 

A Repeated START (Sr) is where you have a START without a preceding STOP; ie, a START while the bus is still in the Busy state.

 

Whether a particular slave cares about this is another matter ...

 

 

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: 1

Simonetta wrote:
A repeated start is the same as a STOP followed by a START when there is only one Master on the I2C system.

Peter Fleury's library has the repeated start just being a start:

 

char i2c_rep_start(char address)
{
    return i2c_start( address );

}/* i2c_rep_start */
char i2c_start(char address)
{
    uint8_t   twst;

	// send START condition
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

	// wait until transmission completed
	while(!(TWCR & (1<<TWINT)));

	// check value of TWI Status Register. Mask prescaler bits.
	twst = TW_STATUS & 0xF8;
	if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;

	// send device address
	TWDR = address;
	TWCR = (1<<TWINT) | (1<<TWEN);

	// wail until transmission completed and ACK/NACK has been received
	while(!(TWCR & (1<<TWINT)));

	// check value of TWI Status Register. Mask prescaler bits.
	twst = TW_STATUS & 0xF8;
	if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;

	return 0;

}/* i2c_start */

 

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

The START condition itself  is identical - whether it's  "regular" or "repeated"

 

The difference is in what precedes it ...

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: 1

From my oscilloscope observations:  A repeated START occurs when:

 

-the Master has sent START and SLA+w___ and the Slave has sent ACK,

 

-the Master has sent at least one data byte___ and the Slave has sent ACK for that byte. 

   The Slave has released both SDA and SCL and the Master is asserting both SDA and SCL.  The Slave is expecting another data byte.

 

-the Master releases SCL, and it floats to logic high.  The Slave expects this to be the rising edge of the bit7 clock for the next data byte. The Slave expects the SDA to be unchanged in the interval that the SCL is released by the Master.  If the Master asserts SCL, then the Slave expects seven more data bits to be forthcoming, and will issue an ACK when they have finished.

 

-However, the Master asserts SDA.  The Slave, if properly designed, stops expecting a data byte from the Master and instead expects the Master to begin sending a new 8-bit Slave Address (7-bit shifted SLA and read/write in bit 0).