This program acquires data from an ADIS16355 IMU via the SPI bus and sends the data to a host computer via UDP over Ethernet.

Dependencies:   mbed StrippedDownNetServices

imu-spi.cpp

Committer:
yahugh
Date:
2012-05-31
Revision:
0:79f663186c05

File content as of revision 0:79f663186c05:

//
// imu-spi.cpp
//
// copyright 2010 Hugh Shane
//
#include "mbed.h"
#include "imu-spi.h"

ImuSpi::ImuSpi(void) :
    spi(p11, p12, p13), // mosi, miso, sclk (Note: mbed uses SSPx for the SPI busses)
    cs(p16),            // IMU chip select, active low
    reset(p15),         // IMU reset, active low. Also resets the altimeter.
    diag(p17),          // diagnostic output
    imuDataReady(p14),  // IMU data-ready interrupt input, active falling edge
    onePPS(p19)         // GPS 1 PPS interrupt input, active rising edge
{
    cs = 1; // set to quiescent state
    diag = 0;
    imuDataReady.mode(PullUp); // just to be safe
    sequenceNumber = 0;
    imuReadIndex = 0;

    // Initialize the IMU command sequence. Note: register 0x00 is a dummy 
    // value that triggers the last read in a burst cycle.
    int8_t regVals[] = {0x04,0x06,0x08,0x0A,0x0C,0x0E,0x3C,0x00};
    for (int i = 0; i < bufferSize; i++) imuRegs[i] = regVals[i];
    
    // init the pingpong buffer
    pingpong = 0;
    
    // init the buffer ready semaphore
    bufferReady = false;
    
    // Setup the SPI bus for 16 bit data, high steady state clock,
    // second edge capture, with a 1 MHz clock rate
    spi.format(16,3);
    spi.frequency(1000000);    
  
    // init the IMU
    InitImu();
    wait_us(100); // allow things to settle before starting interrupts   
    
    // Connect the IMU data-ready signal to its interrupt handler
    imuDataReady.fall(this, &ImuSpi::IMUDataReadyISR);
    
    // Connect the onePPS signal to its interrupt handler
    imuDataReady.rise(this, &ImuSpi::OnePPSISR);
  
}


// initialize the IMU
void ImuSpi::InitImu(void) {
    // deselect the IMU
    cs = 1;
    reset = 1;
    // perform a hard reset
    reset = 0; wait(.1); reset = 1; wait(.1);   
    // send initialization commands
    WriteSynch(0x34, 0x04); // enable active low data ready on DIO1
    WriteSynch(0x39, 0x01); // Set the gyro sensitivity to 75 deg/sec
    WriteSynch(0x38, 0x00); // Set the FIR filter to its widest possible bandwidth
    
}

// When the IMU has data ready, trigger a burst read.
// Launch an interrupt-driven process to read the IMU 
// accelerometer and gyro registers into the pingpong buffer.
void ImuSpi::IMUDataReadyISR(void) {
    // initialize the IMU data buffer
    wp = GetBufferWritePtr();
    imuCmdIndex = 0;
    imuReadIndex = 0;
    
    // reject spurious interrupts
    if (imuDataReady == 0) {      
        // set up the first register read operation
        SendReadCmd(imuRegs[imuCmdIndex++]);
    }

}

// Start an asynchronous (non-blocking) read of the IMU
// register designated by 'adr'
void ImuSpi::SendReadCmd(int8_t adr) {
    int16_t cmd = 0x3f00 & (((int16_t)adr) << 8);
    cs = 0;
    LPC_SSP0->DR = cmd;
    
    // start the timer that triggers the next command write
    writeTrigger.attach_us(this, &ImuSpi::WriteTriggerTimeoutISR, (16+9)); 

}

// handle the write trigger timeout interrupt
void ImuSpi::WriteTriggerTimeoutISR(void) {
diag = 1;
    // buffer the contents of the receive data register that was acquired 
    // during the prior command write
    *wp++ = (int16_t)(LPC_SSP0->DR);
    
    // if we've acquired an entire buffer of data, signal the foreground task
    if (imuReadIndex++ >= dataSize) {
        int16_t* p = GetBufferWritePtr();
        p[0] = sequenceNumber++; // buffer[0] has garbage, replace with the sequence number
        bufferReady = true;
        cs = 1; // deassert the IMU chip select
    } else {
        // we haven't yet read all the IMU registers, trigger the next read 
        SendReadCmd(imuRegs[imuCmdIndex++]);
    }    
diag = 0;  
}

// The foreground process uses this method to determine if 
// if a burst read of the IMU is complete. If read was 
// completed, the ping-pong buffers are toggled so that the
// most recent data is made available to the foreground
// process.
bool ImuSpi::IsBufferReady(void) {
    bool ret;
    
    if (bufferReady == true) {
        __disable_irq();    // ---- critical
        TogglePingpong();
        bufferReady = false;
        __enable_irq();     // ---- \critical
        ret = true;
    } else {
        ret = false;
    }
    
    return ret;
}

// perform a synchronous (blocking) read of the IMU
// register designated by 'adr'
int16_t ImuSpi::ReadSynch(char adr) {
    int16_t cmd = 0x3f00 & (((int16_t)adr) << 8);
    int16_t response;
    cs = 0;
    // (1) command a read of the desired register while reading back garbage
    spi.write(cmd);
    // (2) command a read of register 0x00 while reading back the desired register 
    response = spi.write(0x3f00); 
    cs = 1;
    return response;
}

// perform a synchronous (blocking) write of 'data' to the IMU
// register designated by 'adr'
void ImuSpi::WriteSynch(char adr, char data) {
    int16_t cmd = 0x8000 | (((int16_t)adr) << 8) | data;
    cs = 0;
    spi.write(cmd);
    cs = 1;
    wait_us(9);
}

// When the GPS 1 PPS signal is asserted, clear the sequence number.
// This is how IMU data is synched with GPS data.
void ImuSpi::OnePPSISR(void) {
    sequenceNumber = 0;
}