Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Emulation of I2C Protocol on C#

0.00/5 (No votes)
26 Feb 2013 1  
This article presents code to emulate I2C protocol in C#, this can be useful in applications like data acquisition without microcontrollers.

Introduction

In this article, we describe how the I2C protocol could be simulated in a Visual Studio C# application. I2C is a widely used device low end communication protocol. Various computer peripherals comply with I2C protocols, for example: memories, PDAs, cell phones etc. Usually, to access these peripherals, computers require an additional microcontroller/microprocessor. This significantly increases the cost of the device product. This article presents a possible solution to this problem; it describes how a C# application running on a Windows® OS can access an I2C peripheral.

Background

The I2C peripheral interface was invented by Philips Electronics NV. This protocol is popularly used in processors’ communication with memory devices, PDAs, cell phones, etc.

And, C# is a horse power language supported by Microsoft Visual Studio; C# is a kind of language that equips the programmer with versatile features inherited from Java as well as C++. From the evolution of C# it has been an issue of controversy that it is a direct travesty of Java, but features like LINQ and safe switching to pointers prove C# as more advanced compared to its counterparts. Applications built on C# are equally efficient in data acquisition process as are MATLAB  and Labview. In C# we don’t need any kind of extras like ActiveX or mscomm objects as in VB6 or VC++ 6.0. The SerialPort class in C# is enough to support simple hardware at a variety of baud rates.   

Usually, a C# application communicates with I2C using a bridge microcontroller. This type of communication scheme is shown in Fig 1.

Fig 1: A usually followed scheme for communicating an I2C device through a C# application. 

There are certain disadvantages to this scheme and we are going to discuss these disadvantages in the next section. 

Demerits of indirect communication

The scheme (shown in figure 1) suffers with following disadvantages:

Dm = d1 + d2                                  …………………….(1)

This delay will be significant in applications demanding quick data acquisition (it is always desirable to have less delay). The usual value of d1= 20ms (at 9600 baud).

  • As we can observe there is a two stage communication involved; the first application communicates with a microcontroller, then it communicates with the I2C device. Let d1 and d2 be delays involved in these stages, respectively. The total delay in the communication process will be :
  • Secondly, microcontroller cost is also an undesirable factor in peripheral manufacturing. Microcontrollers (having RS232 as well as I2C) are available at a variety of costs (usually ranging from $9 to $30). And a costly microcontroller can substantially increase the final cost of the prototype.

Undoubtedly, eradication of microcontrollers from the scheme (shown in figure 1) would be advantageous in terms of low processing time as well as in cost reduction of the final device. 

Emulation as a possible solution

Since, in C#, a programmer is equipped with the SerialPort class (this class contains lots of methods for sending and receiving ), serial port is an easy interface for applications built in C# to communicate with the outside world. These pins on the serial port could be directly accessed from the application. These pins are: RTS , DTR and CTS (Refer to figure 2). RTS and DTR are output pins, i.e., a C# software application can write these pins with a boolean value which it wants. While CTS is an input pin, a software application can read a boolean value from this pin.

With these pins, can we emulate the I2C protocol? Of course, yes.  Actually, I2C devices communicate with a microcontroller with two pins, SCL and SDA. A sample I2C device is shown (in figure 3).  If we reference the ground levels, the RS232 port of PC and I2C devices are the same, and we follow connections given in table 1, I2C could be easily emulated, rendering the microcontroller of the scheme (shown in figure 1) as redundant. It might look strange, but the application can write a bit for the SDA pin; while, simultaneously, the application can read a bit value from SDA with the help of CTS.  The only complexity is to implement the whole of the I2C protocol in C# code. Once the required code library is ready, C# programmers can perform operations like: data acquisition (I2C sensor ICs like DS1621), memory access, communication with cellphones, etc., in a hassle free manner. Moreover, there will be no overload of firmware programming in microcontrollers.  

      

Fig 2: RS 232 DB9 Pinout  

Fig 3: DS1621(an I2C device)  pinout  

I2C Protocol at Glance    

Fig 4: Timing diagram of I2C bus (please zoom it to see properly)

The I2C is a bidirectional 2-wire bus. A device that sends data is identified as a transmitter, and a device receiving the data is identified as a receiver. It is responsibility of the master device to generate an SCL (clock signal). Usually, connections between these devices (SDA and SCL lines) are made via the open-drain bidirectional lines. The following bus protocol has been defined (see Figure 4): 

  • Data is initiated when the bus is idle.
  • The data line (SDA) must remain stable when the clock line (SCL) is HIGH. In case there is a transition in SDA while SCL is high, it would be interpreted as a control signal.

Important bus conditions:

  • When SCL and SDA both are high, bus is idle. 
  • Transition of SDA from high to low, while the clock is high, defines a START.
  • Transition of SDA from low to high, while the clock is high, defines a STOP.
  • After START, state of SDA defines data bit (1 or 0) for SCL=1(high). Each bit of data is transmitted in one clock pulse. Data transmission is terminated with a STOP or another START (abandoning previous transmission and start of a new one). Any number of  bytes could transferred between START and STOP conditions  The information is transferred byte-wise, and it is an obligation for   receiver to acknowledge with a ninth-bit.  The master device is responsible for generation an extra clock pulse which is associated with this acknowledge bit. A device that acknowledges, makes SDA line low, and this must remain low when SCL is high (for acknowledgement clock pulse). 
  • To end the data transmission slave puts the data line to high, so that the master could generate the STOP.

If R/W bit (in Figure 4) is 1, data is transferred from master to slave. In this first byte master first sends address of slave, and slave returns an acknowledgement for this.

On the other hand, if R/W bit is 0, data flow is from slave to master. Here also, master first sends the slave’s address, slave returns an acknowledge bit. After this, slave sends the data bytes to the master. Master returns an acknowledgement when it   receives all the bytes expect the last one. When  last byte is received , a ‘not acknowledge’ is returned by master.

In the next section we will see C-sharp implementation of this protocol. It should be noted that this section just mentioned some peripheral details of I2C protocol. 

C# Emulation of I2C  

In this article,  DS1621( a temperature sensor IC, please refer [9] for its specifications) is used as an example of an I2C device. Emulation is divided in following two parts:

Fig 4: Schematic of hardware used for the emulation
  1. Hardware: Figure 5 shows schematic of hardware used for emulation. It could be seen from schematic that no microcontroller is used, for interfacing RS232 with DS1621. Schematic consists of couple of  5.1 V  zeners and  couple of 470 ohm resistances. These resistances and zeners are used to   protect circuitry against high RS232 voltages. A LED with resistance is provided to indicate power supply in the system. A popular voltage converter  LM7805 is used to provide 5V stable DC output from a 9V DC power jack. Please refer to figure 2. for corresponding  pin names (DTR, CTS and RTS) of  RS232 to pin numbers given  in schematic.  We see can that connections are in accordance with as described in third section of this paper. (Hardware PCB files are in supplementary material provided with this paper click here).
  2. Software: This is the C-sharp application for emulation of I2C protocol. To maintain understandability of code. I have made this application as console (no GUI). Methods used in this code are detailed in table 1.
Table 1: Functions in code and their brief descriptions.

Methods

Description

spinit

This function  initializes a ‘serial port’ object. It also sets initial SCL and SDA states

sample

This function samples SDA state,  whenever  SCL =1(High).    

startcmd

This provides START in I2C protocol.

stopcmd

This provides STOP in I2C protocol.

tx_1

This function transmits 1 to I2C device.

tx_0

This function transmits 1 to I2C device.

tx_byte

This method  transmits a byte to I2C device.

rx_bit

Through this method application reads a bit coming from I2C device.

rx_byte

Through this method application reads a byte from I2C device.

one_shot_mode

This method is specific for DS1621; it starts one shot mode temperature conversation.

Start_convert_temperature

This method is specific for DS1621; it starts temperature conversion sequence.

read_temperature

This method reads temperature in 2 byte format.

issue_read_temp

This method implements the read_temperature method with proper initiation.

Main

This method is code entry point method.

The main method first calls spinit for required initialization of ‘serialport’ (this class provides interface with RS232 in C#) instance. Then after, main calls  issue_read_temp.  

issue_read_temp provides a sequence  to read temperature from DS1621 using I2C protocol.

The application runs with a console window. It first asks its user to provide port number to which I2C device is connected. When user provides the required information (port number), application takes some time, and if everything goes well (no runtime trouble), application successfully calculates the temperature and delivers it to the user interface.

It was verified that temperature values given by application are correct (by means of comparing temperature values with simple mercury thermometer readings).

Hence, we can say, that application successfully emulates I2C protocol. Using this scheme we are successful in elimination  of microcontroller in C# applications dealing with I2C devices. 

/*
 TITLE : I2C EMULATION IN C#
 * Author : NAKUL VYAS
  VERSION : 3.0

 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;

namespace ConsoleApplication8
{
    class Program
    {
        public static SerialPort sp;
        public static void spinit(int sn)  // function for serial port initialization
        {
          sp =new SerialPort("COM"+sn);
          Console.WriteLine("openning the port..........");
         try{
              sp.Open();
            }
         catch(Exception ex){
             Console.WriteLine("Cannot open port following exception occured:" + ex.Message.ToString());
         }
        
            //making Clcok and SDA line stable
          sp.RtsEnable = true;
          sp.DtrEnable = true;
        }
       
        //this samples SDA pin of DS1621 by reading value of ctsHolding of RS232
        public static bool sample() 
        {
         return sp.CtsHolding;
        }
    
        // This is start condition for DS1621 
        public static void startcmd()
        {
            sp.DtrEnable= true;
            sp.DtrEnable= true;
            sp.RtsEnable = true;
            sp.DtrEnable = false;
        }
        //this is stop condition for Ds1621
            public static void stopcmd()
        {
            sp.DtrEnable =true;
            sp.DtrEnable = false;
            sp.RtsEnable = true;
            sp.DtrEnable = true;
        }
   
        
        //This function transmitt 1(high) to DS1621
        public static void tx_1()
       {
        sp.RtsEnable = false;
        sp.DtrEnable=true;
        sp.DtrEnable = true;
        sp.RtsEnable = true;
        sp.RtsEnable= false;
       }
        //This function transmitt 0(low) to DS1621
        public static void tx_0()
       { 
        sp.RtsEnable = false;
        sp.DtrEnable= true;
        sp.DtrEnable = false; 
        sp.RtsEnable = true;
        sp.RtsEnable = false;
       }
     
       //This functions tranmitt a byte value as a binary string
        public static bool tx_byte(string b)
       {
           foreach (char c in b)
           {
               if (c == '0')
                   tx_0();
               else if (c == '1')
                   tx_1();
           }
        return rx_bit();
       }
      
        //This reads a bit value from DS1621
        public static bool rx_bit()
       {
           bool temp;
           sp.DtrEnable = true;
           sp.RtsEnable = false;
           sp.RtsEnable = true;
           temp = sample();
           sp.RtsEnable = false;
           return temp;
       }

        //This reads a byte value from DS1621
        public static int rx_byte(bool ack)
        {
            int i; 
            int retval=0; 
            for(i = 0;i <=7;i++) 
            { 
                retval = retval * 2;
                if(rx_bit()) 
                retval = retval + 1;
            }
        if(ack)
            tx_0();
        else
            tx_1();
        return retval;
        }
        //This Squence is for setting DS1621 for setting it in one shot mode (see data sheet)
        public static void one_shot_mode()
        {
            startcmd(); //start command for DS1621
            tx_byte("10010000");
            tx_byte("10101100");
            tx_byte("00000001");
            //no need of stop
        }


        //This Squence is for setting DS1621 for starting conversion of temperature (see data sheet)
        public static void Start_convert_temperature()
        {
            startcmd();
            tx_byte("10010000"); //Bus Master sends DS1621 address; R/ W= 0.DS1621 generates acknowledge bit.
            tx_byte("11101110");// Bus Master sends Start Convert T command protocol.DS1621 generates acknowledge bit.
            stopcmd();
        }
        //This Squence is for setting DS1621 for reading 2 byte temperature format  MSB and LSB (see data sheet)
        public static double read_temperature()
        {
            int temperature_MSB;
            int temperature_LSB;
            startcmd(); 
            tx_byte("10010000"); 
            tx_byte("10101010"); 
            startcmd();
            tx_byte("10010001"); 
            temperature_MSB = rx_byte(true); //MSB is integral value of temperature
            temperature_LSB = rx_byte(false); //LSB is fraction pf temperature
            stopcmd();
            
            //to convert temperature in readable format
            double temp = ((temperature_MSB * 256 + temperature_LSB))/256;
            if (temperature_MSB >= 128)
                temp = temp - 256;
            return temp;
        }
        
        public static double issue_read_temp()
        {
            //an extra stop to stabalize bus
            stopcmd();
            //issue a one shot mode command
            one_shot_mode();
            return read_temperature();
        }
        
        
        static void Main(string[] args)
        {
            Console.WriteLine("Enter the COM port number where Hardware is connected :");
            //serial port initialization
            spinit(int.Parse(Console.ReadLine()));
            Console.WriteLine("Temperature Value of DS1621 is :");
           //issue a read sequence for DS1621
            int i = 0;
            
            Console.WriteLine(issue_read_temp());
            while (i < 20)
                i++;
            Console.ReadKey();
        }
    }
}

Fig 5: A screen shot of application run.

Conclusion 

This article presents a novel approach for communication of a C# application with an I2C device. Conventionally, C# applications first communicate with microcontrollers using a serial communication protocol, and then it is the microcontroller which is responsible for communication with the I2C device. In contrast, this paper presents a scheme in which there is no need of microcontroller for a C# application to communicate with an I2C device. In this scheme, the C# application emulates I2C protocol directly over the RS232 port (DB9 serial). Since there is no need of microcontroller in this scheme, it eliminates firmware overhead and the final cost associated with prototyping. Moreover, unnecessary delay of data processing by microcontroller is also avoided. To verify the proposed scheme, it was successfully demonstrated by emulation of I2C by a C# console application over DS1621 (an example I2C device).

References

Note: Please use the PCB file here (to open this file you will need a free version of DIPTRACE Software) attached with this project for quick  development of the prototype.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here