Sunday, May 23, 2010

Using an Arduino Duemilanove as a quick and dandy logic analyzer

Update (20 Oct 2010): Fixed error in code (due to last minute editing of the code MAX_BITS used in a loop should have been changed to MAX_TRANS). Code has now been updated.

I am attempting to snoop and decode the communication on an I2C bus with the intention of intercepting read/write operations to an EEPROM chip.

I've ordered a Bus Pirate to help, but it won't be here for at least a week. In the mean time I've put my Arduino Duemilanove on the job. I've written a short program that polls the IO pins in a tight loop looking for state changes and recording the state and the time of the transition.

I've opted to store the data in RAM and then dump to serial port at the end of the capture period. Most Duemilanoves come with a ATMega 328 chip which has 2KByte of RAM: enough to capture about 500 transitions. Streaming data to the serial port probably won't work as the pins can't be monitored while data is being transmitted on the serial port resulting in missed transitions. [Update: (25 May 2010)  This may be possible after all as the ATMega chip has a hardware UART.]

Plotting the data

The program will record all pins of the ATMega Port D (Arduino Duemilanove pins 0 to 7).  I'm monitoring two lines: the I2C SCL (clock) and SDA (data) where are connected to pins 2 and 3 of the Arduino respectively.

Data output is obtained from the Serial Monitor and  looks like this:

42778 0 0 0 0 1 1 0 0
42779 0 0 0 0 1 0 0 0
42789 0 0 0 0 1 0 0 0
42790 0 0 0 0 1 1 0 0
42798 0 0 0 0 1 1 0 0
42799 0 0 0 0 0 1 0 0
42816 0 0 0 0 0 1 0 0
42817 0 0 0 0 0 0 0 0
42861 0 0 0 0 0 0 0 0
42862 0 0 0 0 1 0 0 0
42880 0 0 0 0 1 0 0 0

First column is the time stamp. Subsequent columns are pin 7 to pin 0 state.

Gnuplot is a simple to use plotting package that gets results quickly. Cut/paste the output from the serial monitor into a file (eg la.dat). The following command will port pin 4 (SDA) an pin 3 (SCL). I've multiplied the SCL line by 2 so that the two traces are clearly visible.

plot 'la.dat' using 1:6 with lines, 'la.dat' using 1:($7 * 1.2 - 0.1) with lines;


The Arduino program

#define MAX_TRANS 256

char data[MAX_TRANS];
short data_t[MAX_TRANS];

int dp = 0;

void setup()   { 
  DDRD = 0b00000000; // (pins 0 - 7 on Duemilanove) as input
  Serial.begin(115200); // serial port at decent high speed
}

void loop()                     
{
  dp = 0;
  char d, pd;
  pd = PIND & 0b0001100 ;  // only interested in pins 2,3
  unsigned short t = 0, pt;
  
  // Poll port in tight loop. Use iteration counter as timer.
  while (dp < MAX_TRANS) {
    t++;
    d = PIND & 0b0001100; // only interested in pins 2,3
    if (d == pd) {
      continue;
    }
    data[dp] = d;
    pd = d;
    data_t[dp] = t - pt;
    pt = t;
    dp++;
  }

  // Write data to serial port
  int i;
  long tt=0;
  for (i = 1; i < dp; i++) {  
    tt += data_t[i];
    writeData(tt-1, data[i-1]);
    writeData(tt, data[i]);
  }

}

void writeData (unsigned short t, unsigned char data) {
    Serial.print (t);
    for (int i = 0; i < 8; i++) {
      Serial.print (  (data & 0b10000000) == 0 ? " 0" : " 1");
      data<<=1;
    }
    Serial.println ("");
}

No comments: