Recently while working on a project trying to get an atmel mega88 to talk to an Atmel tiny45 over I2C we (Dan Gates, Solarbotics and I) were running into trouble getting the USI on the tiny45 to shift the data out of the USIDR on to the SDA line. It was very annoying. To help debug it I slapped together some code for a mega32 for it to grab what it sees on the SCL/SDA lines and store it EEPROM so I could transfer it to my computer and take a look at it with a simple java program.

Dan found the magic to make our project go in the end but this tool is an added bonus for more I2C work in the future.

It's a sort of "ultra-poor, nay, destitute man's logic analyzer." It ended up being so useful that I decided to clean it up, remove all of my R-rated comments regarding java's lack of unsigned integers and am now posting it here. It's quite limited in what it can do but that also makes it fast, cheap and easy.

This description is somewhat terse so if you have any questions feel free to drop me a line!

There are two main pieces to this tool:

1) mega32 with code to sample the I2C lines

2) Java program on my computer to display the results

Now, for you windows/mac users out there this won't quite work as described here (I use linux exclusively for my deveopment). I know avrdude will likely work the same way but I haven't a clue on how to make the java program go. Anyone out there willing to email me a description of the magic to make it go in those other environments that I can include on this web page?

The Mega32 uses just 3 I/O pins (2 for monitoring the I2C lines and one for LED output). I/O pin-wise it's overkill but the mega32 had the most RAM out of any megas I had in stock while staying in a DIP package for use in my breadboard.

Here is the mega32's source code

You will note that the pins used to sample the I2C lines are not the mega32's SDA/SCL pins. That's because the mega32 just SAMPLES HIs and LOs on these lines. In no way does it attempt to talk I2C.

The code waits until sda gets pull low (the start condition) and starts sampling. It will only get 1800 samples. To maximize the speed it just stores the entire contents of the PINA register into RAM. It then extracts these samples and packs them into EEPROM. Storing it in eeprom makes it easy to transfer through the ISP system rather than having to add a serial port, USB or other communication cruft to the project.

The eeprom contents are split into two where the first half of the bytes are the contents of SDA and the second half (address 1024 and on) are SCL. Once in EEPROM I can then transfer it to my computer using avrdude with these commands:

avrdude -c avrispmkII -p m32 -P usb:0000A0012228 -U eeprom:r:eeprom.hex:i
cat eeprom.hex | cut -c 10-73 | head -32 > debug.data
java debug debug.data 3

The first avrdude command grabs the EEPROM and stores it into an Intel hex format file called eeprom.hex. The big number beside the usb: part is the serial number of my avrispMKII. I have two and I can distinguish between them using their serial numbers. Great for working with multiple processors!

The second command removes the addresses at the beginning of each line and other cruft that I don't care about and stores it in the file called debug.data

The last command fires up the java tool to display it. The third argument is the datafile to use and the last argument is the number of horizontal pixels between each sample in the display. For very fast I2C speeds you want a larger number and for very low I2C speeds you want a smaller number. This is just to format the data display to make it more readable. For 100 KHZ, 3-5 pixels is about right.

I have all of these in my makefile:

MCU=atmega32
P=m32
CC=avr-gcc
OBJCOPY=avr-objcopy
# optimize for size:
CFLAGS=-g -mmcu=$(MCU) -Wall -Wstrict-prototypes -Os -mcall-prologues
#-------------------
all: debug.hex
#-------------------
debug.hex : debug.out
    $(OBJCOPY) -R .eeprom -O ihex debug.out debug.hex
debug.out : debug.o
    $(CC) $(CFLAGS) -o debug.out -Wl,-Map,debug.map debug.o
debug.o : debug.c
    $(CC) $(CFLAGS) -Os -c debug.c
load: debug.hex
    avrdude -c dapa -p $(P) -U flash:w:debug.hex:i
usb1: debug.hex
    avrdude -c avrispmkII -p $(P) -P usb:0000A0012228 -U flash:w:debug.hex:i
usb2: debug.hex
    avrdude -c avrispmkII -p $(P) -P usb:0000B0002652 -U flash:w:debug.hex:i
grab1: debug.hex
    avrdude -c avrispmkII -p $(P) -P usb:0000A0012228 -U eeprom:r:eeprom.hex:i
    cat eeprom.hex | cut -c 10-73 | head -32 > debug.data
    java debug debug.data 3
grab2: debug.hex
    avrdude -c avrispmkII -p $(P) -P usb:0000B0002652 -U eeprom:r:eeprom.hex:i
    cat eeprom.hex | cut -c 10-73 | head -32 > debug.data
    java debug debug.data 3
clean:
    rm -f *.o *.map *.out *.hex

All I have to do is enter make grab1 (or make grab2 if I am using the other AVRISP connected to it) and it will grab the contents and fire up the java tool automatically.

My program tries to interpret the results as best it can. It's not perfect such as the 1 before the second start condition or the 0 before the stop condition. But, hey, it was good enough when I needed it!

Here is the source code for the java program

Note that this will stop displaying contents once a stop condition is reached. To make it stop stopping on stop remove the line inside the line() method that says:

stopcon = true;


to email Craig send to climber at shaw.ca (replace at with @ and remove spaces).
Return to Craig's Electronics page.
Return to Craig's main page.

Last modified: Sept 14, 2007 - WOOHOO!! RAISE!!!!