/**************************************************************************************************
*
*  Accelerator LIS302DL - Module
*
*   Version:      1.0.0 - Januar 2009
*   Author:       Christoph Wartmann / chair for caad - ETH Zürich  /  wartmann[at].arch.ethz.ch
*                 Etienne Ribeiro    / tutorial assistant caad      /  eribeiro[at]ethz.ch
*                 (Based on code from BEN GATTI)
*
*   Desc:         Module to plug an LIS302DL Accelerator Sensor to your porjects. This Module makes
*                 it easy to run LIS302DL Accelerator in either +-2g or +- 8g mode.
*
*   Pins:         VCC: 3.3V
*                 GND
*                 SCL: to Pin 0 (SCL)
*                 MOSI: to Pin 1 (SDA)
*
*   Protocol:     I2C
*
*   Not yet implemented:
*                 - interrepts
*                 - click and double click recognition
*                 - high pass filter
*                 - SPI Interface
*                 - Self Test
*
*   Methodes:     void LIS302_iniWire()
*                    Initialize Wire. Must be called at startup.
*                 boolean LIS302_AppendSensor ()
*                    Append Sensor (using +-2g Mode).
*                 boolean LIS302_AppendSensor (boolean use8g)
*                    Append Sensor.
*                    Using +-2g or +-8g Mode
*                 void LIS302_ConfigR1 (boolean dataRate400Hz, boolean powerUp, boolean use8g, boolean enableSelfTestP, boolean enableSelfTestM, boolean enableZ, boolean enableY, boolean enableX)
*                    Configuration Register 1. See Datasheet if you want to change any settings.
*                 void LIS302_ConfigR2 (boolean useSPI3WireMode, boolean rebootMemory, boolean enabledFilteredData, boolean enableFreeFallAndWakeUp02, boolean enableFreeFallAndWakeUp01, byte highPassFrequency)
*                    Configuration Register 2. See Datasheet if you want to change any settings.
*                 void LIS302_ConfigR3 (boolean interruptActiveLow, boolean interreptOpenDrain, byte int2ControlBits, byte int1ControlBits)
*                    Configuration Register 3. See Datasheet if you want to change any settings.
*                 byte LIS302_Read(char which)
*                    Read values
*                    'x', 'y', 'z': Acceleration
*                    's': Current state of Sensor
*                    'a', 'b', 'c': Get Configuration
*                    'w': Get whoAmI. should be 3B
*                 int LIS302_ParseToG (int val)
*                    Parse value to G. Call val = LIS3L_Read('x') and LIS3L_ParseToG(val).
*                 void LIS302_PrintState (byte state)
*                    Print current state of your sensor to command line. Call state = LIS3L_Read ('s') and LIS3L_PrintState(state)
*
*
*
*   TWI (I2C) sketch to communicate with the LIS302DL accelerometer - Modified and tested Ben Gatti - 6/9/2008
*   http://www.st.com/stonline/products/literature/ds/12726.pdf
*   Note 5.6K pullup resister on data lines.
*   Device is 3 volt was tested at 5 volts no level shifting.
*   Reference claims 6 volts is pin max.
*
*   Modified from // TWI (I2C) sketch to communicate with the LIS302V02DQ accelerometer
*   http://www.nearfuturelaboratory.com/2007/01/11/arduino-and-twi/
*   Modified code from http://research.techkwondo.com/blog/julian/279
*   Thanks Julian.
*
***************************************************************************************************/


#include <TwoWire.h>



//
// Const

//
#define whoAmI 0x0F
#define reg1 0x20
#define reg2 0x21
#define reg3 0x22
#define stateReg 0x27
#define outXhigh 0x29
#define outYhigh 0x2B
#define outZhigh 0x2D
//
#define LIS302_i2cID 0x1D // 0x1C (Pin 12 must be LOW) or 0x1D (Pin 12 must be HIGH)
#define LIS302_id 59 // 0x3B
#define LIS302_2g_factor 18 // Divide value from register with this value to get 'g'
#define LIS302_8g_factor 72 // Divide value from register with this value to get 'g'


//
// Var

boolean LIS302_2g_mode = true;




//
// iniWire

void LIS302_iniWire() {

        Wire.begin(); // join i2c bus (address optional for master)

}



boolean LIS302_AppendSensor () {

        LIS302_AppendSensor (false); // +-2g

}
boolean LIS302_AppendSensor (boolean use8g) {

        // Config Device
        boolean powerup = true;
        LIS302_ConfigR1 (false, powerup, use8g, false, false, true, true, true); // power up, use8g
        LIS302_ConfigR2 (false, false, false, false, false, 0);
        LIS302_ConfigR3 (false, false, 0, 0);


        // query the WHO_AM_I register of the LIS302DL
        // this should return 0x3B, a factory setting
        byte val = LIS302_Read ('w');
        if (val != LIS302_id)
        return false;


        // Return
        return true;

}





//
// LIS302_ConfigR1
//  default: false, false, false, false, false, true, true, true

void LIS302_ConfigR1 (boolean dataRate400Hz, boolean powerUp, boolean use8g, boolean enableSelfTestP, boolean enableSelfTestM, boolean enableZ, boolean enableY, boolean enableX) {

        byte cmd = B00000000;
        if (dataRate400Hz)       // switch data rate for mesurement between 100Hz (default) and 400Hz
        cmd |= B10000000;
        if (powerUp)             // power up/down the device
        cmd |= B01000000;
        if (use8g) {             // switch between +-2g and +-8g mode
                cmd |= B00100000;
                LIS302_2g_mode = false;
        } else {
                LIS302_2g_mode = true;
        }
        if (enableSelfTestP)     // enable selftest P
        cmd |= B00010000;
        if (enableSelfTestM)     // enable selftest M
        cmd |= B00001000;
        if (enableZ)             // enable Z
        cmd |= B00000100;
        if (enableY)             // enable Y
        cmd |= B00000010;
        if (enableX)             // enable X
        cmd |= B00000001;


        // Send command
        Wire.beginTransmission(LIS302_i2cID);
        Wire.send(reg1); //
        Wire.send(cmd);
        Wire.endTransmission();

}


//
// LIS302_ConfigR2
//  default: false, false, false, false, false, 0

void LIS302_ConfigR2 (boolean useSPI3WireMode, boolean rebootMemory, boolean enabledFilteredData, boolean enableFreeFallAndWakeUp02, boolean enableFreeFallAndWakeUp01, byte highPassFrequency) {

        byte cmd = B00000000;
        if (useSPI3WireMode)               // if using SPI-Protocoll (not I2C) you can switch between 4-wire (default) and 3-wire mode
        cmd |= B10000000;
        if (rebootMemory)                  // Reboot Memory (Reload Registers from flash memory, same is done at power up)
        cmd |= B01000000;
        if (enabledFilteredData)           // enable high pass filter in the signal chain of the sensor
        cmd |= B00010000;
        if (enableFreeFallAndWakeUp02)     // High Pass filter enabled for FreeFall/WakeUp # 2.
        cmd |= B00001000;
        if (enableFreeFallAndWakeUp01)     // High Pass filter enabled for FreeFall/WakeUp # 1.
        cmd |= B00000100;
        if (highPassFrequency == 0)        // 2Hz (if dataRate = 100Hz) / 8Hz (if dataRate = 400Hz) (default)
        cmd |= B00000000;
        if (highPassFrequency == 1)        // 1Hz (if dataRate = 100Hz) / 4Hz (if dataRate = 400Hz)
        cmd |= B00000001;
        if (highPassFrequency == 2)        // 0.5Hz (if dataRate = 100Hz) / 2Hz (if dataRate = 400Hz) (default)
        cmd |= B00000010;
        if (highPassFrequency == 3)        // 0.25Hz (if dataRate = 100Hz) / 1Hz (if dataRate = 400Hz) (default)
        cmd |= B00000011;


        // Send command
        Wire.beginTransmission(LIS302_i2cID);
        Wire.send(reg2); //
        Wire.send(cmd);
        Wire.endTransmission();

}


//
// LIS302_ConfigR3
//  default: false, false, 0, 0

void LIS302_ConfigR3 (boolean interruptActiveLow, boolean interreptOpenDrain, byte int2ControlBits, byte int1ControlBits) {

        byte cmd = B00000000;
        if (interruptActiveLow)       // interrupt is active if HIGH (default) or LOW
        cmd |= B10000000;
        if (interreptOpenDrain)       // Push-pull (default) /Open Drain selection on interrupt pad
        cmd |= B01000000;

        if (int2ControlBits == 0)     // Int2 Pad = GND (default)
        cmd |= B00000000;
        if (int2ControlBits == 1)     // Int2 Pad = FF_WU_1
        cmd |= B00001000;
        if (int2ControlBits == 2)     // Int2 Pad = FF_WU_2
        cmd |= B00010000;
        if (int2ControlBits == 3)     // Int2 Pad = FF_WU_1 or FF_WU_2
        cmd |= B00011000;
        if (int2ControlBits == 4)     // Int2 Pad = Data ready
        cmd |= B00100000;
        if (int2ControlBits == 5)     // Int2 Pad = Click interrupt
        cmd |= B00111000;

        if (int1ControlBits == 0)     // Int1 Pad = GND (default)
        cmd |= B00000000;
        if (int1ControlBits == 1)     // Int1 Pad = FF_WU_1
        cmd |= B00000001;
        if (int1ControlBits == 2)     // Int1 Pad = FF_WU_2
        cmd |= B00000010;
        if (int1ControlBits == 3)     // Int1 Pad = FF_WU_1 or FF_WU_2
        cmd |= B00000011;
        if (int1ControlBits == 4)     // Int1 Pad = Data ready
        cmd |= B00000100;
        if (int1ControlBits == 5)     // Int1 Pad = Click interrupt
        cmd |= B00000111;


        // Send command
        Wire.beginTransmission(LIS302_i2cID);
        Wire.send(reg3); //
        Wire.send(cmd);
        Wire.endTransmission();

}



//
// Read
//   'x', 'y', 'z', 's'->state, 'w'->whoAmI (8 Bit Dummie Identification for LIS302DL), 'a'->config Reg1, 'b'->config Reg2, 'c'->config Reg3

byte LIS302_Read(char which) {

        // Send register you want to read from
        Wire.beginTransmission(LIS302_i2cID);
        switch(which) {

                case 'x':
                case 'X':

                Wire.send(outXhigh);
                break;

                case 'y':
                case 'Y':

                Wire.send(outYhigh);
                break;

                case 'z':
                case 'Z':

                Wire.send(outZhigh);
                break;

                case 's':
                case 'S':

                Wire.send(stateReg);
                break;

                case 'w':
                case 'W':

                Wire.send(whoAmI);
                break;

                case 'a':
                case 'A':

                Wire.send(reg1);
                break;

                case 'b':
                case 'B':

                Wire.send(reg2);
                break;

                case 'c':
                case 'C':

                Wire.send(reg3);
                break;

                default:

                Wire.endTransmission();
                return 0;
                break;

        }
        Wire.endTransmission();

        // Read command
        Wire.requestFrom(LIS302_i2cID, 1);

        // return
        while(!Wire.available())
        delay(10);
        return Wire.receive();

}



//
// LIS302_ParseToG
//   Parse Values from Register to mG

int LIS302_ParseToG (int val) {

        long tmpval = val;
        if (LIS302_2g_mode)
        return tmpval * 1000 / LIS302_2g_factor;
        return tmpval * 1000 / LIS302_8g_factor;

}

void LIS302_PrintState (byte state) {

        Serial.print("Status: ");
        Serial.print(state, BIN);
        Serial.print(": ");
        if (int(1<<7 & state) != 0) // byte 1000 0000
        Serial.print (" data over written!");
        if (int(1<<6 & state) != 0) // byte 0100 0000
        Serial.print (" data over written by Z!");
        if (int(1<<5 & state) != 0) // byte 0010 0000
        Serial.print (" data over written by Y!");
        if (int(1<<4 & state) != 0) // byte 0001 0000
        Serial.print (" data over written by X!");
        if (int(1<<3 & state) != 0) // byte 0000 1000
        Serial.print (" new data available!");
        if (int(1<<2 & state) != 0) // byte 0000 0100
        Serial.print (" new data available for Z!");
        if (int(1<<1 & state) != 0) // byte 0000 0010
        Serial.print (" new data available for X!");
        if (int(1<<0 & state) != 0) // byte 0000 0001
        Serial.print (" new data available for Y!");


}