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.

Clean MSP430g2553 library for I2C Communication with ITG3200 gyroscope

Other Parts Discussed in Thread: MSP430G2553, MSP430F2274

I've been creating an I2C library for communicating with ITG3200 gyroscope for MSP430g2553 experimental launchpad. I still have slight problems with sequential reading and the signed values but I'm nearly complete. Can you help out the last problems that I'm dealing with? It seems that I'm stuck on a point. Here is the library 8272.i2c_lib.rar

<edit>

family user guide : http://www.ti.com/lit/ug/slau144i/slau144i.pdf

gyro user guide : https://www.sparkfun.com/datasheets/Sensors/Gyro/PS-ITG-3200-00-01.4.pdf

If you do not want to download those 3 source files, here is the explanation of the required functions: 

Main: 

WDTCTL = WDTPW + WDTHOLD; // Stop WDT
BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1Mhz
DCOCTL = CALDCO_1MHZ;
P1SEL |= BIT1 + BIT2 + BIT6 + BIT7; // Assign I2C pins to USCI_B0 // Assign Uart pins to USCI_A0
P1SEL2 |= BIT1 + BIT2 + BIT6 + BIT7; // Assign I2C pins to USCI_B0 // Assign Uart pins to USCI_A0
init_I2C(); // initialize i2c
initUart(); // initialize uart

..

..

I2C init:

void init_I2C(void) {
UCB0CTL1 |= UCSWRST; // Enable SW reset
UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode
UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
UCB0BR0 = 10; // fSCL = 1Mhz/10 = ~100kHz
UCB0BR1 = 0;
UCB0I2CSA = itgAddress; // Slave Address is 069h
UCB0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
IE2 |= UCB0RXIE + UCB0TXIE; // Enable RX and TX interrupt
}

Receive function: 

uint8_t Receive(char registerAddr){
uint8_t receivedByte;
while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
UCB0CTL1 |= UCTR + UCTXSTT; // I2C start condition with UCTR flag for transmit
while((IFG2 & UCB0TXIFG) == 0); //UCB0TXIFG is set immidiately
UCB0TXBUF = registerAddr; //write registerAddr in TX buffer
while((IFG2 & UCB0TXIFG) == 0); // wait until TX buffer is empty and transmitted
UCB0CTL1 &= ~UCTR ; // Clear I2C TX flag for receive
UCB0CTL1 |= UCTXSTT + UCTXNACK; // I2C start condition with NACK for single byte reading
while (UCB0CTL1 & UCTXSTT); // Start condition sent? RXBuffer full?
receivedByte = UCB0RXBUF;
UCB0CTL1 |= UCTXSTP; // I2C stop condition
return receivedByte;
}

Uart init: 

void initUart(void) {
UCA0CTL1 |= UCSSEL_2; // Use SMCLK
UCA0BR0 = 104; // 1MHz 9600
UCA0BR1 = 0; // 1MHz 9600
UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
}

integer print: 

void serialPrintInteger(uint16_t num)
{
int i;
uint16_t num_send[5];
uint16_t numTemp;
num_send[0] = num/10000; // extract 5th digit
numTemp = num % 10000; // get remaining 4
num_send[1] = numTemp/1000; // extract 4th digit
numTemp = numTemp % 1000; // get remamining 3
num_send[2] = numTemp/100; // extract 3th digit
numTemp = numTemp % 100; // get remaining 2
num_send[3] = numTemp/10; // extract 2th digit
num_send[4] = numTemp % 10; // extract 1th digit

if(num_send[0] > 0) { // if num is 5 digit
for(i = 0 ; i <= 4 ; i++)
serialWrite(num_send[i]); // send each digit as one byte
}
else if(num_send[1] > 0) { // if num is 4 digit
for(i = 1 ; i <= 4 ; i++)
serialWrite(num_send[i]);
}
else if(num_send[2] > 0) { // if num is 3 digit
for(i = 2 ; i <= 4 ; i++)
serialWrite(num_send[i]);
}
else if(num_send[3] > 0) { // if num is 2 digit
for(i = 3 ; i <= 4 ; i++)
serialWrite(num_send[i]);
}
else { // if num is 1 digit
serialWrite(num_send[4]);
}
}

void serialPrintAscii(uint8_t ascii) {
UCA0TXBUF = ascii;
while (!(IFG2&UCA0TXIFG)); // USCI_A0 TX buffer ready?
}

</editend>

And here is my problem. When i try these readings in my main loop seperately i get the results that i should have.

    serialPrintInteger(Receive(0x00));

or

    serialPrintInteger(Receive(0x15));

or 

    serialPrintInteger(Receive(0x16));

and the outputs of these are 0x69 from Receive(0x00) which reads slave address register of the gyroscope, 9 from Receive(0x15) where i wrote 9 inside 0x15 register for configuration and 25 from Receive(0x16) where i wrote aswell.

I don't think my serialPrint functions are corrupted aswell, I've tried with a lot of combination under range of 16 bit where it should be. This sequence is working fine:

serialPrintInteger(5);
serialPrintAscii(Ascii_Comma);
serialPrintInteger(10);
serialPrintAscii(Ascii_And);
serialPrintInteger(15);
serialPrintAscii(Ascii_Dot);

I see an output like this in my serial console: 5,10&15.5,10&15.5...

The funny part starting when i try this logic on my Receive function. Here is the sequence i use

serialPrintInteger(Receive(0x00)); // result 105
serialPrintAscii(Ascii_Comma);
serialPrintInteger(Receive(0x15)); // result 9
serialPrintAscii(Ascii_And);
serialPrintInteger(Receive(0x00)); // result 105
serialPrintAscii(Ascii_Dot);

The sequence in my console is like this: 105,105&9.105,105&9.105... 

First i thought i wasn't properly send NACK to the slave for single byte and i thought it kept incrementing the register address on his own but they are working fine seperately and gyro has x,y,z registers aswell and they are not corrupting my sequence. 

I was struggling with gyro motion registers for a while but i realised that I'm not in full control of my I2C just yet. So can you point out what i'm doing wrong here?

  • From looking at the USCI Operation, I2C mode section in the MSP430x2xx Family User's Guide SLAU144I I think the UCTXSTT bit will be cleared after the slave has ACKed its receive address, and before the data has been read from the slave. Therefore, the following in the Receive function could read the UCB0RXBUF before the data has been received:

    Barışcan Kayaoğlu said:
    while (UCB0CTL1 & UCTXSTT); // Start condition sent? RXBuffer full?
    receivedByte = UCB0RXBUF;

    Try changing to:

     while((IFG2 & UCB0RXIFG) == 0); // Wait until data read

      receivedByte = UCB0RXBUF;

  • When i try to do that, sometime i get the result with only reading WHO_AM_I register 78 which should be 0x69(105) and most of the time bus lock down and i get stucked in the check busy part. I don't think interrupt is the reason because if what you said was true, i could not get a clear reading with only one register.

    Edit: By the way what you said was right. I'm not sure how i actually reading with just this. Here is the code part i'm using right now.

    uint8_t Receive(char registerAddr){
    uint8_t receivedByte;
    while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
    UCB0CTL1 |= UCTR + UCTXSTT; // I2C start condition with UCTR flag for transmit
    while((IFG2 & UCB0TXIFG) == 0); //UCB0TXIFG is set immidiately
    UCB0TXBUF = registerAddr; //write registerAddr in TX buffer
    while((IFG2 & UCB0TXIFG) == 0); // wait until TX buffer is empty and transmitted
    UCB0CTL1 &= ~UCTR ; // Clear I2C TX flag for receive
    UCB0CTL1 |= UCTXSTT; // I2C start condition
    while (UCB0CTL1 & UCTXSTT); // Start condition sent?
    UCB0CTL1 |= UCTXNACK;
    while((IFG2 & UCB0RXIFG) == 0);
    receivedByte = UCB0RXBUF;
    UCB0CTL1 |= UCTXSTP; // I2C stop condition
    return receivedByte;
    }

    The order of the results are still wrong. I thought that i might get the previous result from RX buffer as you said but it still didn't fix my problem.

  • The problem here is not about interrupts or clock. The data is not corrupted, it is just that the order of registers are wrong somehow. If this whole gyroscope thing is confusing, i might try to give another example. Lets say that one MSP is communicating over i2c with another. Slave has 3 registers, 0x00, 0x01 and 0x02. lets say that data in those registers are a1, a2, and a3. When do a sequence like tis: Receive(0x00) , Receive(0x01), Receive(0x02) the result should be a1, a2, a3 but in my code somehow, it is coming like this: a1, a3, a2. In i2c protocol, single byte reading finish with a master NACK and stop condition. Otherwise, slave keeps increasing the register address itself and keep sending the data. In my understanding that something with this NACK is corrupted. Normally i should get some data from other registers that i don't want to read if this is the problem, but i'm getting the exact results with a corrupted order.

    I think my problem lies in this part of the user guide. 

    If a master wants to receive a single byte only, the UCTXSTP bit must be set while the byte is being
    received. For this case, the UCTXSTT may be polled to determine when it is cleared:
    BIS.B #UCTXSTT,&UCBOCTL1 ;Transmit START cond.
    POLL_STT BIT.B #UCTXSTT,&UCBOCTL1 ;Poll UCTXSTT bit
    JC POLL_STT ;When cleared,
    BIS.B #UCTXSTP,&UCB0CTL1 ;transmit STOP cond.
    Setting UCTXSTT will generate a repeated START condition. In this case, UCTR may be set or cleared to
    configure transmitter or receiver, and a different slave address may be written into UCBxI2CSA if desired.
    Figure 17-13 shows the I2C master receiver operation.
    NOTE: Consecutive Master Transactions Without Repeated Start
    When performing multiple consecutive I
    2C master transactions without the repeated start
    feature, the current transaction must be completed before the next one is initiated. This can
    be done by ensuring that the transmit stop condition flag UCTXSTP is cleared before the
    next I
    2C transaction is initiated with setting UCTXSTT = 1. Otherwise, the current transaction
    might be affected.

    Though I'm pretty sure i have this block while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent

    at the start of my Receive function

  • Hello again.

    I fixed my problem by changing some parts in the Receive function. Thanks to  's post, it made me realise that STT interrupt occurs right after the slave ACK the master. After checking the user guide carefully, i found this:

    If a master wants to receive a single byte only, the UCTXSTP bit must be set while the byte is being
    received. For this case, the UCTXSTT may be polled to determine when it is cleared:

    So this STOP condition must occur after the ACK but before the second data ACK. So i changed my receive function into this and it solved all my problems:

    uint8_t Receive(char registerAddr){
    uint8_t receivedByte = 0;
    while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
    UCB0CTL1 |= UCTR + UCTXSTT; // I2C start condition with UCTR flag for transmit
    while((IFG2 & UCB0TXIFG) == 0); //UCB0TXIFG is set immidiately
    UCB0TXBUF = registerAddr; //write registerAddr in TX buffer
    while((IFG2 & UCB0TXIFG) == 0); // wait until TX buffer is empty and transmitted
    UCB0CTL1 &= ~UCTR ; // Clear I2C TX flag for receive
    UCB0CTL1 |= UCTXSTT; // I2C start condition with NACK for single byte reading
    while (UCB0CTL1 & UCTXSTT); // Start condition sent? RXBuffer full?
    UCB0CTL1 |= UCTXSTP;
    while((IFG2 & UCB0RXIFG) == 0); // wait until TX buffer is empty and transmitted
    receivedByte = UCB0RXBUF; // I2C stop condition
    return receivedByte;
    }


  • I see you sorted your problem out yourself. So I'll drop teh mail you sent me.

    However, some comments:

    Barışcan Kayaoğlu said:
    while((IFG2 & UCB0TXIFG) == 0); // wait until TX buffer is empty and transmitted
    UCB0CTL1 &= ~UCTR ; // Clear I2C TX flag for receive

    As you correctly figured out, TXIFG goe shigh teh second time as soon as the first byte (the only one you want to send) has started sending, and you can set STP now.

    However, thi sis a bit dangerous. if for some reason your slave doesn't ACK (maybe because it is busy, or whatever), the second TXIFG will never come.
    You should instead wait for STT being cleared, which means that the ACK cycle for the slave address is complete. at this point either TXIFG is set or NACKIFG is set, in which case you should abort and retry.

    Also, it is not necessary to set STP to stop the sending, if you want to read immediately after. Jsut set STT once more to send a repeated start. Sending a stop will free the bus for a possible other master (in which case you might lose arbitration), but more importantly, some MSPs have an erratum listed by which the minimum time required as delay between stop and start isn't met, violating the specs and possibly causing problems. Also, it may take longer :)

  • Hi Barışcan Kayaoğlu,

    Reading your thread posted i understood that you was able to read and write to gyroscope registers using i2c.

    Even i am struggling to read and write registers of accelerometer which seems to be similar.

    Could you please send me your working code of your gyroscope to umaadevi.6@gmail.com ?

    So i better understand the sequence and modify my code for my device.

    Regards.

  • Hi Barışcan Kayaoğlu,

    Reading your thread posted i understood that you was able to read and write to gyroscope registers using i2c.

    Even i am struggling to read and write registers of accelerometer which seems to be similar.

    Could you please send me your working code of your gyroscope to umaadevi.6@gmail.com ?

    So i better understand the sequence and modify my code for my device.

    Regards.

  • Hey Barışcan, I would like to do the same thing (control and read an ITG3200) but with an MSP430f2274 instead. I've downloaded the library you put together for the MSP430g2553. If I change the header file to , would this alone enable me to use your library for my application? Thanks, Zack
  • Hello Zack.

    It's been a while since i logged to this forum because of my work. Since I'm at work right now, i may not be able to help out much but I will try to explain as much as i can. The way you said should work but you need to check if your device has a pre implemented USCI module for I2C. But I'm not sure if the header files and library is the same one that I'm talking about. Sorry if I'm a little behind the pace, but I will check my computer tonight if it is the latest library that we were talking about here. I remember when i completed my project, i made the library modular so that you could just call the functions to initialize I2C or read, write operation. I will try to keep this place updated and i will post my latest files if i can find them.

    Thank you,

    Barışcan Kayaoğlu

  • Thanks for the timely response. In the file main.c, when you assign the pins for I2c and UART with the following code: P1SEL |= BIT1 + BIT2 + BIT6 + BIT7; // Assign I2C pins to USCI_B0 // Assign Uart pins to USCI_A0 P1SEL2 |= BIT1 + BIT2 + BIT6 + BIT7; // Assign I2C pins to USCI_B0 // Assign Uart pins to USCI_A0 I'm assuming the comments mean the P1SEL is for I2C and P1SEL2 for UART. I know the corresponding pins that I need to assign on the MSP430F2274. Do I need to modify any other instances in the files to compensate for this change?
  • Zack Koontz said:
    I'm assuming the comments mean the P1SEL is for I2C and P1SEL2 for UART.

    No.

    The G2 series has two PxSEL registers (two selection bits per port pin). The 2274 has only PxSEL,a sit has more pins and only one module funciton per pin (well, maybe two, depending on direction)
    Both bits (PxSEL.x and PxSEL2.x) need to be set to switch the pin to USCI operation. If only one of them is set, the pin operates as timer I/O or in PINOSC mode.

    P1.1 and P1.2 are USCI_A0 RX and TX while P1.6 and P1.7 are USCI_B0 SDA and SCL. So P1SEL.1, P1SEL.2, P1SEL2.1 and P1SEL2.2 need to be set for UART RX/TX.

  • I found your library for the 3200 and I am so grateful you posted this for review.  I was having a hard time grasping the way the msp430 implements i2c and thanks to you I think I get it.

    On this line in the i2c.c file

    while((IFG2 & UCB0RXIFG) == 0); // wait until TX buffer is empty and transmitted

    I stay in this loop while the serialPrintInteger function is being called in the main loop.

    Does that mean that my msp430 is not receiving the data values from the gyroscope?

    Thanks for your help. 

  • Patrick McNamara1 said:
    while((IFG2 & UCB0RXIFG) == 0); // wait until TX buffer is empty and transmitted

    The comment is wrong: it waits until something has been received.

    Patrick McNamara1 said:
    I stay in this loop while the serialPrintInteger function is being called in the main loop.

    The I2C logic might wait for something which isn't handled due to a deadlock situation.

    Patrick McNamara1 said:
    Does that mean that my msp430 is not receiving the data values from the gyroscope?

    Apparently, nothing comes in for some reason. The slave my hold SCL low because it isn't ready, you may have received a NACK (slave not available) and the code doesn't check for it and stubbornly waits for incoming data.
    Or, (since the comment is for TX while the code waits for RX) there is a mistake in the sequence.

**Attention** This is a public forum