Classic atmega which supports TWI/I2C fast mode plus 1MHz speed ?

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

Hi,

As I just found out that bottleneck in my project turns out to be i2c speed. Right now I am running i2c bus at 400kHz with atmega328p which is running at 16MHz. My slave devices also support fast mode plus mode. So I was looking at other classic 8 bit avr chips which support this mode and I can't seem to find any. All those chips which do support them are the new chips like atmega 4809, attiny 3216 etc. 

 

Since I have only used two chips up until now atmega328p and atmega2560. Learned all the in's and out's of these. I am also using cheap USBASP clone programmer. Migrating to a new chip seems very difficult. I have to learn a lot and have to buy that pricey atmel ice programmer. Can't find tutorials for the new chips at last just a datasheet. No libraries either.

 

  1. Am I missing something? Or is there a classic atmega which support I2C fast mode plus?
  2. I did experiment with atmega328p to run at higher i2c bus like 800kHz it did work once but didn't test it enough to make sure it's stable. If I go with 328p can I run it higher than 400kHz bus speed or will there be consequences? 
  3. If I had to go with new chips, which one will be the best? I mean little newbie friendly and tutorials wise. Is it atmega 4809? Being it is used in arduino uno wifi rev2.

 

Note :- My end product is for automotive use, stability is a concern.

Last Edited: Sun. Dec 8, 2019 - 09:02 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First up, get your MHz and kHz correct. mhz is millihertz which clearly is not what you want to communicate.

 

The mega328 at 16MHz can only do twi at 1MHz max.

 

Your application is Automotive, but yet you're concerned about the cost of the programmer? If stability is a concern, why are you using twi/i2c? Hopefully your code has timeouts and a recovery mechanism - i2c can lock up.

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

Kartman wrote:

First up, get your MHz and kHz correct. mhz is millihertz which clearly is not what you want to communicate.

 

Ah, my bad fixed now.

 

Kartman wrote:

The mega328 at 16MHz can only do twi at 1MHz max.

 

But datasheet says Up to 400kHz transfer speed or I am seeing things wrong. 

 

Kartman wrote:

Your application is Automotive, but yet you're concerned about the cost of the programmer?

 

Not concerned, up until now I didn't need it. Now I might have to buy it.

 

Kartman wrote:

If stability is a concern, why are you using twi/i2c? Hopefully your code has timeouts and a recovery mechanism - i2c can lock up.

 

indecision Thanks for the reminder. I2C is the only option.

 

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

Calculate the bit rate divisor for the twi peripheral - will only go to 1MHz. Whether or not there's other considerations, you'll have to ask Microchip.

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

You can adjust the Peter Fleury I2C library to run faster than 400KHz (I2C speed).   I believe that there is a "bit-banging" option that is written in assembly language.  By rewriting this code, you should be able to get 1 or 2 MegaHertz in I2C speed. 

I2C speed is limited only by the speed that the released signal can rise from gnd to Vcc.  This speed is primarily determined by the number of devices on the I2C line and the value of the pull-up resistors on SDA and SCL.   These pull-ups are usually 3-5K ohms.    Reduce these to 680 -- 1K ohms and change the I2C code.  Doing bit-banged I2C on the SDA and SCL lines is basically changing the line from a input (a released line) to an output that is pre-set to logic zero (an asserted line).

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

Running the twi at 1MHz with interrupts is going to chew a fair slug of cpu time, so bit banging isn’t that bad an option.

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

So this is what I found.

Using the Bit Rate Generator.

 

= 16000000 / (16 + (2 * 1 * 1)

= 16000000 / 18

= 888kHz

 

Settings TWBR value to 0 does not work at all. So at 16 MHz the max it will go to is 888kHz.

 

Setting the mcu clock to 20MHz I can reach 1MHz bus speed but haven't tested yet. Waiting for the crystal to come.

 

= 20000000 / (16 + (2 * 2 * 1)

= 20000000 / 20

= 1000kHz

 

Kartman wrote:
Running the twi at 1MHz with interrupts is going to chew a fair slug of cpu time

What does it mean?

 

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

I am using Peter Fleury I2C library. It has 3 files.

 

i2cmaster.h

i2cmaster.s

twimaster.c

 

Right now I am only using .h and .c file.

For bit banging I think you are talking about .s file. How do I test bit banging?

 

 

Last Edited: Mon. Dec 9, 2019 - 06:59 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A fair slug of cpu time? If the twi is running at 1MHz, which means it will interrupt, say, every 9 or 10 microseconds. This means the cpu will be working hard servicing those interrupts and doing just that will consume a fair amount of the processor's cycles. If you interrupt every 10uS with a 20MHz clock, you have 200 cpu cycles between interrupts. The isr has to stack the processor state, service the twi then restore the processor state. You've burnt at least 100 cycles already at a guess. It might actually be worse. More modern processors have fifos that buffer the data for the twi, so that it interrupts less.

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

I'm interested in this statement:

Heisen wrote:
I just found out that bottleneck in my project turns out to be i2c speed

As someone who has never found 400kHz or even 100kHz I2C to be a limiting factor, I wonder what sensor this is.

 

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

Display devices like SSD1306 or SSD1327 are very noticeable.   Especially with animations that might be 1024 bytes or 8192 byte transactions.

Most devices are fine on 100kHz.    e.g. 4 byte transactions

 

Fleury code is polled.   So yes,  your app will block until completion.

Interrupt code means that your app can continue normally.

 

Regarding bus speed.   You can achieve 400kHz with a 16MHz mega328P.

 

But do some sums.   400kHz bus means 22.5us per byte.    Or 360 CPU cycles @ 16MHz.    Fine for typical 80-cycle ISR()

The standard 100kHz bus means 90us per byte.    An 80-cycle ISR() will struggle @ 1MHz.

 

The I2C bus will wait for a slow CPU.   So as Mr Winterbottom has said,    not a limiting factor for sensors.

 

Look very carefully at your application.    A quick burst with polling may be preferable to interrupt-driven code.

Decisions are much easier when you put some numbers into them.   e.g. size of traffic,  CPU speed, proposed AVR.

 

David.

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

It is a LED driver chip from NXP, PCA9685. I am running two of those.

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

I am calling this function again and again throughout the program, this takes about 850 cpu cycles, is there a way to make it faster? This is the max amount of data I am sending at a time. 

Atmega328p @ 16MHz

TWI @ 800kHz

void pca9685_send(uint8_t led, uint16_t value)
{
	// send START condition
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
	
	// wait until transmission completed
	while(!(TWCR & (1<<TWINT)));
	
	if(led <= 15)
	{
		// send device address
		TWDR = LEDDRV1;
		TWCR = (1<<TWINT) | (1<<TWEN);
	}
	else
	{
		TWDR = LEDDRV2;
		TWCR = (1<<TWINT) | (1<<TWEN);
		led = led - 16;
	}
	
	// wail until transmission completed and ACK/NACK has been received
	while(!(TWCR & (1<<TWINT)));
	
	// send data to the previously addressed device
	TWDR = (0x08 + 4 * led);
	TWCR = (1<<TWINT) | (1<<TWEN);
	
	// wait until transmission completed
	while(!(TWCR & (1<<TWINT)));
	
	// send data to the previously addressed device
	TWDR = value;
	TWCR = (1<<TWINT) | (1<<TWEN);

	// wait until transmission completed
	while(!(TWCR & (1<<TWINT)));
	
	// send data to the previously addressed device
	TWDR = (value>>8);
	TWCR = (1<<TWINT) | (1<<TWEN);

	// wait until transmission completed
	while(!(TWCR & (1<<TWINT)));
	
	/* send stop condition */
	TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
	
	// wait until stop condition is executed and bus released
	while(TWCR & (1<<TWSTO));
}

 

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

Bit bash the i2c faster! Is there a specific reason you need to update the leds faster than a human can see them?

Last Edited: Mon. Dec 9, 2019 - 11:23 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You have a Start + 2 Write + Stop.   i.e. 3 bytes @ 400kHz = 67.5us + a few us for housekeeping.

67.5 us @ 16MHz is 1080 CPU cycles.

 

You will never achieve a real 800kHz.   But if you did,  it would be 540 cycles.    Your 850 cycles seems as good as you can expect.

 

The PCA9685 seems to be intended for ambient lighting.   The human eye is not going to want ambient colours to change in 70us.     Movies are 30FPS i.e. 33333 us between Frames.

 

As a general rule,   you set background colours every few minutes and not microseconds.

 

What are you trying to do?

 

David.

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

Heisen wrote:
I am calling this function again and again throughout the program,

If you mean your program consists of a primitive "superloop" and you call pca9685_send() once per loop, it still isn't a big CPU cycle load. Then again if you were calling this 32 times per loop; I.e. one call per LED then things start to look different.

 

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

Heisen wrote:
How do I test bit banging?

Same way you'd test non-bit-banged I2C!

 

Either a logic analyser, or a scope (or both).

 

 

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

Heisen wrote:

I am using Peter Fleury I2C library. It has 3 files.

 

i2cmaster.h

i2cmaster.s

twimaster.c

 

Right now I am only using .h and .c file.

For bit banging I think you are talking about .s file. How do I test bit banging?

 

 

To test the bit banging version, include the .s file in the project (for Atmel Studio).

Be sure to remove the .c file from the project.

 

 

Edit:  by "include", I don't mean #include.  Right click on the file name in the

         Atmel Solution Explorer, and select "Include in Project" from the menu.

         You may need to use Project --> Show All Files.

 

Last Edited: Tue. Dec 10, 2019 - 01:44 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the instructions, it worked.

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

Update

 

I was not able to make the TWI faster with bit banging. I tried two different bit banging libraries. 

- TWI Master Bit Bang Driver

- Peter Fleury I2C library

 

Bit banging TWI performed almost same as setting TWI speed to 400kHz.

The fastest I am able to get on atmega328p is with running it at 20MHz and setting TWBR to 3 and TWSR to 0. Which gave effective bus speed of over 900kHz. Stable, at least in my testing.

 

Used the following code as benchmark, lowest time I was able to get is 46 us.

 

void pca9685_send(uint8_t led, uint16_t value)
{
	// send START condition
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

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

	if(led <= 15)
	{
		// send device address
		TWDR = LEDDRV1;
		TWCR = (1<<TWINT) | (1<<TWEN);
	}
	else
	{
		TWDR = LEDDRV2;
		TWCR = (1<<TWINT) | (1<<TWEN);
		led = led - 16;
	}

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

	// send data to the previously addressed device
	TWDR = (0x08 + 4 * led);
	TWCR = (1<<TWINT) | (1<<TWEN);

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

	// send data to the previously addressed device
	TWDR = value;
	TWCR = (1<<TWINT) | (1<<TWEN);

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

	// send data to the previously addressed device
	TWDR = (value>>8);
	TWCR = (1<<TWINT) | (1<<TWEN);

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

	/* send stop condition */
	TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);

	// wait until stop condition is executed and bus released
	while(TWCR & (1<<TWSTO));
}

Kartman wrote:
Is there a specific reason you need to update the leds faster than a human can see them?

 

david.prentice wrote:
What are you trying to do?

 

This is what I am trying, here's my janky protoyping https://youtu.be/nqJIm7NxpRM

 

As stated the application is automotive, I believe if the source is moving at reasonable speed you tend to see the flickering of PWM lights. That's why I am trying to achieve highest refresh rate possible.

I might be wrong though.

 

The real reason is it's taking a lot of time to update the led's.

 

This statement takes 156 cycles alone, after sending the byte.

 

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

Most of the time is wasted because of this.

 

 

Last Edited: Thu. Dec 12, 2019 - 05:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't see the practical application.

You appear to have 32 LEDs performing a sequence.

 

In real life you would set each LED to a specific brightness and job is done.

Occasionally you might update the brightness of individual LEDs.

 

If you do want to write a new PWM pattern it is a total of 36 I2C bytes to update all 32 LEDs e.g. 810 us @ 400kHz bus.

 

Ok,  that is much longer than an SPI Slave would need.   But it is more than any human would notice.

Say that you want to give a 32-channel music display.    It is average light intensity not individual sound waves that your eye responds to.

 

David.

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

Heisen wrote:

As stated the application is automotive, I believe if the source is moving at reasonable speed you tend to see the flickering of PWM lights. That's why I am trying to achieve highest refresh rate possible.

I might be wrong though.

 

The PCA9685 does the pwm for you, so don't confuse update rate with pwm rate. You only need to update as fast as the leds change - updating the same sequence faster makes no difference.