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); }