Tuesday, December 30, 2008

Playing with I2C



Philips developed I²C (Inter-Integrated Circuit) bus, in the 1980s. I²C is a multi-master low bandwidth serial bus for short distance on board communications that is used to attach low-speed peripherals to a motherboard or an embedded system. All devices are connected through two bidirectional wires: Serial Data (SDA) and Serial Clock (SCL). Typical voltages used are +5 V or +3.3 V.

The I²C design has a 7-bit address space with 16 reserved addresses, so a maximum of 112 nodes can communicate on the same bus. The most common I²C bus modes are the 100 kbit/s standard mode and the 10 kbit/s low-speed mode. Recent revisions of I²C can host more nodes and run faster (400 kbit/s Fast mode, 1 Mbit/s Fast mode plus or Fm+, and 3.4 Mbit/s High Speed mode), and also support other extended features, such as 10-bit addressing.

I²C has a master/slave protocol. The master initiates the communication by issuing a start condition. This condition informs all the slave devices to listen on the serial data line for their respective address. Then the master sends the address of the target slave device and a read/write flag. The slave device with the matching address responds with an acknowledgment signal. Communication proceeds between the master and the slave on the data bus, when the communication is complete, the master issues a stop condition.

[References]
http://en.wikipedia.org/wiki/I2C
http://www.embedded.com/story/OEG20010718S0073
http://dmoz.org/Computers/Hardware/Buses/I2C/
http://www.esacademy.com/faq/i2c/
http://www.nxp.com/products/interface_control/i2c/index.html
http://www.nxp.com/acrobat_download/literature/9398/39340011.pdf
http://www.standardics.nxp.com/support/documents/i2c/pdf/i2c.bus.specification.pdf

The DDC (Display Data Channel) is a digital connection between a computer display and a graphics adapter that allows the display to communicate its specifications to the adapter. The standard was created by the VESA (Video Electronics Standards Association). The DDC link is carried on three pins – data, clock and ground – in a 15-pin VGA connector, a DVI connector or an HDMI connector. The current version of DDC, called DDC2B, is based on the I²C bus. DDC2B allows only one master – the graphics adapter. The monitor (e.g. a CRT or LCD) contains a read-only memory (ROM) chip programmed by the manufacturer with information about the graphics modes that the monitor can display.

[References]
http://en.wikipedia.org/wiki/Display_Data_Channel
http://www.vesa.org/summary/sumddcci.mht
http://ddccontrol.sourceforge.net/

Couple of years ago, I was assigned with a project to implement application specific APIs to communicate to a custom made LCD monitor (SE216). The application was running on a RTOS and was using a proprietary display adapter card. The custom made monitor was though DDC compliant but the protocol was not exactly DDC/CI or DDC2Bi. The communication of the display controller inside the LCD was based on RS232 and to offer the DDC communication option additional hardware was designed to translate the DDC communication into RS232 commands. Based on the Specification of the DDC-Protocol for the SE216 I implemented the following APIs for Reading:

Monitor Backlight Range
Monitor Backlight Level
Monitor Backlight Margin
Monitor Run Times
Monitor Temperature
Monitor Current Video Source
Active Video Sources avialable to MonitorMonitor Video Timings
Monitor Serial Number
Monitor Firmware Versions
Monitor Status

for setting:
Monitor Backlight Level
Backlight State(ON/OFF)
Monitor Video Source

After the implementation, It was time to test. I felt the best way to test is by analyzing the DDC communication so started looking for a I²C analyzer.

The monitor vendor suggested me to use the Traceii XL from Telos but it’s an expensive tool. I got demonstration done by a local guy for I2C BUS Monitor Plus from MCC but I found its not exactly meeting my requirement.

I searched over the internet and did come across some products for sniffer and master applications. Few of the sites and approximate prices are:

http://www.prodigytest.com - $1400
http://www.i2cchip.com - $69.95
http://www.saelig.com - $79.00 to $499.00
http://www.demoboard.com - $199.00 to $259.00
http://www.jupiteri.com - $299
http://www.totalphase.com - $300
http://www.teamfdi.com - $499.00
http://www.avitresearch.co.uk - £75.00 to £250.00
http://www.telos.info http://www.telos.de - Euro 1100
http://www.corelis.com - $3,950

Then while some more searching I came across a webpage by Nicolas Boichat, which had a reverse engineering method of analyzing I²C transfers by using a parallel port of the PC. Implemented it and started using it.

[Reference]
http://www.boichat.ch/nicolas/ddcci/method.html

Meanwhile I brought Beagle I2C/SPI/MDIO Protocol Analyzer from Total Phase, but I still loved to use the parallel port program (pportmon) to analyze.

The Parallel Port monitor method is as follows.

[1] Needed hardware

  • A monitor supporting DDC and A computer running Windows 2000/XP, with a graphic card supporting DDC/CI or any other application capable of controlling the monitor (Basically two systems communicating over I²C).
  • Another computer running Linux, with a parallel port, and a relatively fast processor.
  • An old 15-pin VGA cable or DVI Cable.
  • A straight-through parallel cable, with 25 pins at both side.
[2] Building the cable

The I²C bus on which DDC/CI data is transferred uses 2 wires: SCL (clock), and SDA (data). On the VGA cable, these wires need to be connected on pins 15 (SCL) and 12 (SDA).Open the VGA cable by removing the plastic cover on a short length (3-4 cm), and find with an multimeter which wires are connected to pins 12 and 15 (no need to strip all the wires, just pierce them with a pin, without breaking them).
Connect wire 12 of the VGA cable to pin 13 of the parallel cable, and wire 15 to pin 12. In a DVI Cable pin 6 is DDC Clock and pin 7 is DDC Data. So connect 7 of the DVI cable to pin 13 (Select In – STATUS) of the parallel cable, and wire 6 to pin 12(Paper Out/Paper-End – STATUS). I also suggest binding of both the grounds (pins 18 to 25 on parallel port, pin 10 on VGA cable and pin 15 on DVI cable). With this tapped cable I²C transfer can be monitored using the parallel port.







[3] Analyze transfers

Connect the VGA cable between the computer running Windows and the monitor (communicating over I²C), and the other side of the parallel cable to the computer running Linux.On the Linux computer, download the source file pportmon.c from, and build it with:

# gcc -O2 pportmon.c -o pportmon

To run at root prompt simply type:
# ./pportmon

It will monitor I²C transfers for a few seconds.Change quickly a value (for example brightness), by one step (click only one time on the arrow on the scrollbar).
pportmon should print something like :
-->Start
-->Byte : 01101110 0x6e==>Address : 0x37 (W)
-->Byte : 01010001 0x51-->Byte : 10000100 0x84
-->Byte : 00000011 0x03
-->Byte : xxxxxxxx 0xXX [index]
-->Byte : 00000000 0x00
-->Byte : yyyyyyyy 0xYY [new value]
-->Byte : zzzzzzzz 0xZZ [checksum]
-->Stop (8, 1) [8 = byte count, 1 = bits left on the last byte (1 is ok)]

If you have something totally different, for example with another address, or with stops occurring while there are bits left, pportmon could probably not get enough CPU time to get all the data (this is not a real-time process, so sometimes the OS give CPU time to others process), so just retry. If it still doesn't work, try to stop CPU consuming processes, and increase the priority of the process by running it using nice (otherwise try to use a faster computer or slow the bus speed of I²C).
# nice -n -15 ./pportmon

Running on Windows

I modified the program pportmon.c to run on windows by using the inpout32 library. InpOut32 is a DLL file which can send a data to parallel port and which can return the data in the parallel port. This file with source code can be downloaded for free from

http://logix4u.net/Legacy_Ports/Parallel_Port/Inpout32.dll_for_Windows_98/2000/NT/XP.html

To run on windows the program pportmon.c needs the following modifications:

[1] Declaration

short _stdcall Inp32(short PortAddress);
void _stdcall Out32(short PortAddress, short data);

[2] Changing the read call

buf[i++] = inb(STATUS);

to

buf[i++] = (unsigned char)Inp32(STATUS);

[3] Addition of the following function



After the above modifications, I compiled it for both console and dialog based applications in Visual C++ to get a wonderful analysis of the DDC communications. The application will be bit slower compared to running on Linux. If it is slow for you too, try to stop CPU consuming processes, and increase the priority of the thread by using
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
(Otherwise try to use a faster computer).

I²C Send and Receive

There is a program available for I²C interfacing via PC parallel port on the webpage of Mika Iisakkila.

[Reference]
http://users.tkk.fi/iisakkil/stuff.html

The program implements I2C master-mode by bit-banging a couple of lines on the PC parallel port. Includes C source portable for both Borland and Microsoft compilers as well as a pdf schematic of the needed interface circuit. The method is complicated but is totally opto-isolated.
The source can be downloaded from http://users.tkk.fi/iisakkil/pc_i2c.zip and the schematic from http://users.tkk.fi/iisakkil/i2c_adap.pdf



Other References:

http://i2canalyzer.sourceforge.net/
http://www.maxim-ic.com/appnotes.cfm/an_pk/3230
http://pdfserv.maxim-ic.com/en/an/AN3230.pdf
http://www.programmersheaven.com/download/19695/download.aspx
http://www.programmersheaven.com/download/6573/download.aspx
http://oap.sourceforge.net/oap/i2c_para_module/i2c_para_module.pdf
http://www.nxp.com/#/pip/pip=[pip=PCA9564_4]pp=[t=pip,i=PCA9564_4]
http://owfs.org/index.php?page=i2c-parallel-port-design
http://www.gsp.com/cgi-bin/man.cgi?section=4&topic=lpbb
http://www.jcoppens.com/globo/gl4pre/t_common/data/7405-i2c.pdf


Going ahead I found a microcontroller program of I²C implementation in C for devices that DO NOT have I²C hardware. The code did not use interrupts either, and was useful for Master application but not for Slave use.



[Reference]
http://www.mikrocontroller.net/topic/22461

I again modified the program pportmon.c to send and receive I²C data over the parallel port as well making use of the DATA0 and DATA1 line of the parallel port.

To take care of pull-up, open collector, bus conflicts, excessive current drains and voltage level mismatch an open collector IC 74HC07 can be used. Using a multimeter, measure the voltage across SDA to ground and SCL to ground of the I²C device. Use this voltage level to connect the Vcc of 74HC07 as shown below:




To send and receive I²C data the program pportmon.c needs the following modifications:

[1] Declaration

unsigned char _i2c_error; // bit array of error types
int usleep(long usec);
void I2CBitDly(void);
void I2CSCLHigh(void);
void I2CSendAddr(unsigned char addr);
void I2CSendByte(unsigned char bt);
void I2CSendStop(void);

unsigned char _I2CGetByte(unsigned char lastone);
#define I2CGetByte() _I2CGetByte(0)
#define I2CGetLastByte() _I2CGetByte(1)

[2] Setting DATA0 and DATA1 to HIGH
In the main function call Out32 (DATA, 0xFF);

[3] Implementing micro second sleep
On windows there is only Sleep with millisecond granularity. select system call can be used to create a microsecond sleep. The use of select forces to include the winsock library (and select won't allow to be called without any socket), which has to be initialized in main function
like:

WORD wVersionRequested = MAKEWORD(1,0);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);


and the implementation



[4] Functions to send and receive data over I²C bus






I compiled the above functions in dialog based VC++ application and used it for communicating a slave device (which responds to bus speed of ~30kbps) successfully. The results are not 100% satisfactory though. The VC++ dialog based application has been built on MS VC++ 6.0 and tested on windows XP. It has following three parts:

[1] Tracing I²C data



  • Click Start Trace to start a thread for analyzing the data available on SDA & SCL lines (same as pportmon). The analyzed data is displayed in Trace listbox.
  • Click Stop Trace to kill the above thread and is only active when the trace thread is running.
  • Click Clear Trace to clear the Trace listbox.
  • Click Save Trace for exporting the contents of Trace listbox to a file for archiving the analysis and is only active when the trace thread is not running.
[2] Sending data on I²C bus




  • Use the 1st row of Edit boxes and button for sending data over I²C bus.
  • Enter the device Address (in HEX) in 1st Edit box.
  • Enter the Command (in HEX) in 2nd Edit box.
  • Enter the data bytes (in HEX) in subsequent Edit boxes. If the number of data bytes to send are less than the Edit boxes provided, Enter S or s. Data will be sent over I²C bus till an s or S is encountered and then a STOP byte is sent.
  • Click Start Trace an then Click Transmit/Send.
  • Observe for the sent data in Trace listbox and compare for accuracy.
[3] Receiving data on I²C bus




  • Use the 2nd row of Edit boxes and buttons for receiving data over I²C bus.
  • Enter the device Address (in HEX) in 1st Edit box.
  • Enter the command (in HEX) in 2nd Edit box. If there is no command to send then enter s or S.
  • Enter the Address (in Hex) in 3rd Edit box. If there is no Address to send the enter s or S.
  • Click Start Trace an then Click Transmit/Send.
  • Observe for the sent data in Trace listbox and compare for accuracy.
  • Enter the Number of Bytes to Read (in decimal – 0 to 255) in last Edit box.
  • Click Receive.
  • Observe for the received data in Trace listbox and compare for accuracy.

The installable and source code can be downloaded from the below link using SVN

https://i2c.svn.sourceforge.net/svnroot/i2c


and use the application at your own risk, I am not responsible if it destroys your computer, monitor or anything else.
If you like to comment/add/support then please mail me at irfan.net@gmail.com

No comments:

Post a Comment