Electrical – No clock signal on SCL pin of PIC16F1618

i2cmplabxpicpic16fxc8

I have been trying to configure I2C on PIC16F1618 microcontroller. I am using MPLAB X IDE, along with xc8 compiler. The MSSP code is being generated through MPLAB code configurator. I have set the configuration to be I2C Master interrupt, the clock frequency to be 62.5KHz. Slew rate is standard speed, SDA hold time to be 300ns. slave address to be 7bit. I have configured RC0 to be SDA and RC1 to be SCL. So my generated I2C initialize looks like this,

void I2C_Initialize(void) {
    i2c_object.pTrHead = i2c_tr_queue;
    i2c_object.pTrTail = i2c_tr_queue;
    i2c_object.trStatus.s.empty = true;
    i2c_object.trStatus.s.full = false;

    i2c_object.i2cErrors = 0;

    // R_nW write_noTX; P stopbit_notdetected; S startbit_notdetected; BF RCinprocess_TXcomplete; SMP Standard Speed; UA dontupdate; CKE Idle to Active; D_nA lastbyte_address; 
    SSP1STAT = 0x80;
    // SSPEN enabled; WCOL no_collision; CKP Idle:Low, Active:High; SSPM FOSC/4_SSPxADD; SSPOV no_overflow; 
    SSP1CON1 = 0x28;
    // ACKTIM ackseq; SBCDE disabled; BOEN disabled; SCIE disabled; PCIE disabled; DHEN disabled; SDAHT 300ns; AHEN disabled; 
    SSP1CON3 = 0x08;
    // Baud Rate Generator Value: SSP1ADD 3;   
    SSP1ADD = 0x03;

    /* Byte sent or received */
    // clear the master interrupt flag
    PIR1bits.SSP1IF = 0;
    // enable the master interrupt
    PIE1bits.SSP1IE = 1;

}

I am very new to PIC microcontrollers, could you please help me where the problem is? I am using PIC16F1618. I am not able to look at any waveform when I connect my oscilloscope to SCL pin. The output is always LOW. If I connect a pull up resistance, then the pin will always be at logical HIGH. I have also tried to send some read/ write commands to check if clock starts, but sadly it does not. I have worked with ARM controllers, and in that, after initializing, I will be able to observe the clock signals.

in main.c

I2C_Initialize();
bmp280.dev_addr = 0x77;
init_check = bmp280_init(&bmp280);

while (1) {        
        bmp280_read_uncomp_pressure(&tempVar);
}

bmp280.c

BMP280_RETURN_FUNCTION_TYPE bmp280_init(bmp280_t *temp_bmp280)
{
    /* variable used to return communication result*/
    BMP280_RETURN_FUNCTION_TYPE com_rslt = ERROR;
    uint8_t v_data_u8 = BMP280_INIT_VALUE;

    p_bmp280 = temp_bmp280;/* assign BMP280 ptr */
    /* read chip id */
    com_rslt = i2cbus_read(p_bmp280->dev_addr,
    BMP280_CHIP_ID_REG, &v_data_u8,
    BMP280_GEN_READ_WRITE_DATA_LENGTH);
    //com_rslt = p_bmp280->BMP280_BUS_READ_FUNC(p_bmp280->dev_addr,
    //BMP280_CHIP_ID_REG, &v_data_u8,
    //BMP280_GEN_READ_WRITE_DATA_LENGTH);/* read Chip Id */
    p_bmp280->chip_id = v_data_u8;
    /* readout bmp280 calibparam structure */
    com_rslt += bmp280_get_calib_param();
    return com_rslt;
}

BMP280_RETURN_FUNCTION_TYPE bmp280_read_uncomp_pressure(int32_t *v_uncomp_pressure_s32)
{
    /* variable used to return communication result*/
    BMP280_RETURN_FUNCTION_TYPE com_rslt = ERROR;
    /* Array holding the MSB and LSb value
    a_data_u8[0] - Pressure MSB
    a_data_u8[1] - Pressure LSB
    a_data_u8[2] - Pressure LSB
    */
    uint8_t a_data_u8[BMP280_PRESSURE_DATA_SIZE] = {BMP280_INIT_VALUE, BMP280_INIT_VALUE, BMP280_INIT_VALUE};
    /* check the p_bmp280 struct pointer as NULL*/
    if (p_bmp280 == BMP280_NULL) {
        return  E_BMP280_NULL_PTR;
        } else {
            com_rslt = i2cbus_read(p_bmp280->dev_addr,
                       BMP280_PRESSURE_MSB_REG,
                       a_data_u8, BMP280_PRESSURE_DATA_LENGTH);
            *v_uncomp_pressure_s32 = (int32_t)(
                   (((uint32_t)(a_data_u8[BMP280_PRESSURE_MSB_DATA]))
                   << BMP280_SHIFT_BIT_POSITION_BY_12_BITS) |
                   (((uint32_t)(a_data_u8[BMP280_PRESSURE_LSB_DATA]))
                   << BMP280_SHIFT_BIT_POSITION_BY_04_BITS) |
                   ((uint32_t)a_data_u8[BMP280_PRESSURE_XLSB_DATA] >>
                   BMP280_SHIFT_BIT_POSITION_BY_04_BITS));
        }
    return com_rslt;
}

structure bmp280_t used

typedef struct {
    struct bmp280_calib_param_t calib_param;/**<calibration data*/

    uint8_t chip_id;/**< chip id of the sensor*/
    uint8_t dev_addr;/**< device address of the sensor*/

    uint8_t oversamp_temperature;/**< temperature over sampling*/
    uint8_t oversamp_pressure;/**< pressure over sampling*/

}bmp280_t;

in i2c_wrapper.c

int8_t i2cbus_read(uint8_t device_addr, uint8_t register_addr, uint8_t* register_data, uint8_t rd_len)
{
    I2C_MESSAGE_STATUS status;
    uint16_t    timeOut;
    uint16_t    counter;
    uint8_t     *pD; 
    int8_t      ret = 0;

    pD = register_data;

    for (counter = 0; counter < rd_len; counter++)
    {

        // Now it is possible that the slave device will be slow.
        // As a work around on these slaves, the application can
        // retry sending the transaction
        timeOut = 0;
        while(status != I2C_MESSAGE_FAIL)
        {
            // write one byte to slave i2c device, which is the register address
            I2C_MasterWrite(&register_addr, 1, device_addr, &status);

            // wait for the message to be sent or status has changed.
            while(status == I2C_MESSAGE_PENDING);

            if (status == I2C_MESSAGE_COMPLETE)
                break;

            // if status is  I2C_MESSAGE_ADDRESS_NO_ACK,
            //               or I2C_DATA_NO_ACK,
            // The device may be busy and needs more time for the last
            // write so we can retry writing the data, this is why we
            // use a while loop here

            // check for max retry and skip this byte
            if (timeOut == MAX_RETRY)
                break;
            else
                timeOut++;
        }

        if (status == I2C_MESSAGE_COMPLETE)
        {

            // this portion will read the byte from the memory location.
            timeOut = 0;
            while(status != I2C_MESSAGE_FAIL)
            {
                // read from the slave device
                I2C_MasterRead(pD, 1, device_addr, &status);

                // wait for the message to be sent or status has changed.
                while(status == I2C_MESSAGE_PENDING);

                if (status == I2C_MESSAGE_COMPLETE)
                    break;

                // if status is  I2C_MESSAGE_ADDRESS_NO_ACK,
                //               or I2C_DATA_NO_ACK,
                // The device may be busy and needs more time for the last
                // write so we can retry writing the data, this is why we
                // use a while loop here

                // check for max retry and skip this byte
                if (timeOut == MAX_RETRY)
                    break;
                else
                    timeOut++;
            }
        }

        // exit if the last transaction failed
        if (status == I2C_MESSAGE_FAIL)
        {
            ret = -1;
            break;
        }

        pD++;
        register_addr++;

    }
    return (ret);
}

I2C_MasterWrite and I2C_MasterRead is generated by code configurator which is present in i2c.c

Best Answer

...SCL pin. The output is always LOW. If I connect a pull up resistance, then the pin will always be at logical HIGH.

It sounds like you do not have a pullup on DAT line. You need separate resistor pullups on both SCL and DAT lines. You can start with 4.7k.

Without the DAT line pullup, it will be at logic 0 which tells the I2C controller that the bus is busy. Your debugger will show that your code is stuck at the line below waiting for the bus to free up:

while(status == I2C_MESSAGE_PENDING);

Even with no I2C devices on the bus, just adding those pullups should give you SCL toggling and debugger should show getting past that wait line.

Of course, you will get a NAK error since there is no I2C device to respond, but that will at least get you past your first hurdle. (Being new to I2C, you will have more fun hurdles to jump :)