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.

MSP430G2533: I2C Master --> Slave : Address is transmitted but the slave does not respond after first byte, also ACK depends on the pull up resistor

Part Number: MSP430G2533
Other Parts Discussed in Thread: MSP430G2553, RF430FRL152H

I'm trying to use the I2C protocol to communicate between the MSP-EXP430G2 evaluation board (having the MSP430G2533 micro-controller) and a photo-detector (slave). 

I've connected 2.2 K Ohms pull external resistors from the SDA and SCL lines pulled-up to the VDD (3.4 volts). 

The slave address is 0x2A. 

The protocol to communicate with the slave requires the following sequence(shown in the figure below) that I've stored the TX_DATA[8] array. 

The code I'm using is pasted here. The problem is that: I'm either getting no ACK signal from the slave (this happens 90% of the times with some random value of the pull up), or sometimes when I get the ACK signal apart from the address there is no other data being transmitted (this happens only 10% of the time).

When I single step in the code in CCS Studio, the code gets stuck in the I2C transmit interrupt :   __bis_SR_register(CPUOFF + GIE);         and does not come out. 

Does anyone have any idea why it would be that way? or did anyone face any similar problem before? any help would be highly appreciated. 

// Communicating with the I2C Slave for data transfer from the photo-detector
// TI Development starter kit board is used here
#include <msp430.h>
/*
* main.c
*/
void I2C_Initialise(void);
void I2C_Transmit(void);
void I2C_Receive(void);

int TransmitByteCtr = 8;
unsigned char PointerRxData; // Pointer to RX data
int Rx = 0;
unsigned char TX_DATA[8] = {0x2A, 0x00, 0x89, 0x2A, 0x00, 0x09, 0x2A, 0x03};

int main( void )
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
//DISABLE_INT; // Disable all the interrupts
//ClockInitialise();
// Both are Port 1: Port 1.6 Port 1.7 Bits are set for I2C using the port registers, SDA = P1.7 SCL = P1.6 //
P1DIR |= 0xC0;
P1SEL |= BIT6 + BIT7; // Assign I2C pins to USCI_B0 //
P1SEL2|= BIT6 + BIT7; // Assign I2C pins to USCI_B0
I2C_Initialise();
while(1)
{
//Transmit process
Rx = 0;
I2C_Transmit();
while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent

//Receive process
Rx = 1;
I2C_Receive();
while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
}
}

//-------------------------------------------------------------------------------
// The USCI_B0 data ISR is used to move received data from the I2C slave
// to the MSP430 memory. It is structured such that it can be used to receive
//-------------------------------------------------------------------------------
#pragma vector = USCIAB0TX_VECTOR
__interrupt void USCIAB0TX_ISR(void)
{
if(Rx == 1)
{ // Master Recieve?
PointerRxData = UCB0RXBUF; // Get RX data
__bic_SR_register_on_exit(CPUOFF); // Exit LPM0
}

else
{ // Master Transmit
if (TransmitByteCtr<8) // Check TX byte counter
{
UCB0TXBUF = TX_DATA[TransmitByteCtr]; // Load TX buffer
TransmitByteCtr++; // Decrement TX byte counter
}
else
{
UCB0CTL1 |= UCTXSTP; // I2C stop condition
IFG2 &= ~UCB0TXIFG; // Clear USCI_B0 TX int flag
__bic_SR_register_on_exit(CPUOFF); // Exit LPM0
}
}
}

void I2C_Initialise(void)
{
IE2 |= UCB0RXIE; //Enable RX interrupt
IE2 |= UCB0TXIE; // Enable TX interrupt
while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
UCB0CTL1 |= UCSWRST; // Enable SW reset
UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode
UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
UCB0BR0 = 12; // fSCL = SMCLK/12 = ~100kHz
UCB0BR1 = 0;
UCB0I2CSA = 0x2A; // Slave Address is 02Ah
UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
}

void I2C_Transmit(void)
{
// while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
__bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts
}

void I2C_Receive(void)
{
UCB0CTL1 &= ~UCTR ; // Clear UCTR
while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
UCB0CTL1 |= UCTXSTT; // I2C start condition
while (UCB0CTL1 & UCTXSTT); // Start condition sent?
UCB0CTL1 |= UCTXSTP; // I2C stop condition
__bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts
}

I

  • Hi,

    are you using a logic analyzer to view the communication between the devices? Could you provide screenshots of the failures and "successes" you are seeing? Also, what value does the slave device recommend for the pull-up resistor?

    The portion of the code __bis_SR_register(CPUOFF + GIE); puts the device into a low power mode (LPM) and then the rest of the processing is done in the interrupt service routine(ISR). If you set breakpoints within the ISR you should see that code is executing there.

    I'll take a closer look at your code and let you know if I see anything out of the ordinary. I also recommend taking a look at the application report: Solutions to Common eUSCI and USCI Serial Communication Issues on MSP430 MCUs

    Best regards,
    Caleb Overbay

  • Hi,

    Have you made any progress on this? One comment I have on the code is that you initialize TransmitByteCtr to 8 and then in the ISR have a if statement "if (TransmitByteCtr<8)" which will always evaluate to false.

    Best regards,
    Caleb Overbay
  • Hi Caleb,

    Yes I did make progress and am able to transmit data from the master successfully.

    I need some help with the Rx part of the code now.

    After this sequence, the master requests the slave for the data on the SDA line, however the master must generate the clock for the data, do you've any clues of how to go about generating a read ISR for 8 bytes of data totally`?

    After I read those bits of data the next step is to store them in the memory. Any hints for that?

  • Hi,

    Reading data with I2C is certainly more tricky than writing. It depends on the read protocol specified in the slave devices data sheet, so be sure to familiarize yourself with it. Typically though the sequence is this:

    1. Send the start sequence
    2. Send the slave address with the R/W bit low
    3. Send the register number
    4. Send the start sequence again (repeated start)
    5. Send the slave address with the R/W bit high
    6. Read data bytes
    7. Send the stop sequence

    Also, be mindful of when to send the stop sequence. On the MSP430G2553 the stop sequence must be sent on N-1th byte received. Sections 5.2 and 5.3 of the Solutions to Common eUSCI and USCI Serial Communication Issues on MSP430 MCUs go into this in more detail as well as the devices User's Guide.

    Finally, I've attached some example code that performs I2C reads and writes from the MSP430G2553 as the master. This code follows the sequence specified in the application report I've linked to previously. Please use this as a guide for how to read multiple or single bytes from an I2C slave.

    8358.main.c
    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    #include <msp430.h>
    #include <stdint.h>
    /* Slave Address */
    #define Slave_Addr 0x28
    /* I2C Communication States */
    typedef enum
    {
    Idle,
    NACK,
    TX_Reg_Index,
    RX_Data,
    TX_Data,
    Switch_to_RX,
    Timeout
    }I2C_State;
    /* I2C Write and Read Functions */
    I2C_State I2C_Write_Reg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint8_t cnt);
    I2C_State I2C_Read_Reg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data, uint8_t cnt);
    /* Global Variables */
    uint8_t I2C_Reg_Index = 0;
    uint8_t *I2C_Reg_Data;
    uint8_t TXByteCtr = 0;
    uint8_t RXByteCtr = 0;
    I2C_State Comm_State = Idle;
    uint8_t Write_buffer[10];
    uint8_t Read_buffer[10];
    int main(void){
    WDTCTL = WDTPW | WDTHOLD; // Stop watch dog timer
    /* Initialize clock system*/
    BCSCTL1=CALBC1_8MHZ;
    DCOCTL=CALDCO_8MHZ;
    BCSCTL2|=DIVS_2; //SMCLK divider = 4;
    /* Initialize I2C pins */
    P1SEL|=BIT6+BIT7;
    P1SEL2|=BIT6+BIT7; // P1.6=SCL (I2C) P1.7=SDA (I2C)
    /* Initialize USCI_B for I2C Communication */
    UCB0CTL1 |= UCSWRST; // Enable SW reset
    UCB0CTL1 |=UCSSEL_3 + UCTR; // select SMCLK, transmitter mode
    UCB0CTL0 |=UCMODE_3 + UCMST + UCSYNC; // set I2C MODE MASTER MODE
    UCB0BR0=20; // clk divider from SMCLK, 100kHz
    UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
    UCB0I2CIE |= UCNACKIE; // Enable NACK interrupt
    /* Initialize TimerA for timeout detection */
    TACCR0 = 10000; // 5ms
    TACCTL0 |= CCIE; // Enable CCR0 interrupts
    TACTL |= TASSEL_2; // SMCLK = 2MHz
    __delay_cycles(7000000); // Wait for slave to setup
    __enable_interrupt(); // Enable global interrupts
    /* Example of writing to slave */
    Write_buffer[0] = 0x80;
    Write_buffer[1] = 0x0C;
    I2C_Write_Reg(Slave_Addr, 0x3F, &Write_buffer[0], 1); // Write 1 byte to slave register 0x3F
    if(Comm_State != Idle)
    {
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    Best regards,
    Caleb Overbay 

  • Hi,

    Thank you for the answer. If I want to port the same code on the TI RF430FRL152H chip, will it work? I mean is the architecture and register set compatible across MSP430's ? If not, what do I need to change?

    http://www.ti.com/product/RF430FRL152H/technicaldocuments

  • Hi,

    No this code will not work on the RF430FRL152H because the MSP430G2553 uses an USCI module for communication and the RF430FRL152H  uses the eUSCI module. You can find info on switching between the two modules here: Migrating from the USCI Module to the eUSCI Module

    Best regards, 
    Caleb Overbay

**Attention** This is a public forum