ADXL362 Triple Axis SPI Accelerometer with Deep FIFO
Introduction
The ADXL362 is a three-axis digital acclerometer from Analog Devices. It features a very deep FIFO buffer that makes it extremely useful for certain applications. For example, you might want to put the mbed to sleep to save power, or have it perform time consuming tasks such as logging data to a flash drive. The ADXL362 will continue to accumulate measurement data without needing attention from the mbed during these other activities.
The ADXL362 uses a ridiculously low amount of power - many applications can use the ultra-low noise mode, which slightly increases power consumption but provides better noise filtering.
The ADXL362 only has an SPI interface, so use an ADXL345 or other accelerometer if I2C is a requirement.
In this note, I'll mostly focus on mbed related topics and a few issues that the manufacturer doesn't discuss thorougly. For more general information, see the manufacturer's web site and data sheet.
I'm providing two code samples: 1) for a simple and basic setup and 2) a more complicated example using the FIFO buffer. I don't intend to writte a complete library.
I may have missed something in the data sheet, so use at your own risk.
What's Good
- Very deep FIFO - great for data logging applications or for freeing up the mbed for other tasks.
- Extremely low power. Power is so low that most apps can run it in the ultra-low noise mode. The FIFO can be used to let the host processor sleep for long periods.
- Flexible use with serveral modes of operation, such as motion activation.
- Default register configuration works well with a few changes for an easy, basic setup with the mbed.
- Less expensive than the ADXL345 (which has both SPI and I2C interfaces).
Take Care With...
- Maximum +-8g setting, no option for +-16g or higher.
- Data storage format explanation in the datasheet is a bit skimpy - see following section for clarification.
- FIFO a little tricky to set up and terminology is confusing - see following section for clarification.
- Zero offset is optimized for 2.0v power - may need consideration on 3.3v systems.
- Check the "Power Supply Requirements" section on the data sheet - there might an issue if the device is powered down and back up again quickly, but I haven't seen it.
- You won't be hand soldering this chip - it's in a 3mm LGA package, but breakout boards are available.
Data Types and Storage
Acceleration data is normally displayed as a signed value. For example, if you have a Z-axis accelerometer sitting flat on your bench, you probably want to display +1g, and -1g if you turn it upside down. In free-fall or rotated on edge it should display 0g.
You should be familar with signed and unsigned data types, especially integers. Microprocessors typically store signed integers in two's complement form (explained elsewhere on the web). On the mbed these can be declared as 8-bit, 16-bit or 32-bit variables (int8_t, int16_t, int32_t). Math operations, such as addding positive and negative values, work correctly and data can be displayed on the console in + and - form using printf "%d". This won't work correctly if you use unsigned ints.
Interfacing with peripherals that provide signed data can be tricky because they don't always supply it in two's complement form. When working with the ADXL362, you must take care because data can be accessed in three different ways, with different data representation for each:
- 8-bit format - data is stored in three 8-bit data registers, one each for x, y, z axis data. This data in in two's complement form and can be used directly with an int8_t.
- 12-bit format - data is stored in two 8-bit registers for each axis. The data is only 12-bit, but can be used with 16-bit signed int, because it is in two's complement form with extended sign bits. The high byte needs to be bit shifted then ORed with the low byte.
- FIFO format - 12-bit data is stored in two 8-bit registers for each axis. This format is different from the 12-bit format above, with the two high bits indicate the register id. It is a modified form of two's complement and needs special handling.
FIFO Extended Sign Bits
The data sheet talks about extended sign bits and assumes you know what they're talking about. Here's a quick explanation: The internal analog-digital converter has 12-bit resolution with the data is stored in two 8-bit registers. To use this data in the mbed, you will need to store it in a 16-bit variable. The variable has to handle negative numbers, so it's stored in two's complement form as a signed integer. The MSB is the sign bit, which indicates whether the value is positive or negative.
1111 11111111 - 12-bit two's complement of -1 (MSB is 1) 00001111 11111111 - 16-bit two's complement of 4095, not -1 (MSB is 0) 11111111 11111111 - 16-bit two's complement of -1, with extended sign bits (MSB is 1)
However in the FIFO, the two high bits are axis identifiers. So before the data is used in a 16-bit signed int, the id bits have to be stripped out and the sign bits extended further.
1111 11111111 - 12-bit two's complement of -1 10111111 11111111 - FIFO: two's complement of -1, high bits '10' are zaxis ID, then '11' sign bits 11111111 11111111 - 16-bit two's complement of -1, id bits stripped out and sign extended
FIFO Definitions
I found the data sheet a bit cryptic on this topic, here's my attempt to do better:
- One Sample is 12 bits of data stored as two 8-bit values, and is a measurement value for one axis.
- A Sample Set is one complete Sample for each axis. Actually it can be either a 3x Sample Set (x, y, z) or 4x Set (x, y, z,temperature). A 3x Set takes 6 bytes and a 4x Set takes 8 bytes, since each Sample is two bytes.
- Data can only be stored as 3x or 4x Sample Sets.
- The FIFO can hold up to 512 Samples, either as 170 3x Sets or 128 4x Sets.
- The FIFO watermark interrupt is configured for number of 16-bit Samples. So if you want to collect 16 3x Sample Sets, you will need to set the watermark to 48. The watermark should be divisable by 3 for 3x Sets or 4 for 4x Sets.
- Storing the FIFO data in a buffer on the mbed, for a 3x Sample Set requires 6 bytes * the number of Sets. For example 170 3x Sets will require 1020 bytes.
FIFO Use Suggestions
- You can use whatever FIFO depth you want by adjusting the watermark. If your app is not using the data in real time and just logging it to flash, it makes most sense to use the entire FIFO and give the app more time to store the data.
- When you first set up and are testing the FIFO, read and print the status register. Check to make sure that the FIFO overrun bit is not set. If it is set you are losing data and something is wrong with your configuration.
- 0x47 is a typical normal status after the watermark interrupt is triggered. You shouldn't see bit 7 or bit 3 set.
Hardware
Sparkfun has a breakout board. The single filtering capacitor will work but it's minimal - I reccomend adding a 1 to 10 uF tantalum cap between power and ground, observing correct polarity. The manufacturer's breakout board has a 10uF cap.
Analog Devices has various evaluation boards for those who want a complete solution, but they are expensive. Their breakout board is $30, which is double the cost of the Sparkfun board.
Wiring
Here's the wiring for the LPC1768:
- Power - connect to 3.3v or use a GPIO output on the mbed. Check the datasheet "Power Supply Requirements."
- Ground - mbed Gnd
- MOSI - mbed SPI MOSI p5, or p11
- MISO - mbed SPI MISO p6, or p12
- CLK - mbed SPI CLOCK p7, or p13
- CS - mbed, define a DigitalOut pin to drive chip select, used for framing the SPI protocol
- INT1 - mbed, define a pin for InterruptIn, such as p15
Code Examples
No Buffering - the code sample below illustrates the basic use of the ADXL362 without buffering. The example will output either 8-bit or 12-bit data - change this by commenting out appropriate line in the interrupt handler.
FIFO Buffering - an example using the buffer is posted here; it's working but a little rough:: http://mbed.org/users/tkreyche/code/ADXL362_buffer/
FIFO Buffer Logging to USB Flash - I'm working to get an app working that logs buffered FIFO data to USB flash drive. I'm having trouble getting it working...will post more later if I can solve the problems.
There are generic code snippets on Analog Devices web site. I used a few pieces from them in my code.
Troubleshooting
As sensors go, the ADXL262 is pretty easy to set up and get working with the mbed. If you are having problems, check and double check your wiring. Make certain you are using the correct SPI port numbers. Your wiring may be correct but the printf to the serial port may not be working due to a marginal cable or driver problem. For a first step, just try to read the ID register - if you can't do that nothing else will work!
//////////////////////////////////////////////////////////////////////////////////// // Basic, minimal demo code for interfacing the ADXL362 to the mbed // this is working code, not pseudo code - if it doesn't work for you // your wiring or something else is different from my LPC1768 setup // by Tom Kreyche tkreyche@well.com // use at your own risk! // in this example, I'm using variables that reflect the actual data size // for example a uint8_t for sending a one byte command to the Acc //////////////////////////////////////////////////////////////////////////////////// #include "mbed.h" // ACC Registers #define ID0 0x00 #define STATUS 0x0b #define RESET 0x1f #define INTMAP1 0x2a #define INTMAP2 0x2b #define FILTER_CTL 0x2c #define POWER_CTL 0x2d #define WR_SPI 0x0A #define RD_SPI 0x0B #define DOWN 0 #define UP 1 // function definitions void drSub(); uint8_t ACC_ReadReg( uint8_t reg ); void ACC_WriteReg( uint8_t reg, uint8_t reg ); uint32_t drFlag; void ACC_GetXYZ12( int16_t *x, int16_t *y, int16_t *z); void ACC_GetXYZ8( int8_t *x, int8_t *y, int8_t *z); // mbed hardware config SPI spi(p11, p12, p13); // mosi, miso, sclk DigitalOut cs(p14); InterruptIn dr(p15); Serial pc(USBTX, USBRX); // tx, rx int main() { // local variables uint8_t reg; int8_t x8 = 0; int8_t y8 = 0; int8_t z8 = 0; int16_t x12 = 0; int16_t y12 = 0; int16_t z12 = 0; // mbed serial port config pc.baud(115200); // mbed spi config // spi 8 bits, mode 0, 1 MHz for adxl362 spi.format(8,0); // 5 MHz, max for acc - works fine spi.frequency(5000000); // mbed interrupt config // data ready for adxl362 drFlag = 0; dr.mode(PullDown); dr.rise(&drSub); __disable_irq(); // reset the adxl362 wait_ms(100); ACC_WriteReg(RESET, 0x52); wait_ms(100); // read adxl362 registers printf("\r\n"); // read id register reg = ACC_ReadReg(ID0); pc.printf("ID0 = 0x%X\r\n", reg); reg = ACC_ReadReg(FILTER_CTL); pc.printf("FILTER_CTL = 0x%X\r\n", reg); // set adxl362 to 4g range, 25Hz //ACC_WriteReg(FILTER_CTL,0x51); // 2g, 25Hz ACC_WriteReg(FILTER_CTL,0x11); reg = ACC_ReadReg(FILTER_CTL); printf("FILTER_CTL = 0x%X\r\n", reg); // map adxl362 interrupts ACC_WriteReg(INTMAP1,0x01); reg = ACC_ReadReg(INTMAP1); pc.printf("INTMAP1 = 0x%X\r\n", reg); // set adxl362 to measurement mode, ultralow noise ACC_WriteReg(POWER_CTL,0x22); reg = ACC_ReadReg(POWER_CTL); pc.printf("POWER_CTL = 0x%X\r\n", reg); // start continuous processing adxl362 data __enable_irq(); while(1) { if(drFlag == 8) { ACC_GetXYZ8(&x8, &y8, &z8); pc.printf("%+04d %+04d %+04d\r\n", x8,y8,z8); drFlag = 0; } else if(drFlag == 12) { ACC_GetXYZ12(&x12, &y12, &z12); pc.printf ("%+05d %+05d %+05d\r\n",x12, y12, z12); //pc.printf("%04X, %04X, %04X\r\n", x12, y12, z12); drFlag = 0; } } } //////////////////////////////////////////////////////////////////////////////////// // read 8-bit x,y,z data //////////////////////////////////////////////////////////////////////////////////// void ACC_GetXYZ8(int8_t* x, int8_t* y, int8_t* z) { cs = DOWN; spi.write(RD_SPI); spi.write(0x08); *x = spi.write(0x00); *y = spi.write(0x00); *z = spi.write(0x00); cs = UP; } //////////////////////////////////////////////////////////////////////////////////// // read 12-bit x,y,z data //////////////////////////////////////////////////////////////////////////////////// void ACC_GetXYZ12(int16_t* x, int16_t* y, int16_t* z) { int16_t xyzVal[6] = {0, 0, 0, 0, 0, 0}; cs = DOWN; spi.write(RD_SPI); spi.write(0x0E); for (uint32_t i = 0; i < 6; i++) { xyzVal[i] = spi.write(0x00); } *x = (xyzVal[1] << 8) + xyzVal[0]; *y = (xyzVal[3] << 8) + xyzVal[2]; *z = (xyzVal[5] << 8) + xyzVal[4]; cs = UP; } //////////////////////////////////////////////////////////////////////////////////// // read ACC 8-bit registers //////////////////////////////////////////////////////////////////////////////////// uint8_t ACC_ReadReg( uint8_t reg ) { cs = DOWN; spi.write(RD_SPI); spi.write(reg); uint8_t val = spi.write(0x00); cs = UP; return (val); } //////////////////////////////////////////////////////////////////////////////////// // write ACC 8-bit register //////////////////////////////////////////////////////////////////////////////////// void ACC_WriteReg( uint8_t reg, uint8_t cmd ) { cs = DOWN; spi.write(WR_SPI); spi.write(reg); spi.write(cmd); cs = UP; } //////////////////////////////////////////////////////////////////////////////////// // Handle data ready interrupt // just sets data ready flag //////////////////////////////////////////////////////////////////////////////////// void drSub() { drFlag = 8; //drFlag = 12; }
4 comments on ADXL362 Triple Axis SPI Accelerometer with Deep FIFO:
Please log in to post comments.
Hey Tom, got the code examples ready to post yet? I just got the sparkfun breakout and am ready to try it...