Instructables

I2C Bus for ATtiny and ATmega

Picture of I2C Bus for ATtiny and ATmega
I love the Atmel AVR microcontrollers! Since building the Ghetto Development System described in this Instructable, I've had no end of fun experimenting with the AVR ATtiny2313 and the ATmega168 in particular. I even went so far as to write an Instructable on using switches as inputs, and extended the Ghetto Development System concept to CPLDs.

During a recent project, I needed several switches for setting control values. The AVRs did not have enough I/O pins, so I had to think of something. I could have tried a complex input system with a keyboard and display, but the ATtiny2313 would have run out of resources. Fortunately, Atmel has provided a way around this problem by including an interface that can link to additional chips (such as memory or I/O ports) with a simple two wire interface. That's right, by using just two I/O pins on an AVR we can access many additional I/O pins, and other resources as well.

This two wire interface is formally known as the Inter-Integrated Circuit bus, or just the I2C bus and was invented by NXP when it was still Philips Semiconductors. If you're reading this Instructable then you've probably heard of the I2C bus and may even have used it on a PIC or other microcontroller. While conceptually very simple, and supported by hardware resources on the AVRs, software drivers are still necessary to use the I2C bus. Atmel provides Application Notes (see the Resources later in this Instructable), but these are incomplete and don't show any examples beyond communicating with another AVR device.

It is not the purpose of this Instructable to teach anyone how to create I2C drivers for the AVRs. Rather, I'll provide expanded versions of the Atmel drivers for ATtiny2313 and ATmega168 devices, I'll explain the requirements and restrictions that apply when using these, and I'll show you working examples of I2C devices. After you work through this Instructable you'll be able to use the I2C bus successfully in your AVR projects. Obviously, you can ignore the drivers for either tiny or MEGA if you're only interested in one of them. For those interested in learning more about the I2C bus, I'll provide links to appropriate material.
 
Remove these adsRemove these ads by Signing Up
dmoisa1 year ago
First, thanks for providing sources which compile with avr-gcc!

I'm trying to use the random read function and it makes sense but for one step:
in USI_TWI_Start_Transceiver_With_Data, when you wrote the start address (in write mode) and you re-set the buffer, go back into address mode, it seems like you're also missing setting USI_TWI_state.masterWriteDataMode to false. Without that, I ran through the loop and I can't see how you'd ever end up in the "read" section.

What am *I* missing?

Thanks,
Dan.
doctek (author)  dmoisa1 year ago
Glad you find this of interest, Dan!

You ask a good question. You are obviously interested in understanding the code, not just using it. The only thing you are "missing" is how the masterWriteDataMode bit gets cleared - not your fault since the naming and commenting are not clear. Let me try to explain how it works.

The statement
USI_TWI_state.errorState = 0;
clears the masterWriteDataMode bit (line 217). That's why the addressMode bit must be reset at line 218.
(This is not well documented. USI_TWI_state.errorState is a union that includes the mode bits. Setting the uchar errorState to 0 sets all the bits in the union to 0 also. This behavior is documented by a comment in the USI_TWI_Start_Read_Write routine and should have been duplicated everywhere it's done.)

The operation of the loop should make sense now.

Let me know if it's still not clear or if you have other questions.
bvercoelen1 year ago
Worked perfectly! Thanks doctek. Used it to talk with the DS1307 RTC to make a Word Clock. http://no8hacks.com/blog/2012/8/16/led-word-clock
ambient.JPG
doctek (author)  bvercoelen1 year ago
Glad you found my code useful. And that's a very cool clock you built - I'm proud to be a part of it.
Hi docTEK!! im using AVR studio 6 and had a veery hard time getting this to work. but i did!! its awesome.

the problem i have is that I #included te USI_TWI_Master.h file with no poblem but the c file gives me a hard time. i had to copy paste the entire file right into my code and its hell to debug.
what is the correct way to use the USI_TWI_Master.c file? cant i just dump both files into my compiler's file path and have them available for future proyects?

#include "USI_TWI_Master.h"
#include "USI_TWI_MAster.c"

will this work?
doctek (author)  luisma.suarez1 year ago
I haven't used AVR Studio 6, but I can tell you what to do in Studio 4. Hopefully that will help. Put the .c file in the Source Files area under AVR GCC in the File Window. (Don't #include the .c file!) This is to the left of the Edit Window in Studio 4. Put the .h file in the Header Files area. You may have to inform AVR Studio that these files are to be included, I don't remember the details exactly. (I don't use AVR Studio often.) The Atmel documentation should give you some examples. Of course, try Google also.

HTH.
MrShmoe2 years ago
One more (dumb) question:

How do I get my AVR Studio 4 to build your code? In other words, how do I get AVR Studio to process your makefile?

Newbie here, I know.
doctek (author)  MrShmoe2 years ago
I used WinAVR to build the examples in the Ible. I haven't used AVR Studio recently, but as I recall it doesn't need a Makefile. (More correctly, it creates its own invisibly.) Just start a new C project and cut and paste the code into files in AVR Studio, then Make the project. That should do it. You probably need to be sure the processor choice, clock rate, etc. is correct.
MrShmoe2 years ago
Curious: I am trying to use I2C to communicate between two ATtiny44's. Question for you: how do I know what address each has? This is missing from the data sheet.

Also, is there a way using I2C to be both a master and slave? I'm thinking of having one be the master, and the slave toggling a GPIO when it wants to communicate...initiating a read, for example.

Any code that would help me with that? I am relatively new to this. :-(
doctek (author)  MrShmoe2 years ago
Being fully programmable devices, the address is left up to the programmer: pick your favorite. If you have other devices on the I2C bus, pick something that doesn't conflict.

I2C devices are either Master or Slave, not both. Slaves do not speak unless spoken to (by the Master). The correct way to have a Slave communicate is for it to be continuously polled by the Master. Respond with a meaningless value if there is no valid response ready. If you have a device that frequently needs to send bursts of data, then make it the Master and have the receiving device be the Slave and just listen.

If you have only programmable devices on the bus, it is possible for them to switch roles as Master and Slave, but they must do this independently of the I2C Bus when no one is using the bus. Do this only if you're sure other ways won't work since you'll be on your own developing the code.

Have fun with your I2C experiments!
MrShmoe doctek2 years ago
Thanks! Question though: Where do I define the I2C address? Sorry, newbie here. Maybe this is a #define already in your code?

Others have mentioned that there is a TWAR register...but I searched the ATtiny44 datasheet for such a register and none exists.

Thanks again for the great advice!
doctek (author)  MrShmoe2 years ago
I2C is implemented differently in the ATTiny parts (as I discuss in the Ible) - more done in software, less hardware support. So there is no TWAR register. You must implement the slave address decoding in software. This Ible did not discuss making a Slave device using an AVR. Have a look at the Atmel site and you'll find app notes on doing this. Note that the Atmel app notes implement the Slave address decoding in a non-standard way, but the approach works.

This becomes an advanced amateur project. You'll learn a lot making this work, but expect to spend some time learning. Good luck and have fun.
adumas2 years ago
Hi... I'm interested in using an ATTiny85... will it work? If not, what changes do I have to make
doctek (author)  adumas2 years ago
The ATTiny85 has a USI port, so the I2C should work like the ATTiny2313. You should compare the data sheets and be sure you have the pin numbering correct. Also verify that the register naming is the same. It should be similar is not identical.

Good Luck!
hvneck3 years ago
Great tutorial!,

I'm making a turntable for modeltrains which uses a lot of I/O and only has 2 wires available (along with supply of course) to control all functions on the rotating part of the bridge.
Because of the fact that the controller (ATMEGA8) does have to do all the controll-functions (motor / locks / position sensors etc.) as well as the reception and feedback of the loconet signals (protocol for controlling aceccories on a model rail layout) I'm bound to run the ATMEGA8 at the full 16MHz. Will that be possible with your TWI library? When yes, what do i have to modify? Or are there other options. Thanks in advance!
doctek (author)  hvneck3 years ago
Sounds like a fun project!

To use the TWI at 16MHz, all you need to do is adjust the SCL frequency. This is described in the data sheet under the TWI port, Bit Rate Generator Unit. If you change the value of TWI_TWBR from 0x0C to 0x48, that should do it. TWI_TWBR is defined in TWI_Master.h.

Here's the calculation:

SCLfreq = Processor Clock /(16 + 2*(TWBR)*4^TWPS)
SCLfreq=16MHz/(16 + 2(72)*4^0) = 16MHz/160 = 100KHz

HTH. Let me know if this doesn't make sense.
mathman473 years ago
You might want to change your eeprom link, just to be complete. The company was sold. Unfortunately, there are 679 devices listed & I haven't a clue as to which one(s) are applicable. Here is the page:
http://www.onsemi.com/PowerSolutions/parametrics.do?id=2311&lctn=home

This is a print, study, & keep Instructable. Thanks for all the information.
qli0293 years ago
GOOD ONE
CHEERS
Ralphxyz3 years ago
Where are the downloads?? I get a page not found!!

Great article.

Ralph
Got the download from the web download link, the link in the .pdf is broke!!

Ralph
gremlin_13 years ago
I really appreciate the author spending the time to write this tutorial.  I have built the ATTINY2313 and PCA8574 I/O expander circuit. However, I have one question on the schematic.  The LEDs on the PCA 8574 seems to be backward. They will never turn on. Maybe the common  VCC connection is wrong and it should be GND instead? Can the author clarify it?  Thanks.
doctek (author)  gremlin_13 years ago
Nice catch! The LEDs on the I/O expander schematic are indeed backwards and would never turn on. Reversing them makes things work correctly. Do not connect the common connection to ground unless you want the LEDs to default to on instead of off. The PCA8574's outputs are initially high, not low. So the circuit is intended to turn the LEDs on when an output is taken low. Thus, the common connection is to VCC.

Thanks for your attention to detail!
tissit4 years ago
Atmel claims it's possible to use AVR port pull-ups for the I2C bus pull-ups. I haven't figured out a way to make that approach work.

Have you tried setting the pin and then twiddling the DDR instead of the actual pin. ISTR that being the usual way to do open collector-ish lines.
tissit4 years ago
Not all avr's have TWI, though. It would be nice to see some great writeup on developing a bitbang driver. That'd be useful for other controllers as well.
nolte9194 years ago
I used this great instructable and referenced it a couple times in my instructable I2C Controlled 7 Segment LED Display.  I used the LED display as part of a digital thermometer.  I really like I2C.  I also used external 4.7k pullup resistors on each I2C line rather than relying on the AVR internal ones.
doctek (author)  nolte9194 years ago
Nice job with your Instructable! I'm glad you found mine useful and I appreciate the shout-out. The external pull-ups are shown in my schematic for the 2313 and are discussed in the second paragraph of step one. The value you found useful (4.7K) is right in the range I suggest: 1.8 to 5.1K, so I'm glad that worked for you. The internal pull-ups are fine for switches, but I just didn't quite trust them for this application.

Your comments remind me that I need to update this Instructable with my latest code that now handles addresses longer than two bytes. (See discussion with p2otoole.)
nolte919 doctek4 years ago
Too cool.  The project I'm working on right now will have an AVR as an I2C slave to put a 16x2 LCD character display on an I2C bus.  To try and make an LCD module that's super easy to add to a project.  I'm going off the slave versions of the Atmel app. notes you used for this project.
doctek (author)  nolte9194 years ago
Sounds like a great project! I'm eager to see the Instructable. Of course there are already some I2C 16x2 displays out there, but that shouldn't keep you from making a better one!

If you use the code from Atmel for the slave, be aware that they handle addressing in a non-standard way. Have a look at the data sheets for the standard approach. The data sheets for the parts (EEPROM and Port Expander) I used are a good place to start. You could also have a look at some of the I2C displays that exist to see what they do. Or invent your own scheme. It's your project after all.
nolte919 doctek4 years ago
Yeah, the one I plan on making is considerably cheaper than any I could find and I plan on making the user interface easier too.  And as I'm sure you understand, I'm as much interested in learning and teaching how to make a slave I2C device as I am in making a better I2C LCD display.  I think the I2C protocol is so versatile and  over 100 devices with only 2 pins is hard to beat.
dtr24 years ago
 Do you have a "twi slave" code for for ATTINY2313? I want to control multiple of those from a master ARDUINO...

Hey my friends i need make the arduino send a a message to the nxt please how do i make it.I want to program the nxt to use nxc connceted to the arduino,the arduino will be connected to the rfid reader.the rfid tag will be read and the arduino compares with the tags olready stored in its memory,if it corresponds it sends the msg to the nxt,plz help is vry vry agent
LYNTRAN4 years ago
Dear Developer of this source code,

I have a question about the source code.

I went over the code written for ATtiny2313. You select External, positive edge for the Shift register and Software clock strobe (USITC) for 4-bit Counter (USICS1=1, USICS0=0, USICLK=1). In the function unsigned char USI_TWI_Start_Transceiver_With_Data() you define the constant below.

  unsigned char const tempUSISR_8bit = (1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)|      // Prepare register value to: Clear flags, and
                                 (0x0<<USICNT0);                                     // set USI to shift 8 bits i.e. count 16 clock edges.

Why the program has to count 16 clock edges for 8-bit transfer? I thought the programhas to count 16 clock edges for 8-bit transfer when USICS1=1, USICS0=0, USICLK=0 or when USICS1=1, USICS0=1, USICLK=0.

Please explain.

Thank you so much,
doctek (author)  LYNTRAN4 years ago
This part of the code (as I point out) is from Atmel. Have a look at the Apps Notes and data sheets I reference in Step 7. Frankly, the code worked fine for me and I was satisfied with that. If this question is of real concern to you and the resources I mention don't help, then you might post your question at the AVR Freaks site.
fabelizer4 years ago
Great experiment, if I can only get it to compile in AVR Studio4! Does anyone have any tips on opening this project in AVR Studio 4? Even though Studio4 allows me to manually open the USI_I2C_Port.c file, when made into a project file, it complains: >Coordinator: None of the available object file readers can read the specified >object file. Please check the format of the object file. >Error loading object file I:\My Documents\AVR\Programs\I2C\USI I2C\I_O >Port\USI_I2C_Port.c and stops. Suggestions? Thanks, -fab
Ok, I got it compiled. Apparently the USI_TWI_Master.c has to be put into the source files area in the project, then there were a few minor version related changes, but the code is now in the tn2313! -fab
p2otoole5 years ago
I tried using the program TWI_I2C_EEPROM.c, with the circuit hooked up according to the schematic, of course, and got (1) waring: return type of 'main' is not 'int'. (2) error: 'CLKPR' undeclared (first use in this function)
Other than that, this program was what I have been looking for, and I know its gonna work for me,
Thank you
doctek (author)  p2otoole5 years ago
The warning about main seems to come with the gcc compiler. I think there's a way to get rid of it, but I haven't been successful at doing so. Hints I've found about how to do it haven't worked. It can be safely ignored, however. CLKPR is the register for the ATmega168. What processor are you compiling for? You may have to change the name.
p2otoole doctek5 years ago
I was trying to upload to an atmega8, which has the same pin layout and number of pins as an at-168. Also the EEPROM ckt that I have is a 24LC512; two strikes against me right there, I reckon. But I do have an atmega168 that I could use. I don't have the other ckts. that you discussed in this tutorial (port extender, etc) I would really like to get this program going, as it is one that I have been searching for; it will fit right into my plan. Thank you
doctek (author)  p2otoole5 years ago
The parts you are interested in should work, but there's a couple of details you should know about. I downloaded the data sheets and here's what I found.

The ATmega8 does not have a CLKPR register. To set the clock prescaler to give you a 4MHz clock you have to blow fuse bits. With that done, the TWI looks like it would work the same.

The 24LC512 lives in the I2C address block from 50h to 57h. You pick the exact address by tying A0-A2 high or low to select the three least significant bits. That is, all lo = 50h, A0 hi, rest lo = 51h, etc.

But there's an additional wrinkle (isn't there always? ;)} The 24LC512 uses two address bytes to address the 64K memory space. Mostly not a problem, you just put in an extra address byte and make the buffer 1 byte bigger. Skip one additional byte when you try to read from the buffer as well. The block read and write size is 128 bytes, so that needs to change if you want to take advantage of that capability. Finally, the TWi_Start_Random_Read read routine must be modified since you have to send two address bytes instead of one address byte before sending the second START signal.

Unfortunately I don't have a 24LC512 to experiment with so I can't guarantee this will work, but I think all that needs to be done is to pass in an additional argument to the function telling it how long the address is. Right now that value is always 2, but if you add the argument then you could pass in either 2 or 3. If you want to contact me privately I'll send you modified code to try out. If it works, we could post it as an update.
BrianCatlin5 years ago
Impressive. Well written!
Pro

Get More Out of Instructables

Already have an Account?

close

PDF Downloads
As a Pro member, you will gain access to download any Instructable in the PDF format. You also have the ability to customize your PDF download.

Upgrade to Pro today!