//
//    FILE: AD9833.cpp
//  AUTHOR: Rob Tillaart
// PURPOSE: Arduino library for AD9833 function generator
//    DATE: 2023-08-25
// VERSION: 0.4.3
//     URL: https://github.com/RobTillaart/AD9833


#include "AD9833.h"


//  FREQUENCY REGISTER BITS
#define AD9833_FREG1        0x8000
#define AD9833_FREG0        0x4000

//  CONTROL REGISTER BITS
#define AD9833_B28          (1 << 13)
#define AD9833_HLB          (1 << 12)
#define AD9833_FSELECT      (1 << 11)
#define AD9833_PSELECT      (1 << 10)
#define AD9833_RESET        (1 << 8)
#define AD9833_SLEEP1       (1 << 7)
#define AD9833_SLEEP12      (1 << 6)
#define AD9833_OPBITEN      (1 << 5)
#define AD9833_DIV2         (1 << 3)
#define AD9833_MODE         (1 << 1)


//  HARDWARE SPI
AD9833::AD9833(uint8_t slaveSelect, __SPI_CLASS__ * mySPI)
{
  _selectPin = slaveSelect;
  _hwSPI     = true;
  _mySPI     = mySPI;
}


//  SOFTWARE SPI
AD9833::AD9833(uint8_t slaveSelect, uint8_t spiData, uint8_t spiClock)
{
  _selectPin = slaveSelect;
  _dataPin   = spiData;
  _clockPin  = spiClock;
  _hwSPI     = false;
  _mySPI     = NULL;
}


void AD9833::begin()
{
  _useSelect = (_selectPin != 255);
  if (_useSelect)
  {
    pinMode(_selectPin, OUTPUT);
    digitalWrite(_selectPin, HIGH);
  }

  _spi_settings = SPISettings(8000000, MSBFIRST, SPI_MODE2);

  if (_hwSPI)
  {
    //  _mySPI->end();
    //  _mySPI->begin();
  }
  else  //  SOFTWARE SPI
  {
    pinMode(_dataPin,  OUTPUT);
    pinMode(_clockPin, OUTPUT);
    digitalWrite(_dataPin,  LOW);
    digitalWrite(_clockPin, HIGH);

#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)
    uint8_t _port    = digitalPinToPort(_dataPin);
    _dataOutRegister = portOutputRegister(_port);
    _dataOutBit      = digitalPinToBitMask(_dataPin);
    _port            = digitalPinToPort(_clockPin);
    _clockRegister   = portOutputRegister(_port);
    _clockBit        = digitalPinToBitMask(_clockPin);
#endif
  }
  reset();
}


void AD9833::reset()
{
  hardwareReset();
  _control = AD9833_B28;  //  implicit select sine wave.
  writeControlRegister(_control);
}


void AD9833::hardwareReset()
{
  writeControlRegister(_control | AD9833_RESET);
  //  reset all library variables to be in "sync" with hardware.
  _control  = 0;
  _waveform = AD9833_OFF;
  _freq[0]  = _freq[1]  = 0;
  _phase[0] = _phase[1] = 0;
}


bool AD9833::setPowerMode(uint8_t mode)
{
  if (mode > 3) return false;
  //  clear previous power bits
  _control &= ~(AD9833_SLEEP1 | AD9833_SLEEP12);
  _control |= (mode << 6);  //  set the new power bits
  writeControlRegister(_control);
  return true;
}


uint8_t AD9833::getPowerMode()
{
  return (_control & (AD9833_SLEEP1 | AD9833_SLEEP12)) >> 6;
}


void AD9833::setWave(uint8_t waveform)
{
  if (waveform > AD9833_TRIANGLE) return;

  //  store waveform
  _waveform = waveform;

  //  clear bits in control register
  _control &= ~(AD9833_SLEEP1 | AD9833_SLEEP12 | AD9833_OPBITEN | AD9833_MODE | AD9833_DIV2);

  //  set bits in control register
  switch(_waveform)
  {
    case AD9833_OFF:
      _control |= (AD9833_SLEEP1 | AD9833_SLEEP12);
    break;
    case AD9833_SINE:
      //  no bits need to set
    break;
    case AD9833_SQUARE1:
      _control |= (AD9833_DIV2 | AD9833_OPBITEN);
    break;
    case AD9833_SQUARE2:
      _control |= (AD9833_OPBITEN);
    break;
    case AD9833_TRIANGLE:
      _control |= (AD9833_MODE);
    break;
  }
  writeControlRegister(_control);
}


uint8_t AD9833::getWave()
{
  return _waveform;
}

void AD9833::setUseRounding(bool flag)
{
  _useRounding = flag;
}


bool AD9833::getUseRounding()
{
  return _useRounding;
}


float AD9833::setFrequency(float frequency, uint8_t channel)
{
  if (channel > 1) return -1;
  //  if (_freq[channel] == frequency) return frequency;
  //  local variable is faster.
  float newFrequency = frequency;
  if (newFrequency < 0) newFrequency = 0;
  else if (newFrequency > AD9833_MAX_FREQ) newFrequency = AD9833_MAX_FREQ;

  //  convert to bit pattern
  //  freq = round(frequency * pow(2, 28) / 25 MHz));  //  25 MHz == crystal frequency.
  //  _crystalFreqFactor == (pow(2, 28) / crystal frequency);
  //  round() to minimize error / use the whole range
  //  however round() may cause drift with harmonics => see #19
  uint32_t freq = 0;
  if (_useRounding) freq = round(newFrequency * _crystalFreqFactor);
  else              freq = uint32_t(newFrequency * _crystalFreqFactor);

  writeFrequencyRegister(channel, freq);

  //  cache the newFrequency;
  _freq[channel] = newFrequency;
  return newFrequency;
}


float AD9833::getFrequency(uint8_t channel)
{
  //  return round(_freq[channel] * _crystalFreqFactor) / _crystalFreqFactor;
  return _freq[channel];
}


float AD9833::getMaxFrequency()
{
  return AD9833_MAX_FREQ;
}


void  AD9833::setFrequencyChannel(uint8_t channel)
{
  if (channel > 1) return;

  if (channel == 0) _control &= ~AD9833_FSELECT;
  if (channel == 1) _control |= AD9833_FSELECT;
  writeControlRegister(_control);
}


float AD9833::setPhase(float phase, uint8_t channel)
{
  if (channel > 1) return -1;
  //  local variable is faster.
  float newPhase = phase;
  //  get phase within normalized range.
  while (newPhase >= AD9833_MAX_PHASE) newPhase -= AD9833_MAX_PHASE;
  while (newPhase <  0) newPhase += AD9833_MAX_PHASE;

  //  round() to minimize error / use the whole range 0..4095
  //  however round() may cause drift with harmonics => see #19
  uint16_t phs = 0;
  if (_useRounding) phs = round(newPhase * (4095.0 / 360.0));
  else              phs = uint32_t(newPhase * (4095.0 / 360.0));
  writePhaseRegister(channel, phs);

  //  cache the newPhase
  _phase[channel] = newPhase;
  return newPhase;
}


float AD9833::getPhase(uint8_t channel)
{
  //  more precise => more math;
  //  return round(_phase[channel] * (4095.0 / 360.0)) / (4095.0 / 360.0);
  return _phase[channel];
}


//  returns phase set (radians) - not optimized.
//  [0 .. 2 PI>
float AD9833::setPhaseRadians(float phase, uint8_t channel)
{
  return setPhase(phase * RAD_TO_DEG, channel) * DEG_TO_RAD;
}


float AD9833::getPhaseRadians(uint8_t channel)
{
  return getPhase(channel) * DEG_TO_RAD;
}


float AD9833::getMaxPhase()
{
  return AD9833_MAX_PHASE;
}


void  AD9833::setPhaseChannel(uint8_t channel)
{
  if (channel > 1) return;

  if (channel == 0) _control &= ~AD9833_PSELECT;
  if (channel == 1) _control |= AD9833_PSELECT;
  writeControlRegister(_control);
}


void AD9833::setSPIspeed(uint32_t speed)
{
  _SPIspeed = speed;
  _spi_settings = SPISettings(_SPIspeed, MSBFIRST, SPI_MODE2);
}


uint32_t AD9833::getSPIspeed()
{
  return _SPIspeed;
}


bool AD9833::usesHWSPI()
{
  return _hwSPI;
}


////////////////////////////////////////////////////////////////
//
//  LOW LEVEL API - Expert users only
//
void AD9833::writeControlRegister(uint16_t value)
{
  uint16_t data = value & 0x3FFF;  //  bit 15 and 14 == 00
  writeData(data);
}


void AD9833:: writeFrequencyRegister(uint8_t channel, uint32_t freq)
{
  uint16_t LSB = 0;
  uint16_t MSB = 0;
  if (channel > 1) return;
  if (channel == 0) LSB = AD9833_FREG0;  //  bit 15 and 14    01
  if (channel == 1) LSB = AD9833_FREG1;  //  bit 15 and 14    10
  //  copy channel mask.
  MSB = LSB;

  //  be sure B28 bit is set.
  _control |= AD9833_B28;
  writeControlRegister(_control);

  //  28 bits in two sets of 14
  LSB |= (freq & 0x3FFF);
  MSB |= ((freq >> 14) & 0x3FFF);

  //  faster to write them in one SPI transaction
  writeData28(LSB, MSB);
  //  first send the least significant 14 bits
  //  writeData(LSB);
  //  then send the most significant 14 bits
  //  writeData(MSB);
}


void AD9833::writePhaseRegister(uint8_t channel, uint16_t value)
{
  uint16_t data = 0;
  if (channel > 1) return;
  if (channel == 0) data = 0xC000;  //  bit 15 and 14 and 13   110
  if (channel == 1) data = 0xE000;  //  bit 15 and 14 and 13   111

  data |= (value & 0x0FFF);
  writeData(data);
}


void AD9833::setCrystalFrequency(float crystalFrequency)
{
  //  calculate the often used factor
  //  268435456.0 == POW2TO28
  _crystalFreqFactor = 268435456.0 / crystalFrequency;
}


float AD9833::getCrystalFrequency()
{
  //  268435456.0 == POW2TO28
  return 268435456.0 / _crystalFreqFactor;
}



///////////////////////////////////////////////////////////////////
//
//  EXPERIMENTAL
//
void AD9833::writeFrequencyRegisterLSB(uint8_t channel, uint16_t LSB)
{
  if (channel > 1) return;
  //  force 14 bit
  LSB &= 0x3FFF;
  if (channel == 0) LSB |= AD9833_FREG0;  //  bit 15 and 14    01
  if (channel == 1) LSB |= AD9833_FREG1;  //  bit 15 and 14    10

  //  be sure B28 and HLB bit is cleared.
  _control &= ~AD9833_B28;
  _control &= ~AD9833_HLB;
  writeControlRegister(_control);

  //  send the least significant 14 bits
  writeData(LSB);
}


void AD9833::writeFrequencyRegisterMSB(uint8_t channel, uint16_t MSB)
{
  if (channel > 1) return;
  //  force 14 bit
  MSB &= 0x3FFF;
  if (channel == 0) MSB |= AD9833_FREG0;  //  bit 15 and 14    01
  if (channel == 1) MSB |= AD9833_FREG1;  //  bit 15 and 14    10

  //  be sure B28 is cleared and HLB bit is set.
  _control &= ~AD9833_B28;
  _control |= AD9833_HLB;
  writeControlRegister(_control);

  //  send the most significant 14 bits
  writeData(MSB);
}



///////////////////////////////////////////////////////////////////
//
//  PRIVATE
//
void AD9833::writeData(uint16_t data)
{
  if (_useSelect) digitalWrite(_selectPin, LOW);
  if (_hwSPI)
  {
    _mySPI->beginTransaction(_spi_settings);
    _mySPI->transfer16(data);
    _mySPI->endTransaction();
  }
  else
  {
    //  SPI MODE2
#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)

    uint8_t cbmask1  = _clockBit;
    uint8_t cbmask2  = ~_clockBit;
    uint8_t outmask1 = _dataOutBit;
    uint8_t outmask2 = ~_dataOutBit;

    //  MSBFIRST
    for (uint16_t mask = 0x8000; mask; mask >>= 1)
    {
      uint8_t oldSREG = SREG;
      noInterrupts();
      if (data & mask) *_dataOutRegister |= outmask1;
      else             *_dataOutRegister &= outmask2;
      *_clockRegister &= cbmask2;
      *_clockRegister |= cbmask1;
      SREG = oldSREG;
    }

#else  //  REFERENCE IMPLEMENTATION

    //  local variables is fast.
    uint8_t clk = _clockPin;
    uint8_t dao = _dataPin;
    //  MSBFIRST
    for (uint16_t mask = 0x8000; mask; mask >>= 1)
    {
      digitalWrite(dao, (data & mask) != 0 ? HIGH : LOW);
      digitalWrite(clk, LOW);
      digitalWrite(clk, HIGH);
    }

#endif
  }
  if (_useSelect) digitalWrite(_selectPin, HIGH);
}


void AD9833::writeData28(uint16_t LSB, uint16_t MSB)
{
  if (_useSelect) digitalWrite(_selectPin, LOW);
  if (_hwSPI)
  {
    _mySPI->beginTransaction(_spi_settings);
    _mySPI->transfer16(LSB);
    _mySPI->transfer16(MSB);
    _mySPI->endTransaction();
  }
  else
  {
    //  SPI MODE2
#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)

    uint8_t cbmask1  = _clockBit;
    uint8_t cbmask2  = ~_clockBit;
    uint8_t outmask1 = _dataOutBit;
    uint8_t outmask2 = ~_dataOutBit;

    for (uint16_t mask = 0x8000; mask; mask >>= 1)
    {
      uint8_t oldSREG = SREG;
      noInterrupts();
      if (LSB & mask) *_dataOutRegister |= outmask1;
      else            *_dataOutRegister &= outmask2;
      *_clockRegister &= cbmask2;
      *_clockRegister |= cbmask1;
      SREG = oldSREG;
    }

    for (uint16_t mask = 0x8000; mask; mask >>= 1)
    {
      uint8_t oldSREG = SREG;
      noInterrupts();
      if (MSB & mask) *_dataOutRegister |= outmask1;
      else            *_dataOutRegister &= outmask2;
      *_clockRegister &= cbmask2;
      *_clockRegister |= cbmask1;
      SREG = oldSREG;
    }

#else  //  REFERENCE IMPLEMENTATION

    //  local variables is fast.
    uint8_t clk = _clockPin;
    uint8_t dao = _dataPin;

    for (uint16_t mask = 0x8000; mask; mask >>= 1)
    {
      digitalWrite(dao, (LSB & mask) != 0 ? HIGH : LOW);
      digitalWrite(clk, LOW);
      digitalWrite(clk, HIGH);
    }

    for (uint16_t mask = 0x8000; mask; mask >>= 1)
    {
      digitalWrite(dao, (MSB & mask) != 0 ? HIGH : LOW);
      digitalWrite(clk, LOW);
      digitalWrite(clk, HIGH);
    }

#endif
  }
  if (_useSelect) digitalWrite(_selectPin, HIGH);
}



//  -- END OF FILE --
