Greetings,
I'm using an Arduino Mega to log data from an ADIS16405 IMU, a product of Analog Devices. The communication uses the SPI Protocol, and my procedure is:
- Request burst of data from IMU
- Read data from IMU and convert to readable numbers
- Write data to Serial port (logger or computer serial monitor)
There is no "checksum" and so the way I check for valid data is to confirm it makes sense. For example, the supply voltage should always be about 5 v. If it is far off, then I know the data was corrupted and declare it "invalid".
The problem: every couple seconds, I get two invalid data responses. However, the interesting part is that the how often this occurs is affected by the baud rate in step 3. For example, if I'm writing the data out at 115,200 bps then I get invalid IMU data almost every 3 seconds. If instead, I write data at 9600 bps, then I get NO invalid data at all. I tried to write at the faster speed, but add a delay to mimic the longer write time, but the invalid data still appeared.
I've attached my code for reference below. Interestingly, here (Inertial Measurement Unit SPI Troubles - Interfacing - Arduino Forum) is someone who had similar problems with another version of the Analog Devices IMU.
Any suggestions on how to write fast, but avoid invalid data? It seems odd that their performance is connected, since one is serial and the other is SPI.
-Hamid
const int reset_pin = 6;
const int chipSelectPin = 7;
boolean read_FLAG = false;
#include <SPI.h>
#include "datatype.h"
imu isense_data;
void setup(){
Serial1.begin(921600); // for debugging
// Start SPI Library and setup for ADIS 16405
pinMode(chipSelectPin, OUTPUT);
digitalWrite(chipSelectPin, HIGH); // disable device to start with
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV8);
// Defulat on Mega is (16 / 4 Mhz
// ADIS 16405 should be <= 2 Mhz
SPI.setDataMode(SPI_MODE3);
// Initializ reset pin and set to 5v
pinMode(reset_pin, OUTPUT);
digitalWrite(reset_pin, HIGH);
SPI.begin();
delay(1000);
}
void loop(){
delay(10);
read_imu(&isense_data);
}
void read_imu(struct imu *imuData_ptr ){
// References: http://ez.analog.com/message/54453#54453
// Send register address
digitalWrite(chipSelectPin, LOW);
// Burst Mode DIN sequence: 0x3E00
SPI.transfer(0x3E);
SPI.transfer(0x00);
digitalWrite(chipSelectPin, HIGH);
// UMN UAV Code would delay here to wait for IMU to respond, but
// testing showed with or without delay things working fine.
// delayMicroseconds(1); // If using delay, revist the delay value
// Initialize variables. Burst mode, according to documentation, should
// output 12 messages, starting with SUPPLY_OUT and ending with AUX_ADC.
// However, testing (and UAV code) showed 13 messages are needed in order
// that the last message's "ND flag" is zero, indicating no more data to be read.
const int responseLength = 13*2;
unsigned int tmp;
int outputData[13];
byte response[responseLength]={0};
// Read Burst Mode Data
// Note: for correct operation on the Mega, it was observed that the on/off of the
// chip select pin MUST occur inside the loop. Doing otherwise causes intermittent
// data corruption. The reason for this is partly unclear to me.
for (int k = 0; k < responseLength; k++) {
digitalWrite(chipSelectPin, LOW);
response[k] = SPI.transfer(0x00); // send a value of 0 to read the first byte returned:
digitalWrite(chipSelectPin, HIGH);
}
/*
for (int k = 0; k < responseLength; k++) {
Serial.print(response[k], BIN);
Serial.println(" ");
} */
// All inertial sensor outputs are in 14-bit, twos complement format
// Combine data bytes; each regsister covers two bytes, but uses only 14 bits.
for ( int i = 0; i < responseLength; i += 2 ) {
// From the two byte message, the upper byte arrived
// first and then the lower byte. Hence, we combine
// the first 6 bits of the upper byte with the lower
// byte into an unsigned int.
tmp = (response[i] & 0x3F) * 256 + response[i+1];
// Next, convert the 14 bit unsigned int into the
// two's complement format and store as a (signed) int.
// This if statement does exactly that.
if ( tmp > 0x1FFF ) {
outputData[i/2] = (int)(-(0x4000 - tmp));
}
else {
outputData[i/2] = (int)tmp;
}
}
// set status flag if supply voltage is within 4.75 to 5.25
if ((outputData[0]*0.002418 > 4.25) && (outputData[0]*0.002418 < 5.25)){
imuData_ptr->err_type = data_valid;
// update imupacket
// *IMP* IMU axis alignment is different: Z=-Z_body, Y=-Y_body
imuData_ptr->Vs = outputData[0]*0.002418; //unit: Volt
imuData_ptr->p = outputData[1]*0.05; //unit: deg/s
imuData_ptr->q = outputData[2]*0.05;
imuData_ptr->r = outputData[3]*0.05;
imuData_ptr->ax = outputData[4]*0.00333; //unit: g's
imuData_ptr->ay = outputData[5]*0.00333;
imuData_ptr->az = outputData[6]*0.00333;
imuData_ptr->hx = outputData[7]*0.0005; //unit: Gauss
imuData_ptr->hy = outputData[8]*0.0005;
imuData_ptr->hz = outputData[9]*0.0005;
imuData_ptr->T = 25.0 + outputData[10]*0.14; //unit: Degrees Celcius
imuData_ptr->adc = outputData[11]*0.000806; //unit: Volt
//Serial.print("Time: ");
Serial1.print(millis());
Serial1.print("\t");
//Serial1.print("Supply Voltage: ");
Serial1.print(imuData_ptr->Vs);
Serial1.print("\t");
//Print Accelerometer outputs
Serial1.print(imuData_ptr->ax);
Serial1.print("\t");
Serial1.print(imuData_ptr->ay);
Serial1.print("\t");
Serial1.print(imuData_ptr->az);
Serial1.print("\t");
Serial1.print(imuData_ptr->p);
Serial1.print("\t");
Serial1.print(imuData_ptr->q);
Serial1.print("\t");
Serial1.print(imuData_ptr->r);
Serial1.print("\n");
} else {
unsigned long t = millis();
Serial1.print("#-- invalid -- ");
Serial1.println(t);
}
//delay(50);
}