Electrical – SPI Communication between ADXL345 and TM4C123GXL (TIVA C Launchpad) doesn’t work as intended

accelerometercmicrocontrollerspiti-ccstudio

I need to interface the TM4C123GH6PM based Microcontroller board TM4C123GXL (TIVA C Launchpad) with a accelerometer (ADXL345) by Adafruit. As, I am bound to use SPI communication and program on Embedded C using Code Composure Studio, I referred to the datasheet of the accelerometer and tried initialising the device as per the instructions.

I seem to have put everything together, but to no readings on the Serial Monitor. (despite 0 errors). Hence, I suspect there to be some logical error in the code. I am not sure if this the best platform to ask for code inspection, but please help me in any way possible. Here is the code:

//CONNECTIONS:

//PD3 - SSI3Tx    (MOSI3)
// PD2 - SSI3Rx    (MISO3)
// PD1 - SSI3Fss   (CS3)        
// PD0 - SSI3CLK   (SCK)

#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_memmap.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/ssi.h"
#include "driverlib/ssi.c"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
#include "utils/uartstdio.c"



//                      ADXL345 Definitions


#define READ    0x8000

//ADXL Register Map
#define DEVID           0x0000  //Device ID Register
#define THRESH_TAP      0x1D00  //Tap Threshold
#define OFSX            0x1E00  //X-axis offset
#define OFSY            0x1F00  //Y-axis offset
#define OFSZ            0x2000  //Z-axis offset
#define DUR             0x2100  //Tap Duration
#define Latent          0x2200  //Tap latency
#define Window          0x2300  //Tap window
#define THRESH_ACT      0x2400  //Activity Threshold
#define THRESH_INACT    0x2500  //Inactivity Threshold
#define TIME_INACT      0x2600  //Inactivity Time
#define ACT_INACT_CTL   0x2700  //Axis enable control for activity and inactivity detection
#define THRESH_FF       0x2800  //free-fall threshold
#define TIME_FF         0x2900  //Free-Fall Time
#define TAP_AXES        0x2A00  //Axis control for tap/double tap
#define ACT_TAP_STATUS  0x2B00  //Source of tap/double tap
#define BW_RATE         0x2C00  //Data rate and power mode control
#define POWER_CTL       0x2D00  //Power Control Register
#define INT_ENABLE      0x2E00  //Interrupt Enable Control
#define INT_MAP         0x2F00  //Interrupt Mapping Control
#define INT_SOURCE      0x3000  //Source of interrupts
#define DATA_FORMAT     0x3100  //Data format control
/* Clear bit 6 of DATA_FORMAT to use 4 wire SPI mode
 * SET   bit 6 of DATA_FORMAT to use 3 wire SPI mode
 */
#define DATAX0          0x3200  //X-Axis Data 0
#define DATAX1          0x3300  //X-Axis Data 1
#define DATAY0          0x3400  //Y-Axis Data 0
#define DATAY1          0x3500  //Y-Axis Data 1
#define DATAZ0          0x3600  //Z-Axis Data 0
#define DATAZ1          0x3700  //Z-Axis Data 1
#define FIFO_CTL        0x3800  //FIFO control
#define FIFO_STATUS     0x3900  //FIFO status

//Power Control Register Bits
#define WU_0        (1<<0)    //Wake Up Mode - Bit 0
#define WU_1        (1<<1)    //Wake Up mode - Bit 1
#define SLEEP       (1<<2)    //Sleep Mode
#define MEASURE     (1<<3)    //Measurement Mode
#define AUTO_SLP    (1<<4)    //Auto Sleep Mode bit
#define LINK        (1<<5)    //Link bit

//Interrupt Enable/Interrupt Map/Interrupt Source Register Bits
#define OVERRUN     (1<<0)
#define WATERMARK   (1<<1)
#define FREE_FALL   (1<<2)
#define INACTIVITY  (1<<3)
#define ACTIVITY    (1<<4)
#define DOUBLE_TAP  (1<<5)
#define SINGLE_TAP  (1<<6)
#define DATA_READY  (1<<7)

//Data Format Bits
#define RANGE_0     (1<<0)
#define RANGE_1     (1<<1)
#define JUSTIFY     (1<<2)
#define FULL_RES    (1<<3)

#define INT_INVERT  (1<<5)
#define SPI         (1<<6)
#define SELF_TEST   (1<<7)

#define PIN_LOW 0x00
#define PIN_HIGH 0xFF

//This function will read a certain number of registers starting from a
//specified address and store their values in a buffer.
void receiveDataSPI(uint32_t , uint32_t, uint32_t * );

void sendDataSPI(uint32_t , uint32_t );
// This allows user-controlled data frame lengths for SSI communication

//*****************************************************************************
//

//***********************DEFINITION OF UART MODULE*****************************

void
InitConsole(void)
{
    //
    // Enable GPIO port E which is used for UART7 pins.
    // TODO: change this to whichever GPIO port you are using.
    //
    //SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    ///
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    //
    // Configure the pin muxing for UART7 functions on port E0 and E1.
    // This step is not necessary if your part does not support pin muxing.
    // TODO: change this to select the port/pin you are using.
    //
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);

    //
    // Enable UART7 so that we can configure the clock.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

    //
    // Use the internal 16MHz oscillator as the UART clock source.
    //
    UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);

    //
    // Select the alternate (UART) function for these pins.
    // TODO: change this to select the port/pin you are using.
    //
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    //
    // Initialize the UART for console I/O.
    //
    UARTStdioConfig(0, 115200, 16000000);

}

//*****************************************************************************


//******INITIALISATION FUNCTION OF SSI MODULE AND ADXLCONFIGURATIONS************
void InitSSI(void)
{
//
// The SSI3 peripheral must be enabled for use.
///
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI3);
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_SSI3))
    { }

///CONFIGURE ALTERNATIVE FUNCTION OF GPIO PIN
    GPIOPinConfigure(GPIO_PD0_SSI3CLK); // Clock is PD_0
    //GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_1); // PA_3 is manually clocked
    GPIOPinConfigure(GPIO_PD1_SSI3FSS);
    GPIOPinConfigure(GPIO_PD2_SSI3RX); // MISO is PA_4
    GPIOPinConfigure(GPIO_PD3_SSI3TX); // MOSI is PA_5
///" Note that a GPIOPinConfigure() function call is also required to properly configure a pin for the SSI function. "...BUT NOT BEFORE ENABLING SSI MODULE AND SETTING CLOCK SOURCE
    //GPIOPinTypeSSI(GPIO_PORTD_BASE, GPIO_PIN_3 | GPIO_PIN_2 | GPIO_PIN_0);

    ///
    SSIClockSourceSet(SSI3_BASE, SSI_CLOCK_SYSTEM);

    GPIOPinTypeSSI(GPIO_PORTD_BASE, GPIO_PIN_3 | GPIO_PIN_2 | GPIO_PIN_0);
    SSIConfigSetExpClk(SSI3_BASE,
                               SysCtlClockGet(),        // rate of the clock supplied to the SSI module.
                               SSI_FRF_MOTO_MODE_3,     // freescale SPI mode 3 (Polarity:1, Phase:1) , GIVEN AS PER DATASHEET
                               SSI_MODE_MASTER,         //specifies the mode of operation : MASTER in our case
                               1000000,                 //  specifies the clock rate
                              8);                      // specifies number of bits transferred per frame

///


    sendDataSPI(0x31, 0x05); // Put accelerometer in +/-8g mode, DATA_FORMAT = 0x31
    sendDataSPI(0x2E, 0x00);    //DISABLING ALL INTERRUPTS
    sendDataSPI(0x38, 0x80);   //1 0 , 0 ,0 0000  (0x80)
    //sendDataSPI(0x31, 0x01); // Put accelerometer in +/-8g mode, DATA_FORMAT = 0x31
    sendDataSPI(BW_RATE, 0x0A); // Set Output Rate to 100Hz
    sendDataSPI(POWER_CTL, 0x08); //
// For this example SSI0 is used with PortA[5:2].
    SSIEnable(SSI3_BASE);
}

//using the function below, I perform a multi-byte read:
//****************************READ FROM ADXL345*******************************


    //****************************************************************************
void receiveDataSPI(uint32_t registerAddress, uint32_t frameLength, uint32_t * buffer)
{
        int i;
        UARTprintf("receive.");
        uint32_t address = 0x80 | registerAddress;
        //If we're doing a multi-byte read, bit 6 needs to be set as well.
        if(frameLength > 1)
            address = address | 0x40;
        GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_1, PIN_LOW); //TURNING CS3 LOW
        //SSIDataPut(SSI3_BASE, READ|MULT_READ|registerAddress);
        SSIDataPut(SSI3_BASE, address);
        /*
         * Prototype:
         *           void SSIDataGet(uint32_t ui32Base, uint32_t *pui32Data)
         */
        //while(SSIDataGetNonBlocking(SSI3_BASE, &buffer[6]))    //Gets a data element from the SSI receive FIFO
        //   {}
        for(i=0; i<frameLength; i++)
        {
             SSIDataGet(SSI3_BASE, &buffer[i]);
        }

        while(SSIBusy(SSI3_BASE)){}
        GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_1, PIN_HIGH);
}

//************************WRITE TO ADXL******************
void sendDataSPI(uint32_t registerAddress, uint32_t registerData)
{
    //if(modulenumber==0)
    //{
    UARTprintf("send.");
        ///PULLING CS3 LOW
        GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_1, PIN_LOW); //PULLING CS3 LOW BEFORE STARTING DATA TRANSMIT

        SSIDataPut(SSI3_BASE, registerAddress);             //Puts a data element into the SSI transmit FIFO
        SSIDataPut(SSI3_BASE, registerData);
        SysCtlDelay(5);
        while(SSIBusy(SSI3_BASE))
        {}
        GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_1, PIN_HIGH);
}


int main(void)
{
    InitConsole();

    UARTprintf("ADXL Getting to work...");
    InitSSI();
    uint32_t x_axis;
    uint32_t y_axis;
    uint32_t z_axis;
// This buffer will be for reading the SSI Rx FIFO
    uint32_t buffer[10];

    while(1)
    {
            receiveDataSPI(0x32, 6, buffer);
            // Concatenate axis results
            x_axis = (buffer[1]<<8)|buffer[0];
            y_axis = (buffer[3]<<8)|buffer[2];
            z_axis = (buffer[5]<<8)|buffer[4];

            // Display axes results
            UARTprintf("%d\t%d\t%d\n", x_axis, y_axis, z_axis);
            SysCtlDelay(100);
    }
}

Best Answer

Below is section of functional code snippet that initializes the SSI port and reads the device ID from the ADXL345. This code snippet confirm that the SPI interface is functional. This project can be found at Tiva SPI Nokia ADXL345

//
// Enable the SSI3 peripheral
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI3);
//
// Wait for the SSI0 module to be ready.
//
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_SSI3))
{
}
// For this example SSI0 is used with PortD[3:0]. The actual port and pins
// used may be different on your part, consult the data sheet for more
// information. GPIO port D needs to be enabled so these pins can be used.
// TODO: change this to whichever GPIO port you are using.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
// CS pin of the ADXL345 should be set prior to setting Clock polarity and Clock phases.
// This is because ASXL345 was powered up prior to setting up the host processor
GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_1);
GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_1, GPIO_PIN_1);
//
// Configure the pin muxing for SSI3 functions on port D0, D1, D2, and D3.
// This step is not necessary if your part does not support pin muxing.
// TODO: change this to select the port/pin you are using.
//
GPIOPinConfigure(GPIO_PD0_SSI3CLK);
GPIOPinConfigure(GPIO_PD1_SSI3FSS);
GPIOPinConfigure(GPIO_PD2_SSI3RX);
GPIOPinConfigure(GPIO_PD3_SSI3TX);
//
// Configure the GPIO settings for the SSI pins. This function also gives
// control of these pins to the SSI hardware. Consult the data sheet to
// see which functions are allocated per pin.
// The pins are assigned as follows:
// PD3 - SSI3Tx
// PD2 - SSI3Rx
// PD1 - SSI3Fss
// PD0 - SSI3CLK
// TODO: change this to select the port/pin you are using.
//
GPIOPinTypeSSI(GPIO_PORTD_BASE, GPIO_PIN_3 | GPIO_PIN_2 | GPIO_PIN_1 | GPIO_PIN_0);
//
// Disable the SSI3 module.
//
SSIDisable(SSI3_BASE);
//
// Configure and enable the SSI port for SPI master mode. Use SSI3,
// system clock supply, idle clock level low and active low clock in
// freescale SPI mode, master mode, 5MHz SSI frequency, and 8-bit data.
// For SPI mode, you can set the polarity of the SSI clock when the SSI
// unit is idle. You can also configure what clock edge you want to
// capture data on. Please reference the data sheet for more information on
// the different SPI modes.
//
SSIConfigSetExpClk(SSI3_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_3, SSI_MODE_MASTER, 1000000, 8);
//
// Enable the SSI3 module.
//
SSIEnable(SSI3_BASE);
// Read any residual data from the SSI port. This makes sure the receive
// FIFOs are empty, so we don't read any unwanted junk. This is done here
// because the SPI SSI mode is full-duplex, which allows you to send and
// receive at the same time. The SSIDataGetNonBlocking function returns
// "true" when data was returned, and "false" when no data was returned.
// The "non-blocking" function checks if there is any data in the receive
// FIFO and does not "hang" if there isn't.
//
while(SSIDataGetNonBlocking(SSI3_BASE, &pui32DataRx))
{
}

The following lines of code reads the device ID from ADXL345

    while (SSIBusy(SSI3_BASE)) ;
    SSIDataPut(SSI3_BASE, 0x80); //Puts a data element into the SSI transmit FIFO
    while (SSIBusy(SSI3_BASE));
    SSIDataGet(SSI3_BASE, &buffer);

Below is a logic analyzer image verifying communication between the TM4C123 micro controller and ADXL345 accelerometer.

Logic Analyzer Data Confirming SPI communication between TM4C123 and ADXL345


There are issues that need to addressed in your code.

    GPIOPinTypeSSI(GPIO_PORTD_BASE, GPIO_PIN_3 | GPIO_PIN_2 | GPIO_PIN_0);

GPIO_PIN_1 is missing from the above code.

Also I suggest moving SSIEnable(SSI3_BASE) before the sendDataSPI function calls. In the posted code measure bit might not be getting set. Therefore any of the read data commands will not work.

Feel free to fork the code from Github and make the necessary modification to get the rest of your code working.


References: