attiny84 i2c -- what speeds can/should I expect?

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

I'm using the Arduino TinyWireS library, which is a pretty thin wrapper on top of Donald Blake's usi_twi implementation based on the AVR312 app note.

 

Observed behavior: If I set my i2c bus for 5KHz, I can communicate with the attiny84. If I set it for 50KHz, I can't. The controller in this case is a bus pirate, which has about 10k pullups, and the SCL curves do look a little more rounded than I would like, so I tried adding 4.7k pullups, and... no real change, I guess. My assumption is that an 8MHz CPU should be able to handle i2c at 100kHz or 400kHz, but certainly without any difficulty at 50kHz, but at 50kHz, it just doesn't even get noticed. I've poked around and read the source and the app notes, and found nothing at all specifying any particular expectations as to the speed that should work with the USI hardware.

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

I installed TinyCore v1.4.1 via the IDE Boards Manager.

 

Connected an I2C backpack LCD to a Tiny4313 board running from 8MHz RC.

 

LiquidCrystal_I2C sketches ran straight from the box.     Looking at the I2C traffic with a Logic Analyser the bus is ~ 114kHz

 

I tried similar programs with Codevision that bit-bashes the I2C lines via alcd_i2c.h library.   It ran fine too.  This was ~ 83kHz

 

I2C Bus speed is not very critical.   I am happy with 83kHz or 100kHz.

 

From distant memory,  the TinyWireM implementation has a slightly smaller buffer than the regular Wire.h

 

Oh,  there is little point in using USI for an I2C Master.   USI is designed for Slaves.   USI works fine as I2C Slave.

 

Oops.   I have just noticed that you want an I2C Slave.    That requires different hardware !!

 

David.

 

 

Last Edited: Wed. Dec 16, 2020 - 11:29 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


Using the standard "master_reader.ino" from Wire.h running on a Uno with 4k7 pullups.

#include <Wire.h>

void setup() {
    Wire.begin();        // join i2c bus (address optional for master)
    Wire.setClock(400000); // .kbv change to 400kHz
    Serial.begin(9600);  // start serial for output
}

void loop() {
    Wire.requestFrom(8, 6);    // request 6 bytes from slave device #8

    while (Wire.available()) { // slave may send less than requested
        char c = Wire.read(); // receive a byte as character
        Serial.print(c);         // print the character
    }

    delay(500);
}

and standard slave_sender running on 8MHz Tiny4313

#include <Wire.h>

void setup() {
    Wire.begin(8);                // join i2c bus with address #8
    Wire.onRequest(requestEvent); // register event
}

void loop() {
    delay(100);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
    Wire.write("hello "); // respond with message of 6 bytes
    // as expected by master
}

I get this with 400kHz

 

and with the default 100kHz

 

So you can see that the Tiny Slave is working just fine.    The Master sets regular clocks  and you can see that the ACK/NAK bit takes a little time to get serviced.    Very noticeable on the Address byte.    You notice on the Data bytes but only because the fixed ISR() time is relative to the bus period time.

 

David.

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

Well, I have an Uno lying around, so I can do some testing with that to see whether that works. It's possible that the i2c master I was using for testing (a bus pirate) has a problem, although i'm not sure what the problem could be; the signals *look* fine in that direction. Except maybe the edges aren't as clear as I'd like.

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

Are you sure that your ATtiny84 is not running with CLKDIV8 ?

 

Are you using an up to data TinyCore ?

 

No,  I did not look at the signal shape.    4k7 pullup should give a respectable risetime.   All I2C buses have steep falltime.

Since the 8MHz t4313 was happy with 400kHz bus,   a 1MHz t84 would be happy with 50kHz Bus.

 

I was pleasantly surprised by how well TinyCore operates.

 

I can never understand why anyone chooses anything less than a 32kB Uno.    Cheap and plentiful.    Almost everything will run on a Uno.

I ran the t4313 on an old ET-JRAVR dev board.    Required an RS232-USB cable and adapter and an external USBASP for programming and power.

 

Compare that with a Uno plus single USB cable.

 

David.

 

Last Edited: Wed. Dec 16, 2020 - 03:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am pretty sure that attiny84 is not using CLKDIV8. It was a while back, but I added some sanity checks and light-blinking, and it's not now. I assume tinycore is up-to-date, since I installed it pretty recently.

 

EDIT: Update: It was running at /8. I suspect that I did those tests on the other attiny84 (I have a breadboard, and I have a custom PCB, and I was testing the circuit out on the breadboard and making sure the code worked before moving to the PCB, and since this is a hobby I'm working in half-hour to an hour chunks days apart, so...)

 

The reason I use the smaller chips is mostly that they're, like, a dollar each, and an uno is more like $5-10, and some of the things I want to make are actually a bit dependent on the lower power usage.

 

In this case, the thing I'm making is a replacement for the adafruit LCD backpacks they make which use a GPIO controller, and do the bit-banging via GPIO; I'm looking to replace that with a thing which does the bit-banging itself, so the i2c data can just be sending "what do you want done on this display" rather than "what pins do you need to toggle". (Bit-banging the enable pulses over i2c GPIO is a horrible idea.) Basically, with the adafruit design, transmitting a character to the display requires i2c messages, each specifying an address to write and values to write to:

  • Set/clear the RS and RW bits.
  • Set the top four data bits.
  • Set the E bit.
  • Clear the E bit.
  • Set the lower four data bits.
  • Set the E bit.
  • Clear the E bit.

 

The code they provide for it is also using separate i2c transactions to read existing values of GPIO pins, so they can be modified and written back, to emulate the behavior of |= and &=.

 

In my design, an i2c message sending the string "hello" as 5 ASCII bits after the address draws "hello".

 

But I don't want this to require a board that's larger than the LCD it's driving, and I don't really see a need for a 28-pin chip when a 14-pin chip will do.

Last Edited: Fri. Dec 18, 2020 - 01:29 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes,  I quite understand.   MicroCarl produced an I2C/UART backpack using a Tiny2313 over 10 years ago.   i.e. before the PCF8574 backpacks appeared.

 

There are intelligent I2C/UART/SPI backpacks for GLCD too.

 

On the other hand,  you could just buy a modern COG controller which has native I2C in the first place.

 

My argument is generally:   develop on hardware with convenience.    Rebuild for the cheapest chip.

 

You actually end up with a SOIC-16 PCF8574 and a few R, C

Or a SOIC-14 ATtiny84 or perhaps a SOIC-20 ATtiny2313 and a few R, C

 

The PCF8574 solution was taken up by the Chinese.   You can't compete.

The Chinese could have chosen the small AVR, PIC solution.   Or a Chinese MCU.

 

David.

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

 

the_real_seebs wrote:
that they're, like, a dollar each
   

You might want to look at the new AVR chips like the ATtiny1627. Only $0.73/ea  in a 14PSIOC package or the ATtiny3216 at $1.00/ea in a 20SIOC.

"If you find yourself in an even battle, you didn't plan very well."
https://www.gameactive.org
https://github.com/CmdrZin

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

So, I hooked up a logic analyzer, and did some analyzing of some logic. I have two available i2c drivers: a Bus Pirate (3.6a), and an Arduino Uno. With 4.7k pullups, with the Uno, I get reliable communication at 100kHz, but not at 400kHz. With the bus pirate, I get reliable communication at 5kHz, but not at 50 or 100.

 

 

Looking at the scope traces: When the Uno is driving, it sends the address bits, with SCL pulses, then waits about 100-200ms until SDA gets pulled low, and then pulses SCL, and interprets this as an ACK. When the bus pirate is driving, it sends another SCL pulse immediately, at the same timing interval it was using for the address bits, and SDA hasn't been pulled low yet, so it views this as a NACK. And the attiny code seems to be getting Confused by this.

 

 

So, I'm seeing the same thing you're seeing, when I use the uno -- there's a significant gap between the last SCL pulse from the address bits, and the SDA line dropping and another SCL pulse. But I can't find anything in the i2c docs I've read so far that addresses the question of how long to wait for SDA to go down, just "SDA should be held low during that pulse". Intuitively, what the bus pirate is doing seems like it can't reliably work with targets that need time to process things or take interrupts.

 

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

And yeah, I've been looking at stuff like the 3216, 1627, etc. a little bit. I just started with the 84/85 because I had them lying around. (I think originally I read a thing on building circuits for things like blinking, and read a thing pointing out that for a lot of things you might want to do simple control circuits for, an attiny85 is actually not noticably more expensive than buying all the parts to build the circuit directly, and... yeah actually that's true.)

 

(... but the 3216, et al, seem to come only in SOIC, which is probably beyond my comfort zone right now for soldering, and not friendly to protoboards.)

Last Edited: Thu. Dec 17, 2020 - 05:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Go on.  You have a Logic Analyser.   Post the traces for the Bus Pirate.   

 

I2C does not have a maximum time.    Nor a timeout.

The Master always sends the clock pulses.   e.g. the transmitter sends 8 data bits and then waits for the receiver to ACK/NAK.

 

I don't have a Bus Pirate.   I would assume that it obeys the I2C protocols correctly.

 

Most Slaves and many Masters will use interrupts.   A proper peripheral handles the 8 data + ACK/NAK in hardware.   You just get one IRQ for the whole 9-bit transaction.

 

With USI,  you get an IRQ for the 8 data.   Then handle the ACK/NAK in software.   (from my memory)

You can see the effect in my 400kHz trace.    Obviously my tiny4313 only had to service USI interrupts and the occasional Timer interrupt.

 

I can't help without seeing the Bus Pirate behaviour.

 

David.

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

I2C allows the slave to clock stretch by holding the clock signal low until it is ready for the next bit, it can do this because the bus is not driven high by the master but rather it floats externally pulled high when neither end is pulling it low.

My guess is your bus pirate is not doing this, but is driving the bus with a push/pull output rather then a hiZ/pull driver.

As I said, just a guess.

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

Last Edited: Thu. Dec 17, 2020 - 07:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would expect Bus Pirate to obey the rules.
Surely there are other members that possess a Bus Pirate even if the OP is reluctant to show his logic trace.

David.

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

A pirate obeying rules? AAAARRRRGGGHHHH

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Apparently if I try to upload files I get a media uploader thing that does nothing for a while and then says that the machine I'm trying to reach is unavailable. Hmm.

Okay, here we go. Bus Pirate master, 5KHz (working):

bus pirate scope trace 5KHz

Bus pirate master, 50KHz (not working):

bus pirate scope trace, 50KHz

Arduino Uno master, 100KHz (working):

uno scope trace

 

With the bus pirate at 50KHz, the ack just never happens. Or rather, saleae interprets the next clock pulse from the following word as being maybe an ack, but it doesn't actually work that way.

 

The uno is letting SDA go high/low almost immediately after the previous SCL pulse, the bus pirate is waiting until somewhat closer, but SDA is still definitely solidly high or low before the SCL pulse starts.

 

Unfortunately, on the tiny84, the USI hardware used for TWI is also the hardware that would be used for the usart, so I don't have a debugging console on it or anything.

 

I originally thought it might be significant that the 9th SCL pulse from the bus pirate was almost immediate at 5KHz, but actually that's about the same amount of delay, it's just that at 5KHz, that's also the normal amount of delay between the pulses anyway.

 

If I take my gizmo out, and just have the bus pirate talking to an empty bus, it does indeed produce a 9th pulse almost immediately, so the lack of a pulse there indicates that the tiny's holding SCL down until it's ready to respond, but then it just... doesn't respond, I guess?

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

An oscope would be a better troubleshooting tool for this.

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

ki0bk wrote:

An oscope would be a better troubleshooting tool for this.

 

 

Use that oscope with series resistors on pins. Then you can see which device is wiggling the bus at each transition.

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

My 100kHz trace shows about 35us for the Slave to issue the ACK on the i2c_start(16)

This is 280 machine cycles @ F_CPU=8MHz.

 

Your 100kHz trace shows 75us for the Slave to issue the ACK on the i2c_start(50)

 

Your Bus Pirate traces are nowhere near 5kHz or 50kHz which implies that you may not have given the correct commands to the Bus Pirate.   It looks like 3.3kHz and 33kHz

 

Yes,  the "5kHz" ACK looks prompt.   But a 75us interrupt response relative to a 300us SCL period is hardly noticeable.

 

There is no ACK on the 50kHz trace.   75us would be visible.

 

I posted real sketch codes for Master and Slave.

Perhaps you should post your source code too.   So we can compare with those traces.

 

I presume that you are NOT writing to a parallel LCD from within your Slave ISR().

 

You need to get simple I2C traffic working properly first.

Then I would implement a native I2C LCD on your Tiny84.   Which you can test with the hd44780ioClass/hd44780_I2C class from the hd44780.h library.

 

I might even do that tomorrow.  i.e. create a working I2C LCD on my Tiny4313.

 

David.

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

I do not fully understand the "series resistors on pins" and "see which device is wiggling the bus" aspect of this. I'm a programmer, not an electronics engineer. Note, although the saleae is basically a logic analyzer, it does do analog captures, and I have those channels turned on because I wanted to look at the signals in more detail.

 

Your point about the speed on the bus pirate is... not a thing I'd noticed or considered. I can't see why it should matter, but that does seem suspicious. And yes, it does seem like it's taking longer to ack than I expect.

 

So, to clarify: I have simple i2c traffic working when I use the uno to drive it, and I have a fully working thing that does what I want it to do when talking to the Uno. I just happened to notice, while playing around, that the bus pirate didn't seem to get the same results, and started trying to diagnose that to understand it better. Yes, I know enough not to drive the LCD from inside the ISR.

 

 

 

The sketch has lots of irrelevant dependency bits, but I could trim it down; I don't actually have to do any LCD driving, for instance, so I could just take that out, and get it down to "switch LED on/off in response to i2c messages".

 

 

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

It is my bed time.

 

If your intention is to make a parallel HD44780 LCD appear as a native I2C LCD controller,  please say so.   It would be an interesting little project tomorrow.

 

My Saleae clones do not do Analog.   Your traces show perfectly respectable risetimes.   I would just let Saleae software decode the signals and take measurements from the logic traces.

 

David.

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

Okay, progress! Went and rechecked everything AGAIN. Tried the same stuff with a bare attiny84 and a couple of wires, no LCD stuff, and... it worked! So I started bisecting, and... Apparently, I fixed the fuses on one of the two attiny chips, but not the other. So it was partially the /8 CLKDIV. Which I thought I had checked at least twice, so go figure.

 

However, this does not fully explain things, because it still worked talking to the Uno, and not talking to the bus pirate. But if it was running at 1MHz, it's a lot more plausible to imagine that it was highly timing sensitive at 50KHz or 100KHz, I guess?

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

Yes, the ultimate goal is to make an I2C device which can drive an HD44780 or equivalent character LCD, but doesn't require the direct bit manipulation hacks found in stuff like Adafruit's Liquid Crystal:

 

https://github.com/adafruit/Adaf...

 

Why I need to do this:

// Note: _digitalWrite sends a message over i2c to a GPIO chip.

void Adafruit_LiquidCrystal::pulseEnable(void) {
  _digitalWrite(_enable_pin, LOW);
  delayMicroseconds(1);
  _digitalWrite(_enable_pin, HIGH);
  delayMicroseconds(1); // enable pulse must be >450ns
  _digitalWrite(_enable_pin, LOW);
  delayMicroseconds(100); // commands need > 37us to settle
}

Note that the pfleury LCD library can do this by waiting on the enable bit. But wait! _digitalWrite actually works by first *reading* the current state of the GPIO pins, then masking in or out a specific pin, then writing them back. So. (1) Start transmission to target address, write the device-register number you want to read. (2) Send a request to the device to read a single byte. (3) Mask the pin in our out. (4) Send a request to the device with the device-register number and updated value.

 

So, that's sending device address, register, device address again, number of bytes requested. Then reading 8 bits back. Then sending device address, register, and value. So, 8 bytes total transmission, plus acks, so a bare minimum of 72 i2c clocks. At 100KHz, each i2c clock is 10 microseconds, so, each write to a pin is 720 microseconds.

 

So pulsing the enable bit requires about 2 milliseconds. But also we have to call delayMicroseconds(1) in between things.

 

And this is why I need to write a thing that Isn't That.

 

(Note: There's no reason for about 90% of this. The GPIO chip is not being controlled by anything else. The two bytes of storage it would take to store the GPIO direction and data locally would probably be cheaper by far.)

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

If you'd some example code. I did an LCD slave about 6 years ago.  
   LCD_ControlPanelSlaveInC
and an I2C Slave for tinys.
   USI_I2C_Slave
see https://github.com/CmdrZin/chips...

"If you find yourself in an even battle, you didn't plan very well."
https://www.gameactive.org
https://github.com/CmdrZin

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

Wow, that's much heavier weight than I was thinking.

 

I assigned 0x10-0x1F to instructions like clear screen, goto position, etc., and everything else is just "dump character to display at current cursor position". One of the instructions rewrites the graphics data for 0x00-0x0F.

 

So, 0x11 0x00 -> goto line 0 column 0, 0x11 0x20 -> goto line 1 column 0, 0x11 0x0f -> goto line 0 column 15, 0x12 -> clear and reset cursor. And newline jumps to next line, column 0. Good Enough.

 

 

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

I ordered some 3216s to do some experimenting with, because it looks like they may have more comprehensive i2c hardware, and i always wanted to learn how to solder soic, where "always" is since like two days ago. But it looks like the i2c hardware on the 3216 will be better able to handle simultaneous interactions with the LCD controller and the i2c bus, I think. Downside, I probably can't continue using the arduino tools for that. Upside, I don't have to use the arduino IDE, which I have grown to hate with the fiery passion of a thousand burning suns.

 

I'm still a bit bothered because while I certainly understand a thing that was wrong, I still can't adequately explain why driving it from the Uno worked when the clock divider was set, and it feels like this is the sort of thing that wants an explanation. So if anyone has cool ideas for explaining what might have been going on there, or something, I'd be interested in suggestions on how to diagnose more fully.

 

 

 

Last Edited: Fri. Dec 18, 2020 - 06:47 PM