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.

Slow I2C msp430g2553

Other Parts Discussed in Thread: MSP430G2553

I have been trying to interface the msp430g2553 with the serial EEPROM 24LC512. I found this bit of code which works and I am able to store and receive data, however, the I2C bus is extremely slow with data transfer around 8Hz. The code is as follows:

#include "msp430g2553.h"

//Address word + "HELLO WORLD"
unsigned char txdata[14] = {0x00, 0x00, 0x00, 0x48, 0x45, 0x4C, 0x4C, 0x4F, 0x20, 0x57, 0x4F, 0x52, 0x4C, 0x44};

unsigned char rxdata[12];
unsigned char tx_byte_count;
unsigned char rx_byte_count;
unsigned char tx_byte_counter;
unsigned char rx_byte_counter;
unsigned char i;
unsigned char tx_rx;

void i2c_tx(unsigned char tx_count);
void i2c_rx(unsigned char rx_count);


void main(void)
{
WDTCTL = WDTPW + WDTHOLD; //Stop WDT
//DCOCTL = 0x00; // Set DCOCLK to 16MHz
BCSCTL1 = CALBC1_16MHZ; //Set DCO to 1MHz
DCOCTL = CALDCO_16MHZ;
P1SEL |= BIT6 + BIT7; //Set I2C pins
P1SEL2|= BIT6 + BIT7;
UCB0CTL1 |= UCSWRST; //Enable SW reset
UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; //I2C Master, synchronous mode
UCB0CTL1 = UCSSEL_2 + UCSWRST; //Use SMCLK, keep SW reset
UCB0BR0 = 160; //fSCL = SMCLK/12 = ~100kHz
UCB0BR1 = 0;
UCB0I2CSA = 0x50; //Slave Address
UCB0CTL1 &= ~UCSWRST; //Clear SW reset, resume operation
IE2 |= UCB0TXIE; //Enable TX interrupt
IE2 |= UCB0RXIE; //Enable RX interrupt

//__delay_cycles(20000); //Just a start up delay

//i2c_tx(13); //i2c TX 13 bytes(Address word + "HELLO WORLD"
__delay_cycles(20000); //Allow 24LC256 to write data
i2c_tx(2); //i2c TX address
i2c_rx(12); //i2c RX data
__bis_SR_register(CPUOFF + GIE); //Wait for a reset
}

void i2c_tx(unsigned char tx_count)
{
tx_rx = 0;
tx_byte_count = tx_count + 1;
tx_byte_counter = tx_count; // Load TX byte counter
UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
__bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts
// Remain in LPM0 until all data is TX'd
}

void i2c_rx(unsigned char rx_count)
{
tx_rx = 1;
rx_byte_count = rx_count + 1;
rx_byte_counter = rx_count; // Load RX byte counter
UCB0CTL1 &= ~UCTR; // I2C RX
UCB0CTL1 |= UCTXSTT; // I2C start condition
__bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts
// Remain in LPM0 until all data is RX'd
}

//interrupt(USCIAB0TX_VECTOR) USCIAB0TX_ISR(void)
#pragma vector = USCIAB0RX_VECTOR
__interrupt void USCIAB0RX_ISR(void) //For mspgcc
{
if(tx_rx == 0)
{
if (tx_byte_counter > 0) //Check TX byte counter
{
UCB0TXBUF = txdata[tx_byte_count - tx_byte_counter]; // Load TX buffer
tx_byte_counter--; //Decrement TX byte counter
}
else if(tx_byte_counter == 0)
{
UCB0CTL1 |= UCTXSTP; //I2C stop condition
while (UCB0CTL1 & UCTXSTP); //Ensure stop condition got sent
IFG2 &= ~UCB0TXIFG; //Clear USCI_B0 TX int flag
__bic_SR_register_on_exit(CPUOFF+GIE); //Exit LPM0
}
}
else if(tx_rx == 1)
{
if (rx_byte_counter > 0) //Check RX byte counter
{
rxdata[rx_byte_count - rx_byte_counter] = UCB0RXBUF;
rx_byte_counter--; //Decrement RX byte counter
}
else if(rx_byte_counter == 0)
{
UCB0CTL1 |= UCTXSTP; // I2C stop condition
while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
rxdata[rx_byte_count - (rx_byte_counter + 1)] = UCB0RXBUF;
rxdata[rx_byte_count - (rx_byte_counter + 1)] = UCB0RXBUF;
IFG2 &= ~UCB0RXIFG; // Clear USCI_B0 RX int flag
__bic_SR_register_on_exit(CPUOFF+GIE); // Exit LPM0
}
}
}

Changing the value of UCB0BR0 and UCBR0BR1 or changing the clock speed does not affect the speed of communication Any help on this would be greatly appreciated.

  • Why do you use an ISR to handle an USCI interrupt if your ISR code does
    - busy waiting for events
    - not checking for the cause of the interrupt but assuming i tknow what happened bysed on soem global variables

    If your ISR gets called because of an unexpected interrupt, you won't handle it adn it will cause the whole code run in circles until the 'expected' interrupt happens. And after it, ti continues wasting time running in circles through the ISR instead of executing your main code.

    ISRs are fast-in fast-out. The IFG bits tell you what caused the interrupt and you act based on it. No waiting loops, no complex calculations etc.

    However, this shouldn't have the effect of slowing down the I2C clock to 8Hz.
    Did you accidentally erase the DCO calibraiton constants, so the DCO isn't 16MHz, as expected, but only a few kHz?

    Can you try enabling the SMCLK output and check it with a scope/frequency meter? IF SMCLK isn't 16MHz, then I2CCLK won't be 100kHz, obviously.

  • I tried checking the frequency through Timer A using SMCLK as the source and it does show that SMCLK is running at 16 MHZ.

  • Mishel George said:
    I tried checking the frequency through Timer A using SMCLK as the source and it does show that SMCLK is running at 16 MHZ.

    Okay, then I2CCLK should be 100kHz.

    There is a small glitch in the ISR: you shouldn't manually reset TXIFG or RXIFG. This prevents this bit ever being set again until the USCI is reset. However, in this specific situation it doesn't matter. But you should instead clear the IE bit(s). And set them in the i2c_tx/rx functions instead of during USCI init.

    One possible thing that might happen is thatyour slave device uses clock stretching.

    When the master addresses the slave, the slave holds SCL low until it is ready. During this time, the I2C bus is stalled. Once the slave is ready, it will ack the slave request and transfer will continue - with 100kBd.
    If you say there seems to be an 8bd clock, then it may be that the slave holds the bus for ~1s. You'd need a logic analyzer (or a digital scope) to be sure.

    Well, I wouldn't expect this from an EEPROM.

    As a quick test, lower the clock frequency, e.g. don't use 16MHz but 1MHz instead. Is the transfer by a factor of 16 slower too? Or does it stay the same?

    Another thought: do you have proper pullups on the signal lines? I2C needs external ones in the range of 1k to 10k. If not, then the master will detect clock stretching because nobody is pulling the clock line up. It only slowly rises due to leakage currents. So every low clock pulse is followed by a looong charging time.

  • That was silly of me. I didn't have pull-ups in place so far but since it was still working (albeit slow) I didn't think that would be an issue. Thanks a lot and thanks for the explanation as well.

  • Mishel George said:
    That was silly of me.

    You're by far not the first one forgetting the pullups. :) Silly me that I didn't think of it at the first place. However, I too was assuming that this part is okay when you said that it was working, but slow. It took some time to come up with it by 'simulating' it in mind.

**Attention** This is a public forum