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.

I2C problem with USI on G2231

Other Parts Discussed in Thread: MSP430G2231

 Hello,

i'm trying to interface a RTC via I2C with the USI module of an MSP430G2231. I used the sample code provided by TI as a basis to build my own state machine in the USI interrupt vector.

My problem is that SDA line doesn't stay high before and after a transaction with my code. Therefore the START and STOP conditions are missing. See picture below:

 

If i execute the original sample code the START and STOP conditions are generated well. So i could eliminate a hardware error. See picture below:

 

I searched my code about 100 times to find a difference between the sample code or a mistake. But with no success. Maybe someone could help..?

If attached a file with my I2C relevant code. Just call the "rtcReadTime()" function to start a transmission

#include "main.h"
#include "general.h"
#include "rtc.h"

//---------------------------------------------------------
// module variables
//---------------------------------------------------------
u8  mu8_address;        // RTC address to read or write to
u8  mu8_write;          // is true if tx, else rx
u8  mu8_bytesRxTx;      // number of bytes to rx/tx
u8  *mu8_pData;         // pointer to rx/tx buffer
int I2C_State;          // state machine variable

//---------------------------------------------------------
// internal functions
//---------------------------------------------------------
void startTransmission(void)
{
  P1OUT = BIT6 | BIT7;                 // P1.6 & P1.7 Pullups
  P1REN |= BIT6 | BIT7;                // P1.6 & P1.7 Pullups
  P1DIR = BIT6 | BIT7;                 // Pins as Outputs

  CLEAR_GLOBAL(I2C_ERROR);             // reset status
  I2C_State = 0;
  
  _DINT();
  USICTL0 = USIPE6 + USIPE7 + USIMST + USISWRST;    // Port & USI mode setup
  USICTL1 = USII2C + USIIE;                         // Enable I2C mode & USI interrupt
  USICKCTL = USIDIV_6 + USISSEL_2 + USICKPL;        // Setup USI clocks: SCL = SMCLK/64 (~15kHz)
  USICNT |= USIIFGCC;                               // Disable automatic clear control
  USICTL0 &= ~USISWRST;                             // Enable USI
  USICTL1 &= ~USIIFG;                               // Clear pending flag
  _EINT();
  
  USICTL1 |= USIIFG;                                // Set flag and start communication
  LPM0;                                             // CPU off, SMCLK on, ACLK on, await USI interrupt
}

//---------------------------------------------------------
// exported functions
//---------------------------------------------------------
void rtcReadTime()
{
    u8 tempBuff[7];
    // read values from RTC
    mu8_address = 0x00;
    mu8_write = FALSE;
    mu8_bytesRxTx = sizeof(tempBuff);
    mu8_pData = tempBuff;
    // start transmission
    startTransmission();
    
    // check for errors
    if( GET_GLOBAL(I2C_ERROR) ){
      return;
    }
    
    // copy RTC data into time structure
    gs_time.second = ((tempBuff[0] >> 4) * 10) + (tempBuff[0] & 0x0F);
    gs_time.minute = ((tempBuff[1] >> 4) * 10) + (tempBuff[1] & 0x0F);
    gs_time.hour   = ((tempBuff[2] >> 4) * 10) + (tempBuff[2] & 0x0F); 
    gs_time.wday   = tempBuff[3];
    gs_time.day    = ((tempBuff[4] >> 4) * 10) + (tempBuff[4] & 0x0F);
    gs_time.month  = ((tempBuff[5] && 0x10) ? 10 : 0) + (tempBuff[5] & 0x0F);
    gs_time.year   = tempBuff[6];    
}

//---------------------------------------------------------
// interrupt functions
//---------------------------------------------------------
#pragma vector = USI_VECTOR
__interrupt void USI_TXRX (void)
{
  switch(__even_in_range(I2C_State, 20))
    {
      case 0: // Generate Start Condition & send address to slave
              USISRL = 0x00;           // Generate Start Condition...
              USICTL0 |= USIGE+USIOE;
              USICTL0 &= ~USIGE;
              USISRL = RTC_ADDR;       // ... and transmit address, R/W = 0
              USICNT = (USICNT & 0xE0) + 0x08; // Bit counter = 8, TX Address
              I2C_State = 2;           // Go to next state: receive address (N)Ack
              break;
              
      case 2: // Receive Address Ack/Nack bit
              USICTL0 &= ~USIOE;       // SDA = input
              USICNT |= 0x01;          // Bit counter = 1, receive (N)Ack bit
              I2C_State = 4;           // Go to next state: check (N)Ack
              break;

      case 4: // Process Address Ack/Nack & send address byte
              USICTL0 |= USIOE;        // SDA = output
              if (USISRL & 0x01)       // If Nack received...
              { // Send stop...
                SET_GLOBAL(0x80 | I2C_ERROR); // set error flag
                USISRL = 0x00;
                USICNT |=  0x01;       // Bit counter = 1, SCL high, SDA low
                I2C_State = 20;        // Go to next state: generate Stop
              }
              else
              { // Send address
                USISRL = mu8_address;  // Load address byte
                USICNT |=  0x08;       // Bit counter = 8, start TX
                I2C_State = 6;         // Go to next state: receive address (N)Ack
              }
              break;
              
      case 6: // Receive Address/Data Ack/Nack
              USICTL0 &= ~USIOE;       // SDA = input
              USICNT |= 0x01;          // Bit counter = 1, receive (N)Ack bit
              I2C_State = 8;           // Go to next state: check (N)Ack
              
      case 8: // Process Address/Data Ack/Nack
              USICTL0 |= USIOE;        // SDA = output
              if (USISRL & 0x01)       // If Nack received 
              { // Send stop...
                SET_GLOBAL(0x40 | I2C_ERROR); // set error flag
                USISRL = 0x00;
                USICNT |=  0x01;       // Bit counter = 1, SCL high, SDA low
                I2C_State = 20;        // Go to next state: generate Stop
                break;
              }
              // Ack received
              if (mu8_bytesRxTx == 0)  // If no further bytes to tx
              { // Send stop...
                USISRL = 0x00;
                USICNT |=  0x01;       // Bit counter = 1, SCL high, SDA low
                I2C_State = 20;        // Go to next state: generate Stop
                break;
              }
              
              if( mu8_write )
              { // Send data byte
                USISRL = *mu8_pData;   // Load data byte
                mu8_pData++;           // increment data pointer   
                mu8_bytesRxTx--;       // decrement byte counter
                USICNT |=  0x08;       // Bit counter = 8, start TX
                I2C_State = 6;         // Go to next state: receive data (N)Ack
              }
              else
              { // Send repeated start for read data
                USISRL = 0x00;          // Generate Start Condition...
                USICTL0 |= USIGE+USIOE;
                USICTL0 &= ~USIGE;
                __delay_cycles(200);
                USISRL = RTC_ADDR | 0x01;       // ... and transmit address, R/W = 1
                USICNT = (USICNT & 0xE0) + 0x08; // Bit counter = 8, TX Address
                I2C_State = 10;        // Go to next state: receive address (N)Ack
              }
              break;
                
      case 10: // Receive Address Ack/Nack bit
              USICTL0 &= ~USIOE;       // SDA = input
              USICNT |= 0x01;          // Bit counter = 1, receive (N)Ack bit
              I2C_State = 12;          // Go to next state: check (N)Ack
              break;
        
        
      case 12: // Process Address Ack/Nack & receive data byte
              if (USISRL & 0x01)       // If Nack received...
              { // Prep Stop Condition
                SET_GLOBAL(0x20 | I2C_ERROR); // set error flag
                USICTL0 |= USIOE;
                USISRL = 0x00;
                USICNT |=  0x01;       // Bit counter = 1, SCL high, SDA low
                I2C_State = 20;        // Go to next state: generate Stop
              }
              else                     // Ack received
              { // Receive Data from slave
                USICTL0 &= ~USIOE;       // SDA = input
                USICNT |=  0x08;       // Bit counter = 8, RX data
                I2C_State = 14;        // Go to next state: Test data and (N)Ack
              }
              break;
        
      case 14: // copy Data / Send Data Ack/Nack bit
              USICTL0 |= USIOE;        // SDA = output
              *mu8_pData = USISRL;     // copy rxed data
              mu8_bytesRxTx--;         // decrement byte counter
              if (mu8_bytesRxTx > 0)   // If more data to receive
              {
                mu8_pData++;           // increment data pointer
                USISRL = 0x00;         // Send Ack
                I2C_State = 16;        // Go to next state: receive further
              }
              else
              {
                USISRL = 0xFF;         // Send NAck
                I2C_State = 18;        // Go to next state: prep stop
              }
              USICNT |= 0x01;          // Bit counter = 1, send (N)Ack bit
              break;
        
      case 16: // receive further data
              USICTL0 &= ~USIOE;       // SDA = input
              USICNT |=  0x08;         // Bit counter = 8, RX data
              I2C_State = 14;          // Go to next state: copy Data / Send Ack/Nack
              break;
        
      case 18: // Prep Stop Condition
              USICTL0 |= USIOE;        // SDA = output
              USISRL = 0x00;
              USICNT |=  0x01;         // Bit counter = 1, SCL high, SDA low
              I2C_State = 20;          // Go to next state: generate Stop
              break;
        
      case 20:// Generate Stop Condition
              USISRL = 0xFF;           // USISRL = 1 to release SDA
              USICTL0 |= USIGE;        // Transparent latch enabled
              USICTL0 &= ~(USIGE+USIOE);// Latch/SDA output disabled
              I2C_State = 0;           // Reset state machine for next transmission
              LPM0_EXIT;               // Exit active for next transfer
              break;
    }

    USICTL1 &= ~USIIFG;                  // Clear pending flag
}
.

 

The original source code that works well:

//******************************************************************************
//  MSP430G2x21/G2x31 Demo - I2C Master Transmitter, single byte
//
//  Description: I2C Master communicates with I2C Slave using
//  the USI. Master data is sent and increments from 0x00 with each transmitted
//  byte which is verified by the slave.
//  LED off for address or data Ack; LED on for address or data NAck.
//  ACLK = n/a, MCLK = SMCLK = Calibrated 1MHz
//
//  ***THIS IS THE MASTER CODE***
//
//                  Slave                      Master
//          (msp430g2x21_usi_08.c)
//             MSP430G2x21/G2x31          MSP430G2x21/G2x31
//             -----------------          -----------------
//         /|\|              XIN|-    /|\|              XIN|-
//          | |                 |      | |                 |
//          --|RST          XOUT|-     --|RST          XOUT|-
//            |                 |        |                 |
//      LED <-|P1.0             |        |                 |
//            |                 |        |             P1.0|-> LED
//            |         SDA/P1.7|<-------|P1.7/SDA         |
//            |         SCL/P1.6|<-------|P1.6/SCL         |
//
//  Note: internal pull-ups are used in this example for SDA & SCL
//
//  D. Dang
//  Texas Instruments Inc.
//  October 2010
//  Built with CCS Version 4.2.0 and IAR Embedded Workbench Version: 5.10
//******************************************************************************

#include <msp430g2221.h>

char MST_Data = 0;                     // Variable for transmitted data
char SLV_Addr = 0xD0;                  // Address is 0xD0 << 1 bit + 0 for Write
int I2C_State = 0;                     // State variable

void main(void)
{
  volatile unsigned int i;             // Use volatile to prevent removal

  WDTCTL = WDTPW + WDTHOLD;            // Stop watchdog
  if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF)                                     
  {  
    while(1);                          // If calibration constants erased
                                       // do not load, trap CPU!!
  }   
  BCSCTL1 = CALBC1_1MHZ;               // Set DCO
  DCOCTL = CALDCO_1MHZ;

  P1OUT = 0xC0;                        // P1.6 & P1.7 Pullups, others to 0
  P1REN |= 0xC0;                       // P1.6 & P1.7 Pullups
  P1DIR = 0xFF;                        // Unused pins as outputs
  P2OUT = 0;
  P2DIR = 0xFF;

  USICTL0 = USIPE6+USIPE7+USIMST+USISWRST; // Port & USI mode setup
  USICTL1 = USII2C+USIIE;              // Enable I2C mode & USI interrupt
  USICKCTL = USIDIV_3+USISSEL_2+USICKPL; // Setup USI clocks: SCL = SMCLK/8 (~125kHz)
  USICNT |= USIIFGCC;                  // Disable automatic clear control
  USICTL0 &= ~USISWRST;                // Enable USI
  USICTL1 &= ~USIIFG;                  // Clear pending flag
  _EINT();

  while(1)
  {
    USICTL1 |= USIIFG;                 // Set flag and start communication
    LPM0;                              // CPU off, await USI interrupt
    _NOP();                            // Used for IAR
    for (i = 0; i < 5000; i++);        // Dummy delay between communication cycles
  }
}

/******************************************************
// USI interrupt service routine
******************************************************/
#pragma vector = USI_VECTOR
__interrupt void USI_TXRX (void)
{
  switch(I2C_State)
    {
      case 0: // Generate Start Condition & send address to slave
              P1OUT |= 0x01;           // LED on: sequence start
              USISRL = 0x00;           // Generate Start Condition...
              USICTL0 |= USIGE+USIOE;
              USICTL0 &= ~USIGE;
              USISRL = SLV_Addr;       // ... and transmit address, R/W = 0
              USICNT = (USICNT & 0xE0) + 0x08; // Bit counter = 8, TX Address
              I2C_State = 2;           // Go to next state: receive address (N)Ack
              break;

      case 2: // Receive Address Ack/Nack bit
              USICTL0 &= ~USIOE;       // SDA = input
              USICNT |= 0x01;          // Bit counter = 1, receive (N)Ack bit
              I2C_State = 4;           // Go to next state: check (N)Ack
              break;

      case 4: // Process Address Ack/Nack & handle data TX
              USICTL0 |= USIOE;        // SDA = output
              if (USISRL & 0x01)       // If Nack received...
              { // Send stop...
                USISRL = 0x00;
                USICNT |=  0x01;       // Bit counter = 1, SCL high, SDA low
                I2C_State = 10;        // Go to next state: generate Stop
                P1OUT |= 0x01;         // Turn on LED: error
              }
              else
              { // Ack received, TX data to slave...
                USISRL = MST_Data;     // Load data byte
                USICNT |=  0x08;       // Bit counter = 8, start TX
                I2C_State = 6;         // Go to next state: receive data (N)Ack
                P1OUT &= ~0x01;        // Turn off LED
              }
              break;

      case 6: // Receive Data Ack/Nack bit
              USICTL0 &= ~USIOE;       // SDA = input
              USICNT |= 0x01;          // Bit counter = 1, receive (N)Ack bit
              I2C_State = 8;           // Go to next state: check (N)Ack
              break;

      case 8: // Process Data Ack/Nack & send Stop
              USICTL0 |= USIOE;
              if (USISRL & 0x01)       // If Nack received...
                P1OUT |= 0x01;         // Turn on LED: error
              else                     // Ack received
              {
                MST_Data++;            // Increment Master data
                P1OUT &= ~0x01;        // Turn off LED
              }
              // Send stop...
              USISRL = 0x00;
              USICNT |=  0x01;         // Bit counter = 1, SCL high, SDA low
              I2C_State = 10;          // Go to next state: generate Stop
              break;

      case 10:// Generate Stop Condition
              USISRL = 0x0FF;          // USISRL = 1 to release SDA
              USICTL0 |= USIGE;        // Transparent latch enabled
              USICTL0 &= ~(USIGE+USIOE);// Latch/SDA output disabled
              I2C_State = 0;           // Reset state machine for next transmission
              LPM0_EXIT;               // Exit active for next transfer
              break;
    }

  USICTL1 &= ~USIIFG;                  // Clear pending flag
}

 

 

  • Please check code differences by some software tool (for example: Kdiff3, Meld, WinMerge).
    You should find the difference that is the source of the error.

    It is good practice to add changes by small parts, iteratively.
    When you had checked from one version to another you would have found the problem much easier.

    What is initial state of SDA? Is it the same in two compared cases?
    Maybe after first communication in your modified code it is not returned to the correct state?

    Regards,
    Piotr Romaniuk, Ph.D.
    ELESOFTROM

    PS
    Can you attach also original code that works?

  • On first glance, it look sfine. Yet you did not post your main code.

    Also, it is strange that you initialize the port pins on start of transmission, rather than start of main.

    Teh difference between teh code exampl and your code is that the example code initializes everything once on startup and then starts the transfers, while your code is (re-) initializing the USI module and ports for each single transfer.

**Attention** This is a public forum