I2C newbee question

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

I'm having a lot of trouble with programming I2C on a breadboard with an ATmega4809 and I want to DIY a I2C hub.

Researching existing boards/projects they look very simple with just a 4 pin input and a number of 4 pin outputs.  I get it!

 

But why not put the 2 pullup resisters on the board as well?  Is there something I'm missing?

 

The links provided are examples of boards I've found while doing my research.

 

Build your own

Store bought

This topic has a solution.

Happy Trails,

Mike

JaxCoder.com

Last Edited: Sun. May 15, 2022 - 03:47 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

That must be the lowest-tech project that anyone has ever thought of.

 

You only need a a single pair of pullups on the bus.

Most Ebay modules have on-pcb pullups.

 

So by the time you have connected 8 slaves each with 4k7 pullups you end up with an effective 588R pullup which far exceeds the maximum 3mA sink current @ 5V.

 

If none of your slaves have pullups,  you can put a single pair of pullups on your "hub board" 

 

With a regular 3.3V bus the pullup values are correspondingly lower.   e.g. 588R is fine for a 2V bus.

Last Edited: Sun. May 15, 2022 - 03:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
If none of your slaves have pullups,  you can put a single pair of pullups on your "hub board" 

 

Thanks I thought it would be Ok but the boards I found didn't have them and it didn't make sense why they didn't.

 

Thanks

Happy Trails,

Mike

JaxCoder.com

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

Well,  it is more sensible without pullups.

 

But from an Ebay module point of view most punters will only have one or two slaves.  e.g. 4k7 or 2k35.

 

From an Arduino point of view,  they enable AVR internal 30k pullups.    Completely outside I2C spec but it will probably make a punter's I2C slave give some response.

 

Your "hub" boards would be more convenient if they had 8 Grove and one male header.

 

David.

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

david.prentice wrote:
Well,  it is more sensible without pullups.

 

The only reason I'm making this hub at all is the 1/8W resistors I have play h&ll in my breadboard; sometimes work and sometimes not. (Depending on how I hold my jaw)

 

It's really frustrating trying to debug code and the hardware not rock solid. 

So I thought  I would create a board for the slave component but thought while I was at it I would make a hub with a couple of slots for future use.

Happy Trails,

Mike

JaxCoder.com

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

Here are a couple app notes that may be useful.

Attachment(s): 

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

Thanks Mark

 

The problem I'm having is that I'm trying to program an EEPROM using I2C.

 

My Saleae shows that the data is being correctly to the bus; start, address, packet and stop but when I go to read it back it sometimes shows the correct result but most the time it doesn't.

 

I have my I2C Hub board wired up and will do further debugging latrer, but I feel it will be a lot easier knowing that the hardware is now solid.

Happy Trails,

Mike

JaxCoder.com

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

Hi Mike,

 

The ATmega 512 kbit EEPROM I have on that board i put the picture of up, has physical 128 byte blocks you cannot write across if you are just doing a write, write, write, write by giving it the address of the first byte, and then just write one after the other while the EEPROM increments the byte address.  When you try to step over the physical 128 byte block / page boundary in the EEPROM, it goes back to the beginning of the block you were in and starts all over.  I have seen this too.  if I start my write somewhere inside the 128 byte block and finish before the end of the block, it all works fine.  Look at your EEPROM data sheet carefully

 

If you want to write as fast as possible you can do the consecutive writes until you get to the last byte of the block.  Then you have to do the business with putting in the EEPROM device address+I2C_WRITE to prepare the write condition, and then give the address of the first byte of the next block and put the next data write into the first byte of the new block, and then write, write, write for up to 128 bytes.  You just have to keep track of where the physical boundaries are in the EEPROM, and step over them carefully in code.  

 

I dont need my writes to be blazing fast of I just to it the slow way.  I use Peter Fleury's C library for the I2C calls before he rewrote it in assembler

 

//Mark's
void ee24xx_write_byte(uint16_t address, char data)
{
    char addrs;
    i2c_start_wait(EE24C512addr + I2C_WRITE);        // set device address and write mode
    addrs = address >> 8;
    i2c_write(addrs);                                // write address high
    addrs = address;
    i2c_write(addrs);                                // write address low
    i2c_write(data);                                 // write value to EEPROM
    i2c_stop();                                        // set stop conditon = release bus
}

 

To read from the EEPROM none of the physical boundary business needs thinking about.  You give it the EEPROM device address with a I2C_READ, send across the address of the first byte you want to read, and then just read, read, read, read as many as you like.  The read is just from the next byte address.  You need a readNAK() after the last read before the i2c_stop() to terminate properly.  Those reads go blinding fast, compared to the writes.

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

Hey Mark,

 

I created a, what I call a memory Brick board that has 8 24LC512 EEPROMs on it and using an ATmega328P I can read/write to all the devices on the board using I2C and the 328.

 

So I have another project I'm working on that uses the ATMega4809 and I need to get the I2C on the 4809 working and this is where I'm running into problems.  Because the chip is so new there's not much on the web in respect to the workings.  I've found some that has really helped in other areas but not for the I2C.

 

I only used the 24LC512 because, well I had them and also because I had already got it working with the 328 and thought it would be easier than picking a device that Ihadn't worked with previously.  I've put off learning/working with I2XC because I thought it a difficult protocol but I'm finding it's not hard once you understand the mechanics.

 

I'll get it, it'll just take time.  (I'm stubborn that way)  :)

 

Thanks for your feedback.

Happy Trails,

Mike

JaxCoder.com

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

I'm running my I2C with SCL_CLOCK = 400000L, and F_CPU = 16000000L with this to initialize.  Is your F_CPU fast enough?

 

/*************************************************************************
 Initialization of the I2C bus interface. Need to be called only once
*************************************************************************/
void i2c_init(void)
{
  /* initialize TWI clock: 400 kHz clock, TWPS = 0 => prescaler = 1 */
  
  TWSR = 0;                         /* no prescaler */
  TWBR = ((F_CPU/SCL_CLOCK)-16)/2;  /* must be > 10 for stable operation */

}/* i2c_init */

 

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

Go on.  You just write a single universal function.   Then you can write as much or as little as you want.

 

A 24Cxxx device takes the same amount of time to write a page e.g. about 3ms.

 

Your 24C512 has a 128 byte page (from memory)

You just have to check for page boundaries,  fill the page buffer, write to the page.

Repeat until done.

 

e.g. 1 byte = 3ms

e.g. 128 bytes on same page = 3ms

e.g. 128 bytes that straddle pages = 6ms

e.g. 256 bytes on 2 pages   = 6ms

e.g. 256 bytes that straddle pages = 9ms

...

 

Remember that 24Cxxx does not ACK when it is busy with page-write.

You either poll for ACK or just allow 5ms.

 

It does not matter what MCU you use.   It is the 24xxx behaviour as documented in the 24C512 datasheet.

 

You can write the whole 64kB EEPROM in ~ 384 ms for the page-writes.

It is the I2C traffic involved in "filling the page buffer" that takes the time.  e.g 64kB @ 100kHz = 6 seconds

64kB @ 400kHz = 1.5 seconds.   So most of your time is spent  in traffic rather than the EEPROM being busy writing the page.

 

In fact this is very similar to ISP programming an AVR.   Most time in SPI traffic with a few ms involved in actually writing to the Flash pages.

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

MarkThomas wrote:

I'm running my I2C with SCL_CLOCK = 400000L, and F_CPU = 16000000L with this to initialize.  Is your F_CPU fast enough

 

 

Yes I'm running at 20MHz

 

david.prentice wrote:
A 24Cxxx device takes the same amount of time to write a page e.g. about 3ms.

I do a write, 13 bytes then have a 100mS delay before I attempt to read.

 

I haven't and won't have time to work on it until later today but appreciate all the feedback.

Happy Trails,

Mike

JaxCoder.com

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

That sounds crazy.   If you only want to write 13 bytes,  it takes about 1.5 sec to send the byte traffic and 3ms for the page-write.  (if you are not crossing a page boundary)

 

So why do wait for 100ms ?

When the page-write only takes typical 3ms

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

david.prentice wrote:

So why do wait for 100ms ?

When the page-write only takes typical 3ms

 

I put it in there just to be sure no timing problems...I was desperate!  :)

Happy Trails,

Mike

JaxCoder.com

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

It is simple enough to poll for completion.

Or just look at maximum time in the datasheet.  And wait for that worst case time.

 

The good thing about I2C library functions is that they return a status value.   Just make use of it.

Last Edited: Mon. May 16, 2022 - 02:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I got basic I2C code working reading and writing to a 24LC512 EEPROM.

 

Thanks to all that provided feedback.

 

I've provided the code as attachments for those that are interested,  It's written in C++.

 

To use the code;

void TestI2C()
{
	uint8_t outbuf[14] = { 0x00, 0x01, 'B', 'u', 'g', 'g', 'g', 'g', 'g', 'g', 'g', 'e', 'r', 's' };
	uint8_t inbuf[12];
	
	// I use a factory pattern to get an instance of the I2C object.
	i2c = dmgr.GetI2C(F_CPU);	
		
	// The EEPROM required the first 2 bytes, after the start
	// and address byte to be the address where the data is to
	// be read from or wrote to.
    i2c->Start(CLIENT_ADDR, I2C_DIRECTION_BIT_WRITE);
	i2c->SendDataoutbuf, 14);
	i2c->Stop();
	
	// Delay 5mS for the EEPROM to finish digesting.
	_delay_ms(5);

    // Send the address to read from
	i2c->Start(CLIENT_ADDR, I2C_DIRECTION_BIT_WRITE);
	i2c->SendData(outbuf, 2);
	i2c->Stop();
		
	// Probably not needed
	_delay_ms(5);

    // Read data from the EEPROM 
	i2c->Start(CLIENT_ADDR, I2C_DIRECTION_BIT_READ);
	i2c->ReceiveData(inbuf, 12);
	i2c->Stop();
}

 

Attachment(s): 

Happy Trails,

Mike

JaxCoder.com

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

Seriously.   Any respectable I2C libraries provide return values.   And punters can use them.

 

void functions are the work of the Devil.

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

david.prentice wrote:
Seriously.   Any respectable I2C libraries provide return values.   And punters can use them.

 

Did you look at the code?

 

Just because I don't use them in the demo doesn't mean I don't provide for them.  I had the debugger running so I knew the return values.

Happy Trails,

Mike

JaxCoder.com

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

No,  I did not make any effort to read your CPP or H code.

 

I just observed that your example did not do any error checking.

 

It is physically possible to drive your motor car with your eyes closed.   You just have turn the steering wheel at the right time,  apply brakes at the right time, ...

But I bet that your passengers would feel happier if you looked where you were going.

 

Yes,  your example should work.   Your page location and write contents do not cross any page boundary (on a AT24C512)

The delay after setting the (read) page location is unnecessary.    The stop() is unnecessary.   You could just write the two location bytes to SLAVE_W and do an immediate repeated-start() to SLAVE_R.

 

Surely you would sleep soundly in your bed if you knew that you were checking for Slave ACK,  page boundary,  ...

 

David.

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

mike32217 wrote:
I got basic I2C code working reading and writing to a 24LC512 EEPROM.

 

 

david.prentice wrote:

I just observed that your example did not do any error checking.

 

I said it was basic code not production ready or even usable in a program, only to illustrate how it can be performed.

 

If it's not up to this sites standards I'll remove it!

Happy Trails,

Mike

JaxCoder.com

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


Just to say, you are not alone. Peter Fleury's example for his I2C library is:

 

Not a sign of return codes being used (apart from the call to i2c_readNak() for which the returned value is then never inspected!)

 

I think we all know why he's done this - it's to show the "barebones structure" of how to use the API without the additional "noise". However I can't help thinking it should be emphasising that return codes are available from most functions and that they are a fundamental part of how I2C should be used.

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

I agree 100%.

 

Fleury's example shows the barebones steps clearly.   But does not use the return values.

Nor does it emphasize the purpose (and risk) of the i2c_start_wait() function.

 

i2c_start_wait() polls the EEPROM until it ACKs.  

If the EEPROM is busy during its page-write the function polls until the page-write has completed.

If you have the wrong Slave address you will hang forever !!

 

Personally,  I would prefer to use i2c_start() result to check for the Slave existence.

And would be happier with i2c_poll_24Cxxx() as a more intuitive name than i2c_start_wait() because I am not aware of any other I2C devices with this behaviour.

 

I still maintain that :

 

void functions are the work of the Devil.

 

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

Friend,

Try MPLAB X IDE.  Their I2C library is superb; and yet very tricky to make it work.

 

Contact Microchip technical support.  They will guide you though step by step!  yes