Initialization

To begin I2C Slave mode communication, the following registers must be properly configured during initialization (see code example below).

I2C Initialization Example
void I2C1_Initialize(void)
{	
    I2C1ADR0 = 0x30;        // Load address registers with slave addresses
    I2C1ADR1 = 0x80;
    I2C1ADR2 = 0x50;
    I2C1ADR3 = 0xC0;
    I2C1CON0 = 0x00;        // CSTR Enable clocking; MODE four 7-bit address;
    I2C1CON1 = 0x80;        // CSD Clock Stretching enabled;
                            // ACKDT Acknowledge; ACKCNT Not Acknowledge;  
    I2C1CON2 = 0x00;        // ABD enabled; ACNT disabled;
                            // SDAHT 300 ns hold time; 
    I2C1CNT = 0x00;         // Clear count register		 		
    PIR2bits.I2C1RXIF=0;    // Clear all I2C interrupt flags
    PIR3bits.I2C1TXIF=0;
    PIR3bits.I2C1EIF=0;
    I2C1ERRbits.NACKIF=0;
    PIR3bits.I2C1IF=0;
    I2C1PIRbits.PCIF=0;
    I2C1PIRbits.ADRIF=0;
    PIE2bits.I2C1RXIE=1;    // Enable I2C RX interrupt
    PIE3bits.I2C1TXIE=1;    // Enable I2C TX interrupt
    PIE3bits.I2C1EIE=1;     // Enable I2C error interrupt
    I2C1ERRbits.NACKIE=1;   // Enable I2C error interrupt for NACK
    PIE3bits.I2C1IE=1;      // Enable I2C interrupt
    I2C1PIEbits.PCIE=1;     // Enable I2C interrupt for stop condition
    I2C1PIEbits.ADRIE=1;    // Enable I2C interrupt for I2C address match
                            // condition
    I2C1PIR = 0;            // Clear all the error flags
    I2C1ERR = 0;
    I2C1CON0bits.EN = 1;    // Enable I2C module	
}
void PIN_MANAGER_Initialize(void)
{  
    LATC = 0x00;    
    TRISC = 0xE7;           // RC3, RC4 outputs
    ANSELC = 0xE7;          // Disable analog input buffers
    WPUC = 0x00;            // RxyI2C value used instead
    ODCONC = 0x18;          // RC3, RC4 open-drain
    RC3PPS = 0x21;          // RC3->I2C1:SCL1;
    RC4PPS = 0x22;          // RC4->I2C1:SDA1;
    I2C1SCLPPS = 0x13;      // RC3->I2C1:SCL1;
    I2C1SDAPPS = 0x14;      // RC4->I2C1:SDA1;
} 

I2CxCON0: The I2CxCON0 register contains the Module Enable (EN) bit and the Mode Select (MODE[2:0]) bits. The MODE[2:0] bits are used to select the communications mode, and the EN bit enables the Slave state machine hardware. MODE[2:0] bit settings should not be changed while the EN bit is set (module is enabled).

I2CxCON1: The I2CxCON1 register contains the Acknowledge End of Count (ACKCNT), Acknowledge Data (ACKDT), and Clock Stretching Disable (CSD) bits.

The ACKCNT bit reflects the value transmitted after the I2CxCNT register has reached ‘0’, signaling the end of the packet. When ACKCNT is cleared, the module will issue an ACK; when set, the module issues a NACK. ACKCNT can be modified during run time, but should only be changed before the I2CxCNT reaches ‘0’ and before an Acknowledge sequence is issued. If there are errors in either the I2CxERR or I2CxSTAT registers, the slave hardware automatically overrides this bit setting and generates a NACK.

The ACKDT bit reflects the value transmitted after a matching address is received, or after a byte is received while I2CxCNT is not ‘0’. When ACKDT is cleared, an ACK is issued; when ACKDT is set, a NACK is issued. The ACKDT bit value can be modified during run time, but should only be changed before an Acknowledge sequence is issued. If there are errors in either the I2CxERR or I2CxSTAT registers, the slave hardware automatically overrides this bit setting and generates a NACK.

The CSD bit enables/disables the slave’s ability to stretch the SCL signal.

I2CxCON2: The I2CxCON2 register holds the Auto-Load I2C Count Register Enable (ACNT), General Call Address Enable (GCEN), Address Buffer Disable (ABD), and SDA Hold Time Selection (SDAHT[1:0]).

The ACNT bit enables/disables the auto-loading of the I2CxCNT register. Auto-loading of I2CxCNT can be useful when a slave device does not know the size of the data packet, or when the master needs to change the size of the packet to transmit.

When ACNT is set, the first byte following the matching address is used as the value that is loaded into the I2CxCNT register. For example, if the master device intends to transmit three data bytes to a slave, the byte following the address would have a value of ‘3’, and would be loaded into the master device’s I2CxCNT register during transmission.

When the byte is received by the slave device, it is loaded into the slave’s I2CxCNT register. Of course, this assumes that both the master and the slave have the I2CxCNT register feature available, and both devices have ACNT set.

The ABD bit enables/disables the use of the dedicated Address Buffer registers. In Slave mode, a matching address received by the slave can be loaded into the I2CxADB0/1 registers. When ABD = 1, the I2CxADB0/1 registers are ignored, and the matching slave address is loaded into the I2CxRXB receive buffer by hardware. User software must read I2CxRXB to continue communication.

When ABD = 0, the matching received address data stored in I2CxADB0/1.

The SDAHT[1:0] bits are used to configure the amount of time the SDA line is held valid after the falling edge of SCL. The SDAHT[1:0] bits should be configured based on the bus capacitance; buses with larger capacitance may need longer hold times to ensure valid data.

I2CxBTO: The I2CxBTO register selects the timing source used for the Bus Time-Out feature. The current time-out sources are either a CLC or a Timer, and those modules must also be configured during initialization. The time-out source should be configured such that a device does not stall the bus for too long, but does not interfere with timely data processing or clock stretching.

I2CxERR: The I2CxERR register contains the Bus Time-Out Interrupt Enable (BTOIE), the Bus Collision Detect Interrupt Enable (BCLIE), and NACK Detect Interrupt Enable (NACKIE) bits. If these interrupts are not needed by the application, this register does not need to be explicitly initialized.

I2CxCNT: The I2CxCNT register is loaded with the number of data bytes present in a I2C packet. The I2CxCNT can be loaded during initialization or run time directly, but it is recommended to write to this register only if the module is idle or during clock stretching. Writing at any other time may corrupt the register. I2CxCNT can also be automatically loaded during run time when the ACNT bit of I2CxCON2 is set. In this case, the first byte following the address byte(s) is loaded into I2CxCNT by module hardware. The I2CxCNT value should only include the number of data bytes in the packet, and not any address bytes.

I2CxPIE: The I2CxPIE register contains several I2C specific Interrupt Enable bits. Initialization is only required if one or more of the following interrupts are necessary:

RxyI2C: The RxyI2C register controls the I2C specific I/O pads. Most PIC® devices dedicate one or two pairs of I/O pins to the I2C module. The RxyI2C register is used to configure the pin slew rate, input threshold level, and internal pull-up configurations.

The SLEW bit controls the slew rate. When SLEW is set, I2C specific slew rate is enabled, which overrides the standard pin slew rate limiting, and the SLRCONx bit associated with the pin is ignored. When SLEW is cleared, the module uses the standard pad slew rate, which is enabled/disabled via the SLRCONx bit associated with the pin. Lower bus speeds may not need any slew rate limiting, while buses with higher speeds may need slew rate limiting.

The TH bits control the I2C input threshold level. These bits can be configured to SMBus 3.0, SMBus 2.0, I2C specific, or standard I/O input threshold levels to meet the specific protocol requirements. When either the SMBus 3.0, SMBus 2.0, or I2C specific levels are selected, the INLVLx bit associated with the pin is ignored. If standard I/O threshold levels are selected, the INLVLx bit associated with the pin can be configured for either ST or TTL logic levels.

The PU bits are used to select the internal pull-up drive strength. The PU bits can be configured to increase the current drive of the pull-up, making the internal pull-ups strong enough to be used instead of external pull-up resistors. If external pull-ups are to be used in the application, the PU bits can be configured for standard weak pull-ups, which can be enabled/disabled via the WPUx bit associated with the pin.

TRISx Registers: The TRISx registers provide I/O direction support to PORT pins. When using the I2C module, the TRISx bits associated with the SDA and SCL pins must be initialized clear (TRISxy = 0). All previous I2C module designs required the TRISx bits to be set. During run time, direction control is handled by the module hardware.

ODCONx: The I2C module uses an open-drain circuit configuration. The ODCONx bits associated with the SCL and SDA pins must be configured for open-drain (ODCONxy = 1).

I2C PPS Registers: The Peripheral Pin Select (PPS) feature allows digital signals to be moved from their default pin location to another location. To enable a digital peripheral’s input and/or output signals, the appropriate PPS registers must be configured. When using the I2C module, both the input PPS and output PPS registers must be configured due to the bidirectional nature of the I2C bus. Both the input and output PPS registers for each I2C signal must be routed to the same pin. In other words, if the I2CxSCLPPS input register is mapped to pin RC3, the RC3PPS register must also be mapped to pin RC3.

Input configuration is handled by the I2CxSCLPPS and I2CxSDAPPS registers. These registers must be mapped to the desired pins to enable the pin input drivers. Output configuration is handled by the RxyPPS registers. The ‘xy’ in the register name is a placeholder for the actual port and pin number. For example, if the SDA line is mapped to port pin RC4, the correct register name is RC4PPS. The PPS output registers must also be mapped to the desired pins to enable the pin output driver.

The PPS feature allows the I2C pins to be moved from their default locations, but additional steps must be considered. The default I2C pins use the RxyI2C register to define the slew rate, pull-up configuration, and input threshold levels. If the default pin locations are not used, additional registers, such as INLVLx, WPUx, and SLRCONx must also be configured.