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.

RF430CL331H: I2C interface with 400kbits/s, Clock Stretching

Part Number: RF430CL331H
Other Parts Discussed in Thread: MSP430FR2475

I implemented an NDEF Type 4 Tag with RF430CL331H by using the communication stack provided in theRF430CL331H_Host_Firmware (sloc330.zip).

The RF430CL331H is connected to a MSP430FR2475 eUSCI_B1 interface. The code is has been migrated from eUSCI_B0 to eUSCI_B1.

No other I2C peripheral on the bus. MSP430FR2475 is running with 16MHz.

If we operate the I2C interface with 100kBit/s communication works fine and I can read all data from the tag.

But if we want communicate with 400kBits/s the communication stalls at the point where the MSP writes a command to the RF430 to read data is read from the 3. NDEF file.

SCL line is pulled low permanently, no event wakes the MSP430 from sleep.

The RF430CL331H datasheet specifies 400kBit/s support if the Master supports clock stretching which is the case for eUSCI_B according to

MSP430FR4xx and MSP430FR2xx family datasheet.

The example application from TI is configured to run on 400kBits/s

    UCB0BRW = 20;                                 // Baudrate = SMLK/20 = 400kHz

There is something that irritates me: Why is in the example implementation no check for UCSCLLOW in the RF430_I2C_Write function?

What is also unclear to me: The MSP430FR2xx family datasheet states situations in which clock stretching should be avoided.

Do these situations apply for Master mode? If yes where are they handled in the example code?

Thank you for your support!

/*
 * RF430_Comm.c
 * Polarion ID: ISM_CU-2171
 *
 * RF430CL331H Host Example Project
 *
 * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
 *
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *    Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the
 *    distribution.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
*/
#include "config.h"
#if CONFIG_IMPLEMENT_NFC

//#include "msp430.h"
#include "RF430_example.h"
#include "RF430_Comm.h"
#include <stdint.h>
#include <driverlib.h>

#define START           1
#define CONTINUE        0
#define FAIL            0
#define PASS            1
#define SET             1
#define CLEAR           0
#define I2C_NACK_RCVD   2
#define I2C_TRANSMIT    3
#define LPM_MODE        LPM0_bits
uint8_t  *PTxData;                     // Pointer to TX data
uint8_t *PRxData;                     // Pointer to RX data
uint16_t TXByteCtr;
uint16_t RXByteCtr;

uint8_t RF430_I2C_State;
uint8_t RF430_I2C_Start = 0;

//#define I2C_DELAY                 600
//#define PULSE_OUT  P1SEL &= ~BIT4; P1DIR |= BIT4; P1OUT|= BIT4  //scope interrupt
#define I2C_DELAY           0
#define I2C_ALLOW               (I2C_READY_IN & I2C_READY_PIN)

/**************************************************************************************************************************************************
*   Read_Register
***************************************************************************************************************************************************
*
* Brief : Reads the register at reg_addr, returns the result
*
* Param[in] :   reg_addr: the address to read out data from
*
* Param[out]:   None
*
* Return :      uint16_t: the result from the register
**************************************************************************************************************************************************/
uint16_t Read_Register(uint16_t reg_addr)
{
    uint8_t TxAddr[2] = {0,0};
    uint8_t RxBuffer[2] = {0,0};

    TxAddr[0] = reg_addr >> 8;      // MSB of address
    TxAddr[1] = reg_addr & 0xFF;    // LSB of address

    while(!I2C_ALLOW);      //wait until it is safe to transmit

    if(RF430_I2C_Write_Restart_Read((uint8_t *)TxAddr, 2, (uint8_t *)RxBuffer, 2) != FAIL)
        return RxBuffer[1] << 8 | RxBuffer[0];
    else
        __no_operation();

    while (UCB1CTL1 & UCTXSTP);             // Ensure stop condition got sent

    return RxBuffer[1] << 8 | RxBuffer[0];
}

/**************************************************************************************************************************************************
*   Read_Continuous
***************************************************************************************************************************************************
*
* Brief : Continuous read data_length bytes and store in the area "read_data"
*
* Param[in] :   reg_addr: the address to read out data from
*               data_length: the amount of data to read out
*
* Param[out]:   read_data: the array to store the read out data
*
* Return :      None
**************************************************************************************************************************************************/
void Read_Continuous(uint16_t reg_addr, uint8_t* read_data, uint16_t data_length)
{
    uint8_t TxAddr[2] = {0,0};

    TxAddr[0] = reg_addr >> 8;      // MSB of address
    TxAddr[1] = reg_addr & 0xFF;    // LSB of address

    while(!I2C_ALLOW);      //wait until it is safe to transmit

    RF430_I2C_Write_Restart_Read((uint8_t  *)TxAddr, 2, (uint8_t *)read_data, data_length);

    while (UCB1CTL1 & UCTXSTP);             // Ensure stop condition got sent

}

/**************************************************************************************************************************************************
*   Write_Register
***************************************************************************************************************************************************
*
* Brief : writes the register at reg_addr with a value
*
* Param[in] :   reg_addr: the address to write the data to
*               value: the value to write to reg_addr
*
* Param[out]:   None
*
* Return :      None
**************************************************************************************************************************************************/
void Write_Register(uint16_t reg_addr, uint16_t value)
{
    uint8_t TxData[4] = {0,0,0,0};

    TxData[0] = reg_addr >> 8;      // MSB of address
    TxData[1] = reg_addr & 0xFF;    // LSB of address
    TxData[2] = value & 0xFF;
    TxData[3] = value >> 8;

    while(!I2C_ALLOW);      //wait until it is safe to transmit

    RF430_I2C_Write((uint8_t  *)TxData, 4, START);

    RF430_I2C_Send_Stop();

    {
        uint16_t delay = 10;
        while ((UCB1CTL1 & UCTXSTP) && delay)             // Ensure stop condition got sent, added timeout due to infinite loop when it should not be  AK 10-27-2013
        {
            __delay_cycles(10);
            delay--;
        }
    }
}

/**************************************************************************************************************************************************
*   Write_Continuous
***************************************************************************************************************************************************
*
* Brief : writes the register at reg_addr and incrementing addresses with the data at "write_data" of length data_length
*
* Param[in] :   reg_addr: the starting address to write the data
*               write_data: an array of the data to write
*               data_length: the amount of data to write
*
* Param[out]:   None
*
* Return :      None
**************************************************************************************************************************************************/
void Write_Continuous(uint16_t reg_addr, uint8_t  * write_data, uint16_t data_length)
{
    uint8_t TxAddr[2] = {0,0};

    while(!I2C_ALLOW);      //wait until it is safe to transmit

    TxAddr[0] = reg_addr >> 8;      //MSB of address
    TxAddr[1] = reg_addr & 0xFF;    //LSB of address

    RF430_I2C_Write((uint8_t *)TxAddr, 2, START);

    RF430_I2C_Write((uint8_t  *)write_data, data_length, CONTINUE);

    RF430_I2C_Send_Stop();

    while (UCB1CTL1 & UCTXSTP);             // Ensure stop condition got sent
}

/**************************************************************************************************************************************************
*   RF430_I2C_Write
***************************************************************************************************************************************************
*
* Brief : Writes data over I2C
*
* Param[in] :   data: the data array to write
*               length: the amount of data to write
*               cont: if set to START will issue a Start condition on the I2C bus.  Other option is CONTINUE (data write without a Start)
*
* Param[out]:   None
*
* Return :      None
**************************************************************************************************************************************************/
void RF430_I2C_Write(uint8_t  * data, uint16_t length, uint16_t cont)
{
    __delay_cycles(I2C_DELAY);                   // Delay required between transaction

    PTxData = (uint8_t  *)data;      // TX array start address

    TXByteCtr = length;
/* Check if SCL is pulled low externally*/
    while ((UCB1STATW & UCSCLLOW)!=0)
    {}

    if (cont == START) {
        RF430_I2C_Start = SET;                  // need a way of knowing when a start condition has been set in the ISR AK 10-23-2013
        UCB1CTL1 |= UCTR + UCTXSTT;             // I2C TX, start condition
    }
    else {
        UCB1CTL1 |= UCTR;                       // I2C TX
        UCB1IFG |= UCTXIFG;                     // Kickoff IFG since START not sent
    }

    __bis_SR_register(LPM_MODE + GIE);     // Enter LPM, enable interrupts
    __no_operation();                       // Remain in LPM until all data
                                            // is TX'd
    RF430_I2C_Start = CLEAR;                // this should be cleared in the interrupt, but to be sure it is also cleared here, AK-1023-2013

    if (!(RF430_I2C_State == I2C_NACK_RCVD))
    {
        while (TXByteCtr != 0);          // Ensure stop condition got sent
    }

}

/**************************************************************************************************************************************************
*   RF430_I2C_Read
***************************************************************************************************************************************************
*
* Brief : Reads out data over I2C
*
* Param[in] :   data: the array to store the read out data
*               length: the amount of data to read
*
* Param[out]:   None
*
* Return :      None
**************************************************************************************************************************************************/
void RF430_I2C_Read(uint8_t* data, uint16_t length)
{
    __delay_cycles(I2C_DELAY);                   // Delay required between transaction

    PRxData = (uint8_t *)data;    // Start of RX buffer
    RXByteCtr = length;                          // Load RX byte counter

    UCB1CTL1 &= ~UCTR;                      // I2C Read
    UCB1CTL1 |= UCTXSTT;                    // I2C start condition

    __bis_SR_register(LPM_MODE + GIE);     // Enter LPM, enable interrupts
                                            // Remain in LPM until all data
                                            // is RX'd
    __no_operation();                       // Set breakpoint >>here<< and
                                            // read out the RxBuffer buffer
}

/**************************************************************************************************************************************************
*   RF430_I2C_Write_Restart_Read
***************************************************************************************************************************************************
*
* Brief : First sends out a portion of data (tx_data) and then recieves the data transmitted by the RF430CL331H
*
* Param[in] :   tx_data: the array to data to transmit
*               tx_length: the amount of data to transmit
*               rx_data: the array of data to recieve (after the transmit)
*               rx_length: the amount of data to receive
*
* Param[out]:   None
*
* Return :      None
**************************************************************************************************************************************************/
uint8_t RF430_I2C_Write_Restart_Read(uint8_t  * tx_data, uint16_t tx_length, uint8_t* rx_data, uint16_t rx_length)
{
    RF430_I2C_State = I2C_TRANSMIT;

    PTxData = (uint8_t  *)tx_data;     // TX array start address
    TXByteCtr = tx_length;

    RF430_I2C_Start = SET;                  // need a way of knowing when a start condition has been set in the ISR AK 10-23-2013
    UCB1CTL1 |= UCTR + UCTXSTT;             // I2C Master Transmitter Mode, Generate START Condition

    if(RF430_I2C_State == I2C_NACK_RCVD)    // If RF430 NACKed
    {
        //Debug_Print("RF430 NACKed!!!!!!!\n");
        return FAIL;
    }

    __bis_SR_register(LPM_MODE + GIE);     // Enter LPM, enable interrupts
    __no_operation();                       // Remain in LPM until all data

    RF430_I2C_Start = CLEAR;                // this should be cleared in the interrupt, but to be sure it is also cleared here, AK-1023-2013

    if(RF430_I2C_State == I2C_NACK_RCVD)    // If RF430 NACKed
    {
        //Debug_Print("RF430 NACKed!!!!!!!\n");
        return FAIL;
    }

    PRxData = (uint8_t *)rx_data;           // Start of RX buffer
    RXByteCtr = rx_length;                          // Load RX byte counter

    UCB1CTL1 &= ~UCTR;                      // I2C Read
    RF430_I2C_Start = CLEAR;                // only needed on transmit and start condition --AK 10-23-2013
    UCB1CTL1 |= UCTXSTT;                    // I2C start condition

    __bis_SR_register(LPM_MODE + GIE);     // Enter LPM, enable interrupts
                                            // Remain in LPM until all data
                                            // is RX'd
    RF430_I2C_Start = CLEAR;                // this should be cleared in the interrupt, but to be sure it is also cleared here, AK-1023-2013
    __no_operation();                       // Set breakpoint >>here<< and
                                            // read out the RxBuffer buffer
    return PASS;
}

/**************************************************************************************************************************************************
*   RF430_I2C_Send_Stop
***************************************************************************************************************************************************
*
* Brief : Sends out a Stop condition to terminate a packet
*
* Param[in] :   None
*
* Param[out]:   None
*
* Return :      None
**************************************************************************************************************************************************/
void RF430_I2C_Send_Stop()
{
    __delay_cycles(100);
    UCB1CTL1 |= UCTXSTP;                    // I2C stop condition
    __enable_interrupt();
    while (!(UCB1CTL1 & UCTXSTP));             // Ensure stop condition got sent
    __disable_interrupt();
}


/**************************************************************************************************************************************************
*   USCI_B1_ISR
***************************************************************************************************************************************************
*
* Brief : Handles sending and recieving packets of data over I2C.  Controlled by the above functions.
*
* Param[in] :   None
*
* Param[out]:   None
*
* Return :      None
**************************************************************************************************************************************************/
#pragma vector = USCI_B1_VECTOR
__interrupt void USCI_B1_ISR(void)
{
  switch(__even_in_range(UCB1IV,12))
  {
  case  USCI_NONE:
      break;                                // No interrupts
  case  USCI_I2C_UCALIFG:                   // UCALIFG interrupt
      break;
  case  USCI_I2C_UCNACKIFG:                 // NACK recieved
    RF430_I2C_State = I2C_NACK_RCVD;
    UCB1CTL1 |= UCTXSTP;                    // I2C stop condition
    UCB1IFG = 0;                            // Clear All USCI_B0 flags
    __bic_SR_register_on_exit(LPM_MODE);    // Exit LPM
    break;
  case  USCI_I2C_UCSTTIFG:                  // Start condition
      break;
  case  USCI_I2C_UCSTPIFG:                  // Stop condition
      break;
  case USCI_I2C_UCRXIFG0:                   // Handles RX data
    RXByteCtr--;                            // Decrement RX byte counter
    if (RXByteCtr)
    {
      *PRxData++ = UCB1RXBUF;               // Move RX data to address PRxData
      if (RXByteCtr == 1)
        UCB1CTL1 |= UCTXSTP;                // Generate I2C stop condition on next RX
    }
    else
    {
      *PRxData = UCB1RXBUF;                 // Move final RX data to PRxData
      __bic_SR_register_on_exit(LPM_MODE); // Exit active CPU
    }
    break;
  case USCI_I2C_UCTXIFG0:                    // Handles TX data
    if (TXByteCtr)                          // Check TX byte counter
    {
      unsigned int delay = 0;
      UCB1TXBUF = *PTxData++;               // Load TX buffer
      TXByteCtr--;                          // Decrement TX byte counter
      if(TXByteCtr == 1)
      {
          while((delay < 9000) && (UCB1CTL1 & UCTXSTT) && (RF430_I2C_Start == SET)) //only go into this loop when a start condition has been requested
          {
              __delay_cycles(40);
              delay += 40;
          }
          if ((UCB1CTL1 & UCTXSTT) && (RF430_I2C_Start == SET))            // Still Waiting?  --only when start condition is expected,
          {
              RF430_I2C_State = I2C_NACK_RCVD;
              __bic_SR_register_on_exit(LPM_MODE); // Exit LPM
          }
      }
    }
    else
    {
      //UCB1CTL1 |= UCTXSTP;                  // I2C stop condition
      UCB1IFG &= ~UCTXIFG;                  // Clear USCI_B0 TX int flag
      __bic_SR_register_on_exit(LPM_MODE); // Exit LPM
    }
    RF430_I2C_Start = CLEAR;            //Clear after the first TX, start has been passed
    break;
  default:
    __no_operation();
    break;
  }
}

#endif

  • Hello Thomas,

    In the example, it looks like they do not include all the checking and I agree that adding a check of UCSCLLOW would be good.  I think checking the clock low timeout interrupt(UCCLTOIFG) would also be good.  If this interrupt triggers and you reset the eUSCI, does the clock stay low?  

    In regards to the stretching avoidance, this must apply only to the slave as the slave is in control of the clock stretch.