//-----------------------------------------------------------------------------
// Copyright 2024 Peter Balch
// public domain
// fluxgate magnetometer
//
//  Fluxgate 1
//  ==========
//  
//  the circuit has fluxgate magnetometer coils connected to an op-amp to the ADC
//  The sketch sends approx 2mS pos/neg pulses to the FGM
//  The Nano does fast 8-bit ADC to measure the response of the fluxgate sensor coil
//  The Nano sends the ADC values to the PC
//    0xAA
//    0xBB
//    0xCC
//    timer: length of two drive pulses
//    ADCBUFFER: 8-bit values
//  Magnetometer.exe program on the PC displays the values
//  you decide where measurements of the pos and neg sensor wave should be measured
//-----------------------------------------------------------------------------

//#include <TimerOne.h>

//-----------------------------------------------------------------------------
// Defines and Typedefs
//-----------------------------------------------------------------------------

// get register bit - faster: doesn't turn it into 0/1
#ifndef getBit
#define getBit(sfr, bit) (_SFR_BYTE(sfr) & _BV(bit))
#endif

//-----------------------------------------------------------------------------
// Global Constants
//-----------------------------------------------------------------------------

const long BAUDRATE  = 115200;  // Baud rate of UART in bps


//-----------------------------------------------------------------------------
// Global Variables
//-----------------------------------------------------------------------------

uint8_t curAdcPin = 0;
uint8_t curVref = 0;
uint8_t curPrescaler = 2;

const int ADCBUFFERSIZE = 1000;
uint8_t ADCBuffer[ADCBUFFERSIZE];

uint16_t tmr;
  
//-----------------------------------------------------------------------------
// initADC()
//-----------------------------------------------------------------------------
void initADC(void)
{
  //  ADCSRA = 0x00; // ADC Control and Status Register A
  // 0  Bit 7 â€“ ADEN: ADC Enable
  // 0  Bit 6 â€“ ADSC: ADC Start Conversion
  // 1  Bit 5 â€“ ADATE: ADC Auto Trigger Enable
  // 0  Bit 4 â€“ ADIF: ADC Interrupt Flag
  // 1  Bit 3 â€“ ADIE: ADC Interrupt Enable
  // 111  Bits 2:0 â€“ ADPSn: ADC Prescaler Select [n = 2:0]

  ADCSRB = 0x0 ; //ADC Control and Status Register B
  // 0 Bit 6 â€“ ACME: Analog Comparator Multiplexer Enable
  // 000 Bits 2:0 â€“ ADTSn: ADC Auto Trigger Source [n = 2:0] Free Running mode

  ADMUX = 0x20 + (curVref << 6) + curAdcPin; // ADC Multiplexer Selection Register
  // rr    Bits 7:6 â€“ REFSn: Reference Selection = Vcc
  // 1     Bit 5 â€“ ADLAR: ADC Left Adjust Result
  // aaaa  Bits 3:0 â€“ MUXn: Analog Channel Selection

  DIDR0 = 0x3F; // Digital Input Disable Register 0
  // ADC0D=1, ADC1D=1, ADC2D=1, ADC3D=1, ADC4D=1, ADC5D=1, ADC6D=0, ADC7D=0

}

//-----------------------------------------------------------------------------
// GetADCSamples
// read and Tx a buffer-full of samples
// prescaler
//   7 128
//   6 64
//   5 32
//   4 16
//   3 8
//   2 4
//   1 2
//   0 2
//-----------------------------------------------------------------------------

void GetADCSamples(void) {
  uint8_t d;
  uint8_t* p;
  const int hysteresis = 2;

  initADC();

  ADCSRA = 0x80 + (curPrescaler & 7); // ADC Control and Status Register A

  for (d = 0; d < 10; d++ ) { // make sure ADC is running
    bitSet(ADCSRA, ADSC); // start ADC conversion
    while (!getBit(ADCSRA, ADIF)) ; // wait for ADC
    bitSet(ADCSRA, ADIF); // clear the flag
  }

  PORTD = 0x04;
  bitSet(ADCSRA, ADSC); // start ADC conversion
  for (p = ADCBuffer; p < ADCBuffer + ADCBUFFERSIZE/2; p++ ) {
//      bitSet(ADCSRA, ADSC); // start ADC conversion
    while (!getBit(ADCSRA, ADIF)) ; // wait for ADC
    bitSet(ADCSRA, ADIF); // clear the flag
    bitSet(ADCSRA, ADSC);
  }
  
  TCNT1 = 0;
  PORTD = 0x08;
  for (p = ADCBuffer; p < ADCBuffer + ADCBUFFERSIZE/2; p++ ) {
    while (!getBit(ADCSRA, ADIF)) ; // wait for ADC
    bitSet(ADCSRA, ADIF); // clear the flag
    bitSet(ADCSRA, ADSC); // start ADC conversion
    *p = ADCH;
  }
  
  PORTD = 0x04;
  for (; p < ADCBuffer + ADCBUFFERSIZE; p++ ) {
    while (!getBit(ADCSRA, ADIF)) ; // wait for ADC
    bitSet(ADCSRA, ADIF); // clear the flag
    bitSet(ADCSRA, ADSC); // start ADC conversion
    *p = ADCH;
  }
  PORTD = 0;
  tmr = TCNT1;
}

//-----------------------------------------------------------------------------
// SendADC
//-----------------------------------------------------------------------------

void SendADC(void) {
  memset( (void *)ADCBuffer, 0, sizeof(ADCBuffer) );
  noInterrupts();

  GetADCSamples();
    
  interrupts();

  Serial.write((int)0xAA);
  Serial.write((int)0xBB);
  Serial.write((int)0xCC);
  Serial.write((uint8_t *)&tmr,2);
  Serial.write( (uint8_t *)ADCBuffer, ADCBUFFERSIZE);
}

//-----------------------------------------------------------------------------
// Main routines
// The setup function
//-----------------------------------------------------------------------------
void setup (void) {   
  // Open serial port with a baud rate of BAUDRATE b/s
  Serial.begin(BAUDRATE);
  
  // Activate interrupts
  sei();

  initADC();

  Serial.println("FluxGate ready");
  
  pinMode(8,INPUT);
  pinMode(9,INPUT);
  pinMode(10,INPUT);
  pinMode(11,INPUT);

  pinMode(2,OUTPUT); 
  pinMode(3,OUTPUT); 

  curAdcPin = 0;
  curVref = 0;
  curPrescaler = 2;
  TCCR1A = 0; // free-run timer
  TCCR1B = 2; // prescaler = 1/8 
}

void loop (void) {
  SendADC(); 
}
