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.

MSP430F247 Unpredictable SPI Results



Hi,

 

I am having some major difficulty with my SPI interface to the LIS331DLH Accelerometer (at this point I think that part is cursed). Basically I"m having the following problems

  1. When I attempt to write 0x01 to the accelerometer's 4th register and read the value back, I get 0xFF (I hover over the variable in CCS and I get /255 which I'm taking as 0xFF).
  2. The while loop in my initAccelerometer() function turns into a forever loop. I always read back 0xFF. However, When I let the micro idle for 0.5 seconds, when I read that same register, I get 0x00 (Which is what I'm expecting). Just to clarify, this register that I"m reading in the accelerometer has a "boot" flag. If the boot flag is up, it will not be recording data correctly.
  3. I did at one point have this communication protocol working before. I changed no portion of the firmware that interacts with Accelerometer (or hardware) and then this code suddenly broke. Since then I've had to add the flag UCCKPH or UCCKPL in order to get anything but 0xFF from the accelerometer. I'm finding this to be very strange.
  4. I should note that I am reading the Who_AM_I register and getting the correct result for the accelerometer. I can read and write to most registers and get the correct result back (except point number 1).

If anyone has any suggestions or ideas please let me know. This is a very frustrating problem.

 

Thanks for the help,

 

Jec

 

 

The datasheet for the accelerometer is located here: http://www.st.com/stonline/products/literature/ds/15094.pdf

 

 

 

/**
 * Configures the SPI port to be used.
 * @param newMode The new mode for the SPI port.
 */
void SPIConfig(SPI_MODES newMode)
{
    switch (newMode)
    {
        case SPI_MODE_OFF:     

            UCA0CTL0 |= UCSWRST;                     // Set bit before configurations
            P3SEL = 0x00;                            // Port3 = standard I/O port
            UCA0CTL0 &= ( ~ UCSWRST );                 // Clear bit now - ready
            break;
        case SPI_MODE_MASTER:
        default:
        {
            // Reconfiguring Process
            // 1. Set UCSWRST in the UCXCTL1 register
            // 2. Initialize all USCI registers with UCSWRST = 1 (Including UCxCTL1)
            // 3. Configure Ports.
            // 4. Clear UCSWRST via software.
            // Enable Interrupts via UCxRXIE and UCxTXIE.
           
            UCA0CTL1 |= UCSWRST;                    // Disabling SPI bus while reconfiguration is occuring.
           
            UCA0CTL0 = (UCMSB | UCCKPL
             | UCMST | UCSYNC);                        // Data Cap on First Edge, MSB, Clock Polarity (Inactive High), Master SPI, 3-wire SPI, synchronous.
            UCA0CTL1 |= UCSSEL_2;                    // SMCLK.
           
            // 16 bit baud rate register           
              UCA0BR0 |= 0x08;                         // LSB: SMCLK / 2
              UCA0BR1 |= 0x00;                        // MSB:
             
              UCA0MCTL = 0x00;                         // no modulation


            // Configuring Ports
            P3SEL |= ( MOSI | MISO | SCLK );        // Port3 = SPI peripheral
            P3DIR |= ( MOSI | SCLK | ACCEL_EN | FLASH_EN);
            P3OUT |= ( MOSI | SCLK );

            UCA0CTL1 &= ~(UCSWRST);                    // Clear bit now - ready
            break;
        }
    }
}

/**
 * Transfer a byte from the microcontroller to another device.
 * @param spiOutput The output data to be sent from the microcontroller.
 * @return The data received by the microcontroller.
 */
unsigned char SPIByteTransfer(unsigned char spiOutput)
{
    // Clearing received interrupt flag.
    IFG2 &= ~(UCA0RXIFG);
   
    // Starting Transfer.
    UCA0TXBUF = spiOutput;
   
    // Poll Interrupt flag for completion
    while ((IFG2 & UCA0RXIFG) != UCA0RXIFG);

    return (UCA0RXBUF);   
}

 

 

 

/**
 * Transfers a byte of data to accelerometer at the specified address.
 * @param commMode The communication mode used to talk with the accelerometer.
 * @param address The address of the accelerometer's register.
 * @param data The data being sent to the accelerometer.
 * @return The byte of data the accelerometer transmitted.
 */
unsigned char accelTransfer(unsigned char commMode, unsigned char address, unsigned char data)
{
    // This is a simple function to communicate with the accelerometer. If data is expected to
    // be transmitted from the accelerometer, the return statement is used.
    unsigned char dataIn;
   
    // Enabling the accelerometer's SPI control signal.
    P3OUT &= ~(ACCEL_EN);
   
    SPIByteTransfer(commMode | address);
    dataIn = SPIByteTransfer(data);
   
    // Disabling the accelerometer's SPI control signal.
    P3OUT |= ACCEL_EN;
    return dataIn;
}

/**
 * Starts up the accelerometer and sets it to standard settings.
 */
void initAccelerometer()
{
    // This method starts up the accelerometer.
   
    // Reconmended Startup Sequence:
    // 1.  Write CTRL_REG1.
    // 2.  Write CTRL_REG2.
    // 3.  Write CTRL_REG3.
    // 4.  Write CTRL_REG4.
    // 5.  Write Reference.
    // 6.  Write INT1_THS.
    // 7.  Write INT1_DUR.
    // 8.  Write INT2_THS.
    // 9.  Write INT2_DUR.
    // 10. Read HP_FILTER_RESET (If filter is enabled).
    // 11. Write INT1_CFG
    // 12. Write INT2_CFG
    // 13. Write CTRL_REG5
   
   
    unsigned char testus1, testus2;
   
   
    SPIConfig(SPI_MODE_MASTER);
   
    // Must wait 5ms for the accelerometer to warm up.
    timeDelay(5000);
    accelTransfer(ACCEL_WRITE_NOINCREMENT, ACCEL_CTRL_REG1, ACCEL_PM0 | ACCEL_DR1 | ACCEL_DR0 | ACCEL_Xen | ACCEL_Yen | ACCEL_Zen);
    accelTransfer(ACCEL_WRITE_NOINCREMENT, ACCEL_CTRL_REG2, ACCEL_BOOT);
    timeDelay(500000);
   
    while ((accelTransfer(ACCEL_READ_NOINCREMENT, ACCEL_CTRL_REG2, 0x00) & ACCEL_BOOT) == ACCEL_BOOT);
   
   
    testus1 = accelTransfer(ACCEL_READ_NOINCREMENT, ACCEL_CTRL_REG2, 0x00);
    testus2 = accelTransfer(ACCEL_READ_NOINCREMENT, ACCEL_CTRL_REG1, 0x00);
   
   
   
   
    //accelTransfer(ACCEL_WRITE_NOINCREMENT, ACCEL_CTRL_REG3, ACCEL_I1_CFG1);
    accelTransfer(ACCEL_WRITE_NOINCREMENT, ACCEL_CTRL_REG3, 0x00);
    accelTransfer(ACCEL_WRITE_NOINCREMENT, ACCEL_CTRL_REG4, 0x00);
    accelTransfer(ACCEL_WRITE_NOINCREMENT, ACCEL_REFERENCE, 0x00);
    accelTransfer(ACCEL_WRITE_NOINCREMENT, ACCEL_INT1_THS, 0x00);
    accelTransfer(ACCEL_WRITE_NOINCREMENT, ACCEL_INT1_DURATION, 0x00);
    accelTransfer(ACCEL_WRITE_NOINCREMENT, ACCEL_INT2_THS, 0x00);
    accelTransfer(ACCEL_WRITE_NOINCREMENT, ACCEL_INT2_DURATION, 0x00);
    accelTransfer(ACCEL_WRITE_NOINCREMENT, ACCEL_HP_FILTER_RESET, 0x00);
    accelTransfer(ACCEL_WRITE_NOINCREMENT, ACCEL_INT1_CFG, 0x00);
    accelTransfer(ACCEL_WRITE_NOINCREMENT, ACCEL_INT2_CFG, 0x00);
    accelTransfer(ACCEL_WRITE_NOINCREMENT, ACCEL_CTRL_REG5, 0x00);
   
    // Must wait 5ms for the accelerometer to boot.
    timeDelay(5000);
}

/**
 * Converts the acceleration read from the accelerometer into standard
 * binary format. This is necessary because the accelerometer represents
 * 1 gravity with 1024. The acceleration from the accelerometer is also
 * in 2's complement.
 * @param accel The acceleration from the accelerometer.
 * @return The binary formatted accelerometer data.
 */
int accelIntConversion(unsigned int accel)
{
    // This method converts the 2's complement integers from the accelerometer into normally
    // formatted signed integers.
   
    // Testing for negative number (bit 15 is high)
   
   
   
    if ((accel & 0x8000) == 0x8000)
    {

        accel = ~(accel) + 1;                // Performing 2's complement.
       
        // Checking for 2g measured
        if ((accel & 0x0800) == (0x0800))
        {
            accel = 2000;                    // 2g is the greatest value possible.
        }
        else if ((accel & 0x0400) == (0x0400))
        {
            // Checking for 1g measured.
            accel = (accel & ~(0x0400));    // Removing the 1g indicator bit.
            accel += 1000;                    // Adding the 1000 back in.
        }

        return (-accel);            // Taking negative value of the 2's complement result.
    }
    else
    {
        // The number was positive.
        // Checking for 2g measured.
        if ((accel & 0x0800) == (0x0800))
        {
            accel = 2000;                    // 2g is the greatest value possible.
        }
        else if ((accel & 0x0400) == (0x0400))
        {
            // Checking for 1g measured.
            accel = (accel & ~(0x0400));    // Removing the 1g indicator bit.
            accel += 1000;                    // Adding the 1000 back in.
        }
        return(accel);
    }
}

  • Hi,

    well, I cannot tell you what exactly went wrong in your application... However, I can share a little bit my experience with SPI communication...

    The MSP430 allows you to choose at which clock edge it is allowed to change the data line and at which one the data line has to be stable. Have you checked this? When does the slave allow to change the data line, when should it be stable? My experience is, that if you do this incorrect the communication may work, but sometimes it doesn't work....

    Powering or starting up the application with a SPI master and SPI slave is always interesting. Think about a scenario where the slave acts immediately as the SPI slave, while the Master (MCU) has to configure its pins before the SPI bus terminals are available. On MSP430 you have multiplexed pins and the default state of the pins is a digital Input. So when you power-up the MSP430 the SPI clock and data pins are digital inputs. This changes when the code configures the pins into the SPI function.... Well, but for a short time the pins are digital I/Os. So during this short time you may see floating bus lines in case there are no external pull-ups. A disturbance by be good enough that the shift register of the slave may get some spikes on clock line which would shift in some bits..... some fault bits.... The solution for this situation is quite simple, just add a pull-up resistor on the clock line or the chip select line in case there is one.

    Regards.

  • I fixed the problem. Turns out it wasn't an issue with the SPI bus but rather an issue with my accelerometer. I was writing 0x01 to a register on the accelerometer which caused a system reset for that part. Thats why 0xFF was all I was getting from the other registers.

     

    Thanks for the help.

**Attention** This is a public forum