6.1 Initialization

To begin I2C Master mode communication, the following register bits must be properly configured during initialization (see code example below):

I2C Initialization Example
static i2c_error lastError = I2C1_GOOD;

void I2C1_Initialize(void)               // Initialize I2C Module
{
    if(!I2C1CON0bits.EN || lastError != I2C1_GOOD)
    {
    	lastError = I2C1_GOOD;
    	I2C1CON0 = 0x04;                 // Master 7-bit address mode
    	I2C1CON1 = 0x80;                 // ACKDT = ACK, ACKCNT = NACK
    	I2C1CON2 = 0x24;                 // Enable Address Buffers
                                         // BFRET = 8 I2C pulses
                                         // FME = 1
    	I2C1CLK = 0x03;                  // MFINTOSC (500 kHz)
    	I2C1PIR = 0;                     // Clear all interrupt flags
    	I2C1ERR = 0;                     // Clear all error flags
    	I2C1CON0bits.EN = 1;             // Enable I2C module
    }
}

void PIN_MANAGER_Initialize(void)        // Initialize SCL and SDA pins
{  
    LATC = 0x00;                         // Clear PORTC write latches  
    TRISC = 0xE7;                        // RC3, RC4 initialized as outputs  
    ANSELC = 0xE7;                       // Clear RC3, RC4 analog input 
    ODCONC = 0x18;                       // Must configure RC3, RC4 as OD
    RC3I2C = 0x01;                       // Standard GPIO slew rate
                                         // Internal pull-ups not used
                                         // I2C specific thresholds
    SLRCONCbits.SLRC3 = 0;               // No slew rate limiting
    RC4I2C = 0x01;
    SLRCONCbits.SLRC4 = 0;
    
    // PPS configuration
    bool state = (unsigned char)GIE;
    GIE = 0;
    PPSLOCK = 0x55;                      // Unlock sequence
    PPSLOCK = 0xAA;
    PPSLOCKbits.PPSLOCKED = 0x00;        // unlock PPS
    RC3PPS = 0x21;                       // RC3->I2C1:SCL1;
    RC4PPS = 0x22;                       // RC4->I2C1:SDA1;
    I2C1SDAPPSbits.I2C1SDAPPS = 0x14;    // RC4->I2C1:SDA1;
    I2C1SCLPPSbits.I2C1SCLPPS = 0x13;    // RC3->I2C1:SCL1;
    PPSLOCK = 0x55;                      // Lock sequence
    PPSLOCK = 0xAA;
    PPSLOCKbits.PPSLOCKED = 0x01;        // lock PPS
    GIE = state;
}       

I2CxCON0: The I2CxCON0 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 Master state machine hardware. MODE<2:0> bit settings must not be changed while the EN bit is set (module is enabled).

I2CxCON1: The I2CxCON1 register contains the Acknowledge End of Count (ACKCNT) and Acknowledge Data (ACKDT) bits.

The ACKCNT bit reflects the value transmitted after the I2CxCNT register has reached ‘0’, signaling the end of the packet. When ACKCNT is clear, the module will issue an ACK; when set, the module issues a NACK. This bit can be modified during run time, but must 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, master 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 clear, an ACK is issued; when ACKDT is set, a NACK is issued. The ACKDT bit value can be modified during run time, but must only be changed before an Acknowledge sequence is issued. If there are errors in either the I2CxERR or I2CxSTAT registers, master hardware automatically overrides this bit setting and generates a NACK.

I2CxCON2: The I2CxCON2 register holds the Auto-Load I2C Count Register Enable (ACNT), Fast Mode Enable (FME), Address Buffer Disable (ABD), SDA Hold Time Selection (SDAHT<1:0>), and Bus Free Time Selection (BFRET<1:0>) bits.

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 FME bit is used in combination with the I2CxCLK register to determine the SCL frequency. When FME is set, one SCL period consists of four clock periods of the I2CxCLK clock source. When FME is clear, one SCL period consists of five clock periods of the I2CxCLK source.

The ABD bit enables/disables the use of the dedicated Address Buffer registers. In Master mode, the address intended to be transmitted to the slave can be loaded into the I2CxADB0/1 registers.

When ABD = 1, the I2CxADB0/1 registers are ignored, and the slave address must be loaded into the I2CxTXB transmit buffer by user software to initiate communication. Writing to the Start bit is ignored.

When ABD = 0, the address data stored in I2CxADB0/1 is loaded into the transmit shift register automatically after a Start condition is issued by user software.

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 may be configured based on the bus capacitance; buses with larger capacitance may need longer hold times to ensure valid data.

The BFRET<1:0> bits are used to select the amount of I2C clock cycles used to delay hardware from setting the BFRE bit. The BFRET<1:0> bits can be used to meet the minimum stop hold time as defined by the I2C specification. Note that in systems with more than one master, it is possible that the BFRE bit may never become set if another master device takes control of the bus before the BFRE bit becomes set. In this case, care must be used when selecting the BFRET<1:0> timing.

I2CxCLK: The I2CxCLK register selects the I2C clocking source, and is used in combination with the FME bit to determine the SCL frequency. Some source selections, such as a timer, must also be configured and enabled during initialization.

Important: Note that not all I2CxCLK selections can be used to achieve valid SCL frequencies. For example, if a 400 kHz SCL frequency is desired, the HFINTOSC source may not be a feasible selection. The HFINTOSC may be configured to operate at 16 MHz. If the FME bit is set, the SCL frequency would be the HFINTOSC frequency divided by 4, or 4 MHz. If the FME bit is clear, the SCL frequency would be the HFINTOSC frequency divided by 5, or 3.2 MHz.

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 must be configured such that a device does not stall the bus for too long, but doesn’t 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 must 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:

  • Byte Count Interrupts
  • Acknowledge Interrupt and Hold
  • Data Write Interrupt and Hold
  • Address Interrupt and Hold
  • Stop Condition Interrupts
  • Restart Condition Interrupts
  • Start Condition Interrupts

I2CxADB0: The I2CxADB0 register initialization is only required when using 10-bit address Master mode and the ABD bit is clear. In this case, the lower byte of the 10-bit address is loaded into I2CxADB0 and copied into the transmit shift register upon the issue of a Start condition.

I2CxADB1: The I2CxADB1 initialization is required when using 7-bit or 10-bit address Master modes and the ABD bit is clear. In 7-bit address Master mode, the I2CxADB1 register holds the 7-bit slave address and R/W bit, and I2CxADB0 is ignored. In 10-bit address Master mode, I2CxADB1 holds the higher byte of the 10-bit address. The five most significant bits of I2CxADB1 are defined as a constant ‘11110’ value by the I2C specification, and have to be included in the upper address byte. This constant value is followed by bits ‘10’ and ‘9’ of the 10-bit address, and finally the R/W bit.

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 SLRxCON bit associated with the pin is ignored. When SLEW is clear, the module uses the standard pad slew rate, which is enabled/disabled via the SLRxCON 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<1:0> 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 INLVL bit associated with the pin is ignored. If standard I/O threshold levels are selected, the INLVL bit associated with the pin can be configured for either ST or TTL logic levels.

The PU<1:0> bits are used to select the internal pull-up drive strength. The PU<1:0> 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<1:0> bits can be configured for standard weak pull-ups, which can be enabled/disabled via the WPU bit associated with the pin.

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