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.

ADXL345 Interface with TIVA C EK-TM4C123GXL Launchpad (TM4C123GH6PM Micro)

Other Parts Discussed in Thread: TM4C1294NCPDT, TM4C123GH6PM

Hello,


I am trying to interface with an ADXL345 accelerometer breakout board from Adafruit. I have tried for several weeks now to no avail. First, I tried with one word length SPI and reading each individual byte from the results, which doesnt produce a good output. Then I tried manually clocking the Fss, which also didn't work, then also manually clocking SCK, which again did not work. It says that a continuous read is the best way to gather the data from the ADXL345, which would require a 64-bit frame (1 byte instruction + 1 byte address + 6 bytes read), which is not supported nor efficient. Is there a way to get a continuous read of 6 samples? The ADXL345 takes care of the register address read increment on its side, but I cannot figure out how to do a multiple-byte read.

In addition, an easy solution to this problem would be to manually manage the Fss bit and then simply clock the SCK (ssi clock) bit 8 times for each byte needed, as per the ADXL345 datasheet 8 successive periods of clock cycles will read another byte from the next data register in question, but there is no way to do this really with the API framework for SSI that is apparent. Also, inserting 'dummy' values into the transmit FIFO will confuse the peripheral, and probably send/receive more data, clogging the FIFOs.


Another thing I'm not understanding is that in my receive FIFOs, I am getting up to 16 bits of data on the MSB and LSB of the DATAX1,DATAY1,DATAZ1 and DATAX0,DATAY0,DATAZ0 registers, respectively, when the datasheet clearly states that in full-resolution mode, the values are 10-bits per axis, which should be 8 bits for DATAX0,DATAY0,DATAZ0 and only 2 bits for DATAX1,DATAY1,DATAZ1?? It makes sense to receive the 16 bits due to the word-length SSI transfer frame, but the values should only be updated in the [1:0] and [7:0] bits of the MSB and LSB, respectively. However, I am getting values in [15:0] for both...?

Also, my single-byte read code (one word SSI transfer) shown below does not give readable results:

#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/sysctl.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"

//*******************************************************
//						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
#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 sets up UART0 to be used for a console to display information
// as the example is running.
//
//*****************************************************************************
void
InitConsole(void)
{
    //
    // Enable GPIO port A which is used for UART0 pins.
    // TODO: change this to whichever GPIO port you are using.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    //
    // Configure the pin muxing for UART0 functions on port A0 and A1.
    // 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 UART0 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);
}

//*****************************************************************************
//
// Configure SSI0 in master Freescale (SPI) mode.  This example will send out
// 3 bytes of data, then wait for 3 bytes of data to come in.  This will all be
// done using the polling method.
//
//*****************************************************************************
int
main(void)
{
	uint32_t x_value_raw;
	uint32_t y_value_raw;
	uint32_t z_value_raw;
//	float x_value;
//	float y_value;
//	float z_value;
	uint32_t dataready;
	uint32_t pui32DataRx[6];


    //
    // Set the clocking to run directly from the external crystal/oscillator.
    // 
	SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);

    //
    // Set up the serial console to use for displaying messages.  This is
    // just for this example program and is not needed for SSI operation.
    //
    InitConsole();

    //
    // Display the setup on the console.
    //
    UARTprintf("SSI ->\n");
    UARTprintf("  Mode: SPI\n");
    UARTprintf("  Data: 16-bit\n\n");

    //
    // The SSI0 peripheral must be enabled for use.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);

    //
    // For this example SSI0 is used with PortA[5:2].
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    //
    // Configure the pin muxing for SSI0 functions on port A2, A3, A4, and A5.
    //
    GPIOPinConfigure(GPIO_PA2_SSI0CLK);
    GPIOPinConfigure(GPIO_PA3_SSI0FSS);
    GPIOPinConfigure(GPIO_PA4_SSI0RX);
    GPIOPinConfigure(GPIO_PA5_SSI0TX);

    //
    // Configure the GPIO settings for the SSI pins.  This function also gives
    // control of these pins to the SSI hardware.
    // The pins are assigned as follows:
    //      PA5 - SSI0Tx
    //      PA4 - SSI0Rx
    //      PA3 - SSI0Fss
    //      PA2 - SSI0CLK
    //
    GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 | GPIO_PIN_2);

    //
    // Configure and enable the SSI port for SPI master mode.  Use SSI0,
    // system clock supply, idle clock level low and active low clock in
    // freescale SPI mode, master mode, 1MHz SSI frequency, and 16-bit data.
    //
    SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_2, SSI_MODE_MASTER, 1000000, 16);
//	need 16 bit mode - 8 bits for r/w,x,x,ADDR[5:0], then 8 data bits

    //
    // Enable the SSI0 module.
    //
    SSIEnable(SSI0_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(SSI0_BASE, &pui32DataRx[0]))
    {
    }

    SSIDataPut(SSI0_BASE, BW_RATE|0x000A); //Set Output Rate to 100 Hz
    while(SSIBusy(SSI0_BASE)){}
    UARTprintf("DATA RECEIVED: OUTPUT RATE\n");
    SSIDataPut(SSI0_BASE, DATA_FORMAT|0x000A); //set device to +-8g in full resolution mode 3.9mg/lsb accuraccy
    while(SSIBusy(SSI0_BASE)){}
    UARTprintf("DATA RECEIVED: FORMATTED TO +/- 8g FULLRESMODE 3.9mg/LSB\n");

    SSIDataPut(SSI0_BASE, POWER_CTL|MEASURE); //Put the Accelerometer into measurement mode
    while(SSIBusy(SSI0_BASE)){}
    UARTprintf("DATA RECEIVED: POWER ON\n");


    UARTprintf("x\ty\tz\n");

    while(1){
		SSIDataPut(SSI0_BASE, READ|DATAX0);
    		SSIDataGet(SSI0_BASE, &pui32DataRx[0]);
    		while(SSIBusy(SSI0_BASE)){}
		SSIDataPut(SSI0_BASE, READ|DATAX1);
		SSIDataGet(SSI0_BASE, &pui32DataRx[1]);
		while(SSIBusy(SSI0_BASE)){}
		x_value_raw = pui32DataRx[1] | (pui32DataRx[0]>>8);
//		x_value = x_value_raw*0.00390625; //full resolution mode scaling


		SSIDataPut(SSI0_BASE, READ|DATAY0);
		SSIDataGet(SSI0_BASE, &pui32DataRx[2]);
		while(SSIBusy(SSI0_BASE)){}
		SSIDataPut(SSI0_BASE, READ|DATAY1);
		SSIDataGet(SSI0_BASE, &pui32DataRx[3]);
		while(SSIBusy(SSI0_BASE)){}
		y_value_raw = pui32DataRx[3] | (pui32DataRx[2]>>8);
//		y_value = y_value_raw*0.00390625; //full resolution mode scaling

		SSIDataPut(SSI0_BASE, READ|DATAZ0);
		SSIDataGet(SSI0_BASE, &pui32DataRx[4]);
		while(SSIBusy(SSI0_BASE)){}
		SSIDataPut(SSI0_BASE, READ|DATAZ1);
		SSIDataGet(SSI0_BASE, &pui32DataRx[5]);
		while(SSIBusy(SSI0_BASE)){}
		z_value_raw = pui32DataRx[5] | (pui32DataRx[4]>>8);
//		z_value = z_value_raw*0.00390625; //full resolution mode scaling

		UARTprintf("%d\t%d\t%d\r",x_value_raw,y_value_raw,z_value_raw);

	        SysCtlDelay(100);
    }

    return(0);
}

  • In addition, an easy solution to this problem would be to manually manage the Fss bit and then simply clock the SCK (ssi clock) bit 8 times for each byte needed, as per the ADXL345 datasheet 8 successive periods of clock cycles will read another byte from the next data register in question, but there is no way to do this really with the API framework for SSI that is apparent. Also, inserting 'dummy' values into the transmit FIFO will confuse the peripheral, and probably send/receive more data, clogging the FIFOs.
  • Another thing I'm not understanding is that in my receive FIFOs, I am getting up to 16 bits of data on the MSB and LSB of the DATAX1,DATAY1,DATAZ1 and DATAX0,DATAY0,DATAZ0 registers, respectively, when the datasheet clearly states that in full-resolution mode, the values are 10-bits per axis, which should be 8 bits for DATAX0,DATAY0,DATAZ0 and only 2 bits for DATAX1,DATAY1,DATAZ1?? It makes sense to receive the 16 bits due to the word-length SSI transfer frame, but the values should only be updated in the [1:0] and [7:0] bits of the MSB and LSB, respectively. However, I am getting values in [15:0] for both...?

  • Hi Dan,

    I don't have experience with the sensor in question, but skimming through your code I saw that you might have a problem there. After you "flush" your RX FIFO on line 214, you perform four SSIDataPuts (lines 218, 221, 225 and 233) before your first SSIDataGet on line 234. Thus, what you get on line 234 is actually the corresponding RX value from the Put command on line 218 - it's just been sitting in the FIFO in the meantime.

    Now about the "dummy" TX bytes confusing the peripheral -- are you sure it will get confused? The SSI CLK will have to be pulsed anyway to get data out from the peripheral. Think about it: is there any difference between pulsing the CLK while "not sending any data" and pulsing the CLK while sending dummy data full of zeros?

    Also, if you don't already have one, I would strongly suggest getting a logic analyzer so you can eavesdrop on the SSI bus and see what's actually happening. I would go [even more] crazy without one.

    Hope this helps,
    Veikko

    ps. I want to commend you for your detailed & properly code formatted post - a very welcome contrast to the too frequent style of "I have this and that and it doesn't work. Give me a solution! [code vomit follows]"
  • Veikko,

    First off, thank you for the quick response and the praises. I have to give credit to the SPI example given in the TIVA C Launchpad Setup examples, as most of this code and comments were derived from there.
    I would like to first address the SSIDataPuts - according to the most recent revision of the datasheet (located at www.sparkfun.com/.../ADXL345.pdf ) there should not be any residual Rx FIFO data after the configuration SSIDataPut sequences, as these are merely writing to the registers in the peripheral chip, and do not generate any Rx data on their own.
    Secondly, I am apprehensive to send all zeroes to the SSIDataPut function, as this would indicate as per the datasheet a complete re-write of the devices ID register (location 0x0000, write active low), so I have not yet tried this approach. I suppose after the READ|ADDR is written to the device, it does not matter what is transferred, as this results in a read function - bits on the Tx are apparently 'dummy' bits and clocks of the SSISCK are solely for inputting data to the Rx FIFO - but the problem there is telling the difference between 'frames', i.e. how would the DATAX1|DATAX0 be differentiable from DATAY1|DATAY0 and DATAZ1|DATAZ0 in a 6-byte read, and the 32-bit limit of the pointer plus the 16-bit read sequence of the SSI transfer (would discard the upper 16 bits of the transfer). I would love nothing more than to not use the SSI API, but it so elegantly sets the clock frequency to a manageable rate that the only possibility would be to manually clock the SSIFss for successive reads of 2 bytes. The problem with that is, that as per the peripheral datasheet, the FIFO is updated with the next result upon a read (i.e., FIFO[0] read command loads FIFO[1], the next result), and so the code as I've written it reads 6 successive results instead of 1 result for each axis.

    Apologies for the intensity of the peripheral properties in solving this problem. I suppose the main takeaway is that I'm unsure of what the behavior will be with successive zeroes and the coercing issues with the 16-bit SSI 'frame' and the 32-bit result register.

    In a TL;DR approach, thinking through this, there is the possibility of manually clocking the SSIFss so that the necessary 6-byte read can occur will indeed allow for 6 bytes to be read by sending 'dummy' zeroes via the Tx, but how would the Rx values be appropriately distinguished? As the 16-bit SSI 'frame' truncates to the lowest 16 bits during an SSIDataGet?

  • I've made the following changes:
    -manual SSIFss clocking
    -6-byte read with dummy bytes

    ...
    while(1){
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, PIN_LOW);
    SSIDataPut(SSI0_BASE, READ|MULT_READ|DATAX0); //read one byte
    SSIDataPut(SSI0_BASE, 0x00); //read one more byte
    SSIDataPut(SSI0_BASE, 0x00); //read third byte
    SSIDataPut(SSI0_BASE, 0x00); //read fourth byte
    SSIDataPut(SSI0_BASE, 0x00); //read fifth byte
    SSIDataPut(SSI0_BASE, 0x00); //read sixth byte
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, PIN_HIGH);
    SysCtlDelay(100);

    SSIDataGet(SSI0_BASE, &pui32DataRx[0]);
    SSIDataGet(SSI0_BASE, &pui32DataRx[1]);
    SSIDataGet(SSI0_BASE, &pui32DataRx[2]);
    SSIDataGet(SSI0_BASE, &pui32DataRx[3]);
    SSIDataGet(SSI0_BASE, &pui32DataRx[4]);
    SSIDataGet(SSI0_BASE, &pui32DataRx[5]);
    ...

    And only get results of 255 (8 ones) for every axis (MSB and LSB). It seems to no loner be reading anything..
    I also put in an extra SSIDataGetNonBlocking() after the initializations and before the while loop so as to be absolutely sure there is no residual in the Rx FIFO.
  • If you need to clock in more than one byte/16b at a time, you'll need to control the SSIFss manually anyway.

    There's no need/use to put a 'wait while bussy' after a SSIDataGet, you can do that after a SSIDataPut.

    The TIVA has a strange behaviour, when you send a byte(writing to a register), it will always 'receive' a dummy byte as well(even if nothing is returned by the slave). Thus when sending a byte, you need to read a byte as well, or, flush the receive FIFO before clocking in(sending a dummy byte for reading) real data.

    But your problem right now is that you're using mode 2, and you need mode 3(CPOL = 1 or high, and CPHA = 1 or high).

  • So I tried using a SSIDataGetNonBlocking() after every subsequent dummy byte sent after the read command, like this:
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, PIN_LOW);
    while(SSIDataGetNonBlocking(SSI0_BASE, &pui32DataRx[0])){}
    SSIDataPut(SSI0_BASE, READ|MULT_READ|DATAX0); //multiple byte read command
    while(SSIDataGetNonBlocking(SSI0_BASE, &pui32DataRx[0])){}
    SSIDataPut(SSI0_BASE, 0x00); //read one byte
    while(SSIDataGetNonBlocking(SSI0_BASE, &pui32DataRx[0])){}
    ...
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, PIN_HIGH);
    And I also tried just having just one before the read sequence before SSIFss is cleared and just one after the SSIFss is cleared, and both - none of the above iterations worked... I am still getting 255 for all answers.
  • marc_rir said:
    The TIVA has a strange behaviour, when you send a byte(writing to a register), it will always 'receive' a dummy byte as well(even if nothing is returned by the slave).

    Not really strange behaviour, especially not specific to the Tiva series. Think about it - when sending data, the CLK is pulsing - thus the slave could send data if it wanted to, and it would be valid behaviour. The Tiva must therefore record whatever is coming off the RX line. Just as with sending dummy bytes, what's the difference between receiving all zeroes and a dummy byte?

    Dan Haugen said:
    SSIDataPut(SSI0_BASE, READ|MULT_READ|DATAX0); //multiple byte read command
    while(SSIDataGetNonBlocking(SSI0_BASE, &pui32DataRx[0])){}
    SSIDataPut(SSI0_BASE, 0x00); //read one byte
    while(SSIDataGetNonBlocking(SSI0_BASE, &pui32DataRx[0])){}

    What's happening here is that you give the SSI peripheral a (proper or dummy) byte to send with SSIDataPut (which returns immediately in case the TX FIFO is not full, which it probably is not), then right after you perform a non-blocking read to the RX FIFO. At the time you perform the read, the SSI module hasn't yet finished the transmit cycle - thus it hasn't finished receiving either, so nothing is yet available in the RX FIFO. This results to your while loop also exiting immediately, which is followed by another SSIDataPut (that will now fill up the TX FIFO - the first transmit is still in progress). This happens so quickly that probably you'll end up pulling your FSS pin high before even the first byte has been sent (unless you have an SSIBusy loop before that).

    Try changing the SSIDataGetNonBlocking calls to just SSIDataGet, which will wait for data to be available (ie. for the transfer to finish). Change all but the first one, which I assume is supposed to flush the FIFO. And also take care where you'll save the value - all your dataget commands will now write to the same location.

    I'll re-recommend (I'd force it upon you if I could!) getting a logic analyzer and spying on the signal. I cannot stress enough how absolutely essential such a tool is when you're not 200 % sure of your code and hardware.

  • Veikko Immonen said:

    marc_rir

    The TIVA has a strange behaviour, when you send a byte(writing to a register), it will always 'receive' a dummy byte as well(even if nothing is returned by the slave).

    Not really strange behaviour, especially not specific to the Tiva series. Think about it - when sending data, the CLK is pulsing - thus the slave could send data if it wanted to, and it would be valid behaviour. The Tiva must therefore record whatever is coming off the RX line. Just as with sending dummy bytes, what's the difference between receiving all zeroes and a dummy byte?

    You’re right, and I thought about it when writing it down, but for some strange reason, when I used the 8-bit Atmel before, this was not needed…(eh, now I think about it again, the Atmel did not have a FIFO, just a 16bit latch…)

  • Veikko Immonen said:
    I'll re-recommend (I'd force it upon you if I could!) getting a logic analyzer and spying on the signal.

    So true - yet requester "forces" your restatement.    (we note he did not read w/great clarity another poster's request for a "book!")

    In the absence of L.A. - decent storage scope may suffice in the confirmation of 1-3 byte SPI transfers.    (especially if the data is, "doctored" to produce easily recognizable waveforms...)

    As you state - tracking the hardware's (real) behavior (agreement w/the SW) is vital - saves wear & tear upon (hapless) helpers here - and even upon requester!

  • After investigation using the logic analyzer, I've found that I must not have set the Fss pin, pin A3, used for manual clocking, correctly. Here is my initialization function:
    //*****************************************************************************
    //
    // This function sets up SSI0 for communication with manual Fss clocking
    //
    //*****************************************************************************
    void
    InitSSI(void)
    {
    //
    // The SSI0 peripheral must be enabled for use.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);

    //
    // For this example SSI0 is used with PortA[5:2].
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    //
    // Configure the pin muxing for SSI0 functions on port A2, A3, A4, and A5.
    //
    GPIOPinConfigure(GPIO_PA2_SSI0CLK);
    // GPIOPinConfigure(GPIO_PA3_SSI0FSS);
    GPIOPinConfigure(GPIO_PA4_SSI0RX);
    GPIOPinConfigure(GPIO_PA5_SSI0TX);
    GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_3);

    //
    // Configure the GPIO settings for the SSI pins. This function also gives
    // control of these pins to the SSI hardware.
    // The pins are assigned as follows:
    // PA5 - SSI0Tx
    // PA4 - SSI0Rx
    // PA3 - SSI0Fss - CONFIGURED FOR MANUAL CLOCKING
    // PA2 - SSI0CLK
    //
    GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_2);

    //
    // Configure and enable the SSI port for SPI master mode. Use SSI0,
    // system clock supply, idle clock level high and active low clock in
    // freescale SPI mode, master mode, 1MHz SSI frequency, and 8-bit data.
    //
    SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_3, SSI_MODE_MASTER, 1000000, 8);

    //
    // Enable the SSI0 module.
    //
    SSIEnable(SSI0_BASE);

    }

    After it is initialized, I perform my various writes and reads, but using the SSIDataPut() functions still manages to clock the Fss pin somehow, and this may be the reason there is interference... using the function below, I perform a multi-byte read:

    //****************************************************************************
    //
    // This allows user-controlled data frame lengths for SSI communication
    //
    //****************************************************************************
    void
    receiveDataSPI(uint32_t registerAddress, uint32_t frameLength, uint32_t * data)
    {
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, PIN_LOW);
    SSIDataPut(SSI0_BASE, READ|MULT_READ|registerAddress);

    while(frameLength)
    {
    SSIDataPut(SSI0_BASE, 0x00);
    frameLength--;
    }

    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, PIN_HIGH);
    }

    But right after I set the port A pin 3 low (PIN_LOW=0x00 in definitions), I see a clock high then low on the logic analyzer that leads me to believe the module is still somehow accessing the PA3 pin.... However, during a 6 byte read, shortly following the first few bytes of the data sent using SSIDataPut(), there is a clock high then low of the PA3 pin..... What is wrong??? Am I initializing pin PA3 wrong?
  • I successfully obtained data somehow with the changes I've just mentioned, but cannot perform multi-byte reads because the RX FIFO does not know how to differentiate between one sample and the next.. is there a way to tell the RX FIFO to move to the next FIFO location without disrupting the multi-byte read?
    Thanks
  • Not sure if I understand the problem in this last message of yours. However, in the previous one, I spotted a problem in the code: You set the FSS pin high before the transfer has finished. SSIDataPut queues data at the TX FIFO and returns immediately - when it returns, the data has not yet left the SSI TX pin (nor has RX data arrived at the RX pin or FIFO). You need to add a while(SSIBusy()) wait loop before toggling the FSS pin. Ultimately you want to replace the wait with an interrupt, but better to get it working without one first.
  • In my last post, if I set the frameLength to 6, I only get the first two results from my RX. I think this may be because the RX FIFO does not know that this is more than one received value, so it is simply concatenating all the results. Is there a way to manually clock the RX FIFO?
  • The FIFO doesn't concatenate results. It would do good to spend some time thinking (and reading) how the SSI actually works on the hardware level - you seem to be a bit "lost in the woods".

    Does your logic analyzer show a valid pulse train for the whole transaction now? If you can, do take a screenshot and share with us. (SSIFSS, CLK, TX, RX)

  • Veikko Immonen said:
    ...would do good to spend some time thinking (and reading) how the SSI actually works

    Move over "Job" - quote maker (above) nips your heels...    (i.e. "patience" for those theology challenged)

    I've detected such (poster) need - and would similarly recommend - your guidance.     Too often posters', "burn to succeed" - and thus short-change logic or proper scientific method - thus forming incorrect conclusions.     And its always a delight when some drift toward combat - or proclaim theirs as a, "Suggested Answer" - when ripe w/"holes."

    Poster is not alone in "struggling" a bit w/that device - it is complex.     Might the acquisition of a far simpler (temporary) alternative SPI device (say a GPIO extender - or EEProm) better "pave the way" for graduation to the complex ADXL - but only after (needed) "Lessons have been (really) learned!"    (and hard fact replaces, "guess/hope/reach!")

    KISS rules (as always) yet appears not to have much blipped o.p.'s radar.

    Two solid weeks - not much progress - may suggest "method/madness" (attention/commitment to detail) in (some) need of re-think...

  • Tried to delete that last response... it is actually working perfectly now with the multi-bit read. Thanks all for the help!
  • That's quite good - must be a real "sense of relief!" Do consider simpler/faster less involved device when similarly challenged - downstream.

    As its "working perfectly" - and there are many "back-forth" - might you advise the, "Where & how" code changed to achieve success. Others are sure to follow w/that sensor - you can best repay the forum by, "closing the loop."

    Such "working perfectly code" would qualify for full-fledged, "Verify Answer" and benefit those who follow in your footsteps...    (as it stands now - not so much...)

  • Hi Dan,

    I got a similar sensor from a friend who owns a store. He offered some to us to use with Arduinos. Well I don't use arduino but this seemed a great option to get started with accelerometers (never used one, never needed before). Could you provide the code to jump start me a little? Maybe I could even implement it in time for a contest in July.
  • Dan Haugen said:
    it is actually working perfectly now with the multi-bit read. Thanks all for the help!

    More than 48 hours have now passed - and two here have requested (some) detail as to, "How" such, "working perfectly" was achieved.    

    Would not, "real thanks" be demonstrated here by presenting the "magic bits" which led to success?

    Silence by o.p. may suggest: a) a selfish (non-forum like) behavior (not likely to encourage future forum "help") or b) "worked perfectly" claim - perhaps not so much!

    Independent review by those here (often) enables tweaks & improvements to user's code - benefitting all here.    "Worked perfectly" is a very hard claim to substantiate - usually indicates a (lack) of serious, rigorous test methodology & persistence - and subjects proclaimer to major, "legal liability!"

  • Apologies for delay - here is working code:

    void
    sendDataSPI(uint32_t modulenumber, uint32_t registerAddress, uint32_t registerData)
    {
    if(modulenumber==0){
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, PIN_LOW);
    SSIDataPut(SSI0_BASE, registerAddress);
    SSIDataPut(SSI0_BASE, registerData);
    while(SSIBusy(SSI0_BASE)){}
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, PIN_HIGH);
    }

    //
    // This buffer will be for reading the SSI Rx FIFO
    //
    uint32_t pui32DataRx[7];

    SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0); // Enable SSI Peripheral
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); // SSI0 is PortA
    GPIOPinConfigure(GPIO_PA2_SSI0CLK); // Clock is PA_2
    GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_3); // PA_3 is manually clocked
    GPIOPinConfigure(GPIO_PA4_SSI0RX); // MISO is PA_4
    GPIOPinConfigure(GPIO_PA5_SSI0TX); // MOSI is PA_5
    GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_2);
    //
    // Configure and enable the SSI port for SPI master mode. Use SSI0,
    // system clock supply, idle clock level high and active low clock in
    // freescale SPI mode, master mode, 1MHz SSI frequency, and 8-bit data.
    //
    SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_3, SSI_MODE_MASTER, 1000000, 8);
    SysCtlDelay(5);
    SSIEnable(SSI0_BASE);

    while(SSIDataGetNonBlocking(SSI0_BASE, &pui32DataRx[6])){}

    sendDataSPI(0, 0x31, 0x01); // Put accelerometer in +/-8g mode, DATA_FORMAT = 0x31
    //sendDataSPI(0, BW_RATE, 0x0A); // Set Output Rate to 100Hz
    //^^will be used in future for setting higher data rate
    sendDataSPI(0, 0x2D, 0x08); // Put accelerometer into measurement mode, POWER_CTL = 0x2D

    while(1){
    // Clear the SSI0 Rx FIFO
    while(SSIDataGetNonBlocking(SSI0_BASE, &pui32DataRx[6])){}

    // Wait for SSI0 to be finished
    while(SSIBusy(SSI0_BASE)){}

    // Read the values from the ADXL345 sensor
    receiveDataSPI(0, 0x32, 6);

    // Store sensor axis acceleration values in the Rx Data buffer
    SSIDataGet(SSI0_BASE, &pui32DataRx[0]);
    SSIDataGet(SSI0_BASE, &pui32DataRx[1]);
    SSIDataGet(SSI0_BASE, &pui32DataRx[2]);
    SSIDataGet(SSI0_BASE, &pui32DataRx[3]);
    SSIDataGet(SSI0_BASE, &pui32DataRx[4]);
    SSIDataGet(SSI0_BASE, &pui32DataRx[5]);

    // Concatenate axis results
    x_axis = (pui32DataRx[1]<<8)|pui32DataRx[0];
    y_axis = (pui32DataRx[3]<<8)|pui32DataRx[2];
    z_axis = (pui32DataRx[5]<<8)|pui32DataRx[4];

    // Display axes results
    UARTprintf("%d\t%d\t%d\n", x_axis, y_axis, z_axis);
    }
  • Sorry - forgot the receiveDataSPI function:

    void
    receiveDataSPI(uint32_t modulenumber, uint32_t registerAddress, uint32_t frameLength)
    {
    if(modulenumber==0){
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, PIN_LOW);
    SSIDataPut(SSI0_BASE, READ|MULT_READ|registerAddress);

    while(frameLength)
    {
    SSIDataPut(SSI0_BASE, 0x00);
    frameLength--;
    }
    while(SSIBusy(SSI0_BASE)){}
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, PIN_HIGH);

    }

    the most important part!
  • @Dan,

    That's most excellent - should benefit (and be appreciated) by many - thank you!

    Great that you persisted - and employed tools to confirm that the SPI HW "complied" with your (SW directed) commands.

    There's sufficient complexity w/in SPI in general - and when burdened with so complex a device as your sensor - its not unusual for anyone to "struggle."    (thus my recommendation (always) of KISS - employ simplest (SPI in this case) device to build understanding & confidence.)

    Thanks for closing this loop - suspect many will benefit...    And - it may be that one or several here discern (different and/or even better) code means/methods - and you'll personally benefit as well!    (such [really] "closes the loop!")

    And - as an aside - might you indicate any methods you've tried and/or developed to "properly exercise" that tri-axis accelerometer?    (our small firm has placed such devices w/in a small, "X-Y-Z table" so that basic, "Sanity tests" could be performed...)

  • Thanks Dan :D
  • In my code, I set the DATA_FORMAT and POWER_CTL registers to their defaults, allowing 100Hz data rate and turning the device on in measurement mode. The problem I saw when first doing this was the placement of the wait loop (while(SSIBusy(SSI0_BASE)){}). It should be AFTER you place the SPI data on the bus. Also, manual clocking of the Fss bit is IMPERATIVE for this device - reading each individual data register erased all other data registers in the sample FIFO, resulting in 6 reads of CONSECUTIVE measurements (i.e. 6 different samples), instead of 6 one-sample measurements. The data is then concatenated (8-bit Rx FIFO) using << operator for x axis, y axis, and z axis. If you just use the SSIDataPut() and SSIDataGet() functions, there is a timing issue where the SPI bus does not wait to assert the Fss pin (leading to unfinished data on the Tx or Rx buffers), or at least it asserts it before the minimum sample hold time for the data to be read by the sensor), so you must manually clock this pin and use the wait loop before you re-assert it at the end of your communication.

    I will include further updates as I investigate this device's various peripheral functions, such as threshold interrupts, free fall interrupts, and data ready interrupts, and supply the ensuing code for the more savvy hardware/system developer should they like to take a peek at my method. As I am including this accelerometer in a rocket, all these interrupts are highly valuable and will lead to more accuracy in flight-specific parameter handling (such as reaching apogee, and confirming land to set to low power state).

    Good luck to all reading this - PM me if you have further questions!
  • If I recall correctly (we worked w/similar device 3-4 years past) they are sensitive enough to enable (some) calibration by simply aligning each axis with, "center of the earth." This should enable some "rough" testing - to insure that the sensor data you harvest enjoys (some) validity.

    Have you tried this - or another method - to confirm that sensor's outputs are (somewhat) sane?

  • @cb1-
    As this is an adafruit-made breakout board (even though I ordered it from digikey), they have an extensive help guide which includes these calibration steps you mentioned. I have performed this yet, although the x- and y-axis values are not of the utmost concern for when the rocket is launched, just measuring the acceleration in the z-direction and detecting free fall (at which time the drogue parachutes will deploy). An on-board barometer will determine altitude (the LPS331AP chip) which I have also integrated into this project, which is why in my 'solved' code there are bits and pieces revealing the intent of a second SPI module interface (i.e. the if(modulenumber==0) statement). Since these operated at different times and may coincide due to HWI from each, I've chosen to integrate two SPI modules instead of one (which is usually preferable). If you have another suggestion to this approach, any information would be greatly appreciated.

    On another note, I am also trying to integrate a LINX EVM-915-250-FCS RF transceiver module and a LINX EVM-GPS-R4 module, both of which communicate via UART, and both of which I cannot gather any data from. Discussion of this would most definitely be better suited for a completely different thread, but if you also have any information on hardware UART please PM me at your soonest convenience.
  • When you joined this forum multiple options appear - my preference is to converse via, "Conversations" which is an editable, tick-box.    I converse with many here - this method proves efficient - makes the most sense.   

    Its likely that I can assist re: those RF devices.     I'm past amateur extra class and served as communications officer during my US Army tour.     We still design & supply "high power" RF for long range commo - and when the signal/message (really) must, "get thru!"   

    May I note that (some) chip barometers may not "enjoy" the ride.     I've not checked yours - may prove worthwhile to consult the maker - see if measurement spec remains valid under your (novel) "operating conditions."    

    Should the forum demonstrate interest - I can provide a link to a telemetry data packing/transmission scheme - employed during "serious" vehicular and flight operations.

  • Hello again,

    I've since been removed from this project from a year, as I have graduated and passed this project down to the next class, but my curiosity has peaked since getting my own TM4C launchpad...

    I was having an issue decoding the results.. there seems to be a constant value of anywhere between 0 and ~65,000. This does not correspond with what I believe to be accurate values. I'm aware that this is hardly near the measurement capabilities of the device according to how I initialized it (+/-16g), and so these 'resting' values are probably nearest to 0-1g...

    I guess my basic question is - can you provide a hint to how to decode these values? More specifically, would I have to implement a separate chip with floating-point capabilities to process this data (65000=1, employ linear scale perhaps)? In relation to the rocket for which this platform was designed, how can I reliably get an output of +/-16g, based on these unsigned decimal values?
  • hi guyss,

    even i am intrfacing this above'iva launch pad with ADXL 345" can u please post the correct and entire code. thanks for SPI interfacing

  • i have to interface "TM4c1294ncpdt with ADXl-345" please post the correct code if u can .thank ou so much...
  • Rahul,

    That's not how this forum works. I suggest you make an attempt and ask relevant, specific coding questions.. Don't expect anyone to just give you the answer......

    In any case, it seems you haven't bothered to even read the discussion to this topic.. The correct code IS posted here as an answer to this question, but it is for TM4C123GH6PM, which [may] require different initializations (and probably a different pinout) than what you should have already seen here. I hope for your sake it does. This is no way to approach coding.

  • thanks for that,
    but i have interfaced with the "TM4c1294ncpdt" with "ADXl 345" with ur code i modified lot of initilization with respect to tm4c1294ncpdt launch pad.
    i understood ,

    but this function doesnot
    "SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_2, SSI_MODE_MASTER, 1000000, 16);" ia m facing problem here only.
    so only i asked ..i changed all the parameters base don my requreiment of Tm4c1294ncpdt lauch pad requirement.
  • This function in particular is to set the SSI clock for your particular processor, and especially in relation to that specific SSI peripheral in the chip. In this particular case, you will have to check for your chip/SSI peripheral (there are several per chip) what mode coincides with the SSI mode you are initializing your chip to be. Modes are 0,1,2,3, and correlate to how the clock signal is interpreted and the FSS pin is interpreted. For your chip, i don't know the exact register that correlates to SSI_FRF_MOTO_MODE_2... you will have to read the manual.
  • void InitConsole(void) {
    // Enable GPIO port A which is used for UART0 pins.
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    // Configure the pin muxing for UART0 functions on port A0 and A1.
    // This step is not necessary if your part does not support pin muxing.
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);

    // Enable UART0 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.
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

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

    void InitSPI(void) {
    // The SSI2 peripheral must be enabled for use.
    SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI2);

    // Enable Port D
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);

    // SETUP SPI CS Pin (to output)
    GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_5);

    // Set SPI CS to HIGH (active-low)
    GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_6, 0xFF);

    GPIOPinConfigure(GPIO_PD3_SSI2CLK);
    GPIOPinConfigure(GPIO_PD2_SSI2FSS);
    GPIOPinConfigure(GPIO_PD0_SSI2DAT1);
    GPIOPinConfigure(GPIO_PD1_SSI2DAT0);

    // Configure the GPIO settings for the SSI pins.
    GPIOPinTypeSSI(GPIO_PORTD_BASE, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 |
    GPIO_PIN_3);

    // Configure and enable the SSI port for SPI master mode. Use SSI0,
    // system clock supply, idle clock level low and active low clock in
    // freescale SPI mode, master mode, 1MHz 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 datasheet for more information on
    // the different SPI modes.
    SSIConfigSetExpClk(SSI2_BASE, , SSI_FRF_MOTO_MODE_3,
    SSI_MODE_MASTER, 1000000, 8);

    // Enable the SSI0 module.
    SSIEnable(SSI2_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.
    uint32_t scrap;
    while(SSIDataGetNonBlocking(SSI2_BASE, &scrap));
    }


    int main(void) {

    // Set the clocking to run directly from the external crystal/oscillator.
    SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
    SYSCTL_XTAL_16MHZ);

    // Set up UART Serial Output
    InitConsole();

    // Set up SSI2 for SPI Communication
    InitSPI();

    UARTprintf("............................................\n");

    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, 0x00);
    SSIDataPut(SSI2_BASE, 0x31); // DATA_FORMAT
    while(SSIBusy(SSI2_BASE));
    SSIDataPut(SSI2_BASE, 0x01); // +/- 4g
    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, 0xFF);

    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, 0x00);
    SSIDataPut(SSI2_BASE, 0x2D); // POWER_CTL
    while(SSIBusy(SSI2_BASE));
    SSIDataPut(SSI2_BASE, 0x08);
    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, 0xFF);

    while (1) {

    char values[10];
    int16_t x, y, z;

    char address = 0x80 | DATAX0 | 0x40; //Set MSB for read, start at X0 and enable multi-byte

    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, 0x00);;

    SSIDataPut(SSI2_BASE, address); // ask for data

    for(int i=0; i < 6; i++){
    SSIDataPut(SSI2_BASE, 0x00);
    while(SSIBusy(SSI2_BASE));
    SSIDataGet(SSI2_BASE, &values[i]);
    }

    GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_2, 0xFF);

    for(int i = 0; i < 6; i++){
    UARTprintf("%i\t", values[i]);
    }
    UARTprintf("\n");

    //The X value is stored in values[1] and values[2].
    x = ((int16_t)values[1]<<8)|(int16_t)values[0];
    //The Y value is stored in values[2] and values[3].
    y = ((int16_t)values[3]<<8)|(int16_t)values[2];
    //The Z value is stored in values[4] and values[5].
    z = ((int16_t)values[5]<<8)|(int16_t)values[4];

    UARTprintf("%i\t\t%i\t\t%i\n", x, y, z);

    SysCtlDelay(50 * (SysCtlClockGet() /1000 /3));
    }

    return(0); // will never return







    when i execute this program i am getting output as "242" for all the axis.
    i am not able to get device ID also for the adxl-345
    can any one tell wheter is it clock problem or wat...


    can any one suggest me where i am going wrong...
  • It has been a while since I was involved with this project, but I remember getting the same thing you are getting. I believe this is an error in initializing the device properly - please carefully read the spec sheet for the sensor and ensure you have initialized it properly.. Take the earlier advice I was given and invest in a Logic Analyzer. The initialization steps are crucial!
  • Hello Dan,

    Poster Rahul was, "last seen - these parts - more than one year ago!" (mousing over his forum ID reveals "recent activity"  - which appears (neither) recent nor active...)

    His code - employed the FSS feature of SPI - even though you/Veiko detailed such approach as flawed...