This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC430F5137: I2C communication fails with SGP30 co2 sensor

Part Number: CC430F5137
Other Parts Discussed in Thread: MSP430WARE

I am trying to establish the communication with sgp30 and read co2 & voc value from sensor, but it seems to fail. I am using the i2c library by alex (https://github.com/amykyta3/msp430_modules.I am using this library to do i2c communication with si7020, mpl3115a2 and have no issues in reading the values but seems to fail with sgp30.First i pass the reg_id as 0x2003 for init and then 0x2008 to read measurement. It seems to fail at init. 


i2c_package_t i2c_req; i2c_req.slave_addr = slave_addr; i2c_req.addr[0] = reg_id >> 8; //reg_id is 16bit uint i2c_req.addr[1] = reg_id & 0xFF ; i2c_req.addr_len = addr_len; // 2 i2c_req.data_len = destSize; //6 i2c_req.data = dest;// uint8_t meas i2c_req.read = read; // true i2c_in_progress = true; i2c_transfer_start(&i2c_req, i2c_callback); TIMEOUT_HANDLE_INIT(i2c_cmd, 2);

It seems to finish the send_addr, send_restart and assigns next state as read_data ,it fails with I2C_IV == USCI_I2C_UCNACKIFG.

Any suggestions as what is wrong?.

  • Hi Kumar,

    Since the I2C communication is working with the SI7020 and MPL3115A2 devices, I'd like to start by looking at the SGP30 device. I have a few things I'd like you to double check for me:

    Are you following all of the steps outlined in section 6 of the SGP30 datasheet?
    Specifically:
    - Operating at the I2C Fast Mode baud rate (400kHz)?
    - Powering the sensor to the required supply voltage?
    - Waiting for the required power-up time before sending I2C commands (looks to be 0.6ms)?

    Link to SGP30 datasheet for reference:
    cdn-learn.adafruit.com/.../Sensirion_Gas_Sensors_SGP30_Datasheet_EN.pdf

    Thanks,

    Mitch
  • Hi Mitch,

    Earlier the I2C baud rate was equivalent to 60Khz , now i have set it equivalent to 400 Khz. The operating voltage is 3.0 V and the power up time before accessing the I2C register is 2 seconds which should be sufficient. I am using the adafruit board with sgp30

    Now with the clock change i can initialize the sgp30 by reading the 0x2003 register and I2C returns idle status, i can read the feature set register 0x202f and it returns {0x0, 0x20, 0x7}, which also appears to be correct, but when i read the measurement register I2C communication fails.

    I have 20 second delay between init command and measurement command. Any thing i am missing in measurement sequence, i have  read the datasheet but not sure what is missing.

    Do i need any delay within the i2c function (i2c_transfer_start) within the library?.

    Thank you

    Kumar

  • Hi Kumar,

    Great! We now know that we can initialize communication and the Get_feature_set_version command works.

    Can you double check if you are seeing this behavior and also trying to read the measurement results as described below?

    Just to be safe, we have some I2C example code for the CC430F5137. It might be worth checking your I2C code against our examples to make sure the functionality is the same. These examples can be found in CCS by navigating to:

    View > Resource Explorer > Software > MSP430Ware > Devices > CC430FXX > CC430F5137 > Register Level

    I also found a document that shows how to use the SGP30's driver SW. There may be some useful information here:

    If you continue to have problems, can you send me a logic analyzer screenshot of the issue? 

    Thanks,

    Mitch

  • Hi Mitch,

    Thank you for your help, i dont have logic analyzer to see i2c in operation as we are a small startup . I have attached the code i use and will appreciate if you can help me understand whats going wrong. 

    ISR(I2C_ISR_VECTOR){
        if(I2C_IV == USCI_I2C_UCNACKIFG){
            I2C_IFG = 0;
            I2C_IE = 0;
            transfer.status = I2C_FAILED;
            I2C_CTL1 |= UCTXSTP; // set stop condition
            if(transfer.callback){
                transfer.callback(I2C_FAILED);
            }
            
            return;
        }
        
        I2C_IFG = 0;
        
        switch(transfer.next_state){
            case SM_SEND_ADDR: // (TX Register address)
                // arrive here once STT has begun. Safe to write to TXBUF
                
                I2C_TXBUF = transfer.pkg->addr[transfer.idx];
                transfer.idx++;
                if(transfer.idx == transfer.pkg->addr_len){
                    // that was the last address byte.
                    
                    // update next state
                    if(transfer.pkg->data_len != 0){
                        transfer.idx = 0;
                        if(transfer.pkg->read){
                            transfer.next_state = SM_SEND_RESTART;
                        }else{
                            transfer.next_state = SM_WRITE_DATA;
                        }
                    }else{
                        transfer.next_state = SM_DONE;
                    }
                }
                break;
            case SM_WRITE_DATA:
                I2C_TXBUF = transfer.pkg->data[transfer.idx];
                transfer.idx++;
                if(transfer.idx == transfer.pkg->data_len){
                    // that was the last data byte to send.
                    
                    // update next state
                    transfer.next_state = SM_DONE;
                }
                
                break;
            case SM_SEND_RESTART:
                I2C_CTL1 &= ~UCTR;    // Set to receiver mode
                I2C_CTL1 |= UCTXSTT; // write (re)start condition
                
                if(transfer.pkg->data_len == 1){
                    // wait for STT bit to drop
                    while(I2C_CTL1 & UCTXSTT);
                    I2C_CTL1 |= UCTXSTP; // schedule stop condition
                }
                // update next state
                transfer.next_state = SM_READ_DATA;
                break;
            case SM_READ_DATA:
                transfer.pkg->data[transfer.idx] = I2C_RXBUF;
                transfer.idx++;
                if(transfer.idx == transfer.pkg->data_len - 1){
                    // next incoming byte is the last one.
                    I2C_CTL1 |= UCTXSTP; // schedule stop condition
                    break;
                }else if(transfer.idx < transfer.pkg->data_len){
                    // still more to recv.
                    break;
                }
                // otherwise, that was the last data byte to recv.
                // fall through
            case SM_DONE:
                I2C_IE = 0;
                if(transfer.pkg->read == false){
                    // If finished a write, schedule a stop condition
                    I2C_CTL1 |= UCTXSTP;
                }
                transfer.status = I2C_IDLE;
                if(transfer.callback){
                    transfer.callback(I2C_IDLE);
                }
                break;
        }
    }
    
    //--------------------------------------------------------------------------------------------------
    void i2c_init(void){
        
        I2C_CTL0 |= UCSWRST;    
        //Initialize all USCI registers with UCSWRST = 1 (including UCxCTL1).
        I2C_CTL0 = UCMST|UCMODE_3|UCSYNC;
        I2C_CTL1 = UCTR|UCSWRST|(I2C_CLK_SRC << 6);
        I2C_BR = I2C_CLK_DIV;
        
        I2C_CTL1 &= ~UCSWRST; // Clear reset
        
        transfer.status = I2C_IDLE;
    }
    
    //--------------------------------------------------------------------------------------------------
    void i2c_uninit(void){
        I2C_CTL0 = UCSYNC; 
        I2C_CTL1 = UCSWRST;
        I2C_SA = 0;
        I2C_IE = 0;
    }
    
    //--------------------------------------------------------------------------------------------------
    void i2c_transfer_start(const i2c_package_t *pkg, void (*callback)(i2c_status_t result)){
        
        if(i2c_transfer_status() == I2C_BUSY) return;
        
        transfer.pkg = (i2c_package_t*)pkg;
        transfer.idx = 0;
        transfer.callback = callback;
        transfer.status = I2C_BUSY;
        
        // update next state
        if(pkg->addr_len != 0){
            transfer.next_state = SM_SEND_ADDR;
        }else if(pkg->data_len != 0){
            if(pkg->read){
                transfer.next_state = SM_SEND_RESTART;
            }else{
                transfer.next_state = SM_WRITE_DATA;
            }
        }else{
            transfer.next_state = SM_DONE;
        }
        
        I2C_IFG = 0;
        I2C_IE = UCNACKIE | UCTXIE | UCRXIE;
        I2C_SA = pkg->slave_addr;
        I2C_CTL1 |= UCTR; // Set to transmitter mode
        I2C_CTL1 |= UCTXSTT; // start condition (sends slave address)
        
    }
    //The function below is called from application code
    int8_t read(uint8_t slave_addr, uint16_t reg_id , uint8_t addr_len,void* dest, uint8_t destSize, bool read) {
            i2c_package_t i2c_dev;
            i2c_dev.slave_addr = slave_addr;
            i2c_dev.addr[0] = reg_id >> 8;
            i2c_dev.addr[1] = reg_id & 0xFF ;
            i2c_dev.addr_len = addr_len;
            i2c_dev.data_len = destSize;
            i2c_dev.data = dest;
            i2c_dev.read = read;
    
            i2c_in_progress = true;
            i2c_transfer_start(&i2c_dev, i2c_callback);
            TIMEOUT_HANDLE_INIT(i2c_cmd, 2);
    
            while ( i2c_in_progress && !IS_HANDLE_TIMEOUT(i2c_cmd) ) ;
            if (IS_HANDLE_TIMEOUT(i2c_cmd) || i2c_failed)
                    return 1;
            return 0; // OK
    }
    //main
    ....
    uint8_t read[6];
    
    uint8_t init[2];
    
    read(0x58,0x2003,2,init,sizeof(init),true);
    
    delay(20000); // delay for 20 seconds as per data sheet after sending init command
    
    read(0x58,0x2008,2,read,sizeof(read),true);
    ....

    When i compare i2c_transfer_start with i2c examples from cc430f5137 (single byte RX/TX and multibyte RX/TX) it seems to be same, unless i missed out something. 

    I was able to get a digital osc by zeroplus. I have attached the three screen grabs , one is for init, read measurement and read features. I still have no idea why the nack.

    The screen shot below is for init:

    The screenshot below is for read measurements

    The screenshot below is for get features- this seems to return the correct value 0x0020 as per data sheet but i still see the nack.

    Any suggestions as to what i am missing?.

    NOTE: Please disregard the PACKET 3 and PACKET 4 in the screenshots, i have two more i2c devices (0x49,0x7F) which can be connected on the same bus externally since it is disconnected , it will be a nack.

    Thank you 

    Kumar

  • Hey Kumar,

    I think your I2C communication is working. The I2C spec states that in the case where the master reads the slave immediately after the first byte, the master will send a NACK just before the stop bit:

    Here is figure 12 it references:

    Your screenshot of the "Get_feature_set_version" command follows the same format that the I2C spec document describes. The NACK is expected.

    Also after looking at the SGP30 datasheet again, it looks like this is addressed in a few places:

    Figure 9 is below:

    The NACKs you are seeing are expected. Here is a link to the I2C spec document for reference

    Thanks,

    Mitch

  • Hi Mitch,

    Thanks for input regarding the feature set,

    But do you have any suggestions as to why it fails during the read operation while trying to read measurements. 

    It appears i am getting an ack when writing the measurement command and while trying read it sends addr nack. I have no clue why this happens.

    Any inputs on this?.

    Kumar

  • Hi Mitch,

    My understanding now is that while doing measurement read , as per the data sheet once the measurement command (0x2008) is issued, the sensor needs 10-12 ms to complete and be ready to transfer the data to the mcu.  

    Is lack of delay the the reason why when it tries to read the data it fails with A-NACK.? I am also not sure if library code below handles the delay that is required by the sensor, I have also modified the library and have sgp30 communicate with cc430 but do you have any suggestions / workaround without changes to library.

    NOT_WORKING_CODE

    switch(transfer.next_state){
            case SM_SEND_ADDR: // (TX Register address)
                // arrive here once STT has begun. Safe to write to TXBUF
                
                I2C_TXBUF = transfer.pkg->addr[transfer.idx];
                transfer.idx++;
                if(transfer.idx == transfer.pkg->addr_len){
                    // that was the last address byte.
                    
                    // update next state
                    if(transfer.pkg->data_len != 0){
                        transfer.idx = 0;
                        if(transfer.pkg->read){
                            transfer.next_state = SM_SEND_RESTART;
                        }else{
                            transfer.next_state = SM_WRITE_DATA;
                        }
                    }else{
                        transfer.next_state = SM_DONE;
                    }
                }
                break;
    
    case SM_SEND_RESTART: I2C_CTL1 &= ~UCTR; // Set to receiver mode I2C_CTL1 |= UCTXSTT; // write (re)start condition if(transfer.pkg->data_len == 1){ // wait for STT bit to drop while(I2C_CTL1 & UCTXSTT); I2C_CTL1 |= UCTXSTP; // schedule stop condition } // update next state transfer.next_state = SM_READ_DATA; break;

    MODFIED_WORKING_CODE:

           case SM_SEND_RESTART:
                I2C_CTL1 &= ~UCTR;    // Set to receiver mode          
                UCB0IE |= UCRXIE;
                do {
                            UCB0CTL1 |= UCTXSTT; // RE-START 
                            while (UCB0CTL1&UCTXSTT); //wait for STT clear again
                    } while (UCB0IFG & UCNACKIFG);
                if(transfer.pkg->data_len == 1){
                    // wait for STT bit to drop
                    while(I2C_CTL1 & UCTXSTT);
                    I2C_CTL1 |= UCTXSTP; // schedule stop condition
                }
                // update next state
                transfer.next_state = SM_READ_DATA;
                break;

    Kumar

  • Hi Kumar,

    Yes this makes sense. This explains why you get the A-NACK in this scenario, but not when you issue the Get_feature_set_version command. One workaround I can think of is to trigger a timer to count to 12ms after you send the Measure_air_quality command. Once this timer counts to 12ms, send the read command. You can implement this however you see fit (with flag variables, etc). This will require SW changes to your application code, but you shouldn't have to make changes in the I2C library.

    Thanks,

    Mitch
  • Hello Mitch,

    Thanks a ton for your support on this, waiting for 12 ms has to be done inside driver or not use the driver and implement the same in application code. After writing/sending the measure command we need to wait or poll for 12ms before sending the re-start command , sadly this is not done in driver. My guess is when the library was written , the developer did not encounter any devices where wait was needed before reading measurements.

    However we encountered another issue totally unrelated , sgp30 needs to be polled every second for accurate measurment and we need to set humidity and other baseline functionality , unfortunately we have so many external peripherals both analog and digital , implementing all features of sgp30 is causing an SRAM overflow . Our next course of action is use arduino mini pro 3.3 V , mount sgp30 on that and and connect that to our sensor product .

    UPDATE: I have chosen the adafruit trinket m0 as companion board for sgp30 which will be connected to cc430f5137 using serial port for sending the data, it seems extremely compact at 2.7cm x 1.5cm. Just ordered it.


    Kumar