Part Three: The I2C Master

The final part of this project is the I2C master. The job of the master is to read the data from each of the two slaves, convert the data back into the proper variable type, and display the results on a PC terminal program.

The MSSP is configured in Master mode as shown in MSSP Initialization. The master begins by requesting the data from the slave device that controls the pH sensor. Once the master receives the pH data, it reconstructs the three bytes back into a floating-point number and writes the pH value to the PC terminal program. Next, the master requests data from the slave device that controls the temperature and water level sensors. Once the data is received, the master rebuilds the temperature data back into a floating-point number and writes the value to the terminal program. Then, the master converts the water level sensor data back into a 16-bit unsigned integer and compares the value to the expected values that represent either a “pass” or “fail” condition. Once the status has been selected, it is displayed on the PC terminal program. MSSP Master Routines shows the routines in main() that handle the calculations and display the data.
Important: When creating the master project, the C99 standard must also be changed to the C90 standard following the same steps as in Part One.

MSSP Initialization

void I2C_Init(void)
{
    SSP1STAT = 0x80;        // Sample end of data output time 
    SSP1CON1 = 0x28;        // SSPEN enabled; I2C master clk =FOSC/4(SSPxADD+1)
    SSP1CON3 = 0x00; 
    SSP1ADD = 0x09;         // 0x4F = 100 kHz Clock @ 32 MHz

    PIR1bits.SSP1IF = 0;    // Clear the master interrupt flag
    PIE1bits.SSP1IE = 0;    // Disable the master interrupt
}

MSSP Master Routines

// MSSP defines and macros
#define Idle        !(SSP1STATbits.R_nW | (0x1F & SSP1CON2)) // I2C Idle
#define I2C_Start   (SSP1CON2bits.SEN)                       // I2C Start 
#define I2C_Restart (SSP1CON2bits.RSEN)                      // I2C Restart
#define I2C_Stop    (SSP1CON2bits.PEN)                       // I2C Stop


#define PH_ADDRESS      0x30
#define THERM_ADDRESS   0x32
#define PH_READS         0x3
#define THERM_READS      0x5

void main(void)
{
    SYSTEM_Initialize();

    while (1)
    {
        I2C_Read(PH_ADDRESS, 0x00, PH_READS);  // Read pH sensor
        (uint24_t)newpH = ((uint24_t)I2CData[2] << 16);
        (uint24_t)newpH = (uint24_t)newpH + ((uint16_t)I2CData[1] << 8);
        (uint24_t)newpH = (uint24_t)newpH + I2CData[0];
        (float)pHValue = (uint24_t)newpH;
        pHValue /= 100.00;
        printf("pH Value = %1.1f \r\n", pHValue);
        
        I2C_Read(THERM_ADDRESS, 0x00, THERM_READS);    // Read temp sensor
        (uint24_t)newTherm = ((uint24_t)I2CData[2] << 16);
        (uint24_t)newTherm = (uint24_t)newTherm + ((uint16_t)I2CData[1] << 8);
        (uint24_t)newTherm = (uint24_t)newTherm + I2CData[0];
        (float)thermValue = (uint24_t)newTherm;
        thermValue /= 100.00;
        printf("Therm Value = %1.1f \r\n", thermValue);
        
        (uint16_t)lvlSensor = ((uint16_t)I2CData[4] << 8);
        (uint16_t)lvlSensor = (uint16_t)lvlSensor + I2CData[3];
        
        if(lvlSensor > 1010)
        {
            printf("WARNING: WATER LEVEL LOW!! ADD WATER! \r\n");
        }
        else
        {
            printf("Water level OK! \r\n");
        }
        __delay_ms(3500);
    }
}