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.

CCS/MSP430FR2633: FR2633 I2C interface problem with CO2 sensor

Part Number: MSP430FR2633


Tool/software: Code Composer Studio

Good day admin,

Im using MSP430FR2633 (as I2C master) to control a carbon dioxide (CO2) sensor, SCD30 by Sensirion. All technical information on the sensor can be found on the link below:

https://www.sensirion.com/en/download-center/carbon-dioxide-sensors-co2/co2-sensor/

When I use the MSP430FR2633 I2c resource example code for the sensor, only 1 cycle of reading can be captured and then the I2C fail to continue function even though its in the while loop. I will have to keep reseting the MCU on the Dev board to repeat the single cycle of reading and even so, its not a guaranteed any data can be read. Not too sure if there are any issues in the buffer.

For reference, the TI I2C example code is as follows: msp430fr243x_eusci_i2c_standard_master.c, Author: Nima Eskandari and Ryan Meredith, January 2018 , Built with CCS V7.3

I would like to know if I made any mistake or miss out something in my following main.c code on the I2C portion:

I2C initialization portion:

void initGPIO()
{
// Configure GPIO
LED_OUT &= ~(LED0_PIN | LED1_PIN); // P1 setup for LED & reset output
LED_DIR |= (LED0_PIN | LED1_PIN);

// I2C pins
P1SEL0 |= BIT2 | BIT3;
P1SEL1 &= ~(BIT2 | BIT3);

// Disable the GPIO power-on default high-impedance mode to activate
// previously configured port settings
PM5CTL0 &= ~LOCKLPM5;
}

void initClockTo16MHz()
{
// Configure one FRAM waitstate as required by the device datasheet for MCLK
// operation beyond 8MHz _before_ configuring the clock system.
FRCTL0 = FRCTLPW | NWAITS_1;

// Clock System Setup
__bis_SR_register(SCG0); // disable FLL
CSCTL3 |= SELREF__REFOCLK; // Set REFO as FLL reference source
CSCTL0 = 0; // clear DCO and MOD registers
CSCTL1 &= ~(DCORSEL_7); // Clear DCO frequency select bits first
CSCTL1 |= DCORSEL_5; // Set DCO = 16MHz
CSCTL2 = FLLD_0 + 487; // DCOCLKDIV = 16MHz
__delay_cycles(3);
__bic_SR_register(SCG0); // enable FLL
while(CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)); // FLL locked
}

//-----------------------------------------------------------------------------
void SCD30_Init() /* -- adapt the init for your uC -- */
{
// init I2C
UCB0CTLW0 = UCSWRST; // Enable SW reset
UCB0CTLW0 |= UCMODE_3 | UCMST | UCSSEL__SMCLK | UCSYNC; // I2C master mode, SMCLK
UCB0BRW = 160; // fSCL = SMCLK/160 = ~100kHz
UCB0I2CSA = SLAVE_ADDR; // Slave Address
UCB0CTLW0 &= ~UCSWRST; // Clear SW reset, resume operation
UCB0IE |= UCNACKIE;
//SCD30_SetI2cAdr(i2cAddress);
//SCD30_SetI2cTimeout(timeoutI2c);
}

Interrupt portion: 

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
#else
#error Compiler not supported!
#endif
{
//Must read from UCB0RXBUF
uint8_t rx_val = 0;
switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
{
case USCI_NONE: break; // Vector 0: No interrupts
case USCI_I2C_UCALIFG: break; // Vector 2: ALIFG
case USCI_I2C_UCNACKIFG: // Vector 4: NACKIFG
break;
case USCI_I2C_UCSTTIFG: break; // Vector 6: STTIFG
case USCI_I2C_UCSTPIFG: break; // Vector 8: STPIFG
case USCI_I2C_UCRXIFG3: break; // Vector 10: RXIFG3
case USCI_I2C_UCTXIFG3: break; // Vector 12: TXIFG3
case USCI_I2C_UCRXIFG2: break; // Vector 14: RXIFG2
case USCI_I2C_UCTXIFG2: break; // Vector 16: TXIFG2
case USCI_I2C_UCRXIFG1: break; // Vector 18: RXIFG1
case USCI_I2C_UCTXIFG1: break; // Vector 20: TXIFG1


case USCI_I2C_UCBCNTIFG:break;
case USCI_I2C_UCCLTOIFG: break;
case USCI_I2C_UCBIT9IFG: break;

case USCI_I2C_UCRXIFG0: // Vector 22: RXIFG0
rx_val = UCB0RXBUF;
if (RXByteCtr)
{
ReceiveBuffer[ReceiveIndex++] = rx_val;
RXByteCtr--;
}

if (RXByteCtr == 1)
{
UCB0CTLW0 |= UCTXSTP;
}
else if (RXByteCtr == 0)
{
UCB0IE &= ~UCRXIE;
MasterMode = IDLE_MODE;
__bic_SR_register_on_exit(CPUOFF); // Exit LPM0
}
break;
case USCI_I2C_UCTXIFG0: // Vector 24: TXIFG0
switch (MasterMode)
{
case TX_REG_ADDRESS_MODE:
UCB0TXBUF = TransmitRegAddr;
if (RXByteCtr)
MasterMode = SWITCH_TO_RX_MODE; // Need to start receiving now
else
MasterMode = TX_DATA_MODE; // Continue to transmision with the data in Transmit Buffer
break;

case SWITCH_TO_RX_MODE:
UCB0IE |= UCRXIE; // Enable RX interrupt
UCB0IE &= ~UCTXIE; // Disable TX interrupt
UCB0CTLW0 &= ~UCTR; // Switch to receiver
MasterMode = RX_DATA_MODE; // State state is to receive data
UCB0CTLW0 |= UCTXSTT; // Send repeated start
if (RXByteCtr == 1)
{
//Must send stop since this is the N-1 byte
while((UCB0CTLW0 & UCTXSTT));
UCB0CTLW0 |= UCTXSTP; // Send stop condition
}
break;

case TX_DATA_MODE:
if (TXByteCtr)
{
UCB0TXBUF = TransmitBuffer[TransmitIndex++];
TXByteCtr--;
}
else
{
//Done with transmission
UCB0CTLW0 |= UCTXSTP; // Send stop condition
MasterMode = IDLE_MODE;
UCB0IE &= ~UCTXIE; // disable TX interrupt
__bic_SR_register_on_exit(CPUOFF); // Exit LPM0
}
break;

default:
__no_operation();
break;
}
break;
default: break;
}
}

I2C read and write functions: 

I2C_Mode I2C_Master_ReadReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t count)
{
/* Initialize state machine */
MasterMode = TX_REG_ADDRESS_MODE;
TransmitRegAddr = reg_addr;
RXByteCtr = count;
TXByteCtr = 0;
ReceiveIndex = 0;
TransmitIndex = 0;

/* Initialize slave address and interrupts */
UCB0I2CSA = dev_addr;
UCB0IFG &= ~(UCTXIFG + UCRXIFG); // Clear any pending interrupts
UCB0IE &= ~UCRXIE; // Disable RX interrupt
UCB0IE |= UCTXIE; // Enable TX interrupt

UCB0CTLW0 |= UCTR + UCTXSTT; // I2C TX, start condition
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0 w/ interrupts

return MasterMode;

}


I2C_Mode I2C_Master_WriteReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint8_t count)
{
/* Initialize state machine */
MasterMode = TX_REG_ADDRESS_MODE;
TransmitRegAddr = reg_addr;

//Copy register data to TransmitBuffer
CopyArray(reg_data, TransmitBuffer, count);

TXByteCtr = count;
RXByteCtr = 0;
ReceiveIndex = 0;
TransmitIndex = 0;

/* Initialize slave address and interrupts */
UCB0I2CSA = dev_addr;
UCB0IFG &= ~(UCTXIFG + UCRXIFG); // Clear any pending interrupts
UCB0IE &= ~UCRXIE; // Disable RX interrupt
UCB0IE |= UCTXIE; // Enable TX interrupt

UCB0CTLW0 |= UCTR + UCTXSTT; // I2C TX, start condition
__bis_SR_register(LPM0_bits + GIE); // Enter LPM0 w/ interrupts

return MasterMode;
}

void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count)
{
uint8_t copyIndex = 0;
for (copyIndex = 0; copyIndex < count; copyIndex++)
{
dest[copyIndex] = source[copyIndex];
}
}

Thank you for your kind assistance in this.

Best regards,

Wyman

  • Wyman,

    I did not find the main function, would you please share with me the main function? Thanks.

    Best regards,

    Jovi

  • Hi Jovi,

    Thanks for the response, here it is:

    int main(void) {



    float co2Concentration; // CO2 concentration [PPM]
    float temperature; // temperature [°C]
    float humidity; // relative humidty [%]
    uint16_t dataReady; // 0 -> no data in measurement buffer
    // 1 -> data available in measurement buffer

    initClockTo16MHz();
    WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer

    initGPIO();
    //initI2C();
    // __delay_cycles(100000); // wait 100ms after power on [pls check]

    SCD30_Init();

    SCD30_Read_Serial();

    // set periodic measurement interval to 2 seconds
    SCD30_SetMeasurementInterval(2);

    SCD30_ReadMeasurementBuffer(&co2Concentration, &temperature, &humidity);
    SCD30_StartPeriodicMeasurment();

    __enable_interrupt();

    while(1)
    {

    SCD30_GetDataReady(&dataReady);
    if (dataReady>0)
    {
    SCD30_ReadMeasurementBuffer(&co2Concentration, &temperature, &humidity);

    // add your code after analyst data and send to Sigfox


    }
    // __delay_cycles(100000); // wait 100ms after power on [pls check]
    }

    __bis_SR_register(LPM0_bits + GIE);
    return 0;
    }

    Its just a loop to keep reading the data. Here are the 2 major problems i just encountered after looking at my logic analyzer:

    In summary there are 2 issues:

    1. The SCL and SDA line pulled to low after an unpredictable time
    2. After attaining 1 reading, the sensor data ready loops at an unpredictable time even with SEL pin connect ground or not. (i think this is sensor issue)

    Eventually the SCL and SDA line would be pulled to low (red line) all the way at a random period. When i mean by random, it means the time mark in which it drops to low is unpredictable. The longest period of data I acquired is 631ms while the shortest is 24ms.

    After the 2 lines were low, the TI Eval board has to be re-powered (USB unplug) in order for the SDA and SCL to be high again. Re-building, Re-debugging and Pressing the reset button does not work.

    Hope this information helps. Thanks once again

    Best regards,
    Wyman Eng
  • Hi Jovi,

    Upon troubleshooting with my SDA and SCL lines for my sensor, i realized that the TI I2C got timed out because no data was written into the I2C buffer and it pulled both my SDA and SCL line to low. Is there a way to go round this? If im not wrong, technically my SDA line should still be high right?

    Best regards,

    Wyman Eng

       

  • Dear Jovi,

    sorry, forgot to put in my main code for your reference:

    int main(void) {

    float co2Concentration; // CO2 concentration [PPM]
    float temperature; // temperature [°C]
    float humidity; // relative humidty [%]
    uint16_t dataReady; // 0 -> no data in measurement buffer
    // 1 -> data available in measurement buffer

    initClockTo16MHz();
    WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer

    initGPIO();
    //initI2C();
    // __delay_cycles(100000); // wait 100ms after power on [pls check]
    timer_init();

    SCD30_Init();

    SCD30_Read_Serial();

    // set periodic measurement interval to 2 seconds
    SCD30_SetMeasurementInterval(2);

    SCD30_ReadMeasurementBuffer(&co2Concentration, &temperature, &humidity);
    SCD30_StartPeriodicMeasurment();

    __enable_interrupt();

    while(1)
    {
    //Force_calibration();
    SCD30_GetDataReady(&dataReady);
    if (dataReady>0)
    {
    SCD30_ReadMeasurementBuffer(&co2Concentration, &temperature, &humidity);

    // add your code after analyst data and send to Sigfox

    Softreset();
    }
    //timer_wait_ms(100); // wait 100ms after power on [pls check]
    }

    __bis_SR_register(LPM0_bits + GIE);
    return 0;
    }

    Best regards,

    Wyman Eng

  • Wyman,

    Thanks for providing the main code and debug details for reference.

    Regarding the two issues/findings in your debug:

    1. The device I2C timeout feature is disabled by default, and I checked your code, the timeout is not enabled. 

    2. The SDA & SCL pins are pulled low at the end and only the power OFF-ON sequence can get the SDA & SCL back to high, this is very strange. Normally pressing the 'RESET' button should resolve the issue. What's the I2C pull up resistance in the system?

    Do you have the chance to the FR2633 master and slave code? I would suggest to check this firstly to make sure the FR2633 master is working, then debug the sensor.

    You mentioned the 1 byte is captured in the system, would you please capture the 1-byte and the following bytes details to check the fail details (zoom in the waveform)? This should give some information about the fail.

    Best regards,

    Jovi He

  • Dear Jovi,

    Thanks for the response. Will start off with my data results first. I only attain 1 batch of correct data before the MCU endlessly keep pinging the sensor for any data (02 02 digits) for like 600ms. For this portion, I can understand the behaviour but  why the SCL and SDA line pulled low eventually is a mystery.

    Initially I suspected clock stretching got do with it but now that you mention my timeout is disabled, its adds more to the mystery haha.  I will continue check on your Master/sensor suggestion but in the meantime, any idea why the lines behave strangely?

    My I2C pull up resistance uses 4K7 ohm resistor each for SDA and SCL line (prototype board) and for the FR2633 Capsense Dev board, I use the pull up within. same problem.

    Best regards,

    Wyman

  • From the Interface Description (sec 1.1):
    > Clock stretching is necessary to start the microcontroller and might occur before every ACK. I2C master clock
    > stretching needs to be implemented according to the NXP specification. The boot-up time is < 2 s.
    This suggests that the SCD30 may hold SCL low for up to 2 seconds when it starts up. As it is, stretching for 12ms (1200 I2C clocks) is already pretty long. As far as I know, the MSP430 I2C is "implemented according to the NXP specification".

    > SCD30 does not support repeated start condition.
    Your code appears to use Repeated Start. The I/D doesn't say what the device does with a Repeated Start, but the designers seem to rather like clock stretching.

  • Dear Bruce,

    Not sure pinging the sensor to check if any data is ready is consider as repeated start :/ . The sensor itself has continuous and interval measurement configurations. The datasheet recommend as stated: It is recommended to use data ready status byte before readout of the measurement values.

    Either way, how does that relate to shutting down both the SDA and SCL to low?

    Best regards,
    Wyman

  • The fact that resetting the MSP430 doesn't clear the condition tells me that it's the device doing this.

    The I/D says "don't do That [repeated start]" but doesn't say what the result will be if you do That. For all we know, That could cause its MCU to reset (2sec clock-stretch). Or it could just confuse the device's internal state machine -- maybe leaving it stuck waiting for a Stop from the Tx portion of the (Read) transaction.

    Or maybe this isn't what's wrong. But it is something that is wrong.

    For reference, here's the Repeated Start:
    >case SWITCH_TO_RX_MODE:
    [...]
    >UCB0CTLW0 &= ~UCTR; // Switch to receiver
    >MasterMode = RX_DATA_MODE; // State state is to receive data
    >UCB0CTLW0 |= UCTXSTT; // Send repeated start
  • Hi Wyman,

    How about your issue? Do above replies answer your question?

    B.R
    Winter
  • Dear Winter,

    Unfortunately no, after troubleshooting cant seem to resolve it . I am now intending to re-do the whole I2C portion. That said, is there any other simplified I2C example (aside from TI resource) that is compatible with MSP430FR2633 I can use with the CO2 sensor?

    Best regards,
    Wyman Eng
  • I don't see any examples which do write-followed-by-read.
    ---------------------------
    Maybe you already tried this as part of troubleshooting, but one possibility is a quick-hack which forces a Stop just before the (repeated) Start:

    >case SWITCH_TO_RX_MODE:
    [...]
    >UCB0CTLW0 &= ~UCTR; // Switch to receiver

    >>UCB0CTLW0 |= UCTXSTP; // Stop now
    >>while (UCB0CTLW0 & UCTXSTP)/*EMPTY*/; // SLAU445H Table 24-4 says it will clear (honest!) by itself.

    >MasterMode = RX_DATA_MODE; // State state is to receive data
    >UCB0CTLW0 |= UCTXSTT; // Send repeated start

    I can't guarantee it will work, but it should be a quick low-cost experiment.

**Attention** This is a public forum