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.

Msp430g2452 SPI digital potentiometer issue

Other Parts Discussed in Thread: MSP430G2452

Hi I am trying to send data to a digital pot over spi, but for some reason it is not working.

Here is the code:


#include <msp430.h>


void main(void)
{
volatile unsigned int i;

WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
P1OUT = 0;
P1DIR |= 0x02;
P1OUT |= 0X02;
P1SEL |= 18; //Selects SPI pins
USICTL0 |= USIPE7 + USIPE6 + USIPE5 + USIMST + USIOE; // Port, SPI master
USICKCTL = USIDIV_4 + USISSEL_2; // /4 SMCLK
USICNT |= USI16B; // init-load counter
USICTL0 &= ~USISWRST; // USI released for operation

P1OUT &= ~0x02; //Set Chip Select for Digital Potentiometer
USISR = 0x1148;
USICNT |= 16;
for (i = 0xFF; i > 0; i--); //Delay
P1OUT |= 0x02;
while(1);
}

It's pretty straight forward.

  • Erik Westerveld said:
    but for some reason it is not working.

    What does that mean in detail? Is there any data coming out? Do you have an oscilloscope to check this?

  • You don't give time for SPI of digipot to reset, shall put some delay after asserting chip select.
    For(;;) is worst way of making delays, it could be optimized away resulting in no delay at all. Use __delay_cycles() instead.
  • Erik,

    the MSP430G2452 has it's USI pins at P1.5 (SCLK), P1.6 (SDO) and P1.7 (SDI):

    For using the SPI functionality, you have to set P1SEL for each pin you want to use (you do not need SDI if your slave does not output anything). This line:

    P1SEL |= 18; // Selects SPI pins

    is the same like

    P1SEL |= 0x12;
    // OR
    P1SEL |= (BIT4 | BIT 1);

    So none of the SPI pins is selected. Furthermore setting USICNT should be done right before the transmission only, not inside your configuration. Sorry, my fault - you set USI16B inside the configuration. That's OK!

    Dennis

  • And instead of the software delay you could work with the USIIFG in USICTL1 which is set when USICNT counts down to zero. You can poll that flag or enable the interrupt. Un-select the slave when the IFG was set.
  • Okay so I took your guy's advice and made some changes.

    I do not own a oscilloscope, but I do plan on getting one in the near future.

    Anyways here is the updated code... still not working, I may have to wait and get an oscilloscope, it would help immensely with troubleshooting.


    #include <msp430.h>
    #include <intrinsics.h>


    void main(void)
    {

    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
    DCOCTL = 0; // Select lowest DCOx and MODx settings
    BCSCTL1 = CALBC1_1MHZ; // Set range
    DCOCTL = CALDCO_1MHZ;

    P1OUT = 0;
    P1DIR |= 0x03;
    P1SEL |= 0x60; //Selects SPI pins 0110 0000
    USICTL0 |= USIPE6 + USIPE5 + USIMST + USIOE; // Port, SPI master
    USICTL1 |= USIIE;
    USICTL1 &= ~USII2C;
    USICKCTL = USIDIV_4 + USISSEL_2; // /4 SMCLK
    USISR = 0x4811; // load data into counter
    USICTL0 &= ~USISWRST; // USI released for operation

    USICNT |= USI16B;
    P1OUT &= ~0x02; //Set Chip Select for Digital Potentiometer
    __delay_cycles(2000);
    USICNT |= 16;
    __delay_cycles(2000);
    __bis_SR_register(LPM0_bits + GIE); // Enter LPM0 w/ interrupt

    }

    // USI interrupt service routine
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=USI_VECTOR
    __interrupt void universal_serial_interface(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USI_VECTOR))) universal_serial_interface (void)
    #else
    #error Compiler not supported!
    #endif
    {
    P1OUT |= 0x01; // Turn on LED to indicate counter interrupt
    P1OUT |= 0x02; //Reset Chip Select for Pot
    }


    Thanks a lot for the help.
  • If you cannot check if the correct data is sent out of the MSP, I would recommend to connect SDO and SDI and look if the received data equals the sent one. By the way: Which potentiometer do you use? Could you tell the type?

    Dennis
  • Hey Erik!

    I wrote a short program that hopefully helps to get your potentiometer running. I do not have one of those potentiometers, so I could not test it with real hardware and I do not know what type of digital potentiometer you are using, so this is more a basic approach. The code is written for the MSP430G2452 on the MSP-EXP430G2 LaunchPad. I now assumed your potentiometer has 10 bits of resolution and does not need any further command / register bits. For more information please read the comment at the top of my source code.

    #FreeSampleCode

    // Dennis Eichmann
    // TI E2E
    
    #include "msp430g2452.h"
    #include "stdint.h"
    
    // P1.0 output LED1 (red) on the G2 LaunchPad
    // P1.1 chip select from digital potentiometer
    
    // The program sends data to an external digital potentiometer connected via SPI using the USI module of the MSP430G2452.
    // A timer interrupt is generated every 100ms - a variable inside the ISR extends the time for an action by a user selectable value that is a multiple of 100ms.
    // The USI module sends 16 bits of data out of the timer ISR every second. For each next transfer, the value for the potentiometer is altered.
    // Potentiometer data limits and cycle step can be set by user. The program loops incrementing and decrementing the data to it's set limits and by it's set step.
    // The LED1 (red) at P1.0 on the G2 LaunchPad indicates the start of a new transmission.
    //
    // Note: The code assumes that the potentiometer expects a 16 bit data word with data being right aligned.
    //       For a 10 bit digital potentiometer, the data is: XXXX XXMD DDDD DDDL where X is don't care, M is the MSB, L is the LSB and D are the data bits in between.
    //       If there are additional command or register bits required, OR (|=) the additional data to the appropriate line inside the timer ISR.
    
    #define POT_MIN_VAL     0                         // Minimum value
    #define POT_MAX_VAL     1023                      // Maximum value for 10 bits
    #define POT_STEP        64                        // 16 steps at 10 bit
    
    #define TIMER_X_100MS   10                        // 10 x 100ms = 1s
    
    volatile int32_t pot_value = POT_MIN_VAL;         // Digital potentiometer value
    
    void main( void )                                 // Main
    {
      WDTCTL    = (WDTPW | WDTHOLD);                  // Stop watchdog timer
    
      BCSCTL1   = CALBC1_1MHZ;                        // Set DCO range
      DCOCTL    = CALDCO_1MHZ;                        // Set DCO step and modulation
    
      P1SEL     = (0x20 | 0x40);                      // P1.5 (SCLK) and P1.6 (SDO) to USI function
      P1SEL2    = 0x00;                               // P1SEL2 bits all cleared
      P1OUT     = 0x02;                               // Set P1.1 high (deselect potentiometer), others low
      P1DIR     = (0x01 | 0x02);                      // Set P1.0 and P1.1 to output (P1.5 and P1.6 controlled from USI), others input
    
      USICTL0   = USISWRST;                           // Set USI module in reset state
      USICTL0  |= (USIPE6 | USIPE5 | USIMST | USIOE); // SDO, SCLK, master, output enable
      USICTL1   = (USICKPH | USIIE);                  // Data captured on 1st edge, changed on 2nd
      USICKCTL  = USISSEL_2;                          // No divider, SMCLK, inactive clock low
      USICNT    = USI16B;                             // 16 bit shift register
      USICTL0  &= ~USISWRST;                          // Release USI module from reset state
      USICTL1  &= ~USIIFG;                            // Clear pending flag
    
      TA0CCTL1  = CCIE;                               // Enable compare interrupt
      TA0CCR1   = 12500;                              // Compare value: 12500 results in 100ms interrupt @ 1MHz with divider 8
      TA0CTL    = (TASSEL_2 | ID_3 | MC_2 | TACLR);   // SMCLK, divider 8, continuous mode, clear
    
      __bis_SR_register( GIE );                       // Enable global interrupts
    
      while( 1 )                                      // Endless-loop (main-program)
      {
                                                      // Do nothing - all interrupt driven
      }
    }
    
    
    // Timer 0 A1 interrupt service routine
    #pragma vector = TIMER0_A1_VECTOR
    __interrupt void Timer0_A1_ISR( void )
    {
      static uint8_t  cycle_counter = 0;              // Additional prescaler for timer interrupt
      static uint8_t  direction     = 1;              // Up (1) or down (0)
             uint16_t pot_data;                       // Data that is sent to the potentiometer
    
      switch( TA0IV )                                 // Reading TA0IV clears highest pending flag
      {
        case 2:                                       // TA0CCR1
        {
          TA0CCR1 += 12500;                           // Add CCR1 value for next interrupt
          P1OUT   &= ~0x01;                           // LED off 100ms after transmission started
    
          if( ++cycle_counter >= TIMER_X_100MS )      // Cycle counter reached desired count
          {
            cycle_counter = 0;                        // Clear cycle counter
    
            pot_data = pot_value;                     // Copy potentiometer value to potentiometer data variable
    
            if( direction )                           // Cycle up
            {
              pot_value += POT_STEP;                  // Increment potentiometer value by defined step
    
              if( pot_value >= POT_MAX_VAL )          // Reached maximum
              {
                pot_value = POT_MAX_VAL;              // Limit to maximum
                direction = 0;                        // Next cycle down
              }
            }
            else                                      // Cycle down
            {
              pot_value -= POT_STEP;                  // Decrement potentiometer value by defined step
    
              if( pot_value <= POT_MIN_VAL )          // Reached minimum
              {
                pot_value = POT_MIN_VAL;              // Limit to minimum
                direction = 1;                        // Next cycle up
              }
            }
    
            //pot_data |= <any_command>;              // Add command or register data if necessary
    
            P1OUT  |= 0x01;                           // LED on to signal start of data transfer
            P1OUT  &= ~0x02;                          // Chip select of potentiometer low (selected)
            USISR   = pot_data;                       // Copy potentiometer data to shift register
            USICNT |= 16;                             // Start sending 16 bits
          }
    
          break;
        }
    
        default: break;
      }
    }
    
    
    // USI interrupt service routine
    #pragma vector = USI_VECTOR
    __interrupt void USI_ISR( void )
    {
      USICTL1 &= ~USIIFG;                             // Clear pending flag
      P1OUT   |= 0x02;                                // Chip select of potentiometer high (deselected)
    }

    The code works, as I can see from the oscilloscope pictures, but I do not know if your potentiometer just needs the value without any further command or register address, so this might not be plug and play for you.

    The yellow trace is the red LED1 on P1.0 that is set each time a transmission starts. It turns off after 100ms (interval of the timer interrupt) which is much longer than the transmission, so you do not see it turn off on the scope screen. The blue trace is chip select on P1.1, the purple trace is the clock signal and the green trace is SDO. In this picture the value 768d (0000 0011 0000 0000) is transmitted.

    The following two pictures show only LED1 at P1.0 and you can see that a) a transmission starts every second and b) the LED1 turns on for 100ms each time a transmission is started.

    Dennis

  • Hey Dennis,

    Wow thanks a lot. I will have to try this out when I get home.

    Regarding the Digital Pot, it is a MCP41010, which  takes 16 bits of data. The first 8 bits is the command and the second 8 bits is the data for the pot).

    I will let you know how it goes tonight.

    Erik

  • Hi Erik!

    I just edited my code - it was 1024 as maximum, but it is 1023, of course. This means, the code will start at 0 and increments by 64, having it's maximum value at 1023 and decrements down in steps of 64 back to 0. This means, the incrementing values will be off by 1 compared to the decrementing values. But this was just an example.

    It's like this:

    0 64 128 192 256 320 384 448 512 576 640 704 768 832 896 960 1024 ==> software sets last value down to 1023

    1023 959 895 831 767 703 639 575 511 447 383 319 255 191 127 63 -1 ==> software sets last value up to 0

    Anyway: For your 8 bit potentiometer: Set POT_MAX_VAL to 255, choose the step you want and search for the commented out line to remove the '//' and replace <any_command> by 0x1100. That should work then.

    Dennis

  • Hey dude it worked! Thanks again your a genius.
  • Great to hear that!

    Dennis

**Attention** This is a public forum