//*****************************************************************************
//
// acquire.c - Data acquisition module for data logger application.
//
// Copyright (c) 2011-2015 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
// 
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
// 
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
// 
// This is part of revision 2.1.2.111 of the DK-TM4C123G Firmware Package.
//
//*****************************************************************************

#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include "driverlib/debug.h"
#include "driverlib/adc.h"
#include "driverlib/gpio.h"
#include "driverlib/hibernate.h"
#include "driverlib/interrupt.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "grlib/grlib.h"
#include "grlib/widget.h"
#include "grlib/canvas.h"
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "inc/hw_ints.h"
#include "inc/hw_gpio.h"
#include "utils/ustdlib.h"
#include "drivers/slidemenuwidget.h"
#include "drivers/stripchartwidget.h"
#include "stripchartmanager.h"
#include "clocksetwidget.h"
#include "qs-logger.h"
#include "usbstick.h"
#include "usbserial.h"
#include "flashstore.h"
#include "menus.h"
#include "acquire.h"
#include "sensorlib/hw_mpu9150.h"
#include "sensorlib/hw_ak8975.h"
#include "sensorlib/i2cm_drv.h"
#include "sensorlib/ak8975.h"
#include "sensorlib/mpu9150.h"
#include "sensorlib/comp_dcm.h"
#include "driverlib/pin_map.h"

//*****************************************************************************
//
// This is the data acquisition module.  It performs acquisition of data from
// selected channels, starting and stopping data logging, storing acquired
// data, and running the strip chart display.
//
//*****************************************************************************

//*****************************************************************************
//
// Define MPU9150 I2C Address.
//
//*****************************************************************************
#define MPU9150_I2C_ADDRESS  0x69

//*****************************************************************************
//
// The following defines which ADC channel control should be used for each
// kind of data item.  Basically it maps how the ADC channels are connected
// on the board.  This is a hardware configuration.
//
// The Accelerometer, Gyro, and Compass info is brought across I2C3 as a 
// single data chunk an then unpacked.
//
//*****************************************************************************
#define CHAN_USER0              ADC_CTL_CH0
#define CHAN_USER1              ADC_CTL_CH1
#define CHAN_USER2              ADC_CTL_CH2
#define CHAN_USER3              ADC_CTL_CH3
#define CHAN_EXTTEMP            ADC_CTL_CH20
#define CHAN_CURRENT            ADC_CTL_CH23
#define CHAN_INTTEMP            ADC_CTL_TS

//*****************************************************************************
//
// The following are indexes for values returned from the ADC's. These index's
// map directly to g_pui32ADCSeq[]
//
//*****************************************************************************
#define ADCSEQ_USER0            0
#define ADCSEQ_USER1            1
#define ADCSEQ_USER2            2
#define ADCSEQ_USER3            3
#define ADCSEQ_EXTTEMP          4
#define ADCSEQ_INTTEMP          5
#define ADCSEQ_CURRENT          6

//*****************************************************************************
//
// The following maps the order that items are acquired and stored by the
// ADC sequencers.  Note that 16 samples are specified, using 2 of the
// 8 sample sequencers.  The current is sampled multiple times deliberately
// because that value tends to bounce around.  It is sampled multiple
// times and will be averaged.
//
//*****************************************************************************
uint32_t g_pui32ADCSeq[] =
{
    CHAN_USER0, CHAN_USER1, CHAN_USER2, CHAN_USER3, CHAN_EXTTEMP, CHAN_INTTEMP, 
    CHAN_CURRENT, CHAN_CURRENT,CHAN_CURRENT, CHAN_CURRENT, CHAN_CURRENT, 
    CHAN_CURRENT, CHAN_CURRENT,
};
#define NUM_ADC_CHANNELS        (sizeof(g_pui32ADCSeq) /                      \
                                 sizeof(g_pui32ADCSeq[0]))
#define NUM_CURRENT_SAMPLES     7

//*****************************************************************************
//
// Global storage for most recent Sensor Data
//
//*****************************************************************************
//
// Index values for sensor data
//
#define X 0
#define Y 1
#define Z 2
//
// Floating point data from SensorLib code
//
float g_pfAccel[3]; 
float g_pfGyro[3];
float g_pfMag[3];
//
// 16bit values post translation from floating point
//
int16_t g_i16Accel[3];
int16_t g_i16Gyro[3];
int16_t g_i16Mag[3];

//*****************************************************************************
//
// Global instance structure for the I2C master driver.
//
//*****************************************************************************
tI2CMInstance g_sI2CInst;

//*****************************************************************************
//
// Global instance structure for the MPU9150 sensor driver.
//
//*****************************************************************************
tMPU9150 g_sMPU9150Inst;

//*****************************************************************************
//
// Global flags to alert main that MPU9150 I2C transaction is complete
//
//*****************************************************************************
volatile uint_fast8_t g_vui8I2CDoneFlag;

//*****************************************************************************
//
// Global flags to alert main that MPU9150 I2C transaction error has occurred.
//
//*****************************************************************************
volatile uint_fast8_t g_vui8ErrorFlag;

//*****************************************************************************
//
// Global flags to alert main that MPU9150 data is ready to be retrieved.
//
//*****************************************************************************
volatile uint_fast8_t g_vui8DataFlag;

//*****************************************************************************
//
// A buffer to hold one set of ADC data that is acquired per sample time.
//
//*****************************************************************************
static uint32_t g_pui32ADCData[NUM_ADC_CHANNELS];

//*****************************************************************************
//
// The following variables hold the current time stamp, the next match time
// for sampling, and the period of time between samples.  All are stored in
// a 32.15 second.subsecond format.
//
//*****************************************************************************
static volatile uint32_t g_pui32TimeStamp[2];
static volatile uint32_t g_pui32NextMatch[2];
static uint32_t g_pui32MatchPeriod[2];

//*****************************************************************************
//
// The number of data items that are selected for acquisition.
//
//*****************************************************************************
static uint32_t g_ui32NumItems;

//*****************************************************************************
//
// A counter for the ADC interrupt handler.  It is used to track when new
// ADC data is acquired.
//
//*****************************************************************************
static volatile uint32_t g_ui32ADCCount;
static uint32_t g_ui32LastADCCount = 0;

//*****************************************************************************
//
// A counter for the RTC interrupt handler.
//
//*****************************************************************************
static volatile uint32_t g_pui32RTCInts;

//*****************************************************************************
//
// A flag to indicate that a keep alive packet is needed (when logging to host
// PC).
//
//*****************************************************************************
static volatile bool g_bNeedKeepAlive = false;

//*****************************************************************************
//
// Storage for a single record of acquired data.  This needs to be large
// enough to hold the time stamp and item mask (defined in the structure
// above) and as many possible data items that can be collected.  Force the
// buffer to be a multiple of 32-bits.
//
//*****************************************************************************
#define RECORD_SIZE             (sizeof(tLogRecord) + (NUM_LOG_ITEMS * 2))
static union
{
    uint32_t g_pui32RecordBuf[(RECORD_SIZE + 3) / sizeof(uint32_t)];
    tLogRecord sRecord;
}
g_sRecordBuf;

//*****************************************************************************
//
// Holds a pointer to the current configuration state, that is determined by
// the user's menu selections.
//
//*****************************************************************************
static tConfigState *g_psConfigState;

//*****************************************************************************
//
// MPU9150 Sensor callback function.  Called at the end of MPU9150 sensor
// driver transactions. This is called from I2C interrupt context. Therefore,
// we just set a flag and let main do the bulk of the computations and display.
//
//*****************************************************************************
void
MPU9150AppCallback(void *pvCallbackData, uint_fast8_t ui8Status)
{
    //
    // If the transaction succeeded set the data flag to indicate to
    // application that this transaction is complete and data may be ready.
    //
    if(ui8Status == I2CM_STATUS_SUCCESS)
    {
        g_vui8I2CDoneFlag = 1;
    }

    //
    // Store the most recent status in case it was an error condition
    //
    g_vui8ErrorFlag = ui8Status;
}

//*****************************************************************************
//
// Function to wait for the MPU9150 transactions to complete. Use this to spin
// wait on the I2C bus.
//
//*****************************************************************************
void
MPU9150AppI2CWait(char *pcFilename, uint_fast32_t ui32Line)
{
    //
    // Put the processor to sleep while we wait for the I2C driver to
    // indicate that the transaction is complete.
    //
    while((g_vui8I2CDoneFlag == 0) && (g_vui8ErrorFlag == 0))
    {
        //
        // Do Nothing
        //
    }

    //
    // If an error occurred call the error handler immediately.
    //
    if(g_vui8ErrorFlag)
    {
        //MPU9150AppErrorHandler(pcFilename, ui32Line);
        //UARTprintf("ERROR in MPU9150AppErrorHandler\n");
    }

    //
    // clear the data flag for next use.
    //
    g_vui8I2CDoneFlag = 0;
}

//*****************************************************************************
//
// Called by the NVIC as a result of GPIO port B interrupt event. For this
// application GPIO port B pin 2 is the interrupt line for the MPU9150
//
//*****************************************************************************
void
IntGPIOb(void)
{
    unsigned long ulStatus;

    ulStatus = GPIOIntStatus(GPIO_PORTB_BASE, true);

    //
    // Clear all the pin interrupts that are set
    //
    GPIOIntClear(GPIO_PORTB_BASE, ulStatus);

    if(ulStatus & GPIO_PIN_2)
    {
        //
        // MPU9150 Data is ready for retrieval and processing.
        //
        MPU9150DataRead(&g_sMPU9150Inst, MPU9150AppCallback, &g_sMPU9150Inst);
    }
}

//*****************************************************************************
//
// Called by the NVIC as a result of I2C3 Interrupt. I2C3 is the I2C connection
// to the MPU9150.
//
//*****************************************************************************
void
MPU9150I2CIntHandler(void)
{
    //
    // Pass through to the I2CM interrupt handler provided by sensor library.
    // This is required to be at application level so that I2CMIntHandler can
    // receive the instance structure pointer as an argument.
    //
    I2CMIntHandler(&g_sI2CInst);
}

//*****************************************************************************
//
// This function is called when in VIEW mode.  The acquired data is written
// as text strings which will appear on the eval board display.
//
//*****************************************************************************
static void
UpdateViewerData(const tLogRecord *psRecord)
{
    static char pcViewerBuf[24];
    uint32_t ui32Idx, pui32RTC;
    struct tm sTime;

    //
    // Loop through the analog channels and update the text display strings.
    //
    for(ui32Idx = LOG_ITEM_USER0; ui32Idx <= LOG_ITEM_USER3; ui32Idx++)
    {
        usnprintf(pcViewerBuf, sizeof(pcViewerBuf), " CH%u: %u.%03u V ",
                  ui32Idx - LOG_ITEM_USER0, psRecord->pi16Items[ui32Idx] / 1000,
                  psRecord->pi16Items[ui32Idx] % 1000);
        MenuUpdateText(ui32Idx, pcViewerBuf);
    }

    //
    // Loop through the accel channels and update the text display strings.
    //
    for(ui32Idx = LOG_ITEM_ACCELX; ui32Idx <= LOG_ITEM_ACCELZ; ui32Idx++)
    {
        int16_t i16Accel = psRecord->pi16Items[ui32Idx];
        i16Accel *= (i16Accel < 0) ? -1 : 1;
        usnprintf(pcViewerBuf, sizeof(pcViewerBuf), " %c: %c%d.%02u g ",
                  (ui32Idx - LOG_ITEM_ACCELX) + 'X',
                  psRecord->pi16Items[ui32Idx] < 0 ? '-' : '+',
                  i16Accel / 100, i16Accel % 100);
        MenuUpdateText(ui32Idx, pcViewerBuf);
    }

    //
    // Update the display string for internal temperature.
    //
    usnprintf(pcViewerBuf, sizeof(pcViewerBuf), " INT: %d.%01u C ",
              psRecord->pi16Items[LOG_ITEM_INTTEMP] / 10,
              psRecord->pi16Items[LOG_ITEM_INTTEMP] % 10);
    MenuUpdateText(LOG_ITEM_INTTEMP, pcViewerBuf);

    //
    // Update the display string for external temperature.
    //
    usnprintf(pcViewerBuf, sizeof(pcViewerBuf), " EXT: %d.%01u C ",
              psRecord->pi16Items[LOG_ITEM_EXTTEMP] / 10,
              psRecord->pi16Items[LOG_ITEM_EXTTEMP] % 10);
    MenuUpdateText(LOG_ITEM_EXTTEMP, pcViewerBuf);

    //
    // Update the display string for processor current.
    //
    usnprintf(pcViewerBuf, sizeof(pcViewerBuf), " %u.%01u mA ",
              psRecord->pi16Items[LOG_ITEM_CURRENT] / 10,
              psRecord->pi16Items[LOG_ITEM_CURRENT] % 10);
    MenuUpdateText(LOG_ITEM_CURRENT, pcViewerBuf);

    //
    // Update the display strings for time and data.
    //
    pui32RTC = HibernateRTCGet();
    ulocaltime(pui32RTC, &sTime);
    usnprintf(pcViewerBuf, sizeof(pcViewerBuf), "%4u/%02u/%02u",
              sTime.tm_year+1900, sTime.tm_mon + 1, sTime.tm_mday);
    MenuUpdateText(TEXT_ITEM_DATE, pcViewerBuf);
    usnprintf(pcViewerBuf, sizeof(pcViewerBuf), "%02u:%02u:%02u",
              sTime.tm_hour, sTime.tm_min, sTime.tm_sec);
    MenuUpdateText(TEXT_ITEM_TIME, pcViewerBuf);
    
    //
    // Loop through the gyro channels and update the text display strings.
    //
    for(ui32Idx = LOG_ITEM_GYROX; ui32Idx <= LOG_ITEM_GYROZ; ui32Idx++)
    {
        int16_t i16Gyro = psRecord->pi16Items[ui32Idx];
        i16Gyro *= (i16Gyro < 0) ? -1 : 1;
        usnprintf(pcViewerBuf, sizeof(pcViewerBuf), " %c: %c%d.%02u  rad/s",
                  (ui32Idx - LOG_ITEM_GYROX) + 'X',
                  psRecord->pi16Items[ui32Idx] < 0 ? '-' : '+',
                  i16Gyro / 100, i16Gyro % 100);
        MenuUpdateText(ui32Idx, pcViewerBuf);
    }
    
    //
    // Loop through the mag channels and update the text display strings.
    //
    for(ui32Idx = LOG_ITEM_COMPASSX; ui32Idx <= LOG_ITEM_COMPASSZ; ui32Idx++)
    {
        int16_t i16Compass = psRecord->pi16Items[ui32Idx];
        i16Compass *= (i16Compass < 0) ? -1 : 1;
        usnprintf(pcViewerBuf, sizeof(pcViewerBuf), " %c: %c%d uT ",
                  (ui32Idx - LOG_ITEM_COMPASSX) + 'X',
                  psRecord->pi16Items[ui32Idx] < 0 ? '-' : '+',
                  i16Compass);
        MenuUpdateText(ui32Idx, pcViewerBuf);
    }
}


//*****************************************************************************
//
// This function is called from the AcquireRun() function and should be in
// context of the main thread.  It pulls data items from the ADC data buffer,
// and the I2C 9 axis data, converts units as needed, and stores the results 
// in a log record that is pointed at by the function parameter.
//
//*****************************************************************************
static void
ProcessDataItems(tLogRecord *psRecord)
{
    int32_t i32TempC;
    uint32_t ui32SelectedMask, ui32Millivolts, ui32Current;
    uint_fast8_t ui8Idx, ui8ItemIdx;

    //
    // Initialize locals.
    //
    ui8ItemIdx = 0;
    ui32SelectedMask = g_psConfigState->ui16SelectedMask;
    ui32Current = 0;
    
    //
    // Save the time stamp that was saved when the ADC data was acquired.
    // Also save into the record the bit mask of the selected data items.
    //
    psRecord->ui32Seconds = g_pui32TimeStamp[0];
    psRecord->ui16Subseconds = (uint16_t)g_pui32TimeStamp[1];
    psRecord->ui16ItemMask = (uint16_t)ui32SelectedMask;

    //
    // Process the user analog input channels.  These will be converted and
    // stored as millivolts.
    //
    for(ui8Idx = LOG_ITEM_USER0; ui8Idx <= LOG_ITEM_USER3; ui8Idx++)
    {
        //
        // Check to see if this item should be logged
        //
        if((1 << ui8Idx) & ui32SelectedMask)
        {
            ui32Millivolts = (g_pui32ADCData[ui8Idx] * 4100) / 819;
            psRecord->pi16Items[ui8ItemIdx++] = (int16_t)ui32Millivolts;
        }
    }

    //
    // Process the accelerometer data, because the data all comes across
    // I2C in one chunk if any accelerometer is desired just go ahead and 
    // process them all to save time. 
    //
    // floating point data provided in m/s^2.
    // divide by 9.81 to convert m/s^2 to g's
    // multiply by 100 to provide 2 decimal points of precision.
    //
    if( ((1 << LOG_ITEM_ACCELX) & ui32SelectedMask) | 
        ((1 << LOG_ITEM_ACCELY) & ui32SelectedMask) |
        ((1 << LOG_ITEM_ACCELZ) & ui32SelectedMask) )
    {
        // Get floating point version of the Accel Data in m/s^2.
        MPU9150DataAccelGetFloat(&g_sMPU9150Inst, &g_pfAccel[X], &g_pfAccel[Y],
                                     &g_pfAccel[Z]);

        g_i16Accel[0]= (int16_t) ((g_pfAccel[0] /9.81f)*100.f);
        g_i16Accel[1]= (int16_t) ((g_pfAccel[1] /9.81f)*100.f);
        g_i16Accel[2]= (int16_t) ((g_pfAccel[2] /9.81f)*100.f);
        // Log selected data to the record        
        if((1 << LOG_ITEM_ACCELX) & ui32SelectedMask){
            psRecord->pi16Items[ui8ItemIdx++] = g_i16Accel[X];}
        if((1 << LOG_ITEM_ACCELY) & ui32SelectedMask){
            psRecord->pi16Items[ui8ItemIdx++] = g_i16Accel[Y];}
        if((1 << LOG_ITEM_ACCELZ) & ui32SelectedMask){
            psRecord->pi16Items[ui8ItemIdx++] = g_i16Accel[Z];}
    }

    //
    // Process the external temperature. The temperature is stored in units
    // of 1/10 C.
    //
    if((1 << LOG_ITEM_EXTTEMP) & ui32SelectedMask)
    {
        i32TempC = (1866300 - ((200000 * g_pui32ADCData[ADCSEQ_EXTTEMP]) /
                                273)) / 1169;
        psRecord->pi16Items[ui8ItemIdx++] = (int16_t)i32TempC;
    }

    //
    // Process the internal temperature. The temperature is stored in units
    // of 1/10 C.
    //
    if((1 << LOG_ITEM_INTTEMP) & ui32SelectedMask)
    {
        i32TempC = 1475 - ((2250 * g_pui32ADCData[ADCSEQ_INTTEMP]) / 4095);
        psRecord->pi16Items[ui8ItemIdx++] = (int16_t)i32TempC;
    }

    //
    // Process the current. The current is stored in units of 100 uA,
    // (or 1/10000 A).  Multiple current samples were taken in order
    // to average and smooth the data.
    //
    if((1 << LOG_ITEM_CURRENT) & ui32SelectedMask)
    {
        //
        // Average all the current samples that are available in the ADC
        // buffer.
        //
        for(ui8Idx = ADCSEQ_CURRENT;
            ui8Idx < (ADCSEQ_CURRENT + NUM_CURRENT_SAMPLES); ui8Idx++)
        {
            ui32Current += g_pui32ADCData[ui8Idx];
        }
        ui32Current /= NUM_CURRENT_SAMPLES;

        //
        // Convert the averaged current into units
        //
        ui32Current = (ui32Current * 200) / 273;
        psRecord->pi16Items[ui8ItemIdx++] = (int16_t)ui32Current;
    }

    //
    // Process the Gyro data, because the data all comes across
    // I2C in one chunk if any gyro is desired just go ahead and 
    // process them all to save time. 
    //
    // floating data provided in rad/sec
    // multiply by 100 to provide 2 decimal points of precision.
    //
    if( ((1 << LOG_ITEM_GYROX) & ui32SelectedMask) | 
        ((1 << LOG_ITEM_GYROY) & ui32SelectedMask) |
        ((1 << LOG_ITEM_GYROZ) & ui32SelectedMask) )
    {
        // Get floating point version of angular velocities in rad/sec
        MPU9150DataGyroGetFloat(&g_sMPU9150Inst, &g_pfGyro[X], &g_pfGyro[Y],
                                     &g_pfGyro[Z]);

        g_i16Gyro[X]= (int16_t) (g_pfGyro[X] *100.f);
        g_i16Gyro[Y]= (int16_t) (g_pfGyro[Y] *100.f);
        g_i16Gyro[Z]= (int16_t) (g_pfGyro[Z] *100.f);

        // Log selected data to the record        
        if((1 << LOG_ITEM_GYROX) & ui32SelectedMask){
            psRecord->pi16Items[ui8ItemIdx++] = g_i16Gyro[X];}
        if((1 << LOG_ITEM_GYROY) & ui32SelectedMask){
            psRecord->pi16Items[ui8ItemIdx++] = g_i16Gyro[Y];}
        if((1 << LOG_ITEM_GYROZ) & ui32SelectedMask){
            psRecord->pi16Items[ui8ItemIdx++] = g_i16Gyro[Z];}
    }

    //
    // Process the Compass data, because the data all comes across
    // I2C in one chunk if any Compass is desired just go ahead and 
    // process them all to save time. 
    //
    // floating point data provided in tesla 
    // multiply by 1,000,000 to human friendly micro tesla unit
    if( ((1 << LOG_ITEM_COMPASSX) & ui32SelectedMask) | 
        ((1 << LOG_ITEM_COMPASSY) & ui32SelectedMask) |
        ((1 << LOG_ITEM_COMPASSZ) & ui32SelectedMask) )
    {
        // Get floating point version of magnetic fields strength in tesla
        MPU9150DataMagnetoGetFloat(&g_sMPU9150Inst, &g_pfMag[X], &g_pfMag[Y],
                                     &g_pfMag[Z]);

        //convert to micro-tesla
        g_i16Mag[X]= (int16_t) (g_pfMag[X] *1000000.f);
        g_i16Mag[Y]= (int16_t) (g_pfMag[Y] *1000000.f);
        g_i16Mag[Z]= (int16_t) (g_pfMag[Z] *1000000.f);
        
        // Log selected data to the record
        if((1 << LOG_ITEM_COMPASSX) & ui32SelectedMask){
            psRecord->pi16Items[ui8ItemIdx++] = g_i16Mag[X];}
        if((1 << LOG_ITEM_COMPASSY) & ui32SelectedMask){
            psRecord->pi16Items[ui8ItemIdx++] = g_i16Mag[Y];}
        if((1 << LOG_ITEM_COMPASSZ) & ui32SelectedMask){
            psRecord->pi16Items[ui8ItemIdx++] = g_i16Mag[Z];}
    }


    
}

//*****************************************************************************
//
// This is the handler for the ADC interrupt.  Even though more than one
// sequencer is used, they are configured so that this one runs last.
// Therefore when this ADC sequencer interrupt occurs, we know all of the ADC
// data has been acquired.
//
//*****************************************************************************
void
ADC0SS0Handler(void)
{
    //
    // Clear the interrupts for all ADC sequencers that are used.
    //
    MAP_ADCIntClear(ADC0_BASE, 0);
    MAP_ADCIntClear(ADC1_BASE, 0);

    //
    // Retrieve the data from all ADC sequencers
    //
    MAP_ADCSequenceDataGet(ADC0_BASE, 0, &g_pui32ADCData[0]);
    MAP_ADCSequenceDataGet(ADC1_BASE, 0, &g_pui32ADCData[8]);

    //
    // Set the time stamp, assume it is what was set for the last match
    // value.  This will be close to the actual time that the samples were
    // acquired, within a few microseconds.
    //
    g_pui32TimeStamp[0] = g_pui32NextMatch[0];
    g_pui32TimeStamp[1] = g_pui32NextMatch[1];

    //
    // Increment the ADC interrupt count
    //
    g_ui32ADCCount++;
}

//*****************************************************************************
//
// This is the handler for the RTC interrupt from the hibernate peripheral.
// It occurs on RTC match.  This handler will initiate an ADC acquisition,
// which will run all of the ADC sequencers.  Then it computes the next
// match value and sets it in the RTC.
//
//*****************************************************************************
void
RTCHandler(void)
{
    uint32_t ui32Status, ui32Seconds;

    //
    // Increment RTC interrupt counter
    //
    g_pui32RTCInts++;

    //
    // Clear the RTC interrupts (this can be slow for hib module)
    //
    ui32Status = HibernateIntStatus(1);
    HibernateIntClear(ui32Status);

    //
    // Read and save the current value of the seconds counter.
    //
    ui32Seconds = HibernateRTCGet();

    //
    // If we are sleep logging, then there will be no remembered value for
    // the next match value, which is also used as the time stamp when
    // data is collected.  In this case we will just use the current
    // RTC seconds.  This is safe because if sleep-logging is used, it
    // is only with periods of whole seconds, 1 second or longer.
    //
    if(g_psConfigState->ui32SleepLogging)
    {
        g_pui32NextMatch[0] = ui32Seconds;
        g_pui32NextMatch[1] = 0;
    }

    //
    // If we are logging data to PC and using a period greater than one
    // second, then use special handling.  For PC logging, if no data is
    // collected, then we must send a keep-alive packet once per second.
    //
    if((g_psConfigState->ui8Storage == CONFIG_STORAGE_HOSTPC) &&
       (g_pui32MatchPeriod[0] > 1))
    {
        //
        // If the current seconds count is less than the match value, that
        // means we got the interrupt due to one-second keep alive for the
        // host PC.
        //
        if(ui32Seconds < g_pui32NextMatch[0])
        {
            //
            // Set the next match for one second ahead (next keep-alive)
            //
            HibernateRTCMatchSet(0, ui32Seconds + 1);

            //
            // Set flag to indicate that a keep alive packet is needed
            //
            g_bNeedKeepAlive = true;

            //
            // Nothing else to do except wait for next keep alive or match
            //
            return;
        }

        //
        // Else, this is a real match so proceed to below to do a normal
        // acquisition.
        //
    }

    //
    // Kick off the next ADC acquisition.  When these are done they will
    // cause an ADC interrupt.
    //
    MAP_ADCProcessorTrigger(ADC1_BASE, 0);
    MAP_ADCProcessorTrigger(ADC0_BASE, 0);

    //
    // Set the next RTC match.  Add the match period to the previous match
    // value.  We are making an assumption here that there is enough time from
    // when the match interrupt occurred, to this point in the code, that we
    // are still setting the match time in the future.  If the period is too
    // long, then we could miss a match and never get another RTC interrupt.
    //
    g_pui32NextMatch[0] += g_pui32MatchPeriod[0];
    g_pui32NextMatch[1] += g_pui32MatchPeriod[1];
    if(g_pui32NextMatch[1] > 32767)
    {
        //
        // Handle subseconds rollover
        //
        g_pui32NextMatch[1] &= 32767;
        g_pui32NextMatch[0]++;
    }

    //
    // If logging to host PC at greater than 1 second period, then set the
    // next RTC wakeup for 1 second from now.  This will cause a keep alive
    // packet to be sent to the PC
    //
    if((g_psConfigState->ui8Storage == CONFIG_STORAGE_HOSTPC) &&
       (g_pui32MatchPeriod[0] > 1))
    {
        HibernateRTCMatchSet(0, ui32Seconds + 1);
    }
    else
    {
        //
        // Otherwise this is a normal match and the next match should also be a
        // normal match, so set the next wakeup to the calculated match time.
        //
        HibernateRTCMatchSet(0, g_pui32NextMatch[0]);
        HibernateRTCSSMatchSet(0, g_pui32NextMatch[1]);
    }

    //
    // Toggle the LED on the board so the user can see that the acquisition
    // is running.
    //
    MAP_GPIOPinWrite(GPIO_PORTG_BASE, GPIO_PIN_2,
                     ~MAP_GPIOPinRead(GPIO_PORTG_BASE, GPIO_PIN_2));

    //
    // Now exit the int handler.  The ADC will trigger an interrupt when
    // it is finished, and the RTC is set up for the next match.
    //
}

//*****************************************************************************
//
// This function is called from the application main loop to keep the
// acquisition running.  It checks to see if there is any new ADC data, and
// if so it processes the new ADC data.
//
// The function returns non-zero if data was acquired, 0 if no data was
// acquired.
//
//*****************************************************************************
int32_t
AcquireRun(void)
{
    tLogRecord *psRecord = &g_sRecordBuf.sRecord;

    //
    // Make sure we are properly configured to run
    //
    if(!g_psConfigState)
    {
        return(0);
    }

    //
    // Check to see if new ADC data is available
    //
    if((g_ui32ADCCount != g_ui32LastADCCount) & (g_vui8I2CDoneFlag ==1) )
    {

        g_ui32LastADCCount = g_ui32ADCCount;

        //
        // Process the ADC data and store it in the record buffer.
        //
        ProcessDataItems(psRecord);

        //
        // Reset data ready flag for I2C accel/gyro/compass data-
        //
        g_vui8I2CDoneFlag=0;

        //
        // Add the newly processed data to the strip chart.  Do not add to
        // strip start if sleep-logging.
        //
        if((g_psConfigState->ui8Storage != CONFIG_STORAGE_VIEWER) &&
           !g_psConfigState->ui32SleepLogging)
        {
            StripChartMgrAddItems(psRecord->pi16Items);
        }

        //
        // If USB stick is used, write the record to the USB stick
        //
        if(g_psConfigState->ui8Storage == CONFIG_STORAGE_USB)
        {
            USBStickWriteRecord(psRecord);
        }

        //
        // If host PC is used, write data to USB serial port
        //
        if(g_psConfigState->ui8Storage == CONFIG_STORAGE_HOSTPC)
        {
            USBSerialWriteRecord(psRecord);
        }

        //
        // If flash storage is used, write data to the flash
        //
        if(g_psConfigState->ui8Storage == CONFIG_STORAGE_FLASH)
        {
            FlashStoreWriteRecord(psRecord);

            //
            // If we are sleep logging, then save the storage address for
            // use in the next cycle.
            //
            if(g_psConfigState->ui32SleepLogging)
            {
                g_psConfigState->ui32FlashStore = FlashStoreGetAddr();
            }
        }
        else if(g_psConfigState->ui8Storage == CONFIG_STORAGE_VIEWER)
        {
            //
            // If in viewer mode, then update the viewer text strings.
            //
            UpdateViewerData(psRecord);
        }

        //
        // Return indication to caller that data was processed.
        //
        return(1);
    }
    else if((g_psConfigState->ui8Storage == CONFIG_STORAGE_HOSTPC) &&
            (g_bNeedKeepAlive == true))
    {
        //
        // Else there is no new data to process, but check to see if we are
        // logging to PC and a keep alive packet is needed.
        //
        // Clear keep-alive needed flag
        //
        g_bNeedKeepAlive = false;

        //
        // Make a keep alive packet by creating a record with timestamp of 0.
        //
        psRecord->ui32Seconds = 0;
        psRecord->ui16Subseconds = 0;
        psRecord->ui16ItemMask = 0;

        //
        // Transmit the dummy record to host PC.
        //
        USBSerialWriteRecord(psRecord);
    }

    //
    // Return indication that data was not processed.
    //
    return(0);
}

//*****************************************************************************
//
// This function is called to start an acquisition running.  It determines
// which channels are to be logged, enables the ADC/I2C sequencers, and computes
// the first RTC match value.  This will start the acquisition running.
//
//*****************************************************************************
void
AcquireStart(tConfigState *psConfig)
{
    uint32_t ui32Idx, pui32RTC[2], ui32SelectedMask;

    //
    // Check the parameters
    //
    ASSERT(psConfig);
    if(!psConfig)
    {
        return;
    }

    //
    // Update the config state pointer, save the selected item mask
    //
    g_psConfigState = psConfig;
    ui32SelectedMask = psConfig->ui16SelectedMask;

    //
    // Get the logging period from the logger configuration.  Split the
    // period into seconds and subseconds pieces and save for later use in
    // generating RTC match values.
    //
    g_pui32MatchPeriod[0] = psConfig->ui32Period >> 8;
    g_pui32MatchPeriod[1] = (psConfig->ui32Period & 0xFF) << 8;

    //
    // Determine how many channels are to be logged
    //
    ui32Idx = ui32SelectedMask;
    g_ui32NumItems = 0;
    while(ui32Idx)
    {
        if(ui32Idx & 1)
        {
            g_ui32NumItems++;
        }
        ui32Idx >>= 1;
    }

    //
    // Initialize the strip chart manager for a new run.  Don't bother with
    // the strip chart if we are using viewer mode, or sleep-logging.
    //
    if((psConfig->ui8Storage != CONFIG_STORAGE_VIEWER) &&
       !psConfig->ui32SleepLogging)
    {
        StripChartMgrInit();
        StripChartMgrConfigure(ui32SelectedMask);
    }

    //
    // Configure USB for memory stick if USB storage is chosen
    //
    if(psConfig->ui8Storage == CONFIG_STORAGE_USB)
    {
        USBStickOpenLogFile(0);
    }
    else if(psConfig->ui8Storage == CONFIG_STORAGE_FLASH)
    {

        //
        // Flash storage is to be used, prepare the flash storage module.
        // If already sleep-logging, then pass in the saved flash address
        // so it does not need to be searched.
        //
        if(psConfig->ui32SleepLogging)
        {
            FlashStoreOpenLogFile(psConfig->ui32FlashStore);
        }
        else
        {
            //
            // Otherwise not sleep logging, so just initialize the flash store,
            // this will cause it to search for the starting storage address.
            //
            FlashStoreOpenLogFile(0);
        }
    }

    //
    // Enable the ADC sequencers
    //
    MAP_ADCSequenceEnable(ADC0_BASE, 0);
    MAP_ADCSequenceEnable(ADC1_BASE, 0);

    //
    // Flush the ADC sequencers to be sure there is no lingering data.
    //
    MAP_ADCSequenceDataGet(ADC0_BASE, 0, g_pui32ADCData);
    MAP_ADCSequenceDataGet(ADC1_BASE, 0, g_pui32ADCData);

    //
    // Enable ADC interrupts
    //
    MAP_ADCIntClear(ADC0_BASE, 0);
    MAP_ADCIntClear(ADC1_BASE, 0);
    MAP_ADCIntEnable(ADC0_BASE, 0);
    MAP_IntEnable(INT_ADC0SS0);

    //
    // Enable data ready interrupt from accel/gyro/compass
    //
    MAP_IntEnable(INT_GPIOB);



    //
    // If we are not already sleep-logging, then initialize the RTC match.
    // If we are sleep logging then this does not need to be set up.
    //
    if(!psConfig->ui32SleepLogging)
    {
        //
        // Get the current RTC value
        //
        do
        {
            pui32RTC[0] = HibernateRTCGet();
            pui32RTC[1] = HibernateRTCSSGet();
        }
        while(pui32RTC[0] != HibernateRTCGet());

        //
        // Set an initial next match value.  Start with the subseconds always
        // 0 so the first match value will always be an even multiple of the
        // subsecond match.  Add 2 seconds to the current RTC just to be clear
        // of an imminent rollover.  This means that the first match will occur
        // between 1 and 2 seconds from now.
        //
        g_pui32NextMatch[0] = pui32RTC[0] + 2;
        g_pui32NextMatch[1] = 0;

        //
        // Now set the match value
        //
        HibernateRTCMatchSet(0, g_pui32NextMatch[0]);
        HibernateRTCSSMatchSet(0, g_pui32NextMatch[1]);
    }

    //
    // If we are configured to sleep, but not sleeping yet, then enter sleep
    // logging mode if allowed.
    //
    if(psConfig->bSleep && !psConfig->ui32SleepLogging)
    {
        //
        // Allow sleep logging if storing to flash at a period of 1 second
        // or greater.
        //
        if((psConfig->ui8Storage == CONFIG_STORAGE_FLASH) &&
           (psConfig->ui32Period >= 0x100))
        {
            psConfig->ui32SleepLogging = 1;
        }
    }

    //
    // Enable the RTC interrupts from the hibernate module
    //
    HibernateIntClear(HibernateIntStatus(0));
    HibernateIntEnable(HIBERNATE_INT_RTC_MATCH_0 | HIBERNATE_INT_PIN_WAKE);
    MAP_IntEnable(INT_HIBERNATE);

    //
    // Logging data should now start running
    //
}

//*****************************************************************************
//
// This function is called to stop an acquisition running.  It disables the
// ADC sequencers and the RTC match interrupt.
//
//*****************************************************************************
void
AcquireStop(void)
{
    //
    // Disable RTC interrupts
    //
    MAP_IntDisable(INT_HIBERNATE);
    
    //
    // Disable 9axis IRQ signal
    //
    MAP_IntDisable(INT_GPIOB);

    //
    // Disable ADC interrupts
    //
    MAP_IntDisable(INT_ADC0SS0);
    MAP_IntDisable(INT_ADC1SS0);

    //
    // Disable ADC sequencers
    //
    MAP_ADCSequenceDisable(ADC0_BASE, 0);
    MAP_ADCSequenceDisable(ADC1_BASE, 0);

    //
    // If USB stick is being used, then close the file so it will flush
    // the buffers to the USB stick.
    //
    if(g_psConfigState->ui8Storage == CONFIG_STORAGE_USB)
    {
        USBStickCloseFile();
    }

    //
    // Disable the configuration pointer, which acts as a flag to indicate
    // if we are properly configured for data acquisition.
    //
    g_psConfigState = 0;
}

//*****************************************************************************
//
// This function initializes the ADC hardware in preparation for data
// acquisition.
//
//*****************************************************************************
void
AcquireInit(void)
{
    uint32_t ui32Chan, ui32Base, ui32Seq, ui32ChCtl;


    //
    // Enable the ADC peripherals and the associated GPIO port
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC1);
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOP);

    //
    // Enable I2C & associated GPIO ports
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C3);
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

    //
    // Configure I2C Pins
    //
    MAP_GPIOPinConfigure(GPIO_PD0_I2C3SCL);
    MAP_GPIOPinConfigure(GPIO_PD1_I2C3SDA);
    MAP_GPIOPinTypeI2CSCL(GPIO_PORTD_BASE, GPIO_PIN_0);
    MAP_GPIOPinTypeI2C(GPIO_PORTD_BASE, GPIO_PIN_1);
    MAP_GPIOPinTypeGPIOInput(GPIO_PORTB_BASE, GPIO_PIN_2);
    GPIOIntEnable(GPIO_PORTB_BASE, GPIO_PIN_2);
    MAP_GPIOIntTypeSet(GPIO_PORTB_BASE, GPIO_PIN_2, GPIO_FALLING_EDGE);
    MAP_IntEnable(INT_GPIOB);
    
    //
    // Enabled LED GPIO
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);
    MAP_GPIOPinTypeGPIOOutput(GPIO_PORTG_BASE, GPIO_PIN_2);

    //
    // Configure the pins to be used as analog inputs.
    //
    MAP_GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 |
                   GPIO_PIN_7 | GPIO_PIN_3);
    MAP_GPIOPinTypeADC(GPIO_PORTP_BASE, GPIO_PIN_0);

    //
    // Select the external reference for greatest accuracy.
    //
    MAP_ADCReferenceSet(ADC0_BASE, ADC_REF_EXT_3V);
    MAP_ADCReferenceSet(ADC1_BASE, ADC_REF_EXT_3V);

    //
    // Apply workaround for erratum 6.1, in order to use the
    // external reference.
    //
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    HWREG(GPIO_PORTB_BASE + GPIO_O_AMSEL) |= GPIO_PIN_6;

    //
    // Initialize both ADC peripherals using sequencer 0 and processor trigger.
    //
    MAP_ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_PROCESSOR, 0);
    MAP_ADCSequenceConfigure(ADC1_BASE, 0, ADC_TRIGGER_PROCESSOR, 0);

    //
    // Enter loop to configure all of the ADC sequencer steps needed to
    // acquire the data for the data logger.  Multiple ADC and sequencers
    // will be used in order to acquire all the channels.
    //
    for(ui32Chan = 0; ui32Chan < NUM_ADC_CHANNELS; ui32Chan++)
    {
        //
        // If this is the first ADC then set the base for ADC0
        //
        if(ui32Chan < 8)
        {
            ui32Base = ADC0_BASE;
            ui32Seq = 0;
        }
        else if(ui32Chan < 16)
        {
            //
            // Second ADC, set the base for ADC1
            //
            ui32Base = ADC1_BASE;
            ui32Seq = 0;
        }

        //
        // Get the channel control for each channel.  Test to see if it is the
        // last channel for the sequencer, and if so then also set the
        // interrupt and "end" flags.
        //
        ui32ChCtl = g_pui32ADCSeq[ui32Chan];
        if((ui32Chan == 7) || (ui32Chan == 15) ||
           (ui32Chan == (NUM_ADC_CHANNELS - 1)))
        {
            ui32ChCtl |= ADC_CTL_IE | ADC_CTL_END;
        }

        //
        // Configure the sequence step
        //
        MAP_ADCSequenceStepConfigure(ui32Base, ui32Seq, ui32Chan % 8,
                                      ui32ChCtl);
    }

    //
    // Initialize I2C3 Peripheral
    //
    I2CMInit(&g_sI2CInst, I2C3_BASE, INT_I2C3, 0xff, 0xff,
             MAP_SysCtlClockGet());

    //
    // Initialize the MPU9150 Driver.
    //
    MPU9150Init(&g_sMPU9150Inst, &g_sI2CInst, MPU9150_I2C_ADDRESS,
                MPU9150AppCallback, &g_sMPU9150Inst);

    //
    // Wait for transaction to complete
    //
    MPU9150AppI2CWait(__FILE__, __LINE__);

    //
    // Write application specifice sensor configuration such as filter settings
    // and sensor range settings.
    //
    g_sMPU9150Inst.pui8Data[0] = MPU9150_CONFIG_DLPF_CFG_94_98;
    g_sMPU9150Inst.pui8Data[1] = MPU9150_GYRO_CONFIG_FS_SEL_250;
    g_sMPU9150Inst.pui8Data[2] = (MPU9150_ACCEL_CONFIG_ACCEL_HPF_5HZ |
                                  MPU9150_ACCEL_CONFIG_AFS_SEL_2G);
    MPU9150Write(&g_sMPU9150Inst, MPU9150_O_CONFIG, g_sMPU9150Inst.pui8Data, 3,
                 MPU9150AppCallback, &g_sMPU9150Inst);
    
    //
    // Wait for transaction to complete
    //
    MPU9150AppI2CWait(__FILE__, __LINE__);

    //
    // Configure the data ready interrupt pin output of the MPU9150.
    //
    g_sMPU9150Inst.pui8Data[0] = MPU9150_INT_PIN_CFG_INT_LEVEL |
                                    MPU9150_INT_PIN_CFG_INT_RD_CLEAR |
                                    MPU9150_INT_PIN_CFG_LATCH_INT_EN;
    g_sMPU9150Inst.pui8Data[1] = MPU9150_INT_ENABLE_DATA_RDY_EN;
    MPU9150Write(&g_sMPU9150Inst, MPU9150_O_INT_PIN_CFG,
                 g_sMPU9150Inst.pui8Data, 2, MPU9150AppCallback,
                 &g_sMPU9150Inst);

    //
    // Wait for transaction to complete
    //
    MPU9150AppI2CWait(__FILE__, __LINE__);

    //
    // Initialize the DCM system. 50 hz sample rate.
    // accel weight = .2, gyro weight = .8, mag weight = .2
    //
    //CompDCMInit(&g_sCompDCMInst, 1.0f / 50.0f, 0.2f, 0.6f, 0.2f);

    //
    // Erase the configuration in case there was a prior configuration.
    //
    g_psConfigState = 0;
}
