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.

How does this example code work? I2C Receive using TX interrupt? How?

TI distributes example code for various MSP430 parts.  There's a specific example file which isn't making sense to me: msp430x22x4_uscib0_i2c_12.  It transmits and then receives, all from within an ISR for USCIAB0TX.  It enables RX interrupts at some point (UCB0RXIE) but there's no ISR for RX, and the RX flag (UCB0RXIFG) is never checked for.  So, my question is, how does it work without these features, and why does the code enable an interrupt it appears never to use?

Here's the code:

//******************************************************************************
//  MSP430F22x4 Demo - USCI_B0 I2C Master TX/RX multiple bytes from MSP430 Slave
//                     with a repeated start in between TX and RX operations.
//
//  Description: This demo connects two MSP430's via the I2C bus. The master
//  transmits to the slave, then a repeated start is generated followed by a
//  receive operation. This is the master code. This code demonstrates how to
//  implement an I2C repeated start with the USCI module using the USCI_B0 TX
//  interrupt.
//  ACLK = n/a, MCLK = SMCLK = BRCLK = default DCO = ~1.2MHz
//
//    ***to be used with msp430x22x4_uscib0_i2c_13.c***
//
//                                /|\  /|\
//               MSP430F24x      10k  10k     MSP430F22x4
//                   slave         |    |        master
//             -----------------   |    |  -----------------
//           -|XIN  P3.1/UCB0SDA|<-|---+->|P3.1/UCB0SDA  XIN|-
//            |                 |  |      |                 |
//           -|XOUT             |  |      |             XOUT|-
//            |     P3.2/UCB0SCL|<-+----->|P3.2/UCB0SCL     |
//            |                 |         |                 |
//
//  R. B. Elliott / H. Grewal
//  Texas Instruments Inc.
//  April 2008
//  Built with IAR Embedded Workbench Version: 3.42A
//******************************************************************************
#include "msp430x22x4.h"

#define NUM_BYTES_TX 3                         // How many bytes?
#define NUM_BYTES_RX 2

int RXByteCtr, RPT_Flag = 0;                // enables repeated start when 1
volatile unsigned char RxBuffer[128];       // Allocate 128 byte of RAM
unsigned char *PTxData;                     // Pointer to TX data
unsigned char *PRxData;                     // Pointer to RX data
unsigned char TXByteCtr, RX = 0;
unsigned char MSData = 0x55;

void Setup_TX(void);
void Setup_RX(void);
void Transmit(void);
void Receive(void);

void main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  P3SEL |= 0x06;                            // Assign I2C pins to USCI_B0
 
  while(1){
   
  //Transmit process
  Setup_TX();
  RPT_Flag = 1;
  Transmit();
  while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent
 
  //Receive process
  Setup_RX();
  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
// any 2+ number of bytes by pre-loading RXByteCtr with the byte count.
//-------------------------------------------------------------------------------
#pragma vector = USCIAB0TX_VECTOR
__interrupt void USCIAB0TX_ISR(void)
{
  if(RX == 1){                              // Master Receive?
  RXByteCtr--;                              // Decrement RX byte counter
  if (RXByteCtr)
  {
    *PRxData++ = UCB0RXBUF;                 // Move RX data to address PRxData
  }
  else
  {
    if(RPT_Flag == 0)
        UCB0CTL1 |= UCTXSTP;                // No Repeated Start: stop condition
      if(RPT_Flag == 1){                    // if Repeated Start: do nothing
        RPT_Flag = 0;
      }
    *PRxData = UCB0RXBUF;                   // Move final RX data to PRxData
    __bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
  }}
 
  else{                                     // Master Transmit
      if (TXByteCtr)                        // Check TX byte counter
  {
    UCB0TXBUF = MSData++;                   // Load TX buffer
    TXByteCtr--;                            // Decrement TX byte counter
  }
  else
  {
    if(RPT_Flag == 1){
    RPT_Flag = 0;
    PTxData = &MSData;                      // TX array start address
    TXByteCtr = NUM_BYTES_TX;                  // Load TX byte counter
    __bic_SR_register_on_exit(CPUOFF);
    }
    else{
    UCB0CTL1 |= UCTXSTP;                    // I2C stop condition
    IFG2 &= ~UCB0TXIFG;                     // Clear USCI_B0 TX int flag
    __bic_SR_register_on_exit(CPUOFF);      // Exit LPM0
    }
  }
 }
 
}

void Setup_TX(void){
  _DINT();
  RX = 0;
  IE2 &= ~UCB0RXIE; 
  while (UCB0CTL1 & UCTXSTP);               // Ensure stop condition got sent// Disable RX interrupt
  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 = 0x48;                         // Slave Address is 048h
  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
  IE2 |= UCB0TXIE;                          // Enable TX interrupt
}
void Setup_RX(void){
  _DINT();
  RX = 1;
  IE2 &= ~UCB0TXIE; 
  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 = 0x48;                         // Slave Address is 048h
  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
  IE2 |= UCB0RXIE;                          // Enable RX interrupt
}
void Transmit(void){
    PTxData = &MSData;                      // TX array start address
    TXByteCtr = NUM_BYTES_TX;                  // Load TX byte counter
    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 Receive(void){
    PRxData = (unsigned char *)RxBuffer;    // Start of RX buffer
    RXByteCtr = NUM_BYTES_RX-1;              // Load RX byte counter
    while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent
    UCB0CTL1 |= UCTXSTT;                    // I2C start condition
    __bis_SR_register(CPUOFF + GIE);        // Enter LPM0 w/ interrupts
}

  • Scott Brenneman said:
    So, my question is, how does it work without these features, and why does the code enable an interrupt it appears never to use?

    Oh, it uses it. It's just a bit different than in SPI/UART mode.

    In I2C mode, RX and TX interrupts are triggering the TX ISR, while the status interrupts trigger the RX ISR. The reason is that I2C is half-duplex, so during one transmission, you only either receive or transmit, but not both.
    This is described in the users guide, bt you can easily miss this info. And it's nothing you would expect. But after some thinking it makes sense and allows more efficient programming.

    On the 5x devices, the (else identical) USART has the interrupts distributed differently: Here ther i s only one USCIARXTX and one USCIBRXTX ISR. So RX and TX (and in case of I2C the status interrupts) arrive in the same ISR, but there are different ISRs for each USCI sub-module (and for each USCI). With this setup, you won't tap into the trap you stepped in for the 2x series USCI.

  • Wow, that's certainly counter-intuitive.  Where in the Guide does it explain this, so I can read more about it?

    Thank you so much for clarifying!

    Scott

  • Scott Brenneman said:
    Wow, that's certainly counter-intuitive.

    Reason was since only RX or TX are active during one complete transmission, to have one interrupt for the transmission and one for the status changes.

    Scott Brenneman said:
    Where in the Guide does it explain this, so I can read more about it?

    There are only 2 sentences about it in the users guide which explain everything and nothing:

    slau144e.pdf chapter 17.3.7 (page 543): One interrupt
    vector is associated with the transmit and receive interrupt flags. The other
    interrupt vector is associated with the four state change interrupt flags.

    THe problem is that I cannot locate wnywhere which USART interrupt is used for what. The interrupt vector table in the datasheet  does not list this I2C special handling and associates one vector with RX and one with TX (which is correct for UART and SPI). You can only see it in the provided code example where the two ISRs are named
    USCIA0_RX_USCIB0_I2C_STATE_ISR and USCIA0_TX_USCIB0_I2C_DATA_ISR.

  • Thank you Jens-Michael.  I guess documentation isn't what it used to be...!

    Scott

  • Scott Brenneman said:
    I guess documentation isn't what it used to be...!

    Well, the TI documentation is quite good, compared to soem crap you find for other devices.

    It's jsut that the whole stuff is so complex that not everything can be written everywhere, so you need to carefully read all of it so you do not miss something.
    It's not bug-free, however, and sometimes it really isn't exhausting enough or explanation is so short that that you just might miss the point on first (or second) reading.

     

**Attention** This is a public forum