I2C Source Code For Microcontrollers Part 2 PDF Print E-mail
Written by Administrator   
Sunday, 17 May 2009 02:39

I2C Data Transfer Code

These functions form the basis of shifting bits onto the bus, or reads the serial stream of bits off the bus in the case of a read.

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\   i2c_msb_out() shifts one bit out onto bus
\\   Set data line to MSB of byte
\\   Shift byte left once
\\   Sets Clock line to high
\\   Waits for Clock to go high
\\   leaves byte on stack shift shifted up
\\   caller should set clock back low upon return
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
BYTE i2c_msb_out( BYTE data )
{
  if( data & 0x80)            \\ Put output bit to MSB of data byte
  {
    SDAOUT(1);
  } else
  {
    SDAOUT(0);
  }
  MSEC_DELAY(10);             \\ Waste time
  SCLOUT(1);                  \\ Put rising edge on SCL clock line
  while( SCL_IN == 0 );       \\ Wait for SCL to  go high (slave might be holding it low for more time.)
  return (data << 1);         \\ shift to the left one bit
}


\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ void i2c_shiftout_data( BYTE data )
\\   Assumes clock is low, and data is unknown.
\\   REPEAT 8 TIMES
\\   Set data to the current MSB
\\   Sets clock high, but it could be held low by another device as a wait state.
\\   Wait for clock to go high.
\\   Set clock low
\\   Shift the data byte left
\\
\\   This primitive does not wait for an ack.
\\   This function leaves clock low.
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\


void i2c_shiftout_data(int data)
{
  int i;
  for (i=0; i<8; i++)           \\ run through 8 bits
  {
     MSEC_DELAY(10);
     data=i2c_msb_out(data);    \\ Shift out MSB
     SCLOUT(0)                  \\ Put clock line low
  }
  SDAOUT(1);                    \\ Let device have data line for ACK
                                \\ Leave clock low
}



\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ BYTE i2c_msb_in(BYTE data)  
\\  shift byte right
\\  Put clk high
\\  wait for clock to go high
\\  read data bit into MSB of byte
\\  Set Clock low 
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
BYTE i2c_msb_in( BYTE data)
{
  data=data>>1;
  SCLOUT(1);                    \\ Set clk high
  while( SCL_IN == 0 );         \\ Wait for clk to go high (slave may be stalling for time)
  data |= (SDA_IN << 7)&0x80;   \\ Gets val of data line into bit 7 (msb)
  MSEC_DELAY(10);               \\ waste time
  SCLOUT(0)
}




\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ BYTE i2c_shiftin_data(void)
\\   Assumes clock is low, and data is unknown.
\\   Set data to high
\\   REPEAT 8 TIMES
\\   Shift the data byte right
\\   Sets clock high, but it could be held low by another device as a wait state.
\\   Wait for clock to go high.
\\   Read most significant bit
\\   Set clock low
\\
\\   This primitive does not wait for an ack.
\\   This function leaves clock low.
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\


BYTE i2c_shiftin_data(void)
{
  BYTE data;
  int i;
  data=0;                       \\ Our data byte starts out as zero
  for(i=0; i<8; i++)            \\ run through 8 bits
  {
     MSEC_DELAY(10);
     data=i2c_msb_in(data)
  }
  SDAOUT(1)                     \\ Leave the data line high so caller can either ack or not-ack
                                \\ Leave clock low so other code can detect the ack
  return data;
}

 

 

Waiting for an Ack from the Slave

After 8 bits of data have been written onto the bus, the CPU must wait for an ACK to see if a slave has gotten the data.

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ int i2c_ack_wait(void)
\\   Puts data line high and then clock line high, then waits for slave
\\   to start holding the data line low.
\\   Once a low is detected, the clock is brought back low so that slave
\\   knows to let go of the data line.
\\   Leaves clock low
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
int i2c_ack_wait(void)
{
  int i;
  int iRes;
  iRes=0;
  SDAOUT(1);
  SCLOUT(1);
  MSEC_DELAY(5);
  for(i=0; i<I2TIMEOUT_ACKWAIT; i++) \\ This is a constant which is the loop count
  {
    if(SDA_IN==0)    \\ Check on the status of the data line
    {
      iRes=1;
      break;
    }
  }
  SCLOUT(0);        \\ Allow slave to release the data line
  return iRes;      \\ Return 0= timeout, 1=Ack received OK.
}

I2C Read and Write Functions

These functions call the above primitives to do the entire master read and write transactions.

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\ int i2c_dev_write(BYTE i2c_address, BYTE data)
\\   Writes a byte to device on the i2c bus.
\\   On failure (no ack) returns a zero. 
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
int i2c_dev_write(BYTE i2c_address, BYTE data)
{
  int iRet;
  i2c_do_start();
  i2c_address = i2c_address << 1;       \\ shift device address up one bit and leave a 0=read bit in lsb
  SCLSET(0);                            \\ bring clock line low before trying to shift out data
  MSEC_DELAY(10);
  i2c_shiftout_data(i2c_address);     
  iRet=i2c_ack_wait;
  if(iRet==1)
  {
     i2c_shiftout_data;                 \\ Provide the data byte to write
     iRet=i2c_ack_wait;                 \\ Leave return code on TOS as our return value
  }
  MSEC_DELAY(10);
  1 i2c_set_data;
  1 i2c_set_clock;                      \\ Set bus Idle
  return iRet;
}

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
\\  int i2c_dev_read(void)
\\   reads an unsigned byte from device on the i2c bus.
\\   On failure (no ack) returns a negative value integer,
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
int i2c_dev_read(BYTE i2c_address)
{
  int iRet;
  int data;
  i2c_do_start();

  MSEC_DELAY(10);
  i2c_address= i2c_address<<1 | 1;  \\ shift device address up one bit and leave a 1=read bit in lsb
  SCLOUT(0);                        \\ bring clock line low before trying to shift out data
  MSEC_DELAY(10);
  i2c_shiftout_data();              \\ shift out address bits
  iRet=i2c_ack_wait();              \\ wait for address ack from slave
  if(iRet==1)
  {
     data=i2c_shiftin_data();       \\ Provide the data byte read but with a 1 on TOS to indicate success
     MSEC_DELAY(10);    
  }

  SCLOUT(1);                        \\ Do a not-Ack to tell device we won't be reading again
  MSEC_DELAY(10);
  SDAOUT(1);
  MSEC_DELAY(10);                   \\ And leave bus Idle
  if(iRet==0) return -1;            \\ Return error condition if error occured
  return (int) data;
}

 

In Conclusion

The above code is enough to read from and (write to) simple I2C devices such as the PCF8574 I/O expander chip.  Generic I/O lines from the 8051 are used for the two I2C signals, SDA and SCL.

I2C 8051 Example Circuit

 //////////////////////////////////////////////////////////////
// Coil pulse Activation
// ActivateCoil() This function energizes the coil and returns a integer signifying
// a negative value on error, a 1 if not centered, or a 0 if centered.
//////////////////////////////////////////////////////////////
int ActivateCoil(void)
{
   // Send P1 and P2 Low, but leave P0 high so centering input can be read
   if( !i2c_dev_write( 0x38, 0x01))
   {
   return -2; // I2C write error
   }

   // Read P0 line to see if coil is centered. Will return -1 on I2C read error.
   return i2c_dev_read(0x38);
}

 

 

This function uses the I/O expander to control two outputs and one input. The PCF8574A chip has eight I/O lines, so a different application could easily use all 8. However, the most important detail is that the I/O chip has three address lines to identify the lower three bits of the I2C address. So up to 8 PCF8574A chips can be connected, for a total of 8x8 = 64 lines of digital I/O with simple code like this example. In addition, there are many other kinds of I2C chips available, such as RAM, EEPROM, A/D and D/A devices. Some of these devices would require more complex transactions, but with some simple additional code they can be made to work.

In Conclusion

The I2C is a very useful protocol for doing I/O with small controllers which are lacking in I/O lines. It isn't blazingly fast, but the advantage lies in the forgiving nature of the hardware requirements, and the simplicity of the software necessary to run it.

<- Prev

 

 

 

 

 

Last Updated on Sunday, 17 May 2009 13:47
 

Sponsored Links

Bold Inventions, Powered by Joomla!; Joomla templates by SG web hosting