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.

MSP430G2 with an External 16 Bit ADC

Other Parts Discussed in Thread: MSP430G2553

Hello Everyone,

This is my first post ever so bare with me please. I''ve been using a MSP430G2553 with a project. I read through the data sheets for both the micro and the MAX1415 16 Bit ADC I plan to use. I tried looking at some sample code for SPI communication but I just don't understand how the Master (MSP430) is exactly writing out to the slave. This the code I have been following from TI. I follow the initializing of the clock and setting the output of the SMCLK but up until the line "UCA0MCTL = 0;  //No modulation" I get lost. If someone could explain what is going on with the UCA0TXBUF and UCA0RXBUF since I think these are the ones doing the communication.

#include <msp430.h>

unsigned char MST_Data, SLV_Data;

int main(void)
{
  volatile unsigned int i;

  WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
  P1OUT = 0x00;                             // P1 setup for LED & reset output
  P1DIR |= BIT0 + BIT5;                     //
  P1SEL = BIT1 + BIT2 + BIT4;
  P1SEL2 = BIT1 + BIT2 + BIT4;
  UCA0CTL0 |= UCCKPL + UCMSB + UCMST + UCSYNC;  // 3-pin, 8-bit SPI master
  UCA0CTL1 |= UCSSEL_2;                     // SMCLK
  UCA0BR0 |= 0x02;                          // /2
  UCA0BR1 = 0;                              //
  UCA0MCTL = 0;                             // No modulation
  UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
  IE2 |= UCA0RXIE;                          // Enable USCI0 RX interrupt



  P1OUT &= ~BIT5;                           // Now with SPI signals initialized,
  P1OUT |= BIT5;                            // reset slave

  __delay_cycles(75);                 // Wait for slave to initialize

  MST_Data = 0x03;                          // Initialize data values
  SLV_Data = 0x04;

  UCA0TXBUF = MST_Data;                     // Transmit first character

  __bis_SR_register(LPM0_bits + GIE);       // CPU off, enable interrupts
}

// Test for valid RX and TX character
#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCIA0RX_ISR(void)
{
  volatile unsigned int i;

  while (!(IFG2 & UCA0TXIFG));              // USCI_A0 TX buffer ready?

  if (UCA0RXBUF == SLV_Data)                // Test for correct character RX'd
    P1OUT |= BIT0;                          // If correct, light LED
  else
    P1OUT &= ~BIT0;                         // If incorrect, clear LED

  MST_Data++;                               // Increment master value
  SLV_Data++;                               // Increment expected slave value
  UCA0TXBUF = MST_Data;                     // Send next value

  __delay_cycles(50);                     // Add time between transmissions to
}                                           // make sure slave can keep

  • Being an SPI master is quite simple.

    SPI is a bi-directional synchronous protocol.
    It means, the master provides the clock signal for the transaction. To do so, the USCI requires a clock source and a clock divider. Since the clock signal is forwarded to the slave, there is no need to maintain an exact baudrate. SO modulation is not needed (it is there for compensating the fractional part of the clock/baudrate ratio in asynchronous/UART mode).

    The demo code is nto an exact SPI demo. It is meant to interact with the SPI slave demo of the same package. It resets the slave by pulling its reset line, then releases it and the slave will init and be ready for communication. This won't work with any other SPI slave.

    For a real SPI slave, you need to select the slave with a normal GPIO signal (CS = chip select, or similar). This has to be done by your software. Once you assert CS (usually, you have to pull it low), the slave is notified that a new transfer will start. Depending on the slave, this may even reset a high-level protocol, like a command/data sequence etc.
    The SPI standard donesn't define all implications of CS, it jsut states that when CS is de-asserted, the slave has to be silent and deaf. What exactly happens, is a matter of the slave datasheet to explain.

    For the transfer, you simply write a data byte to TXBUF. The USCI will immediately start to generate 8 clock pulses and shift out the data. At the same time, it shift in 8 data bits form the slave. There is no check whether there is a slave at all. If there is none, you're talking into the void and will get 0xff out of the void. When the 8 bits have been received, UCRXIFG is set (and hte RX ISR is called). When teh 8 bits are send, the next byte is moved from TXBUF into the out put shift register(if TXBUF holds a new byte). Once TXBUF is empty, it will set TXIFG and trigger the TX ISR. At thsi point, the last byte written to TXBUF has just started sending(!)
    Note, that the byte you're receiving is not an answer to the byte you're sending. You receive it while your data is still being sent. In order to receive an answer, you'll have to send another (dummy) byte.
    Also, keep in mind that TXBUF is a buffer. When you write to TXBUF, another byte might still be in the pipeline, so the next byte you receive is the byte received while sending this other byte. And the answer to the last byte you wrote to TXBUF comes with 2 bytes delay.

    It's a good idea to draw a picture of what is happening when, on a timeline. It really helps understanding what to do in which order.

    BTW: the demo code makes some more things that aren't good examples. It does a busy-waiting loop for TXIFG inside the RX ISR. Yow you never should do a busy-waiting loop inside an ISR. ISRs are fast-in, fast-out. No loops, no time-consuming calculations. ISRs are no parallel threads.
    The code also adds a delay at the end. Another death sin.
    And it handles writing to TXBUF inside the RX ISR.

    However, the lfirst and alst aren't really a problem (just a bad example) since in SPi mode, RX and TX interrupts come together (remember: one byte out means one byte in). Just with a delay of 1/2 SPI clock cycle. But the demo code for UART mode does the same, which is a very, very bad idea and only works for exactly this demo.

  • Hey Jens,

    I really appreciate the time you took in writing out the detailed reply to my post. I looked over your message and other users using SPI with other MSP430's and along with the data sheets I came up with the following code. I placed all the CheckX in the watch window but all it seems to show me is what I believe is decimal 255 = hFF. So it is not seeing my ADC chip as a slave?? I am also not sure if to communicate and receive do I need to do interrupts or is the way I laid out here able to accomplish communication.

    Thanks,

    #include <msp430g2553.h>

    unsigned long Check, Check1, Check2, Check3, Check4; //I put these checks to try and see what was being received

    void PinSetup();  // Not sure if I need this since with another TI Micro I used in school I had to
    void SpiSetup();
    void SetMaxim();

    void PinSetup()
    {
        P1OUT = 0x00;                           // P1 setup for LED & reset output
        P1DIR |= BIT5 + BIT3;                    // P1 direction as output 1.5 = Reset 1.3=CS
        P1SEL = BIT1 + BIT2 + BIT4;                //    [1.1 = SOMI-> PIN 13 DOUT of ADC] [1.2 = SIMO-> PIN 14 DIN of ADC]
        P1SEL2 = BIT1 + BIT2 + BIT4;            // Not Sure what the difference is between Sel1 and Sel2 seems redundant
    }

    void SpiSetup()
    {
        // UCMSB = Selects Most Sig Fig 1st //  UCMST = Select the SPI Module as the Master // UCSYNC = Selecting Synchronous mode
        UCA0CTL0 |= UCMSB + UCMST + UCSYNC;  // 3-pin, 8-bit SPI master Is UCCKPL needed???
        UCA0CTL1 |= UCSSEL_2;                     // USCI Clock source will be SMCLK
        UCA0BR0 |= 0x02;                          // /2
        UCA0BR1 = 0;                              //
        UCA0MCTL = 0;                             // No modulation
        UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**set low
    }

    void SetMaxim() // Trying to set up the ADC registers for clock rate and gain
    {
        P1OUT &= ~BIT5;                   // Pin 1.5 is pulled low to reset ADC
        P1OUT |= BIT5;                    // After ADC is reset Pin 1.5 pulled high to prevent reset

        P1OUT &= ~BIT3;                  // Pin 1.3 CS pulled low
        UCA0TXBUF = 0x00;               // Send Dummy to Start SPI

        //IFG2 &= UCA0RXIFG;             // Clear Flag Not sure if I need this????

        while (!(IFG2 & UCA0TXIFG));    // USCI_A0 TX buffer ready?
        UCA0TXBUF = 0x20;               // h20 to to tell ADC to be prepared to write to clock reg next

        while (!(IFG2 & UCA0TXIFG));    // USCI_A0 TX buffer ready?
        Check = UCA0RXBUF;
        UCA0TXBUF = 0xB3;               // hB3 for ADC clock run at 1Mhz

        while (!(IFG2 & UCA0TXIFG));    // USCI_A0 TX buffer ready?
        Check1 = UCA0RXBUF;
        UCA0TXBUF = 0x10;               // h10 to tell ADC to be prepared to write to the setup reg next

        while (!(IFG2 & UCA0TXIFG));    // USCI_A0 TX buffer ready?
        Check2 = UCA0RXBUF;
        UCA0TXBUF = 0x44;               // h44 for ADC to setup

        P1OUT |= BIT3;                  // Pull CS high
    }

    void main(void)
    {
      WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer  //NV Need from here
      PinSetup();
      SpiSetup();
      SetMaxim();
      //IE2 |= UCA0RXIE;                          // Enable USCI0 RX and TX interrupt // Do I need interrupts for this??
      //__bis_SR_register(LPM0_bits + GIE);       // CPU off, enable interrupts
      for(;;)                                      // Just threw in this dumy infinite loop for later use
      {}

    }


  • With UCA0BR=2, each transaction only takes 16 (MCU) clocks, which is only 4-5 instructions. I think using interrupts will just slow you down.

    The DS doesn't say anything about returning data on a write, so getting back all 0xFF-s may be normal. Try writing a similar function to, say, read back all the registers.

  • Well the first thing I fixed was P1DIR was missing BIT4 so I was never outputting a clock so I added that and in the datasheet it also says P1SEL2 should be 0 for BIT4 so I changed that.

    Then I added some delays in the setup first since the ADC needs some time for the internal oscillator to start up. The delays in between the different transfers seemed to have helped but I still don't receive what I expect. In the watch window hFA instead of h20, hE9 instead of hA5, hFF instead of h10, and hFF instead of h44.

    I hooked up a logic level analyzer I can see that initially both SOMI and SIMO are 255 then SIMO just goes to 192 then 0 and I don't get why.


    Thanks,
    Noe

    #include <msp430g2553.h>

    unsigned int Check, Check1, Check2, Check3, Check4; //I put these checks to try and see what was being received

    void PinSetup()
    {
        P1OUT = 0x00;                           // P1 setup for LED & reset output
        P1DIR |= BIT3 + BIT4 + BIT5 + BIT6;            // P1 direction as output 1.5 = Reset 1.3=CS 1.6 = Blink LED
        P1SEL |= BIT1 + BIT2 + BIT4;            //    [1.1 = SOMI-> PIN 13 DOUT of ADC] [1.2 = SIMO-> PIN 14 DIN of ADC]
        P1SEL2 |= BIT1 + BIT2;            //    [1.1 = SOMI-> PIN 13 DOUT of ADC] [1.2 = SIMO-> PIN 14 DIN of ADC]
    }

    void SpiSetup()
    {
        // UCMSB = Selects Most Sig Fig 1st //  UCMST = Select the SPI Module as the Master // UCSYNC = Selecting Synchronous mode
        //UCA0CTL0 |= UCMSB + UCMST + UCSYNC;  // 3-pin, 8-bit SPI master
        UCA0CTL1 |= UCSWRST;                       // **Put state machine in reset**
        UCA0CTL0 |= UCCKPH + UCCKPL + UCMSB + UCMST + UCSYNC;// made ucckpl ->ucckph
        UCA0CTL1 &= ~UCSSEL_2;                     // USCI Clock source will be SMCLK
        UCA0BR0 = 0x02;                          // /2
        UCA0BR1 = 0;                              // Clock divider
        UCA0MCTL = 0;                             // No modulation
        UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**set low
    }

    void SetMaxim() // Trying to set up the ADC registers for clock rate and gain
    {
        //P1OUT &= ~BIT5;                   // Pin 1.5 is pulled low to reset ADC
        //_delay_cycles(1000000);
        P1OUT |= BIT5;                    // After ADC is reset Pin 1.5 pulled high to prevent reset
        //_delay_cycles(500);
        P1OUT &= ~BIT3;                  // Pin 1.3 CS pulled low
        //_delay_cycles(500);
        //IFG2 &= UCA0TXIFG;             // Clear Flag Not sure if I need this????
        //while ((UCA0STAT & UCBUSY));          // Transmission Done? Not sure if I need this????

        while (!(IFG2 & UCA0TXIFG));    // USCI_A0 TX buffer ready?
        UCA0TXBUF = 0x20;               // send h20 to tell ADC to be ready to write to clock next
        _delay_cycles(50);
        Check1 = UCA0RXBUF;

        while (!(IFG2 & UCA0TXIFG));    // USCI_A0 TX buffer ready?
        UCA0TXBUF = 0xA5;               // hA5 for ADC clock run at 2.5Mhz
        _delay_cycles(50);
        Check2 = UCA0RXBUF;

        while (!(IFG2 & UCA0TXIFG));    // USCI_A0 TX buffer ready?
        UCA0TXBUF = 0x10;               // h10 to tell ADC to be ready to write to the setup reg next
        _delay_cycles(50);
        Check3 = UCA0RXBUF;

        while (!(IFG2 & UCA0TXIFG));    // USCI_A0 TX buffer ready?
        UCA0TXBUF = 0x44;               // h44 for ADC to setup
        _delay_cycles(50);
        Check4 = UCA0RXBUF;

        P1OUT |= BIT3;                  // Pull CS high
    }

    void main(void)
    {
      WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer  //NV Need from here
      PinSetup();
      SpiSetup();

      SetMaxim();
      //__bis_SR_register(LPM0_bits + GIE);       // CPU off, enable interrupts
      for(;;)                                      // Just threw in this dumy infinite loop for later use
      {
          _delay_cycles(100000);
          P1OUT ^= BIT6;                        // Toggle green LED (P1.6)
      }
    }

  • Noe Villanueva said:
    UCA0TXBUF = 0x20;               // send h20 to tell ADC to be ready to write to clock next
        _delay_cycles(50);
        Check1 = UCA0RXBUF;

    Instead of a fixed delay, you should check for RXIFG bit, whcih is set after a byte has been received.

    Note that the byte you receive at this point, is the byte received while the 0x20 is being sent. It is not the 'reponse' to 0x20.
    To get a response to the final 0x44, you'll have to send another dummy byte.

    Also, it is uncertain whether you raise CS while the last byte is still sending. Here you should check for UCBUSY, and also add a small delay (see slave datasheet for required delay between last SPI clock edge and CS going high).

**Attention** This is a public forum