Building a remote interface for Canon ETTL flash interface

April 10, 2009

I haven’t posted for awhile.  I’ve been busy with more demanding tasks.

I think I’ve done enough research into the Canon ETTL interface and I believe I now can build a prototype that connects to the Camera and provides control of one or more additional flashes, wired or wirelessly, including other Canon ETTL flashes.  My understanding is that the Canon flash IR mechanism doesn’t really work too well (high noise, low range, etc.), and this interface would potentially allow the use of RF for communication and allow >4 ETTL flash units over a larger distance.  I believe my solution should have a interface that connects to the camera’s ETTL hot-shoe (call it the “master”) and a remote interface that provide an effective ETTL hot-shoe for connection to each flash (call them the “remotes”) you want to control.  The master would be able to set the different remote flash intensities and control remote flash occurrence.  I don’t see any reason for limiting the number of remotes other than the user interface on the “master” to setup the different remote parameters.   This interface should also allow the high-speed sync mode to work correctly with the camera for any flash.

This would be similar to the commercial pocket wizard, but would take advantage of the ETTL interface and ETTL flash controllability.

I hope to finish the prototype soon.

Advertisement

Canon ETTL protocol

March 19, 2009

This is a continuing story from https://billgrundmann.wordpress.com/2009/03/18/sniffing-canons-ettl-protocol-2/ and previous posts.

Good news!  I found out today that the drop of CLK to ground (after command message 0xb4 0x3d) before the “X” pin drops is exactly 4.00 msec.  This has been measured several times.  This is good as we now have a leading indicator when the flash is requested to do its job!   This is sufficient time for even wireless modes.  Of course I don’t really have the data of the shutter curtain positions relative to the “X” pin.

I’m working on a collective document that describes everything that I’ve found so far.  This would be easier for people to look at than these piece-meal postings.

 

bill

Sniffing Canon’s ETTL protocol

March 18, 2009

This is a continuation of several posts that relate to my experiences sniffing the communication protocol between my Canon XSi camera and 580ex flash.  The previous posts are:

https://billgrundmann.wordpress.com/2009/03/16/sniffing-canons-ettl-protocol/

https://billgrundmann.wordpress.com/2009/03/10/ettl-continued/

https://billgrundmann.wordpress.com/2009/03/04/ettl-interface/

I have now achieved stability of results.  The messages between the camera and flash are now consistent from sample to sample.  The program had significant modifications from my starting program, but now it works.

Here is the program: ettl_program_1   This program requires some additional hardware support to perform the level shifting and logic threshold as described in my first post.

Here is a sample of the raw messages seen for some random settings of the camera and flash: putty log   After reduction (elimination of already seen messages) and some amount of analysis the ordered messages reduce to:

0xff – 0x8c 0x8c
zoom: 49 0xbd 0x00 0x31 0x99 0xff – 0xaa 0x59 0x00 0xff 0x8c
0xb5 0x4c – 0xcc 0x8c
camera mode: 0xb9 0x80 – 0x00 0x8c
zoom: 49 0xbd 0x00 0x31 – 0x18 0x69 0x46
0xfb 0xff – 0x02 0x8c
0xf9 0xff – 0x74 0x8c
0xb3 0x13 0x46 0x2f – 0x00 0x00 0x00 0x00
0xf5 0xff 0xff 0xff 0xff – 0x53 0x49 0x5c 0x0f 0x00
0xbe 0x20 – 0x8c 0x8c
ISO: 1600 – 0xbb 0x58 0xff 0xff – 0x00 0x00 0x8c 0x8c
0xf9 0xff – 0x55 0x8c
ISO: 1600 – 0xbb 0x58 0xff – 0x00 0x00 0x8c
Aperture: F/5.6 – 0xb7 0x30 – 0x8c 0x8c
Shutter: 1/60 – 0xb8 0x68 – 0x8c 0x8c
0xb4 0x1d – 0x8c 0x8c
0xb3 0x13 0x46 0x2f 0xff – 0x00 0x00 0x00 0x00 0x8c
0xb4 0x1d 0xff – 0x8c 0x8c 0x8c
0xf9 0xff – 0x75 0x8c
zoom: 49 0xbd 0x00 0x31 – 0x18 0x69 0x32
Shutter: 1/60 – 0xb8 0x68 0xff – 0x8c 0x8c 0x8c
0xb4 0x03 – 0x8c 0x8c
0xf2 0xff – 0xa0 0x8c
Set flash intensity: 0xa0 – 0xb0 0xa0 – 0x8c 0x8c
0xb1 0x04 – 0x8c 0x8c
0xb4 0x23 0xff – 0x8c 0x80 0x8c
0xb3 0x33 0x46 0x2f – 0x00 0x00 0x00 0x00
0xf8 0xff – 0x5e 0x8c
Set flash intensity: 0x63 – 0xb0 0x63 – 0x8c 0x8c
0xf2 0xff – 0xc0 0x8c
0xb3 0x37 0x46 0x2f – 0x00 0x00 0x00 0x00
0xb4 0x3d 0xff 0xff 0xff 0xff 0xff 0xff – 0x8c 0x63 0xaa 0x1a 0x09 0x86 0x06 0x8c
Aperture: unknown @ 0x00 – 0xb7 0x00 0xff – 0x8c 0x8c 0x8c
Shutter: 1/40 – 0xb8 0x63 – 0x8c 0x8c
0xb4 0x1d – 0x8c 0x8c

I’ve also spent some time analyzing when AF focus, preflash and actual flash events occur during these messages. 

Preflash message: 0xb4 0x23 0xff – 0x8c 0x80 0x8c     The preflash occurs between the 0x23 and 0xff bytes.  CLK drops to signal-ground (not actual camera ground) for about 83msec.  D2 pulses after the end of CLK.  The actual preflash itself is about 35msec after CLK drops.

Flash message: 0xb4 0x3d 0xff 0xff 0xff 0xff 0xff 0xff – 0x8c 0x63 0xaa 0x1a 0x09 0x86 0x06 0x8c  The flash occurs right after the 0x3d byte.  CLK drops all the way to camera ground.  The actual flash occurs after the “X” pin drops.

AF assists message: 0xb3 0x13 0x46 0x2f 0xff – 0x00 0x00 0x00 0x00 0x8c  The assist lamp happens right after the 0x2f byte of data.  I’m not quite sure yet whether it is just timed or if there is another pin interaction.

Now to look at changing different parameters of the camera and flash and see their effect on the messages.

Stay tuned…

Sniffing Canon’s ETTL protocol

March 16, 2009

I have been trying to stabilize the results I get from sniffing the protocol (https://billgrundmann.wordpress.com/2009/03/10/ettl-continued/).  Unfortunately, I’m finding the different approaches I’ve tried all seem speed limited and miss data.  I believe I have to have repeatable results before I can deduce any new information about the ETTL protocol.  For instance, I’ve finding similar messages to those reported in http://81.216.246.116/e/ettl/, but I’m also finding several messages that are both different and new.  Unfortunately, the new messages seem to change data values from sample to sample, so that it hard to deduce a parameter change dependency or even if the message was correct to begin with. 

I suspect some of my problems have to do with how I sample the data itself.  I’ve gone to a complex scheme that uses alternating change-on-pin interrupts.  First looking for an interrupt on rise of D1 and then a series of interrupts on rises of CLK for each data bit.  This scheme only has one of the interrupts enabled at a time, where each interrupt ultimately enabling the other interrupt while disabling its own.  I’ve learned that I had to flush pending interrupts on the disabled interrupt input.  Since there is some amount of time between bytes, I’m going to try staying in a spin-loop upon the first CLK rising interrupt and look for all of the bits within that event.  I hope this will allow enough time to still output the prior data bytes before the interrupt sequence resumes.

Here’s an example snap-shot of the data seen with normal settings on both flash and camera.  This snap-shot is the result of post-processing the original data.  The messages are NOT in the order they appear between the camera/flash; they are sorted by numeric message value instead and redundant messages have been eliminated.  For some messages I have the probable message meaning per interpretation at http://81.216.246.116/e/ettl/.  The syntax of the messages are the command bytes followed by the response bytes for that message.  Response bytes have already been deskewed and associated to the associated message.  New message shown in this sample is “0xbe”.  I have also seen messages with “0xfc”, “0xfd”, “0xfe”.  Messages that I’m confident with are the “0xb7” for Aperture setting, “0xb8” for shutter speed, “0xbb” for ISO setting and “0xbd” for zoom setting (but I don’t yet know what the other bytes of the zoom response mean).

 

0xb3 0x12 0x46 0x2f – 0x00 0x00 0x00 0x00
0xb3 0x13 0x46 0x2f – 0x00 0x00 0x00 0x00
0xb4 0x1d – 0x8c 0x8c
0xb5 0x4c – 0xcc 0x8c
Aperture: unknown @ 0x00 – 0xb7 0x00 – 0x8c 0x8c
Aperture: F/4 – 0xb7 0x28 – 0x8c 0x8c
Shutter: 1/60 – 0xb8 0x68 – 0x8c 0x8c
camera mode: 0xb9 0x80 – 0x00 0x8c
ISO: 1600 – 0xbb 0x58 0xff – 0x00 0x00 0x8c
zoom: 24 0xbd 0x00 0x18 – 0x18 0x69 0x18
zoom: 24 0xbd 0x00 0x18 – 0x18 0x69 0x1c
zoom: 24 0xbd 0x00 0x18 – 0x18 0x69 0x23
zoom: 24 0xbd 0x00 0x18 0x99 0xff – 0xaa 0x59 0x00 0xff 0x8c
0xbe 0x20 – 0x8c 0x8c
0xf5 0xff 0xff 0xff 0xff – 0x53 0x49 0x5c 0x0f 0x00
0xf9 0xff – 0x55 0x8c
0xf9 0xff – 0x75 0x8c
0xfb 0xff – 0x02 0x8c

Canon ETTL protocol continued

March 10, 2009

I’m getting back to this interface and looking more closely at the signaling between the XSi and 580ex flash.   This is a continuance of the post: https://billgrundmann.wordpress.com/2009/03/04/ettl-interface/

I realized that I needed to look at how the flash informs the camera that it exists and is turned on.

img_2598

This picture shows “D1” from the camera (on top) and the leading edge of “CLK” from the camera (on bottom).  It looks like the flash informs the camera of its “on” status by pulsing high right after the clock goes high.  This particular pulse starts about 1.6usec after the clock and last for about 11usec.

The flash also seems to indicate a ready concept using D1 as shown in the following picture.  This is a capture of D1 (on top) and CLK (on bottom).  D1 seems to go high before the camera starts to clock some data.  D1 stays high until the clock drops and then, for this example, changes for the first bit of the clocked data (in this example the data bit is “0”.    This D1 high before the clock is likely the flash ready to receive flag to the camera.  This also means that if the last data bit, for the current clocked byte, is “1”, then D1 has to drop to “0” so that it can rise again as ready later.

img_2599

The following picture shows a chain of clocked bytes from the flash (D1 on top, clock on bottom).  Notice that D1 always goes high before the first clock of a new byte and then changes to whatever is the value of the first bit after CLK 1st falls.  Notice also at the point marked with an “X”; this is a case where the last data bit is a “1” and D1 returns to zero and later returns to one to indicate ready for the next byte.

img_2600

So, you can see that there is a handshake between the flash and camera.  The camera can’t send another byte of data until the flash indicates it is ready to receive it.

I found no similar characteristics on the camera’s data, D2.

eTTL interface

March 4, 2009

I want to build a flash interface for my Canon XSi camera.  Unfortunately, it is Canon, which means it is a proprietary interface.  There is some information that has been found on the web and it is a very good starting point:

http://81.216.246.116/e/ettl/

For now I’m tapping into the communication by opening up my shoe cord adaptor instead of trying to build a hot-shoe adaptor myself.  Here’s a picture of the internal connections of the Canon off-camera shoe cord 2. 

img_2543

 The connections are blue: “X” or flash, white: “CLK” from the camera, green: “D1” or data from flash, red: “ID”,  black: “D2” or data from camera, and the braided wire is the ground reference.  I took a picture of the CLK waveform as it starts after I do a pre-focus:

img_2544

This is the leading operation of the CLK pin with a Canon XSi and Speedlite 580ex.  The CLK starts out at 0 volts and has a High of 4.6v and a signal low of 2.1v.  It looks like there is about 80ms between bytes of data (the smaller blips down to 2.1v).  If true, then this means we have plenty of time to analyze the received bytes and even send their data over the slower USB Serial connection.

Here is a close up of the clock chain for one byte of data transfer (one of those blips in the previous picture).  The clock as shown has a period of ~9.6usec, which is ~104KHz.  Measuring all 8 clocks; the average is 9.5usec per clock cycle.  The levels looks like we could use a comparator against 3.3v to determine “0” and “1” of the clock and another comparator with about 1.5 v to detect active clock or communication.

img_2545

The following picture has an example of the data from the flash + clock.  The data only transitions betweens 2.1v and 3.1v.  The voltage swing is probably limited by the battery in the flash which I measured to only be 4.5v (4 AA batteries = ~6v).  The other web site reported D1 swinging between 3.7 and 2.2.

img_25471

Now to go  build an Arduino interface circuit.  This protocol looks doable with Arduino with a little bit of hardware support – at least for sniffing the traffic.   It also looks possible to output the data bytes in real-time through the USB serial port.  To actually create an alternative flash will first take some studying of the traffic while changing different parameters (both camera and flash).  Second, I’ll have to come up with additional circuitry to do the level shifted outputs.

update.. march 5th:

I built a translation circuit for the Arduino.  I had an old LM339 comparator that I knew was fast enough for the purpose and with 7 resistors it was everything I needed.

opamp

I selected v1 = 2.6v, v2 = 1.5v.  I wrote a simple program for the Arduino to sample the bytes of data on both D1 and D2.  The program used interrupt on rising edge of CLK’.  “active” was needed to determine when I was in a commication window.

Here is a sampling of the data that occurred right after a cold start of the XSi camera (aka, I pushed the shutter button all the way when the camera was shutdown and the flash happened).  I haven’t captured data after the “X” pin is set to zero.  Counter to the other web’s information, I see a “8C” versus “86” acknowledgement byte from the flash.

D1 D2 with some translation:

 

8C FF
8C BD set zoom
AA 00
59 28
00 99
FF FF
8C B7 aperture
CC 4C
8C B9 camera mode
00 80
8C BD set zoom
18 00
61 20
32 FB flash mode
02 FF
8C F9 status
74 FF
8C B3 unknown
00 03
00 46
00 2F
00 F5 anoter status
53 FF
49 FF
5C FF
0F FF
00 BE ???
8C 20
8C BB ISO 400
00 58
00 FF
8C FF
9C A5 ???
CC 4C
8C B9 camera mode
00 80
8C BD set zoom
18 00
69 28
32 FB flash mode
02 FF
8C F9 status
54 FF
8C B3 unknown
00 13
00 46
00 2F
00 F5 snother status
53 FF
49 FF
5C FF
0F FF
00 BE ???
8C 20
8C BB ISO 400
00 58
00 FF
8C B3 unknown
00 13
00 46
00 2F
00 B7 aperture
8C 2D
 
March 6th update:
I reformed the output from Arduino to what seems to be messages.
 https://billgrundmann.files.wordpress.com/2009/03/cold_start_flash_taken1.docx
 
March 8th update:
I’ve done two things: 1) I’m using putty.exe as an interface to arduino.  It lets me log all of the messages. 2) I wrote a separate C++ program on windows to process the putty logs and reduce the total messages to the unique messages.  Since the messages from the camera are highly redundant, I needed to reduce it down to just those that are unique.  I can then sample different camera/flash switches to see what changes.  For instance, I now have the complete shutter speed and aperture numbers and their correspondence to the camera settings.
 
fstop
 
I also have the 54 entry shutter timing information.  I’ve written the C++ processing program to start to convert the camera/flash messages to english statement.  I’ll continue to see how the different camera/flash options show up in the messages.
 
 
Stay tuned…

Another NunChuk to Arduino adaptor

March 3, 2009

I saw the little PC board that someone is selling on the Web, but I wanted to brushup on PC layout techniques and low-volume PC board manufacturing (aka diy PC boards).  I measured the size of the connector on NunChuk and used the pin-out information I found and designed my own board.  It is a two-sided board without plated-through holes (which causes yet other problems).  Since the individual boards are so small, I decided to do a strip containing 4 at a time and then cut them out for use later.

These work great.  Here are some pictures.  I beg your forgiveness on the sloppyness of the soldering as I had hole drilling problems and non plated holes.

img_2539

img_2538

I printed the image using a laser printer on photo-paper, a hot iron to transfer the image to the board, water to remove the paper, and etched using a 2-to-1 mixture of Hydrogen Peroxide and Muriatic Acid.  The etching itself only took about 2-3 minutes – very fast.  I didn’t have the correct drill bits (this time around), so I kindof burned the holes with a wire (not the recommended way to do this).  Since I made these I have invested in the appropriate drill hardware.

for now..

To use or not use digitalWrite

March 3, 2009

All Arduino users are familiar with “digitalWrite”.  This is just one of the built-in functions that have made the Arduino an easy to use device.   This particular function was built to reduce the burden of the software details of working with hardware and to generalize input/output across a variety of different parts.

But, do you know the consequences of using this type of function?  The following is a simple program, similar to the the example program that everyone 1st run on their board.  This program doesn’t do any explicit delay from turning on an output to when an output is turned off.

void setup() {
  pinMode(2, OUTPUT);  // LED on board
}

void loop() {
  while(1) {
    digitalWrite(2, HIGH);
    digitalWrite(2, LOW);
  }
}

The program is simple enough.   Here is the waveform produced on pin #2 (16MHz Arduino board).

img_25311

The waveform high time is about 3.3usec (~53 clock cycles) , the low time is about 3.45usec (~55 clock cycles, picture is not shown), and a pulsing frequency of ~148KHz.   Now let’s change the program to something a bit more difficult to understand, but more efficient

void loop() {
  while(1) {
    PORTD |= _BV(2);
    PORTD &= ~_BV(2);
  }
}

Now the high time is only 125ns (only 2 clocks), the low is 251ns (only 4 clocks), and a pulsing frequency of  2.7MHz.  This is considerably faster.

img_25341

img_2536

 The GNU compiler that is used for the Arduino IDE optimizes the “PORT |= _BV(2)” to a single instruction “SBI”, while the other operation is converted to a single instruction “CBI”, each taking 2 cycles.  

The primary difference between the first and second approaches is flexibility and portability.  The “digitalWrite” is more portable and the pin parameter is a variable, while the second approach is a fixed pin parameter (at compile time) and you have to know the port group (PORTB, PORTC, etc.).  For example, the equivalent of “digitalWrite(13, HIGH)” is “PORTB |= _BV(5)” – you have to look at the schematic to see that pin #13 is on PORTBand is bit 5.  Most often, the pin and port connections are frozen early in the design and this manual mapping isn’t a problem.

In addition, there is another not well known hardware feature for pins declared as OUTPUT.  For those who don’t know, there are several registers associated with a port that are readable/writable.  The most popular are the PORTx, which is used for output operations, and the PINx, which is typically used for input operations.  But many don’t know that if you write to the PINx when the port is declared as output will toggle the current output value.

So, if I change the loop to the following:

void loop() {
  while(1) {
    PIND |= _BV(2);
  }
}

Observe that the high and low times are exactly the same at 250ns each and the pulsing frequency is 2MHz.

img_2537

I typically create a C macro to contain the operation, but you could also use an “inline” function.  Using “inline” as a function modifier is another subject altogether.

for now…

Sniffing the i2C traffic of a NunChuk

March 3, 2009

There are several articles on the web about how to use a NunChuk with an Arduino.  There are also articles about how to get NunChuk data via communication to the WiiRemote.  But what I haven’t found is a sampling of the actual interchange between the NunChuk and WiiRemote during their initialization and use.  I developed this program to help me understand that communication traffic.

The communication between the NunChuk and Remote utilizes i2C at 400KHz.  I studied the atmega128 product specification and couldn’t determine how to use Arduino’s i2C hardware for sniffing and not as a master or slave device.  I decided to write my own i2C protocol parsing algorithm. 

i2C is a simple communication protocol.  There are only three wire connections needed: SCL, SDA and ground reference.  SDA is primarily data, but is used for signifying start and end of message.  SCL is the data clock.   The state relationship is shown in the following diagram.

capture91

There are six fundamental operations: 1) Idle, 2) Start condition, 3) Sample SDA, 4) Process Sampled SDA Bit, 5) Stop condition, and 6) Repeated Start condition.  Startup condition is in the idle state.  A byte of data occurs by accumulating the first 8 bits (from MSB 1st bit to LSB) of the sampled SDA.   A ninth bit is then processed as the byte acknowledge with a “0” meaning acknowledged and “1” a not acknowledge.  The next byte + ack is then processed unless there is either a “stop condition” or a “repeated start” condition.  A message is a collection of bytes that start with a “start condition” or “repeated start” and ending with a “stop condition” or “repeated start”.   The first byte of a message is the message address and message read/write information.

Because of the i2C data rate, the Arduino USB serial interface is not fast enough and has too much overhead to simultaneously transmit the data received.  Because of this, we have to store the bytes in Arduino memory, which isn’t very much (<<1024 bytes).  This also means that this memory will fill rather quickly.  This full condition is checked every time a new data byte has been accumulated.  When “full” is detected, the process shown above is aborted and the program outputs all of the accumulated messages that were received.

Now for some of the messages.   The following stream of messages occurs immediated upon powerup of both the NunChuk + Remote (with comments).  The syntax of each line is the message byte count (e.g “(2):” for two bytes) and each of the message bytes (in hex).   After each byte is either a “+” or “-” signifying acknowledge/not-acknowledge respectively.  The first byte of a message always contains the address of the device (bits 7:1) and bit 0 is either read (=1) or write (=0).  Bytes with 0xa4 or 0xa5 have been previously identified as NunChuk messages by others in their web sites.  I don’t know what is being addressed by all of the other addresses.  Keep in mind that several of the values in these messages are particular to my NunChuk and other NunChuks will likely have different values.  I’m assuming at this point that the sequence of messages won’t change with the values however.  It would be nice to get any confirmation to that assumption.

 .. looks like a non-NunChuk addressed device
(2):A0+ 00-
(8):A1+ 00+ 00+ 00+ 00+ 00+ 00+ 00+
(8):A1+ 00+ 00+ 00+ 00+ 00+ 00+ 00+
(2):A0+ 00-
(8):A1+ 00+ 00+ 00+ 00+ 00+ 00+ 00+
(8):A1+ 00+ 00+ 00+ 00+ 00+ 00+ 00+
(2):A0+ 00-
(8):A1+ 00+ 00+ 00+ 00+ 00+ 00+ 00+
(8):A1+ 00+ 00+ 00+ 00+ 00+ 00+ 00+
(2):A0+ 00-
(8):A1+ 00+ 00+ 00+ 00+ 00+ 00+ 00+
(2):A0+ 00-
(8):A1+ 00+ 00+ 00+ 00+ 00+ 00+ 00+
(8):A1+ 00+ 00+ 00+ 00+ 00+ 00+ 00+
(2):A0+ 00-
(8):A1+ 00+ 00+ 00+ 00+ 00+ 00+ 00+
(8):A1+ 00+ 00+ 00+ 00+ 00+ 00+ 00+
(2):A0+ 00-
(8):A1+ 00+ 00+ 00+ 00+ 00+ 00+ 00+
(8):A1+ 00+ 00+ 00+ 00+ 00+ 00+ 00+
(2):A0+ 02+
(8):A1+ 00+ 00+ 00+ 00+ 00+ 00+ 00+
(2):A0+ 00+
(8):A1+ 1E- F5+ 5A+ 46+ 09+ CE+ 80-
(8):A1+ 2C+ E5+ E2+ F5- A8- D2- 30+
(2):A0+ 00-
(8):A1+ 2E+ CC+ 1A+ 63- 29- 0F- A0+
(8):A1+ FE+ 15+ C2+ B4- 01+ 88+ 3D-
(2):A0+ 00-
(8):A1+ FE- 04- F8+ 54- 09- D3+ 04-
(2):A1+ 80-

.. something happening with NunChuk addresses
(3):A4+ F0+ 55+
(3):A4+ FB+ 00+
(2):A4+ FA+
(7):A5+ 00+ 00+ A4+ 20+ 00+ 00-
(3):A4+ F0+ AA+
(8):A4+ 40+ AC+ B9+ 8B+ 35+ 1F+ C8+
(8):A4+ 46+ 28+ BE+ B2+ 89+ 07+ 18+
(6):A4+ 4C+ 19+ FF+ 01+ C6+

… the following looks like the calibration data being read
… this is the parameters for my NunChuk
(2):A4+ 20+
(9):A5+ 3F+ 6B+ 0C+ 67+ BD+ D2+ 5F+ 72-
(9):A5+ 3F+ 6B+ 0C+ 67+ BD+ D2+ 5F+ 72-
(2):A4+ 30+
(9):A5+ 3F+ 6B+ 0C+ 67+ BD+ D2+ 5F+ 72-
(9):A5+ 3F+ 6B+ 0C+ 67+ BD+ D2+ 5F+ 72-

.. some more non-NunChuk data
(2):B0+ 60+
(8):B0+ 00+ 08+ 00+ 03- 20+ 40+ 2A-
(3):B0+ 0E+ 00-
(3):B0+ 34+ 8C+
(2):B0+ 66+
(2):B0+ 60+
(2):B0+ 60+
(8):B0+ 00+ 08+ 00+ 03- 20+ 40+ 2A-
(3):B0+ 0E+ 00- (3):B0+ 34+ 8C+
(2):B0+ 66+
(2):B0+ 60+
(1):B0+
(8):B1+ FE- FD- FB- F7- EF- DF- BF-
(2):B1+ FE-

… the following 5 messages are then repeated, which is the polling of the accelerations and buttons
… of course the NunChuk data varies with changes of position of the NunChuk device
    (2):A4+ 00+
    (7):A5+ B0+ EB+ B3+ 1F+ 00+ B7-

… non-NunChuk messages (part of the 5 repeating messages)
    (1):B0+
    (8):B1+ FE- FD- FB- F7- EF- DF- BF-
    (2):B1+ FE-

————————————————————————————————-

Here is the program.  The way the “loop” routine was written was to make it possible to process the i2C messages at 400KHz.  It does not follow normal structured programming rules – there are lots of “goto” statements instead of for/while/do structured techniques.   I found that normal structured C code executed too slowly and missed the SDA data.

i2c_sniffer2

The overhead of Arduino Interrupts

March 2, 2009

I wanted to build an application that could sniff  i2C transactions.  In particular, I was interested in the actual data transactions between the Wii NunChuk and WiiRemote.  This particular data interchange utilizes 400KHz i2C protocol.  So, I thought this was a good opportunity to use arduino interrupts, especially the change on pin interrupts.  What I envisioned is a state-machine that would perform work on transitions of either SCL or SDA – or so I thought.

What I found out was the overhead in the simple version of attaching interrupts was too much to do anything useful at the 400KHz data rate.  I changed to edge-sensitive interrupts without any improvements.  I had to find out what was going on.

I started looking at the actual assembly code generated from the “attachInterrupt” arduino reference library.  Here’s what I found.

Here’s a simple code example:

#define LED_PIN 5  // digital pin #13 (portb)
#define LED_ON() PORTB |= _BV(LED_PIN)
#define LED_OFF() PORTB &= ~_BV(LED_PIN)

void myfunc() {
  LED_OFF();
}
void setup() {
   pinMode(13, OUTPUT);
  attachInterrupt(0, myfunc, RISING);
}

void loop() {
  LED_ON();
  delayMicroseconds(20);
}

I tied the LED output back to Digital pin #2 and this is running on a 16MHz Arduino.  Notice, all this program does is turn an LED on then the interrupt, which responds to the rising of the LED voltage, turns off the LED.  Here is a picture of the LED “on” strobe:

img_25261

As can be seen, the width of the LED “on” is 3.435usec, which is considerable longer than the 600ns that SCL of the i2C protocol is high!  So, this won’t work to respond to SCL changes.  This is about 55 clock cycles @ 16MHz!  What is going on?  Also, what about the 20usec delay in the loop?

img_2527

Notice that the total period for the LED is 26.60usec.  That means the off time for the LED is 23.165usec, which is 3.165usec longer than the delay statement request.   Let’s now look at what happens under the hood.  To do that we have to look at the assembled instructions that came from the program’s compile.

The following is what is generated for the interrupt handler for INT0.  Using the atmegas168 datasheet and information about instructions we can count the number of instruction cycles.  There are 45 cycles before there is a call to the “myfunc” and there are 35 cycles after return of “myfunc” and return from the interrupt handler.  There is also 3 cycles in the pin synchronizer and another 3 cycles for a JMP instruction (which is in the interrupt vector table).  So, we have a total of 51 cycles before we start to execute “myfunc”.

capture4

Now the “myfunc” has some amount of delay before it sets the LED off.   As can be seen, the overhead for a “cbi” instruction  is about 1 cycle.  We’ve accounted for 52 cycles, there are another in 2 cycles in the “sbi”  instruction in the “loop” that actually turns the LED on, and also this blocks interrupts until after the instruction completes.  Now we’ve accounted for 54 cycles.  The other cycle is likely due to the fact that we are setting the LED synchronously with the same clock as the interrupt sampling; this will add another cycle for synchronization.  There we are, all 55 cycles accounted for!

capture51

So, the simple to use “attachInterrupt” has a too much of a significant overhead for the application that I would like to do.  I’ll have to find another solution. 

I made changes to the program.  I got rid of the generic “attachInterrupt” and manually enabled the interrupt.  I also used the “ISR” construct to declare the interrupt handler.  To compile this I had to unfortunately modify Arduino’s “WInterrupts.c” source (located in the \hardware\cores\arduino folder) – I commented out the SIGNAL operations for INT0_vect as this conflicts with the use of “ISR” statement in my application.

#define LED_PIN 5  // digital pin #13 (portb)
#define LED_ON() PORTB |= _BV(LED_PIN)
#define LED_OFF() PORTB &= ~_BV(LED_PIN)

ISR(INT0_vect) {
  LED_OFF();
}
void setup() {
  pinMode(13, OUTPUT);
  // enable INT0 interrupt on change
  EICRA = 0x03;  // INT0 – rising edge on SCL
  EIMSK = 0x01;  // enable only int0
}

void loop() {
  LED_ON();
  delayMicroseconds(20);
}

The changes to the actual waveform on LED were remarkable:

img_2528

The overhead of 1.118usec is now only about 18 clock cycles.  Unfortunately, this is still too much for me to use interrupts to process i2C message.  In addition, it may now be obvious that the background interrupts to keep the delay clock alive are also adding cycles everytime that supporting interrupt occurs.  This extra delay occuring at random places in your application could be significant and cause the occasional strange behavior.

This is a good example of using the instruction and cycle counting methodology to better understand how to improve critical timing in an application.

cheers!