• Join
  • Sign In with my.TI Login
Texas Instruments
  • Products
  • Applications
  • Tools & Software
  • Support & Community
  • Sample & Buy
  • About TI
Sample & Purchase Cart Sample & Purchase Cart
  • Search
  • Advanced
TI E2E™ Community
  • Support Forums
  • Blogs
  • Groups
  • Videos
  • 简体中文
  • More ...
TI Home » TI E2E Community » Support Forums » Microcontrollers » MSP430™ Microcontrollers » MSP430 Ultra-Low Power 16-bit Microcontroller Forum » USCI (I2C Mode) and LPM0 on MSP430F5309
Share
MSP430™ Microcontrollers
  • Forum
  • Announcements
  • E2E Wiki
Options
  • Subscribe via RSS
MSP430 Resources
  • MSP430 Product Folder
  • MSP-EXP430G2 - MSP430 LaunchPad Value Line Development kit
  • MSP430 Getting Started Guide
  • MSP430 Microcontroller Projects
  • More Resources >
  • Forums

    USCI (I2C Mode) and LPM0 on MSP430F5309

    This question is not answered
    Mehdi Rahman
    Posted by Mehdi Rahman
    on May 15 2012 19:21 PM
    Intellectual350 points

    Hello,

    In my design, I am currently using an MSP430F5309 to communicate with an MPU-6050 sensor by Invensense: http://invensense.com/mems/gyro/sixaxis.html  

    I am using the USCI in I2C Mode, and I used the sample code as a guideline.  My intent is to use a Timer to read the sensors via I2C every X ms (let's say for example, every 20 ms, or 50 Hz).  I tried to implement this using TimerA0, and having the MSP430 operate in LPM0.  

    I have the following problem.  Before entering LPM0, I do some writing and reading via I2C.  This works out fine, as I expected.  However, after entering LPM0, it seems as if  the system no longer enters the I2C interrupt.  My code is shown below:

    -------------------------------------------------

    #include <msp430f5310.h>

    volatile static const unsigned char MPU6050_ADDR = 0x68;
    volatile static unsigned char *PTxData; // Pointer to Tx data
    volatile static unsigned char TxByteCtr;
    volatile static unsigned char TxData[256];
    volatile static unsigned char Temp;
    volatile static unsigned char WhoAmI = 0;
    volatile static unsigned char TestReg1 = 0;
    volatile static unsigned char TestReg2 = 0;
    volatile static unsigned char TestReg3 = 0;

    volatile static unsigned char RxByteCtr;
    volatile static unsigned char RxData[256];
    volatile static unsigned char RxByteDummy;

    volatile static signed int Accel[3];
    volatile static unsigned char Accel_Buffer[6];
    volatile static signed int Gyro[3];
    volatile static unsigned char Gyro_Buffer[6];

    volatile static unsigned char ReadWrite = 0;

    volatile static unsigned int Period = 655;
    volatile static unsigned char I2C_Stop_Flag = 0;

    void I2C_Send(unsigned char length, unsigned char addr);
    void Initialize_MPU6050(void);
    unsigned char MPU6050_Read_Register(unsigned char TX);
    void MPU6050_Read_Sensors(void);

    void main(void)
    {
    WDTCTL = WDTPW + WDTHOLD; // Stop WDT

    UCSCTL4 |= SELA_2; // Set ACLK = REFO

    // Initialize I2C Port
    P4SEL |= 0x06; // Assign I2C pins to USCI_B1
    UCB1CTL1 |= UCSWRST; // Enable SW reset
    UCB1CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode
    UCB1CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
    UCB1BR0 = 10; // fSCL = SMCLK/10 = ~100kHz
    UCB1BR1 = 0;
    UCB1I2CSA = 0x68; // Slave Address is 068h
    UCB1CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
    UCB1IE |= UCTXIE + UCRXIE; // Enable TX interrupt

    // Initialize MPU-6050
    __enable_interrupt();

    Initialize_MPU6050();
    WhoAmI = MPU6050_Read_Register(0x75);
    TestReg1 = MPU6050_Read_Register(0x6B);
    TestReg2 = MPU6050_Read_Register(0x1B);
    TestReg3 = MPU6050_Read_Register(0x1C);
    MPU6050_Read_Sensors();

    __disable_interrupt();

    P6DIR |= 0x04; // P6.2 output
    TA0CCTL0 = CCIE; // CCR0 interrupt enabled
    TA0CCR0 = 50000;
    TA0CTL = TASSEL_1 + MC_2 + TACLR; // ACLK, Continuous Mode, clear TAR

    __bis_SR_register(LPM0_bits + GIE);
    }

    //------------------------------------------------------------------------------
    // The USCIAB1TX_ISR is structured such that it can be used to transmit any
    // number of bytes by pre-loading TxByteCtr with the byte count. Also, TXData
    // points to the next byte to transmit.
    //------------------------------------------------------------------------------
    #pragma vector = USCI_B1_VECTOR
    __interrupt void USCI_B1_ISR(void)
    {
    switch(__even_in_range(UCB1IV,12))
    {
    case 0: break; // Vector 0: No interrupts
    case 2:
    __no_operation();
    break; // Vector 2: ALIFG
    case 4:
    __no_operation();
    break; // Vector 4: NACKIFG
    case 6: break; // Vector 6: STTIFG
    case 8: break; // Vector 8: STPIFG
    case 10: // Vector 10: RXIFG
    RxByteDummy = UCB1RXBUF; // Move final RX data to PRxData
    if (RxByteCtr == 1) {
    RxByteCtr--; // Increment RX byte counter
    RxData[RxByteCtr] = RxByteDummy;
    UCB1CTL1 |= UCTXSTP; // I2C stop condition
    UCB1IFG &= ~UCTXIFG; // Clear USCI_B1 TX int flag
    UCB1IFG &= ~UCRXIFG; // Clear USCI_B1 RX int flag
    I2C_Stop_Flag = 1;
    }
    else {
    __no_operation();
    }
    __no_operation();
    break;
    case 12: // Vector 12: TXIFG
    if (!ReadWrite)
    {
    if (TxByteCtr) // Check TX byte counter
    {
    UCB1TXBUF = *PTxData++; // Load TX buffer
    TxByteCtr--; // Decrement TX byte counter
    }
    else
    {
    UCB1CTL1 |= UCTXSTP; // I2C stop condition
    UCB1IFG &= ~UCTXIFG; // Clear USCI_B1 TX int flag
    I2C_Stop_Flag = 1;
    }
    }
    else {
    if (TxByteCtr) // Check TX byte counter
    {
    UCB1TXBUF = *PTxData++; // Load TX buffer
    TxByteCtr--; // Decrement TX byte counter
    }
    else {
    UCB1CTL1 &= ~UCTR;
    UCB1CTL1 |= UCTXSTT;
    }
    }
    default: break;
    }
    }

    void I2C_Send(unsigned char length, unsigned char addr)
    {
    PTxData = (unsigned char *)TxData; // TX array start address
    // Place breakpoint here to see each
    // transmit operation.
    TxByteCtr = length; // Load TX byte counter

    UCB1I2CSA = addr;
    I2C_Stop_Flag = 0;
    UCB1CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition

    __no_operation();

    while (!I2C_Stop_Flag); // Ensure stop condition got sent

    __no_operation();
    }

    void Initialize_MPU6050(void)
    {
    TxData[0] = 0x6B; TxData[1] = 0x01; // Power Management Register
    unsigned char Buffer_Size = 2; ReadWrite = 0;
    I2C_Send(Buffer_Size, MPU6050_ADDR);
    __no_operation();

    Buffer_Size = 2; ReadWrite = 0;
    TxData[0] = 0x1B; TxData[1] = 0x08; // Gyro Range Register
    I2C_Send(Buffer_Size, MPU6050_ADDR);
    __no_operation();

    Buffer_Size = 2; ReadWrite = 0;
    TxData[0] = 0x1C; TxData[1] = 0x18; // Lin Acc Range Register
    I2C_Send(Buffer_Size, MPU6050_ADDR);
    __no_operation();

    }


    // Timer0 A0 interrupt service routine
    #pragma vector=TIMER0_A0_VECTOR
    __interrupt void TIMER0_A0_ISR(void)
    {
    P6OUT ^= 0x04; // Toggle P6.2
    MPU6050_Read_Sensors();
    TA0CCR0 += Period;
    }

    unsigned char MPU6050_Read_Register(unsigned char TX)
    {
    RxByteCtr = 1;
    unsigned char Buffer_Size = 1;
    TxData[0] = TX;
    ReadWrite = 1;
    for (int i=0;i<10;i++); // Delay required between transaction
    I2C_Send(Buffer_Size,MPU6050_ADDR);
    ReadWrite = 0;
    for (int i = 0; i < 10; i++); // Delay required between transaction
    return RxData[0];
    }

    void MPU6050_Read_Sensors(void)
    {
    Accel_Buffer[0] = MPU6050_Read_Register(0x3B);
    Accel_Buffer[1] = MPU6050_Read_Register(0x3C);
    Accel_Buffer[2] = MPU6050_Read_Register(0x3D);
    Accel_Buffer[3] = MPU6050_Read_Register(0x3E);
    Accel_Buffer[4] = MPU6050_Read_Register(0x3F);
    Accel_Buffer[5] = MPU6050_Read_Register(0x40);

    for (int j = 0; j < 6; j = j + 2)
    {
    Accel[j >> 1] = (((signed int)Accel_Buffer[j]) << 8) | Accel_Buffer[j+1];
    }

    Gyro_Buffer[0] = MPU6050_Read_Register(0x43);
    Gyro_Buffer[1] = MPU6050_Read_Register(0x44);
    Gyro_Buffer[2] = MPU6050_Read_Register(0x45);
    Gyro_Buffer[3] = MPU6050_Read_Register(0x46);
    Gyro_Buffer[4] = MPU6050_Read_Register(0x47);
    Gyro_Buffer[5] = MPU6050_Read_Register(0x48);

    for (int j = 0; j < 6; j = j + 2)
    {
    Gyro[j >> 1] = (((signed int)Gyro_Buffer[j]) << 8) + Gyro_Buffer[j+1];
    }
    }

    -------------------------------------------------

    I would greatly appreciate any assistance anyone can provide.

    Sincerely,

    Mehdi

    I2C USCI LPM0
    Report Abuse
    • Reply
    You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    All Replies
    • Jens-Michael Gross
      Posted by Jens-Michael Gross
      on May 16 2012 11:11 AM
      Guru140135 points

      Mehdi Rahman
       However, after entering LPM0, it seems as if  the system no longer enters the I2C interrupt.

      It has nothing to do with LPM0.

      You call a function (MPU5060_Read_Sensors)  that work based on an ISR from within an ISR. So it runs in interrupt context itself, and normally, an ISR cannot be interrupted by anothe rinterrupt (interrupt priorities determine which one to handle first when two are pending, not that the higher priority one can interrupt the other)

      What you should do is: in the timer ISR you exit LPM. And following the enter LPM code in main the read_register code is executed and then LPM is entered again.

      P.s.: you can allow other interrupts to interrupt a running ISR by setting GIE again inside the ISR. However, this is highly dangerous. The ISR that does it must be reentrant (because it may interrupt itself), stack usage may significantly rise up to stack overflow and many other possible fatal problems. Nested interrupts are really only for top experts. And there is almost no case ever where it is really necessary (it's usually a general application desing flaw if it appear to be necessary)

      _____________________________________
      Before posting bug reports or ask for help, do at least quick scan over this article. It applies to any kind of problem reporting. On any forum. And/or look here.
      If you cannot discuss your problem in the public, feel free to start a private conversation: click on my name and then 'start conversation'. But please do so only if you really cannot do it in a public thread, as I usually read all threads. And I prefer to answer where others can profit from it (or contribute to it) too.

      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Mehdi Rahman
      Posted by Mehdi Rahman
      on May 16 2012 11:35 AM
      Intellectual350 points

      Hi Jens-Michael Gross,

      Thank you for your reply.

      Based on the following, which method is better.  This MSP430 will always act as the master to I2C devices, and will always initiate a transmission.  Instead of using the solution method you described, would it be better to have a function which implements polling of the I2C module.  This function can be called every time Timer_A0 is entered.

      I guess another consideration is reducing power consumption.  Would it not be more power-efficient to stay in LPM0 rather than exiting into active mode to perform the I2C task, and then re-entering LPM0?  

      Thank you.

      Sincerely,

      Mehdi

      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Jens-Michael Gross
      Posted by Jens-Michael Gross
      on May 16 2012 13:21 PM
      Guru140135 points

      Generally, ISR should be fast-in, fast out. Just recognize an event, set  aflag, wake the main program. Doing a complete I2C transfer is nothing that belongs into an ISR. ISRs are not parallel threads.

      So no, polling the USCI for th etransfer would not only prevent all otehr itnerrupts (if any) to be handled, it would also keep the processor needlessly active.

      Mehdi Rahman
      Would it not be more power-efficient to stay in LPM0 rather than exiting into active mode to perform the I2C task, and then re-entering LPM0?  

      In the described szenario it makes no difference. If you do the transfer inside the ISR or in main, the processor is active in both cases. And entering/exiting LPM0 is a matter of one clock cycle.

      The transfer function itself may enter LPM0 until its own ISR tells that the transfer is done. This would save some power. :)

      _____________________________________
      Before posting bug reports or ask for help, do at least quick scan over this article. It applies to any kind of problem reporting. On any forum. And/or look here.
      If you cannot discuss your problem in the public, feel free to start a private conversation: click on my name and then 'start conversation'. But please do so only if you really cannot do it in a public thread, as I usually read all threads. And I prefer to answer where others can profit from it (or contribute to it) too.

      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Mehdi Rahman
      Posted by Mehdi Rahman
      on May 23 2012 14:38 PM
      Intellectual350 points

      Hello Jens-Michael Gross,

      Thank you for your assistance.  I do see your point in having an ISR simply set a flag which then sets certain actions in the main routine, as opposed to doing a whole set of actions in the ISR.  I will re-think my approach to allow for this.

      In the meantime, I decided to do more I2C testing.  I changed the clock speed from 100 kHz to 400 kHz , and also increased the DCO speed to 8 MHz, by using the following initialization code.

      ------------------------------------------------------------------------------------------------------------------------------

      UCSCTL3 = SELREF_2; // Set DCO FLL reference = REFO
      UCSCTL4 |= SELA_2; // Set ACLK = REFO
      UCSCTL0 = 0x0000; // Set lowest possible DCOx, MODx

      // Loop until XT1,XT2 & DCO stabilizes - In this case only DCO has to stabilize
      do
      {
      UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);
      // Clear XT2,XT1,DCO fault flags
      SFRIFG1 &= ~OFIFG; // Clear fault flags
      }while (SFRIFG1&OFIFG); // Test oscillator fault flag

      __bis_SR_register(SCG0); // Disable the FLL control loop
      UCSCTL1 = DCORSEL_5; // Select DCO range 16MHz operation
      UCSCTL2 |= 249; // Set DCO Multiplier for 8MHz
      // (N + 1) * FLLRef = Fdco
      // (249 + 1) * 32768 = 8MHz
      __bic_SR_register(SCG0); // Enable the FLL control loop

      // Worst-case settling time for the DCO when the DCO range bits have been
      // changed is n x 32 x 32 x f_MCLK / f_FLL_reference. See UCS chapter in 5xx
      // UG for optimization.
      // 32 x 32 x 8 MHz / 32,768 Hz = 250000 = MCLK cycles for DCO to settle
      __delay_cycles(250000);

      // Initialize I2C Port
      P4SEL |= 0x06; // Assign I2C pins to USCI_B1
      UCB1CTL1 |= UCSWRST; // Enable SW reset
      UCB1CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode
      UCB1CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
      UCB1BR0 = 20; // fSCL = SMCLK/20 = ~400kHz
      UCB1BR1 = 0;
      UCB1I2CSA = MPU6050_ADDR; // Slave Address is 068h
      UCB1CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
      UCB1IE |= UCTXIE + UCRXIE; // Enable TX interrupt

      ------------------------------------------------------------------------------------------------------------------------------

      I took some oscilloscope tracings, which I have attached here.  It seems as if the 400 kHz tracings are quite noisy (what do you think).  I just want to make sure I'm not violating any rules.  

      100 kHz

      400 kHz

       

      Sincerely,

      Mehdi

      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    • Eltimir Stoimenov
      Posted by Eltimir Stoimenov
      on Nov 01 2012 06:58 AM
      Prodigy20 points

      This is a very useful post. Thanks Mehdi!

      Report Abuse
      • Reply
      You have posted to a forum that requires a moderator to approve posts before they are publicly available.
    TI E2E™ Community
    • Support Forums
    • Blogs
    • Videos
    • Groups
    • Site Support & Feedback
    • Settings
    TI E2E™ Community Groups
    • TI University Program
    • Make the Switch
    • Microcontroller Projects
    • Motor Drive & Control
    Other Communities
    • Deyisupport
    • Designsomething.org
    • beagleboard.org
    • TI on Element 14
    • TI on TechXchangeSM
    Other Technical & Support Resources
    • WEBENCH® Design Center
    • Product Information Centers
    • Technical Documents
    • TI Design Network
    • TI Technical Articles
    • TI Training

    All content and materials on this site are provided "as is". TI and its respective suppliers and providers of content make no representations about the suitability of these materials for any purpose and disclaim all warranties and conditions with regard to these materials, including but not limited to all implied warranties and conditions of merchantability, fitness for a particular purpose, title and non-infringement of any third party intellectual property right. TI and its respective suppliers and providers of content make no representations about the suitability of these materials for any purpose and disclaim all warranties and conditions with respect to these materials. No license, either express or implied, by estoppel or otherwise, is granted by TI. Use of the information on this site may require a license from a third party, or a license from TI.

    Content on this site may contain or be subject to specific guidelines or limitations on use. All postings and use of the content on this site are subject to the Terms of Use of the site; third parties using this content agree to abide by any limitations or guidelines and to comply with the Terms of Use of this site. TI, its suppliers and providers of content reserve the right to make corrections, deletions, modifications, enhancements, improvements and other changes to the content and materials, its products, programs and services at any time or to move or discontinue any content, products, programs, or services without notice.

    Follow Us Texas Instruments on Facebook Texas Instruments on Twitter Texas Instruments on LinkedIn Texas Instruments on Google+
    TI Worldwide | Contact Us | my.TI Login | Site Map | Corporate Citizenship | mobile m.ti.com (Mobile Version)

    TI is a global semiconductor design and manufacturing company. Innovate with 100,000+ analog ICs and
    embedded processors, along with software, tools and the industry’s largest sales/support staff.

    © Copyright 1995-2013 Texas Instruments Incorporated. All rights reserved.
    Trademarks | Privacy Policy | Terms of Use