0
\$\begingroup\$

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

\$\endgroup\$
  • \$\begingroup\$ Do you actually have data to send? You need to point to a 'send' file with data already loaded, then start the IC2 unit after enabling interrupts. The MPU will fetch the data and load the 'send' buffer. The SCL will stop after the last byte has been sent. Have you already done this? \$\endgroup\$ – Sparky256 May 25 '16 at 20:51
  • \$\begingroup\$ Yes, at start, I have a code to read the chip ID. Once this is done, there is a while(1), to read a register every 100ms. I have followed the example code that was provided in generated i2c.h. \$\endgroup\$ – Abhishek G May 25 '16 at 20:59
  • 1
    \$\begingroup\$ Maybe check TRIS and see if you have accidentally configured it as an input. \$\endgroup\$ – John Birckhead May 25 '16 at 21:03
  • 1
    \$\begingroup\$ So you did not display all of your code, but if you have 5,000 lines of code just display what inits pointers, etc. What you have is general purpose config files. \$\endgroup\$ – Sparky256 May 25 '16 at 21:04
  • \$\begingroup\$ @JohnBirckhead I tried with TRISC0 and TRISC1 as 0. It is the same. \$\endgroup\$ – Abhishek G May 25 '16 at 21:21
0
\$\begingroup\$

You probably need pull-up resistors on the I2C lines. I'm using ~5k resistors to pull the I2C lines up to Vdd. The internal pull-ups are too weak for use with I2C. I don't have anything connected to the I2C bus, so the communication ends in a NAK:I2C Signal

This is my main:

void main(void)
{
    // initialize the device
    SYSTEM_Initialize();
    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();

    while (1)
    {
        I2C_MESSAGE_STATUS status = I2C_MESSAGE_PENDING;
        uint8_t buffer[20];
        I2C_MasterRead(buffer, 5, 42, &status);
        while(I2C_MESSAGE_PENDING == status);
        __delay_ms(10);
        status = I2C_MESSAGE_PENDING;
        I2C_MasterWrite(buffer, 5, 42, &status);
        __delay_ms(250);
    }
}

Everything else is generated by MCC for a PIC16F1619 on a curiosity board. I used C0 and C1 for my I2C lines, like in your case.

I am using MPLABX 3.30, with MCC 3.15, and XC8 1.37.

\$\endgroup\$
  • 1
    \$\begingroup\$ Thanks for the reply. I was able to resolve the issue. Had SMBus problem after this. Also, the generated I2C code has to be modified at two locations by adding an extra uint16_t typecasting. \$\endgroup\$ – Abhishek G Jun 30 '16 at 17:02
  • \$\begingroup\$ @AbhishekG What locations? \$\endgroup\$ – Ryan B Jul 27 '16 at 20:07
0
\$\begingroup\$

...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 :)

\$\endgroup\$
  • 1
    \$\begingroup\$ Thanks for the reply. I was able to resolve the issue. Had SMBus problem after this. Also, the generated I2C code has to be modified at two locations by adding an extra uint16_t typecasting. \$\endgroup\$ – Abhishek G Jun 30 '16 at 17:03
  • \$\begingroup\$ Its is fun to work with I2C, I do agree. One thing I observed when I worked with pic controller was, the clock signal will be generated only during the data communication. \$\endgroup\$ – Abhishek G Jun 30 '16 at 17:05
  • \$\begingroup\$ Oh interesting. I've only used PIC for I2C so I figured it was normal to generate the SCL clock only when it was needed. Seems wasteful to always leave it running. \$\endgroup\$ – Vince Patron Jun 30 '16 at 19:54
  • \$\begingroup\$ True! It is not needed to generate the clock signals all the time. But one of the other development board I worked on had a ARM controller and it used to generate clock all the time. I was in the same mode when I asked this question. Anyway, learnt about it the hard way.! :D \$\endgroup\$ – Abhishek G Jun 30 '16 at 20:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.