These weekend I spent some time recording sample data sets from the sparkfun SEN-10724 and looking at it. I'd say I was analyzing the data, except that might give the impression that I actually have a good idea what I'm doing.
The recording of data, I actually set up using the sparkfun "ethernet pro" board, which is basically an Arduino with built-in ethernet and seemingly poorly designed voltage regulation (it gets pretty hot if you power it with the intended voltage levels). I felt more comfortable using the ethernet rather than serial-over-USB since I would be sending more data than was reasonable for the 9600bps that the Arduino UNO is fixed at (over USB - the firmware for the atmega8u2 that handles the USB-to-serial interface is programmed such that it will only operate at 9600bps).
After finding a few errors with decoding the data (mostly in the PC side, the microcontroller was programmed to send the raw measurements as-is), I found that the ADXL345 accelerometer has noise levels way out of spec. I've plotted the measurements and attached images of said plots at the end of this post. The XYZ measurements of the ADXL345 all had significant levels of noise (RMSD 5-12 LSBs) and the magnetometer had noise on the X-axis only (RMSD 9 LSBs). During the test data recording, the sensor board was in a breadboard sitting on my desk with no significant sources of vibration.
One concern I had is that the schematic for the SEN-10724 had two .1μF capacitors "near" the ADXL345. The datasheet calls for a single .1μF capacitor between ground and VDDI/O, and for a 1μF tantalum capacitor at VS, with an optional 10μF tantalum capacitor in parallel.
If the circuit instead only has a .1μF capacitor at VS, that might explain why the measurements look so noisy. In-circuit measurement of the capacitors is impossible, and I don't have a stockpile of SMD capacitors (strangely enough, after the 3 separate LED cube builds) to replace it with. Time to email customer support.
Showing posts with label i2c. Show all posts
Showing posts with label i2c. Show all posts
Sunday, October 2, 2011
Arduino, Wire, and I2C Part 4: Noise and analysis
Labels:
9dof,
accelerometer,
ADXL345,
arduino,
atmel,
avr,
electronics,
ethernet pro,
gyroscope,
HMC5883L,
i2c,
imu,
magnetometer,
maker,
microcontroller,
SEN-10724,
sparkfun
Tuesday, September 27, 2011
Arduino, Wire, and I2C Part 3: gyro
The SEN-10724 board includes three distinct sensors on it. The linear accelerometer and magnetometer were covered in previous posts. The experience gained in working with those two sensors made it a relatively trivial matter to implement an Arduino sketch to get measurements from the 3-axis gyro.
Set-up of the IC is relatively straight-forward. The defaults are fine in most cases. Because my intent is to eventually couple the measurements from each sensor (the first being the magnetometer and gyro), I configured the data rate to be the same as the magnetometer's default. There is a single register that sets that, the sample rate divisor (SMPLRT_DIV). The datasheet provides a formula (in section 8.2) to derive the desired output rate:
One other piece of initialization needs to be done to take measurements, and that is to set the DLPF_FS (digital low-pass filter/full-scale) register. The "full-scale" part of the register has only one valid value, which is to set the full sale range of the gyros to ±2000°/s. The DLPF portion of the register sets the bandwidth of the filter in conjunction with the internal sample rate. For this test, I picked the value that had the most bandwidth in the 1kHz sample rate. I honestly don't understand what the low-pass filter is being used for, but that's not important right now.
I've made the Arduino sketch available. There's no self-test available, but I can get an idea as to whether the data makes any sense by picking up the board and rotating it around. It seems to work. Also, I can look at the temperature sensor measurements and do a quick sanity check.
According to the table in section 3.1, the reference point for the temperature sensor is -13200LSB=35°C, with 1°C=280LSB (i.e. each bit in the temperature measurement is 1/280°C). The measurements looked fairly stable so I just picked one at random: -15106:
Now that I've gotten measurements (or at least test measurements) out of each device, it's time to start aggregating the data...
Set-up of the IC is relatively straight-forward. The defaults are fine in most cases. Because my intent is to eventually couple the measurements from each sensor (the first being the magnetometer and gyro), I configured the data rate to be the same as the magnetometer's default. There is a single register that sets that, the sample rate divisor (SMPLRT_DIV). The datasheet provides a formula (in section 8.2) to derive the desired output rate:
Fsample=Finternal/(divider+1)F is the frequency (sample rate) in Hz. The internal sample frequency can be either 1kHz or 8kHz. I chose an internal sample frequency of 1kHz to start with, and with the desired 15Hz output rate, that resulted in:
15Hz=1000Hz/(divider+1)
divider+1=1000Hz/15Hz
divider=65
One other piece of initialization needs to be done to take measurements, and that is to set the DLPF_FS (digital low-pass filter/full-scale) register. The "full-scale" part of the register has only one valid value, which is to set the full sale range of the gyros to ±2000°/s. The DLPF portion of the register sets the bandwidth of the filter in conjunction with the internal sample rate. For this test, I picked the value that had the most bandwidth in the 1kHz sample rate. I honestly don't understand what the low-pass filter is being used for, but that's not important right now.
I've made the Arduino sketch available. There's no self-test available, but I can get an idea as to whether the data makes any sense by picking up the board and rotating it around. It seems to work. Also, I can look at the temperature sensor measurements and do a quick sanity check.
According to the table in section 3.1, the reference point for the temperature sensor is -13200LSB=35°C, with 1°C=280LSB (i.e. each bit in the temperature measurement is 1/280°C). The measurements looked fairly stable so I just picked one at random: -15106:
(-15106+13200)/280=-6.8°C + 35°C = 28.2°C = 82.75°FThis seems a tad warm to me, but it's not completely unbelievable.
Now that I've gotten measurements (or at least test measurements) out of each device, it's time to start aggregating the data...
Monday, September 26, 2011
9DOF/SEN-10724: corrections
Looking at the schematic more closely this evening I noticed I'd made a couple of (apparently inconsequential) mistakes. First, the SEN-10724 board has built-in pull-up resistors on the SDA and SCL lines, so those in my circuit were redundant. Second, the board has a voltage regulator to keep the voltage between gnd and Vcc on the devices at 3.3V, so you can actually hook it up to your 5V source and it should still work.
The updated (and simpler) circuit diagram is on the right.
Also, I've attached my (currently very sloppy) Fritzing part to the "code" base, so you can download it.
Edit: Reading the source for the Arduino Wire library, it appears that the library activates the AVR-internal pull-ups for the pins being used to connect to SCL and SDL, apparently making even the resistors on the SEN-10724 board redundant.
Labels:
9dof,
accelerometer,
arduino,
fritzing,
gyroscope,
i2c,
imu,
magnetometer,
maker,
SEN-10724,
sparkfun
Sunday, September 25, 2011
Arduino, Wire, and I2C Part 2: RTFDS
In my last post, I wrote a simple Arduino sketch that would get data from the ADXL345 accelerometer, but the "data" was all zeroes. This time around, I actually go about looking at the data sheet in more detail to learn how to properly get measurements from the accelerometer.
The first mention of enabling measurements is in the "Power Sequencing" section of the datasheet. The second paragraph indicates that to enter measurement mode, you must set the measurement bit in the POWER_CTL register at 0x2D. Another statement worth noting is that the manufacturer recommends configuring the device in standby mode and then enabling measurements. The datasheet also states that the device is in standby mode on start-up. The final bit of important information (for this project) in this section of the datasheet is tables 6 and 7, which contain the bit codes for data output rates.
The next section of the datasheet discusses power consumption as it relates to the data rate of the device. For this project, I'm not going to concern myself with how much power is being used as it's all being powered through the Arduino via USB (note that the Arduino does have a limited amount of supply current, as does USB, but this is in the mA range, where this device is at most using 1/10th of 1mA - 145μA).
Page 10 of the datasheet is where the I2C documentation starts, and that's more or less where I left off previously. Moving forward from here, we reach the section on interrupts. The SEN-10724 board does not have the interrupt pins (8 and 9) connected, so interrupts are not available. That said, the interrupt section does contain interesting information about what you could do with the sensor, if you chose to "wire up" your own board.
Page 13 is the first significant discussion of self-test mode for this device. This seems like a good place to start in terms of learning about the sensor and making sure the board and sensor are both functional. Clearly, there are a number of things to pay attention to. Enabling self-test involves setting a bit in the DATA_FORMAT register (0x31), but based on prior experience, I think it's safe to assume that more is involved than that. The next seven pages of the datasheet cover the details of each individual register, followed by even more information about self-test mode. A fair number of these registers are intended to set thresholds for interrupt functions and can therefore be ignored for any application involving the SEN-10724. For implementing a rudimentary test of the sensor using the Arduino, we need to look at:
With the above information in hand, I added the following constants to the previously posted Arduino sketch:
The resulting Arduino sketch, which is also available for direct download, executes and reports on a self-test of the ADXL345 accelerometer. If you open the serial monitor (set to 9600 baud) in the Arduino software, it will show the progress of the register settings (helpful debugging, and left-over from the HMC5883L version of the same, which has a slightly better datasheet IMO) and if the device pasts the tests (measurements are within ranges described in table 2 of the datasheet), it will print the measurements, followed by "OK", then turn off measurement and test mode. Otherwise, it will continue to show measurements and indicate which axis is reading out of spec.
... And that's it. Self test for the ADXL345 (and HMC5883L). The ITG-3200 does not have a self-test mode, so there won't be a follow-up for that.
The first mention of enabling measurements is in the "Power Sequencing" section of the datasheet. The second paragraph indicates that to enter measurement mode, you must set the measurement bit in the POWER_CTL register at 0x2D. Another statement worth noting is that the manufacturer recommends configuring the device in standby mode and then enabling measurements. The datasheet also states that the device is in standby mode on start-up. The final bit of important information (for this project) in this section of the datasheet is tables 6 and 7, which contain the bit codes for data output rates.
The next section of the datasheet discusses power consumption as it relates to the data rate of the device. For this project, I'm not going to concern myself with how much power is being used as it's all being powered through the Arduino via USB (note that the Arduino does have a limited amount of supply current, as does USB, but this is in the mA range, where this device is at most using 1/10th of 1mA - 145μA).
Page 10 of the datasheet is where the I2C documentation starts, and that's more or less where I left off previously. Moving forward from here, we reach the section on interrupts. The SEN-10724 board does not have the interrupt pins (8 and 9) connected, so interrupts are not available. That said, the interrupt section does contain interesting information about what you could do with the sensor, if you chose to "wire up" your own board.
Page 13 is the first significant discussion of self-test mode for this device. This seems like a good place to start in terms of learning about the sensor and making sure the board and sensor are both functional. Clearly, there are a number of things to pay attention to. Enabling self-test involves setting a bit in the DATA_FORMAT register (0x31), but based on prior experience, I think it's safe to assume that more is involved than that. The next seven pages of the datasheet cover the details of each individual register, followed by even more information about self-test mode. A fair number of these registers are intended to set thresholds for interrupt functions and can therefore be ignored for any application involving the SEN-10724. For implementing a rudimentary test of the sensor using the Arduino, we need to look at:
- DATA_FORMAT - register for enabling self-test mode
- BW_RATE - register for defining the data output rate
- POWER_CTL - register for turning measurements on
With the above information in hand, I added the following constants to the previously posted Arduino sketch:
The data rate must be at least 100Hz for proper self-test function, according to the datasheet, but the datasheet also indicates that this is the default data rate on power-up, and that low-power mode (which trades cleaner measurements for power levels) is not selected. As such, I'll leave the BW_RATE register alone for now. The datasheet also states that the measurement range be set to the full resolution 16g mode. Note that "full resolution" is a separate configuration bit in of itself. The bits for self-test mode and for the measurement range all reside in the DATA_FORMAT register, so updating that one register should be enough. The datasheet also recommends averaging samples. For this stage of the process, I'm not going to do that, for two reasons: 1) I'm a bit lazy, and 2) the atmega328 chip on which the Arduino UNO is based doesn't even have a divide instruction, so averaging would have to be done using a software implementation of add and sub instructions on the chip. Finally, as far as code changes for this stage of effort, I'm going to cut the delay between reading samples down to match the 100Hz sample rate. The sample rate of 100Hz means that there is an accelerometer measurement every 1/100th of a second, or every 10 ms.static const uint8_t ADXL345 = 0x53; static const uint8_t BW_RATE = 0x2C; static const uint8_t POWER_CTL = 0x2D; static const uint8_t BIT_MEASURE = 0x08; static const uint8_t DATA_FORMAT = 0x31; static const uint8_t BIT_SELF_TEST = (1 << 7); static const uint8_t BIT_RANGE_2G = 0x00; static const uint8_t BIT_RANGE_4G = 0x01; static const uint8_t BIT_RANGE_8G = 0x02; static const uint8_t BIT_RANGE_16G = 0x03; static const uint8_t BIT_FULL_RES = 0x08; static const uint8_t DATAX0 = 0x32;
The resulting Arduino sketch, which is also available for direct download, executes and reports on a self-test of the ADXL345 accelerometer. If you open the serial monitor (set to 9600 baud) in the Arduino software, it will show the progress of the register settings (helpful debugging, and left-over from the HMC5883L version of the same, which has a slightly better datasheet IMO) and if the device pasts the tests (measurements are within ranges described in table 2 of the datasheet), it will print the measurements, followed by "OK", then turn off measurement and test mode. Otherwise, it will continue to show measurements and indicate which axis is reading out of spec.
... And that's it. Self test for the ADXL345 (and HMC5883L). The ITG-3200 does not have a self-test mode, so there won't be a follow-up for that.
Labels:
9dof,
accelerometer,
arduino,
gyroscope,
i2c,
imu,
magnetometer,
maker,
SEN-10724,
sparkfun
Friday, September 23, 2011
Arduino, Wire, and I2C
In previous postings, I described connecting a sparkfun.com sensor board to the Arduino and getting magnetometer readings from it. In this post, I intend to go into a little more detail about how I2C works, while providing similar functionality using the accelerometer on the same board.
The first thing to understand is that I2C is a completely 8-bit bus protocol, which means that all addresses and all data are 8-bit quantities (0-255). The bus consists of a single "master" with up to 112 unique "slave" devices. The limit of 112 is due to the fact that a device address is actually a 7-bit quantity (more on this later), and 16 addresses are reserved. Multiple masters are apparently possible, but aren't relevant for this project. Slave devices can't talk to other slave devices. Off-the-shelf devices, like the three on the SEN-10724 board, have pre-assigned addresses managed by a central authority.
Devices have a 7-bit address. This 7-bit address is stored in the 7 MSB (most significant bits) of an 8-bit quantity when sequenced on the bus, with the least significant bit indicating whether the operation is a read or a write (1 or set being "read"). As an example, the HMC5883L datasheet lists 8-bit addresses 0x3D for read, and 0x3C for write. Convert these to binary and you'll find a 7-bit address of 0x1E followed by a binary 1 for read or 0 for write. Some manufacturers will list only a 7-bit address in their data sheet, some will list 8-bit addresses, some will do both.
The datasheet for the Analog Devices ADXL345 lists both - 0x1D is the 7-bit address, which results in 0x3A for writes and 0x3B for reads. However, it also supports an alternate addressing of 0x53/0xA6/0xA7 if pin 12 is tied to ground. The schematic for the sparkfun board indicates that pin 12 is indeed tied to ground, so let's assume that we're going to be using alternate addressing for that device.
Each I2C device will also have a number of registers that are readable and/or writable. Table 16 on page 14 of the ADXL345 datasheet lists this device's register map. For this project, we're most interested in registers 0x32-0x37. If you look at the table, you'll see how manufacturers design for 16-bit quantities, which is (at least in this case and in the case of the HMC5883L) to have adjacent 8-bit registers containing the most significant and least significant bytes of a 16-bit quantity.
Let's start with the X axis on the accelerometer, which according to the board's silkscreen is the side-to-side direction (along the short edge of the board). The following Arduino sketch implements a simple program that prints out the X axis acceleration once a second.
If you upload this sketch to your Arduino, with the IMU connected as described in the earlier post, you'll probably get a sequence of zeroes in the serial monitor. If, for example, you tried to change the address in the above code to the primary (0x1D), you probably wouldn't see anything at all. So why is it only showing zeroes, even if I shake the heck out of the board? My guess is that the device doesn't start up in a mode where continuous measurements are taken (which is how the HMC5883L operates), and that to get these measurements, other registers must be manipulated...
To be continued...
The first thing to understand is that I2C is a completely 8-bit bus protocol, which means that all addresses and all data are 8-bit quantities (0-255). The bus consists of a single "master" with up to 112 unique "slave" devices. The limit of 112 is due to the fact that a device address is actually a 7-bit quantity (more on this later), and 16 addresses are reserved. Multiple masters are apparently possible, but aren't relevant for this project. Slave devices can't talk to other slave devices. Off-the-shelf devices, like the three on the SEN-10724 board, have pre-assigned addresses managed by a central authority.
Devices have a 7-bit address. This 7-bit address is stored in the 7 MSB (most significant bits) of an 8-bit quantity when sequenced on the bus, with the least significant bit indicating whether the operation is a read or a write (1 or set being "read"). As an example, the HMC5883L datasheet lists 8-bit addresses 0x3D for read, and 0x3C for write. Convert these to binary and you'll find a 7-bit address of 0x1E followed by a binary 1 for read or 0 for write. Some manufacturers will list only a 7-bit address in their data sheet, some will list 8-bit addresses, some will do both.
The datasheet for the Analog Devices ADXL345 lists both - 0x1D is the 7-bit address, which results in 0x3A for writes and 0x3B for reads. However, it also supports an alternate addressing of 0x53/0xA6/0xA7 if pin 12 is tied to ground. The schematic for the sparkfun board indicates that pin 12 is indeed tied to ground, so let's assume that we're going to be using alternate addressing for that device.
Each I2C device will also have a number of registers that are readable and/or writable. Table 16 on page 14 of the ADXL345 datasheet lists this device's register map. For this project, we're most interested in registers 0x32-0x37. If you look at the table, you'll see how manufacturers design for 16-bit quantities, which is (at least in this case and in the case of the HMC5883L) to have adjacent 8-bit registers containing the most significant and least significant bytes of a 16-bit quantity.
Let's start with the X axis on the accelerometer, which according to the board's silkscreen is the side-to-side direction (along the short edge of the board). The following Arduino sketch implements a simple program that prints out the X axis acceleration once a second.
#include <Wire.h>
// Wire library uses 7-bit addresses and automatically
// sets the r/w bit
static const uint8_t ADXL345 = 0x53;
static const uint8_t DATAX0 = 0x32;
void setup()
{
Wire.begin();
Serial.begin(9600);
}
void loop()
{
// Put the read address on the bus
Wire.beginTransmission(ADXL345);
Wire.send(DATAX0);
Wire.endTransmission();
// Request data starting with the X register
Wire.beginTransmission(ADXL345);
// 2 bytes gets us the LSB and MSB of the X data
Wire.requestFrom(ADXL345, (uint8_t)2);
// store the data in a signed integer quantity
int16_t x;
// pointer to use to store the data
byte *p = (byte*)&x;
// wait for 2 bytes to be available
while (Wire.available() < 2) {}
*p = Wire.receive();
p++; // advance the pointer to the next 8 bits
*p = Wire.receive();
Wire.endTransmission();
Serial.println(x); // finally, print the quantity
delay(1000); // delay a bit to avoid flooding
}
If you upload this sketch to your Arduino, with the IMU connected as described in the earlier post, you'll probably get a sequence of zeroes in the serial monitor. If, for example, you tried to change the address in the above code to the primary (0x1D), you probably wouldn't see anything at all. So why is it only showing zeroes, even if I shake the heck out of the board? My guess is that the device doesn't start up in a mode where continuous measurements are taken (which is how the HMC5883L operates), and that to get these measurements, other registers must be manipulated...
To be continued...
Labels:
9dof,
accelerometer,
ADXL345,
arduino,
gyroscope,
HMC5883L,
i2c,
imu,
magnetometer,
maker,
SEN-10724,
sparkfun
Monday, September 12, 2011
"9" degrees of freedom IMU on Arduino
This past weekend I finally got around to playing with the "9 degrees of freedom" sensor board (SEN-10724) I'd purchased from sparkfun.com. I'm not sure if it makes sense to call it that, but what you get is a 3-axis gyro, a 3-axis accelerometer and a 3-axis magnetometer. The gyros give you information about rotational acceleration, the accelerometer measures linear acceleration, and the magnetometer works like a 3-axis compass.
The circuit off to the side there shows how I managed to get it hooked up and talking through my Arduino UNO. The short of it is this: it's a 3.3V device (well, 3 devices) and it needs pull-up resistors (I used 4.7K) on the data and clock lines in order to function. It's wired up to the analog inputs 4 and 5 on the Arduino board, which is what the provided "Wire" library uses to talk to devices like this, that use the I2C protocol.
Honestly, I'm a little bit unclear as to how it's actually able to talk to the sensor stick/IMU (inertial measurement unit) without doing level conversion between the 5V Arduino and the 3.3V IMU, but it does seem to work reliably in this configuration. If I were doing something more significant (a production board, for example) I'd probably be a bit more careful about matching the signal levels.
The Wire library for Arduino is just a basic library for talking to I2C devices. Getting into the specific interfaces is another matter, though for a quick start, I used the HMC588L compass library provided by Love Electronics in the UK. This was enough to get me started and verify that I was able to communicate with the magnetometer. There's a pretty decent tutorial on that page on how to use the library, though the sample works pretty well as-is. If you have trouble compiling the sample code, I found that for some reason it has a period (".") at the very beginning of the file - remove that period to make it compile.
Quick update - looking at the example .pde file in linux using hd (hex dump), it turned out there were three unprintable characters at the beginning of the file. The easiest way I found to fix the problem was to remove any odd characters before the /* at the start of the file, then save it as a new sketch. That new .pde file can then be used to replace the original example .pde file in the library, or you can just use the fixed version in your sketchbook.
The circuit off to the side there shows how I managed to get it hooked up and talking through my Arduino UNO. The short of it is this: it's a 3.3V device (well, 3 devices) and it needs pull-up resistors (I used 4.7K) on the data and clock lines in order to function. It's wired up to the analog inputs 4 and 5 on the Arduino board, which is what the provided "Wire" library uses to talk to devices like this, that use the I2C protocol.
Honestly, I'm a little bit unclear as to how it's actually able to talk to the sensor stick/IMU (inertial measurement unit) without doing level conversion between the 5V Arduino and the 3.3V IMU, but it does seem to work reliably in this configuration. If I were doing something more significant (a production board, for example) I'd probably be a bit more careful about matching the signal levels.
The Wire library for Arduino is just a basic library for talking to I2C devices. Getting into the specific interfaces is another matter, though for a quick start, I used the HMC588L compass library provided by Love Electronics in the UK. This was enough to get me started and verify that I was able to communicate with the magnetometer. There's a pretty decent tutorial on that page on how to use the library, though the sample works pretty well as-is. If you have trouble compiling the sample code, I found that for some reason it has a period (".") at the very beginning of the file - remove that period to make it compile.
Quick update - looking at the example .pde file in linux using hd (hex dump), it turned out there were three unprintable characters at the beginning of the file. The easiest way I found to fix the problem was to remove any odd characters before the /* at the start of the file, then save it as a new sketch. That new .pde file can then be used to replace the original example .pde file in the library, or you can just use the fixed version in your sketchbook.
Subscribe to:
Posts (Atom)




