PIC32MX: I2C

From Mech
Jump to navigationJump to search

Inter-Integrated Circuit, or I2C (pronounced I squared C), is used as a serial communication between other devices including other PICs.

I2C can be used in the following cases:

    • As a slave device
    • As a master in a single master system
    • As a master in a multi-master system

I2C for PIC32 is discussed in more detail in the Microchip I2C documention.

Available Pins

There are two sets of pins for I2C. Each consists of SDAx and SCLx where x is either 1 or 2. SDA is the data pin and SCL is the clock pin.

Methodology: Single Master System

This section discusses the general approach for I2C between a single master and a slave.

To begin communication, the master first transmits a start command.

Each slave has a unique address. For the PIC32, the slaves can have either 7 bit or 10 bit addresses. For communicating with 10 bit addresses, see the Microchip I2C documention. The master transmits an 8 bit number. Bits 7-1 make up the slave address and bit 0 indicates whether or not the slave is expected to read or write. If bit 0 is a 1, the data transfer is output from the slave. If bit 0 is a 0, the data transfer is input to the slave.

When the master transmits the 8 bit number, the slaves compare their address to the sent address. If there is a match, the slave automatically generates an acknowledge that is sent to the master.

The slave then must read the address and R/W bit. This is done to clear the buffer.

The master then either waits for a response (output from slave) or sends up to 8 bits of data (input to slave). If output is from the slave, after each transmission the master sends an acknowledge. The slave continues to send data until the master does not acknowledge.

The master then either sends a restart condition to send or read more data or sends a stop condition. If the master sends a restart condition, the procedure above is followed.

I2C Master Code

This section details the master code for I2C between two PICs. The slave has a 7 bit address in this example. What we are going to do is send a message to the slave and then read the same message back from the slave. If the correct message is read LEDs 2 and 4 will turn on. If there is an error, LED 3 will turn on. The code commented out can be used to communicate with EEPROM.

Include the standard header files

  #include "GenericTypeDefs.h"
  #include "Compiler.h"
  #include "HardwareProfile.h"
  #include <plib.h>

Define constants for the system and peripheral bus clocks.

  #define SYSCLK	(80000000)
  #define PBCLK  (SYSCLK/2)

The Baud Rate Generator is used to set the SCL clock frequency. The equations used for the BRG are shown below: FSCK = (PBCLK) / ((I2CxBRG+2) * 2) I2CBRG = (PBCLK / (2 *FSCK)) - 2

Define constants for BRG. Fsck is chosen to be 50 kHz.

  #define Fsck	          50000
  #define BRG_VAL 	 ((PBCLK/2/Fsck)-2)

Define a function to wait a little bit. (May not be necessary)

  void i2c_wait(unsigned int cnt)
  {
     while(--cnt)
     {
         Nop();
         Nop();
     }
  }

Start main function

  int main(void)
  {

Set up some variables

  unsigned char SlaveAddress;
  char i2cData[10];
  int  DataSz;

Initialize LEDs

  mInitAllLEDs();

Turn on I2C - Enable channel

  OpenI2C1( I2C_EN, BRG_VAL );

Set up slave address (7 bits in this case)

  SlaveAddress = 0x50;	//0b1010000 Slave address

Set up data to be sent (commented is for eeprom)

  i2cData[0] = (SlaveAddress << 1) | 0;	//Slave Device Address and WR Command
  //i2cData[1] = 0x05;//eeprom location to program (high address byte)
  //i2cData[2] = 0x40;//eeprom location to program (low address byte)
  //i2cData[3] = 0xAA;	//data to write
  //DataSz = 4;
  i2cData[1] = 0xAA; // data to write
  DataSz = 2; 

Assert a Start Condition

  StartI2C1();	        //Send the Start Bit
  IdleI2C1();		//Wait to complete

Send Address and Data (after each transmission check for an acknowledge)

  int Index = 0;
  while( DataSz )
  {
     MasterWriteI2C1( i2cData[Index++] );
     IdleI2C1();		//Wait to complete
  
     DataSz--;
  
     //ACKSTAT is 0 when slave acknowledge. if 1 then slave has not acknowledge the data.
     if( I2C1STATbits.ACKSTAT )
         break;
  }

Send Stop condition

  StopI2C1();	//Send the Stop condition
  IdleI2C1();	//Wait to complete

Wait for Slave to complete write process by polling the ack status

  while(1)
  {
     i2c_wait(10);
  
     StartI2C1();	//Send the Start Bit
     IdleI2C1();		//Wait to complete
  
     MasterWriteI2C1( i2cData[0] );
     IdleI2C1();		//Wait to complete
  
     if( I2C1STATbits.ACKSTAT == 0 )	//eeprom has acknowledged
     {
        StopI2C1();	//Send the Stop condition
        IdleI2C1();	//Wait to complete
        break;
     }
     StopI2C1();	//Send the Stop condition
     IdleI2C1();	//Wait to complete
  }

Now we are going to read back the data from the slave (commented is for EEPROM)

  i2cData[0] = (SlaveAddress << 1) | 0;	//Device Address and WR Command (to write the address)
  //i2cData[1] = 0x05;	//eeprom location to read (high address byte)
  //i2cData[2] = 0x40;	//eeprom location to read (low address byte)
  //DataSz = 3;
  DataSz = 1;
  StartI2C1();	//Send the Start Bit
  IdleI2C1();		//Wait to complete
  

Send the address to read from the Slave (if you are not using EEPROM, you do not need to have a while loop)

  Index = 0;
  while( DataSz )
  {
      MasterWriteI2C1( i2cData[Index++] );
      IdleI2C1();		//Wait to complete
      DataSz--;
     //ACKSTAT is 0 when slave acknowledge. if 1 then slave has not acknowledge the data.
     if( I2C1STATbits.ACKSTAT )
        break;
  }
  StopI2C1();	//Send the Stop condition
  IdleI2C1();	//Wait to complete

Now send a start sequence again

  RestartI2C1();	//Send the Restart condition
  //wait for this bit to go back to zero
  IdleI2C1();	//Wait to complete

Transmit Read Command

  MasterWriteI2C1( (SlaveAddress << 1) | 1 ); //transmit read command
  IdleI2C1();		//Wait to complete

Read from the Slave

  unsigned char i2cbyte;
  i2cbyte = MasterReadI2C1();

Stop transmission

  StopI2C1();	//Send the Stop condition
  IdleI2C1();	//Wait to complete

Check to see if correct data was sent back

  if( i2cbyte != 0xAA )
  {
      while(1) //error: verify failed
      {
        mLED_3_On();
      }
  }
  while(1) // Success
  {
     mLED_2_On();
     mLED_4_On();	

}

  }

I2C Slave Code

This section details the master code for I2C between two PICs. The slave has a 7 bit address in this example.