Introduction to I2C protocol
I2C (Inter-Integrated Circuit) is a master-slave protocol that may have one master or many master and many slaves whereas SPI has only one master.
It is a synchronous serial protocol that communicates data between two devices.
It is generally used for communication over a short distance. It is a slow speed protocol as compared to SPI communication.
The I2C device has 7-bit or 10-bit unique address. So to access these devices, the master has to address them by the 7-bit or 10-bit unique address.
I2C is used in many applications like reading RTC (Real-time clock), accessing external EEPROM memory. It is also used in sensor modules like gyro, magnetometer, etc.
It is also called as Two Wire Interface (TWI) protocol.
I2C bus uses 2 lines for communication:
Serial Clock (SCL): It is a clock signal. Data will be sent to other devices on the clock tick event. The only master device has control over this SCL line.
Serial Data(SDA): It is a serial data line which is used to send data in either direction.
Generalized I2C Lines
The I2C bus is an open-drain configuration which means they can pull the corresponding signal line low but cannot drive it high. Hence the line will go into an unknown state. In order to avoid this, pull up resistors need to be connected to SCL and SDA pins.
PIC18F4550 I2C Pins
PIC18F4550 I2C Pins
PORTB.0 and PORTB.1 pins are used for I2C communication as SDA and SCL respectively.
PIC18F4550 has Master Synchronous Serial Protocol module (MSSP) which includes I2C protocol.
I2C Registers
To establish I2C communication between devices, we have to configure PIC18F4550. To do this, PIC18F4550 has different registers which are as follows
SSPBUF: Serial Transmit/Receive Buffer
In I2C protocol, SSPBUF is a data register which is used for transmission and reception of data.
SSPSR: MSSP Shift Register
It is a shift register that is used to shift data in and out on the SDA line. It is not directly accessible.
SSPADD Register: MSSP Address Register
In Master mode:
SSPADD register is used to decide or generate a baud/bit rate.
Now how to calculate the value for baud/bit rate.
I2C Baud Generation
Load the generated value in the SSPADD register.
The above formula is used to decide I2C clock frequency.
In Slave mode:
SSPADD register holds the address of the slave device.
SSPCON1: MSSP Control 1 Register
PIC18F4550 SSPCON1 Register
Bit 7 – WCOL: Write Collision Detect bit
1 = SSPBUF Register is written while it is still transmitting the previous word.
0 = No collision.
Bit 6 – SSPOV: Receive overflow indicator flag
1 = A byte is received while SSPBUF Register is still holding the previous word.
0 = No overflow.
Bit 5 – SSPEN: Synchronous Serial Port Enable bit
1 = Enables the Serial Port and configures SDA and SCL pin as a serial port pin.
0 = Disable Serial Port and configure these pins as GPIO.
Bit 4 – CKP: SCK Release control bit
In slave mode:
1 = Release clock
0 = Holds clock low
Not used in master mode
Bit 3:0 – SSPM3:SSPM0:
0110 = I2C Slave mode, 7-bit address
0111 = I2C Slave mode, 10-bit address
1000 = I2C Master mode, Clock=FOSC / (4*(SSPADD+1))
1011 = I2C Firmware Controlled Master mode
1110 = I2C Slave mode, 7-bit address with Start and Stop interrupt enabled
1111 = I2C Slave mode, 10-bit address with Start and Stop interrupt enabled
SSPCON2: MSSP Control 2 Register
PIC18F4550 SSPCON2 Register
Bit 6 – ACKSTAT: Acknowledgement Status bit
1 = acknowledgment signal was not received from the slave
0 = acknowledgment signal was received from the slave
Bit 5 – ACKDT: Acknowledge Data bit
This bit is used when the master requests to slave devices for reading/receiving of data.
1 = Not acknowledge which tells other devices stop sending data.
0 = Acknowledge which tells the device to send another data.
Bit 4 – ACKEN: Acknowledge Sequence Enable bit
1 = Initiate acknowledge sequence on SDA and SCL pins and transmit ACKDT data bit which is automatically cleared by hardware.
0 = Acknowledge sequence is disabled or not in use.
Bit 3 – RCEN: Receive Enable bit
1 = Enable receive mode for I2C.
0 = Disable receive mode.
Bit 2 – PEN: Stop Condition Enable bit
1 = Enable stop condition and cleared automatically by hardware.
0 = Stop condition is Idle.
Bit 1 – RSEN: Restart Condition Enable bit
1 = Enable repeated start condition and cleared automatically by hardware.
0 = Repeated start condition is Idle.
Bit 0 – SEN: Start Condition Enable bit
1 = Enable start condition and cleared automatically by hardware.
0 = Start condition is Idle.
SSPSTAT: MSSP Status Register
PIC18F4550 SSPSTAT Register
Bit 7 – SMP: Slew Rate Control bit
In Master or Slave mode:
1 = Slew rate control disabled for standard speed mode (100 kHz and 1MHz).
0 = Slew rate control enabled for high speed mode (400 kHz).
Bit 6 – CKE: SMBus Select bit
1 = Enable SMBus specific input.
0 = Disable SMBus specific input.
Bit 5 – D/A: Data/Address bit
In Slave mode:
1 = indicates that the last byte received or transmitted was data
0 = indicates that the last byte received or transmitted was address
Bit 4 – P: Stop bit
1 = indicates that the stop bit has been detected last
0 = stop bit was not detected last
Bit 3 – S: Start bit
1 = indicates that the start bit has been detected last
0 = start bit was not detected last
Bit 2 – R/W: Read/Write Information bit
In Slave mode:
1 = Read
0 = Write
In Master mode:
1 = Transmit is in progress
0 = Transmit is not in progress
Bit 1 – UA: Update Address bit.
It is used for 10-bit slave mode only.
1 = Indicates that the user needs to update the address in the SSPADD register.
0 = Address does not need to be updated
Bit 0 – BF: Buffer Full Status bit
In Transmit mode:
1 = SSPBUF is full.
0 = SSPBUF is empty.
In Receive mode:
1 = SSPBUF is full (does not include ACK and stop bit.)
0 = SSPBUF is empty.
I2C Clock Frequency
Only the master initiates the clock on the SCL line.
SSPADD register in the master is used to generate I2C clock frequency.
Status Flag and Interrupt Flag
After complete transmission of data/address, Buffer full flag is set, and also acknowledge signal is received. After reception of the acknowledge signal, the SSPIF interrupt flag is set.
Whereas in data reception, the Buffer full flag is set when a complete byte is received and the device needs to send an Acknowledge signal for indication of reading continuation. And when data is shifted from SSPSR register to SSPBUF, SSPIF flag is set.
So to check whether the transmission/reception is complete or not, we have to monitor either BF flag or the SSPIF interrupt flag.
Programming for PIC18F4550 I2C Communication
Initialization
Configure PORTB.0 and PORTB.1 as an input pin.
Also, configure the SSPSTAT and SSPCON2 register.
Enable I2C by setting SSPEN bit in the SSPCON1 register. Also select master/slave mode.
Set frequency of I2C communication by loading value in the SSPADD register in master mode.
I2C_Master
void I2C_Init()
{
TRISB0=1; /* Set up I2C lines by setting as input */
TRISB1=1;
SSPSTAT=0x80; /* Slew rate disabled, other bits are cleared */
SSPCON1=0x28; /* Enable SSP port for I2C Master mode,
clock = FOSC / (4 * (SSPADD+1))*/
SSPCON2=0;
SSPADD=Bit_rate; /* Clock 100 kHz */
SSPIE=1; /* Enable SSPIF interrupt */
SSPIF=0;
}
Start I2C communication
Send start pulse to the slave device by setting a bit in SEN in the SSPCON2 register.
Wait for SEN bit to clear.
Also, check S bit in SSPSTAT register which indicates the start bit is detected or not.
And then send the device slave address along with write operation and check for acknowledgment signal.
char I2C_Start(char slave_write_address)
{
SSPCON2bits.SEN=1; /* Send start pulse */
while(SSPCON2bits.SEN); /* Wait for completion of start pulse */
SSPIF=0;
if(!SSPSTATbits.S) /* Check whether START detected last */
return 0; /* Return 0 to indicate start failed */
return (I2C_Write(slave_write_address)); /* Write slave device address
with write to communicate */
}
Write/Transmit Data
We have to monitor BF flag or SSPIF which will set when transmission/reception of a byte is complete.
I2C Bus Ready
Check the BF flag and R/W bit in the SSPSTAT register for finding the I2C bus is ready or not.
void I2C_Ready()
{
while(BCLIF); /* Wait if bit collision interrupt flag is set*/
/* Wait for Buffer full and read write flag*/
while(SSPSTATbits.BF || (SSPSTATbits.R_nW));
SSPIF=0; /* Clear SSPIF interrupt flag*/
}
Also SSPIF flag can be used to poll the complete byte transmit/receive status. Also, it is necessary to clear the SSPIF interrupt flag through software.
void I2C_Ready()
{
while(!SSPIF); /* Wait for operation complete */
SSPIF=0; /* Clear SSPIF interrupt flag*/
}
Copy/write data to the SSPBUF register for transmission and wait for the completion of the transmission.
char I2C_Write(unsigned char data)
{
SSPBUF=data; /* Write data to SSPBUF*/
I2C_Ready();
if (ACKSTAT) /* Check for acknowledge bit*/
return 1;
else
return 2;
}
After write if we want to Stop I2C communication,
Set the PEN bit in SSPCON2 register and wait for the PEN bit to clear.
Also, check P bit in the SSPSTAT register which indicates the stop bit is detected.
char I2C_Stop()
{
I2C_Ready();
PEN=1; /* Stop communication*/
while(PEN); /* Wait for end of stop pulse*/
SSPIF = 0;
if (!SSPSTATbits.P) /* Check whether STOP is detected last */
return 0; /* If not return 0 to indicate start failed*/
}
Read/Receive Data
Enable receive mode by setting bit RCEN in SSPCON2.
Then wait for the Buffer flag to set which is set when a complete byte is received.
Read the buffer immediately.
After reading one byte, send an acknowledge signal if we want to read the next data else send negative acknowledge which tells we don’t want to read the next data.
To do this, ACKDT =1 or 0 and ACKEN should be enabled.
/* Read data from location and send ack or nack depending upon parameter*/
char I2C_Read(char flag)
{
int buffer=0;
RCEN=1; /* Enable receive */
/* Wait for buffer full flag which when complete byte received */
while(!SSPSTATbits.BF);
buffer=SSPBUF; /* Copy SSPBUF to buffer */
/* Send acknowledgment or negative acknowledgment after read to
continue or stop reading */
if(flag==0)
I2C_Ack();
else
I2C_Nack();
I2C_Ready();
return(buffer);
}
Acknowledgment
void I2C_Ack()
{
ACKDT=0; /* Acknowledge data 1:NACK,0:ACK */
ACKEN=1; /* Enable ACK to send */
while(ACKEN);
}
Negative Acknowledgement
void I2C_Nack()
{
ACKDT=1; /* Acknowledge data 1:NACK,0:ACK */
ACKEN=1; /* Enable ACK to send */
while(ACKEN);
}
Comments