/***************************************************
  Arduino library for the Allegro A1335 Magnetic angle sensor
  The A1335 sensor detects the absolute angular position of a 
  permanent magnet, typically a diametrically
  magnetized cylinder on the rotating shaft.

  * by Florian von Bertrab
 ****************************************************/

#include "A1335.h"


bytes_2::bytes_2(){
  data.integer = 0;
}

bytes_2::bytes_2(int16_t integ){
  data.integer = integ;
}

int16_t&   bytes_2::in(){
  return data.integer;
}

byte&      bytes_2::lsBy(byte n){
  if (n > 1) {
    n = 1;
  } else if (n < 0) {
    n = 0;
  }
  return data.bytes[n];
}

byte&      bytes_2::msBy(byte n){
  if (n > 1) {
    n = 1;
  } else if (n < 0) {
    n = 0;
  }
  return data.bytes[1-n];
}

bytes_4::bytes_4(){
  data.integer = 0;
}

bytes_4::bytes_4(int32_t integ){
  data.integer = integ;
}

int32_t&   bytes_4::in(){
  return data.integer;
}

byte&      bytes_4::lsBy(byte n){
  if (n > 3) {
    n = 3;
  } else if (n < 0) {
    n = 0;
  }
  return data.bytes[n];
}

byte&      bytes_4::msBy(byte n){
  if (n > 3) {
    n = 3;
  } else if (n < 0) {
    n = 0;
  }
  return data.bytes[3-n];
}

//--- Normal Write Registers ---//

const byte EWA    = 0x02;   // Extended Write Address
const byte EWD    = 0x04;   // Extended Write Data
const byte EWCS   = 0x08;   // Extended Write Control and Status
const byte ERA    = 0x0A;   // Extended Read Address
const byte ERCS   = 0x0C;   // Extended Read Control and Status
const byte ERD    = 0x0E;   // Extended Read Data
const byte CTRL   = 0x1E;   // Device control
const byte ANG    = 0x20;   // Current angle and related data
const byte STA    = 0x22;   // Device status
const byte ERR    = 0x24;   // Device error status
const byte XERR   = 0x26;   // Extended error status
const byte TSEN   = 0x28;   // Temperature sensor data
const byte FIELD  = 0x2A;   // Magnetic field strength
const byte ERM    = 0x34;   // Device error status masking
const byte XERM   = 0x36;   // Extended error status masking

//--- Extended Write Registers ---//

const int16_t ORATE = 0xFFD0;  // Output Rate


// Control bytes and their respective keycodes:

//                      CTRL(0x1E) KEY(0x1F)
//  Processor State
const byte ipm[]    = { B10000000, 0x46     }; // Idle mode
const byte rpm[]    = { B11000000, 0x46     }; // Run  mode
//  Hard Reset
const byte hre[]    = { B00100000, 0xB9     }; // Hard reset
//  Soft Reset
const byte sre[]    = { B00010000, 0xB9     }; // Soft reset
//  Clear STA
const byte csta[]   = { B00000100, 0x46     }; // Clear (STA)  registers
//  Hard Reset
const byte cxerr[]  = { B00000010, 0x46     }; // Clear (XERR) registers
//  Hard Reset
const byte cerr[]   = { B00000001, 0x46     }; // Clear (ERR)  registers
 

// Angle Register read masks

//                      ANG(0x20)  ANG+1(0x21)
// Register Identifier Code
const byte ria[]    = { B10000000, B00000000}; // Always 0
// Error Flag
const byte efa[]    = { B01000000, B00000000}; // At least one error in register 0x24
// New Flag
const byte nfa[]    = { B00100000, B00000000}; // A new angle is in the angle register
// Parity
const byte par[]    = { B00010000, B00000000}; // Odd parity bit for the whole register
// Angle
const byte ang[]    = { B00001111, B11111111}; // Encoded angle reading (n * 360/4096 = angle in deg.)


// Status Register read masks

//                      STA(0x20)  STA+1(0x21)
// Register Identifier Code
const byte ris[]    = { B11110000, B00000000}; // Always 1000
// Power-On Reset Flag
const byte por[]    = { B00001000, B00000000}; // There was a power-on reset since last field reset
// Soft Reset Flag
const byte srf[]    = { B00000100, B00000000}; // There was a soft reset since last field reset
// New Flag
const byte nfs[]    = { B00000010, B00000000}; // A new angle is in the angle register
// Current Error Flag
const byte efs[]    = { B00000001, B00000000}; // At least one error in register 0x24
// Processor Processing Status
const byte mps[]    = { B00000000, B11110000}; // 0000 Booting; 0001 Idle or Processing angles; 1110 Self-testmode
// Processor Phase Status
const byte phase[]  = { B00000000, B00001111}; // 0000 Idle; 0001 Processing angles; Only in Self-test mode [0100 Built in self test; 0110 ROM checksum; 0111 CVH self test]


// Temperature Register read masks

//                      TSEN(0x28) TSEN+1(0x29)
// Register Identifier Code
const byte rit[]    = { B11110000, B00000000}; // Always 1111
// Temperature
const byte temp[]   = { B00001111, B11111111}; // Encoded temperature reading (n / 8 = temperature in K)


// Field Strenght Register read masks

//                      FIELD(0x28) FIELD+1(0x29)
// Register Identifier Code
const byte rif[]    = { B11110000, B00000000}; // Always 1110
// Magnetic field strenght
const byte field[]  = { B00001111, B11111111}; // Magnetic field strenght reading (n = field strenght in Gauss (1/10000 T))


A1335::A1335(){
  
}

int16_t   A1335::getAddress(){                  // returns I2C address of the sensor
  return address;
}

byte      A1335::getProcessorState(){           // returns current processor state of the sensor
  return processorState;
}

byte      A1335::getOutputRate(){               // returns the current sample rate of the sensor
  return outputRate;
}

byte      A1335::start(int16_t address_){        // Initializes the sensor at the address given and fills the private variables
  Wire.beginTransmission(address);
  byte error = Wire.endTransmission();
  if (error) {
    processorState = 4;
    return error;
  }
  address = address_;
  bytes_2 state(normalRead(STA));
  bytes_4 orate(extendedRead(ORATE));
  
  byte processing_status = (state.msBy(1) & mps[1]) >> 4;
  byte processing_phase =  (state.msBy(1) & phase[1]) >> 0;
  switch (processing_status) {
    case B00000000:
      processorState = 0;
      break;
    case B00000001:
      if (processing_phase == 0){
        processorState = 1;
      } else {
        processorState = 2;
      }
      break;
    case B00001110:
      processorState = 3;
      break;
  }
  outputRate = orate.msBy(3);
  delay(1);
  return error;
}

double    A1335::readAngle(){       // returns Angle in Degrees
  return double(readAngleRaw()) * 360.0 / 4096.0;
}
uint16_t  A1335::readAngleRaw(){    // returns raw angle data
  bytes_2 angReg(normalRead(ANG));
  
  int16_t parityCheck = angReg.in();
  parityCheck ^= parityCheck >> 8;
  parityCheck ^= parityCheck >> 4;
  parityCheck ^= parityCheck >> 2;
  parityCheck ^= parityCheck >> 1;
  parityCheck &= 1;                 // parity Check now contains the parity of all bits in angReg
  if(!parityCheck) {                // odd Parity in this register => a 0 means an error
    return 0;
  }
  
  angReg.msBy(0) &= ang[0];           // mutes bits, that dont contain the angle data
  return angReg.in();
}

double    A1335::readTemp(){        // returns temperature in Kelvin
  return double(readTempRaw())/8.0;
}

uint16_t  A1335::readTempRaw(){     // returns raw temperature data
  bytes_2 tempReg(normalRead(TSEN));
  tempReg.msBy(0) &= temp[0];         // mutes bits, that dont contain temperature data
  return tempReg.in();
}

double    A1335::readField(){       // returns field strenght in Tesla
  return double(readFieldRaw())/10000.0;
}

uint16_t  A1335::readFieldRaw(){    // returns raw field strenght data
  bytes_2 fieldReg(normalRead(FIELD));
  fieldReg.msBy(0) &= field[0];       // mutes bits, that dont contain temperature data
  return fieldReg.in();
}

byte      A1335::readOutputRate(){  // reads the log2() of the sample rate 
  bytes_4 oRate(extendedRead(ORATE));
  return oRate.msBy(3);             // !!!  I don't know yet, which byte holds the output rate  !!!
}

byte      A1335::setOutputRate(byte rate){  // sets the log2() of the sample rate => (ORate = 2^rate)
  if (rate < 0) {
    rate = 0;
  } else if (rate >=8) {
    rate = 7;
  }
  bytes_2 idle_mode;
  bytes_2 run_mode;
  for (int i = 0; i < 2; i++){
    idle_mode.msBy(i) = ipm[i];
    run_mode.msBy(i)  = rpm[i];
  }
  normalWrite(CTRL, idle_mode.in());
  delayMicroseconds(150);
  bytes_4 oRate;
  oRate.msBy(3) = rate;
  extendedWrite(ORATE, oRate.in());       // !!!  I don't know yet, which byte gets the output rate  !!!
  delayMicroseconds(50);
  normalWrite(CTRL, run_mode.in());
  delayMicroseconds(150);
}

byte      A1335::normalWrite(byte reg, int16_t data){      // writes the 2 bytes in "bytes" to the register with address reg to the sensor with I2C address adress.
  bytes_2 data_bytes(data);
  Wire.beginTransmission(address);
  Wire.write(reg);                                        // choose target register
  for (int i = 0; i<2; i++){
    Wire.write(data_bytes.msBy(i));                       // Writes data MSB first
  }
  return Wire.endTransmission();
}

byte      A1335::extendedWrite(int16_t reg, int32_t data){ // writes the 4 bytes in "bytes" to the extended register with address reg to the sensor with I2C address adress.
  bytes_4 data_bytes(data);
  Wire.beginTransmission(address);
  Wire.write(EWA);                                        // choose write Register
  Wire.write(byte(reg >> 8));                             // Fill with target address
  Wire.write(byte(reg));
  for (int i = 0; i<4; i++){
    Wire.write(data_bytes.msBy(i));                       // Writes data MSB first
  }
  Wire.write(0x80);                                       // Confirm write
  Wire.endTransmission();
  delayMicroseconds(10);
  Wire.requestFrom(address, 1);
  return Wire.read();                                     // Returns 1 if it works
}

int16_t   A1335::normalRead(byte reg){
  bytes_2 data;
  Wire.beginTransmission(address);
  Wire.write(reg);                // choose target Register
  Wire.endTransmission();
  Wire.requestFrom(address, 2);
  for(int i=0; i< 2; i++){
    if (Wire.available()){
      data.msBy(i) = Wire.read();   // read data bytes
    }
  }
  return data.in();
}

int32_t   A1335::extendedRead(int16_t reg){
  bytes_4 data;
  byte rstate;
  Wire.beginTransmission(address);
  Wire.write(ERA);                // choose write Register
  Wire.write(byte(reg >> 8));     // Fill with target address
  Wire.write(byte(reg));
  Wire.write(0x80);               // Confirm read
  Wire.endTransmission();
  delayMicroseconds(10);
  Wire.requestFrom(address,5);
  rstate = Wire.read();           // Reads status byte
  for(int i=0; i < 4; i++){
    if (Wire.available()){
      data.msBy(i) = Wire.read();    // Reads data bytes
    }
  }
  return data.in();
}


