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.

CC430: I2C and SPI at same time

Other Parts Discussed in Thread: CC430F5133

Dear all,

I'm trying to get I2C and SPI working on a CC430F5133. I successfully set up and tested I2C on UCB0. I then continued on adding SPI on UCA0 (both implementations base on the examples from TI).

I first initialize I2C, then SPI in my code, then write something over I2C and then (try to) write something over SPI.

Now, while both initializations work, the SPI-TXIFG is set to 0 as soon as I write something over I2C - and it just stays 0 (i.e. it won't become 1 after the I2C-write is done).

Is this a known problem or am I just doing something wrong?

Thanks in advance
Sebastian

I2C-Init:

void initI2C() {
  P1SEL |= BIT2 + BIT3;
  UCB0CTL1 |= UCSWRST;
  UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;
  UCB0CTL1 = UCSSEL_2 + UCSWRST;
  UCB0BR0 = 3;  // fSCL = SMCLK/12 = ~100kHz, /3 = ~400kHz
  UCB0BR1 = 0;
  UCB0CTL1 &= ~UCSWRST;
}

SPI-Init:

void initSPI() {
  P1SEL |= BIT1 + BIT5 + BIT7;
  UCA0CTL1 |= UCSWRST;  // Put in reset
  UCA0CTL0 |= UCMST+UCSYNC+UCCKPL+UCMSB;  // 3-pin, 8-bit SPI master, Clock polarity high, MSB first
  UCA0CTL1 |= UCSSEL_2;  // SMCLK
  UCA0BR0 = 1;  // SMCLK / (BR1 * 256 + BR0)
  UCA0BR1 = 0;
  UCA0MCTL = 0;  // No modulation
  UCA0CTL1 &= ~UCSWRST;  // Activate
}

I2C-write:

void i2c_write(unsigned char counter, unsigned char * txData, unsigned char stop) {
  UCB0IE |= UCTXIE | 0x3F;  // Enable interrupt on TX
  i2cStopAfter = stop;
  i2cTxCounter = counter;
  i2cTxPointer = (unsigned char *)txData;
  while(UCB0CTL1 & UCTXSTP);
  // Send data
  UCB0CTL1 |= UCTR | UCTXSTT;  // I2C TX, I2C start condition
  // Wait till data is sent
  __bis_SR_register(LPM0_bits + GIE);
  i2cStopAfter = 0;
  UCB0IE &= ~UCTXIE;  // Disable interrupt on TX
}      (plus some code in the ISR which is close to the examples)

SPI-write:
void stripe_sendData(unsigned char * txData) {  // must always be 32 bytes
  UCA0IE |= UCTXIE;
  while (!(UCA0IFG&UCTXIFG));  // USCI_A0 TX buffer ready?
  spiTxCounter = 32;
  spiTxPointer = (unsigned char *)txData;
  UCA0TXBUF = *spiTxPointer++; // Transmit first character
  __bis_SR_register(LPM0_bits + GIE);       // CPU off, enable interrupts
  UCA0IE &= ~UCTXIE;
}    (again, plus some code in the ISR which is similiar to what's in the examples)
  • Sebastian Sester said:
    UCB0IE |= UCTXIE | 0x3F; // Enable interrupt on TX

    This is a little bit curious, here you enable ALL interrupts but probably don’t have ISR’s for all and your program ends up somewhere into the sky.

    Sebastian Sester said:
    UCA0CTL1 |= UCSWRST; // Put in reset UCA0CTL0 |= UCMST+UCSYNC+UCCKPL+UCMSB; // 3-pin, 8-bit SPI master, Clock polarity high, MSB first

    Just a suggestion; When initializing a register and knowing this is the first time don’t use ‘|=’ but force it completely to the value you want to have it with ‘=’ as you do with the UCB0CTL0, its faster and more secure.

    And JMG’s advice: Don’t use …+… but use …|… it’s less catastrophic when one (or more) definitions is used on the wrong place, ‘+’ ADD’s to a complete wrong value (all bits can be changed), ‘|’ OR leads to just one or two wrong bits.


  • Leo Bosch said:
    but probably don’t have ISR’s for all and your program ends up somewhere into the sky.

    I really have ISRs for all of them; but it's merely for debugging purposes (that's why I wrote it has hex and not as a combination of defines).

    Leo Bosch said:
    When initializing a register and knowing this is the first time don’t use ‘|=’ but force it completely to the value you want to have it with ‘=’ as you do with the UCB0CTL0, its faster and more secure.

    Yes, right. Didn't clean up the code yet.

    Leo Bosch said:
    And JMG’s advice: Don’t use …+… but use …

    Same for here; I started with the examples and went on from there (and those use +, so that's why I'm not using | everywhere yet).

  • Leo Bosch said:
    program ends up somewhere into the sky

    This was not handy from me while it has only one vector.

    Then it’s strange to me also. What I learned to do when debugging, copying all the registers from interest to (volatile) variables at the immediate begin of the ISR, after stopping at a break point the registers can be altered in the mean time.

  • BTW: Some registers can be altered just by reading them or another one, be aware of this.

  • UCA0 and UCB0 should be completely independent. They don’t share an ISR (as they did on 2x family) nor do they share any other registers. As soon as you clear UCSWRST for SPI, TXIFG goes 1. And using SPI on a different USCI won’t change this. But the ISR code might (by clearing the wrong IFG bits manually). Also, the debugger might read the interrupt vector register in the register display window, which under some circumstances (interrupt active) clears the pending IRQ and resets the IFG bits. Sometimes the debugger is a bugger :)

     

    Leo Bosch said:
    And JMG’s advice:
    :)

    Leo Bosch said:
    ‘|’ OR leads to just one or two wrong bits.

    if at all. Setting the same bit twice (|) results in the bit set. Adding a bit value twice (+) results in a completely different value.
    Luckily, I’ve never seen a += in any demo code.

  • Jens-Michael Gross said:

    UCA0 and UCB0 should be completely independent. They don’t share an ISR (as they did on 2x family) nor do they share any other registers.

    Well, yes, that's what I was expecting after reading the datasheet, too.

    Jens-Michael Gross said:

    As soon as you clear UCSWRST for SPI, TXIFG goes 1.

    Yes, I was able to verify that.

    Jens-Michael Gross said:

    But the ISR code might (by clearing the wrong IFG bits manually).

    #pragma vector=USCI_A0_VECTOR
    __interrupt void USCI_A0_ISR(void) {
      switch(__even_in_range(UCA0IV,4)) {
        case 0: break;  // Vector 0 - no interrupt
        case 2: break;  // Vector 2 - RXIFG
        case 4:  // Vector 4 - TXIFG
          if (spiTxCounter) {
            while (!(UCA0IFG&UCTXIFG));  // USCI_A0 TX buffer ready?
            UCA0TXBUF = *spiTxPointer++;  // Send next value
            spiTxCounter--;
          }
          if (!spiTxCounter) {
        	__bic_SR_register_on_exit(LPM0_bits);  // CPU off, enable interrupts
          }
          break;
        default: break;
      }
    }

    I don't clear the flag at all; I expect it to be cleared as soon as I write TXBUF.

    Jens-Michael Gross said:

    Also, the debugger might read the interrupt vector register in the register display window, which under some circumstances (interrupt active) clears the pending IRQ and resets the IFG bits. Sometimes the debugger is a bugger :)

    The problem also happens when not having any breakpoints in the critical section and also when not using the debugger :-(

     

    Thanks

    Sebastian

  • Well ... turns out I had a bug in my I2C-ISR (didn't catch the final TXIFG) and after fixing that bug, SPI was suddenly working - I don't really understand why, but well.

    I also had to remove the

      while (!(UCA0IFG&UCTXIFG)); // USCI_A0 TX buffer ready?

    line in the ISR (which shouldn't be necessary anyway since the ISR is only called whenever the buffer is empty).

    But still a big thanks to both of you for all the help :-)

    Sebastian

  • Sebastian Sester said:
    I also had to remove the
    while (!(UCA0IFG&UCTXIFG)); // USCI_A0 TX buffer ready?
    line in the ISR (which shouldn't be necessary anyway since the ISR is only called whenever the buffer is empty).

     It is not only not necessary, it is wrong. When reading UCA0IV in the switch, it is not only reporting TXIFG, it is also clearing it. So you wait for the next interrupt – which won’t come because you didn’t write anything to TXBUF after UCTXIFG was cleared.

     For the UCB0 ISR, I don’t recommend using UCB0IV (in I2C mdoe) at all. Because reading it will clear RXIFG. Which may (It is not specified nor tested and confirmed by me or anyone else so far) release the I2C R state machine if it was locked due to an imminent buffer overflow, making the buffer overflow happen.
    Normally, if a byte was received, RXIFG is set, but the USCI continues receiving the next byte. If by the 7th bit of the next byte, RXBUF wasn’t read, the receive stalls until you read RXBUF. It might be that clearing RXIFG also ends the stall, even though you didn’t read RXBUF yet.
    As a result you might get a buffer overlow or receive one byte more than you want (with possible side-effects on the slave)

  • Jens-Michael Gross said:

    It is not only not necessary, it is wrong. When reading UCA0IV in the switch, it is not only reporting TXIFG, it is also clearing it.

    Okay; I put that line there by accident when copying the ISR from the example code (where it's used in the RX-ISR); thanks for the clarification; I wasn't expecting UCAxIV to be cleared upon reading.

    Also,

    Leo Bosch said:
    BTW: Some registers can be altered just by reading them or another one, be aware of this.

    sorry for ignoring this, I forgot that

    switch(__even_in_range(UCA0IV,4)) {

    also reads the variable - d'oh!

    Jens-Michael Gross said:

    For the UCB0 ISR, I don’t recommend using UCB0IV (in I2C mdoe) at all. Because reading it will clear RXIFG. Which may (It is not specified nor tested and confirmed by me or anyone else so far) release the I2C R state machine if it was locked due to an imminent buffer overflow, making the buffer overflow happen.

    Well, according to the user guide:

    CC430 Family Guide said:

    If the previous data was not read from the receive buffer UCBxRXBUF at the end of a reception, the bus
    is stalled by holding SCL low. As soon as UCBxRXBUF is read, the new data is transferred into
    UCBxRXBUF

    So shouldn't this be okay? Also, the TI-examples do exactly the same (reading the IV); how else would you do it as long as you don't want to ignore all other interrupts to make sure that only the RX-ISR is triggered?

    Thanks for the explanations,

    Sebastian

  • You’re right, the users guide tells only about reading RXBUF, but I’m not sure whether manually or indirectly (IV-Register) clearing the interrupt bit will have the same effect. I didn’t check so far (my only I2C code requires a sequence that cannot be done by ISR, so I have to use a polling function), so I’m not sure. Better safe than sorry. If someone can confirm that clearing RXIFG won’t continue the I2C transfer, then be it. Nobody did so far.

    TI sample codes are not ‘general purpose solutions’ and often work only coincidentally ’by design’ (like nothing else going on, only half duplex operation, only on this specific MSP due to internal pinout connections etc.).
    An alternative is just by checking for the IFG bits by reading the IFG register (not the IV register). It won’t work in a switch statement (due to possibly multiple bits), but it works fine with cascaded IF. Before introduction of the IV registers, this was the only option (e.g. on 1x family devices)

  • **Attention** This is a public forum