3 Master Mode Code Example

Example 3.1 shows the use of the MSSP in 7-bit I2C Master mode. The example uses bit polling to determine when to issue a Start condition, load data and other I2C functions. The functions used in the code example are used to read and write a single data byte to a specific address location within the slave’s memory. In the ‘I2C_WriteByte()’ routine, the byte following the address byte represents the register or memory address within the slave that will receive the data. In the ‘I2C_ReadByte()’ routine, the byte following the address is also the register or memory address within the slave’s memory that will be read by the master. In this routine, once the slave acknowledges the register address, the master must issue a Restart condition and transmit the slave address again with the R/W bit set.

Important: For this code example, the slave software must be written such that the slave understands that the first data byte is actually a register address and not actual data.

MSSP in I2C Master Mode

// main() routine
uint8_t slaveAddress = 0x30;                  // 7-bit slave address
uint8_t regAddress = 0x00;                    // Register location
uint8_t writeVal = 0xCB;                      // Value to write
uint8_t readVal = 0x00;                       // Value from slave

void main(void)
{
    SYSTEM_Initialize();

    while (1)
    {
        I2C_WriteByte(slaveAddress, regAddress, writeVal);   // Write to slave
        __delay_ms(100);                                     // Short delay
        readVal = I2C_ReadByte(slaveAddress, regAddress);    // Read back data
        __delay_ms(1000);                                    // 1 second delay
    }
}

// I2C initialize routine
void I2C_Initialize(void)
{
    SSP1STAT = 0x80;                       // Sample end of data output
    SSP1CON1 = 0x08;                       // SCL =FOSC/4(SSPxADD+1)
    SSP1CON3 = 0x00;
    SSP1ADD = 0x4F;                        // 0x4F = 100 kHz Clock @ 32 MHz
    PIR3bits.SSP1IF = 0;                   // Clear the interrupt flag
    PIE3bits.SSP1IE = 0;                   // Disable the interrupt
    SSP1CON1bits.SSPEN = 1;                // Enable MSSP
}

// Master write routine
void I2C_WriteByte(uint8_t deviceADDR, uint8_t registerADDR, uint8_t val)
{
    while (!Idle);                        // Wait until MSSP module is idle
    I2C_Start = 1;                        // Set START bit
    while (I2C_Start);                    // Wait until hardware clears SEN
    SSP1BUF = deviceADDR;                 // Load device address (write)
    while (!Idle);                        // Wait until MSSP module is idle
    SSP1BUF = registerADDR;               // Send register address (write)
    while (!Idle);                        // Wait until module is idle
    SSP1BUF = val;                        // Send data to slave
    while (!Idle);                        // Wait until module is idle
    I2C_Stop = 1;                         // Set STOP bit 
    while (!Idle);                        // Wait until module is idle
}

// Master read routine
uint8_t I2C_ReadByte(uint8_t deviceADDR, uint8_t registerADDR)
{
    uint8_t data;                        // Data byte
    while (!Idle);                       // Wait until module is idle
    I2C_Start = 1;                       // Set START bit 
    while (I2C_Start);                   // Wait until hardware clears SEN
    SSP1BUF = deviceADDR;                // Load device address (write)
    while (!Idle);                       // Wait until module is idle
    SSP1BUF = registerADDR;              // Send register address (write)
    while (!Idle);                       // Wait until module is idle

    I2C_Restart = 1;                     // Restart condition
    while (I2C_Restart);

    SSP1BUF = (deviceADDR | 0x01);       // Send device address (read)
    while (!Idle);                       // Wait until module is idle
    SSP1CON2bits.RCEN = 1;               // Set RCEN (Master receiver)
    while (!Idle);                       // Wait until module is idle
    data = SSP1BUF;                      // Read SSPBUF
    SSP1CON2bits.ACKDT = 1;              // ACK bit, 1 = Not acknowledge 
    SSP1CON2bits.ACKEN = 1;              // Start ACK sequence 
    while (SSP1CON2bits.ACKEN);          // Wait for end of ACK sequence 
    SSP1CON2bits.ACKDT = 0;              // ACK bit, 0 = acknowledge 
    I2C_Stop = 1;                        // Set STOP bit 
    while (!Idle);                       // Wait until module is idle
    return data;                         // Return the data 
}