'''
Created on Tue Jul 05 2022

Driver class for MAX11270: 24-Bit, 24-Bit, 10mW, 130dB SNR, 64ksps Delta-Sigma ADC with Integrated PGA

The MAX11270 interface operates in two modes, conversion mode or register access mode,

MSB First
Conversion Mode (MODE = 0)
BIT         B7          B6          B5      B4      B3      B2      B1      B0
BIT NAME    START = 1   MODE = 0    CAL     IMPD    RATE3   RATE2   RATE1   RATE0

CAL = 1 to perform a calibration, CAL = 0 for all other operates
IMPD = 1 to power down the MAX11270 and enter sleep mode
data rate bits RATE[3:0] determine the conversion speed

Register Access Mode (MODE = 1)
BIT         B7          B6          B5      B4      B3      B2      B1      B0
BIT NAME    START =1    MODE = 1    RS4     RS3     RS2     RS1     RS0     R/W

bits RS[4:0] determine the register
R/W = 0 to write to the selected register and R/W = 1 to read from the selected register

Status Register (Read Only)
BIT         B15 B14 B13 B12 B11 B10 B09 B08 B07          B06          B05      B04      B03      B02      B01      B00

00  0   RDY     Ready bit. RDY = 1 when a new conversion result is available. A read of the DATA register resets RDY = 0. The function of the RDY bit is redundant and is duplicated by RDYB pin.
01  0   MSTAT   Ready bit. RDY = 1 when a new conversion result is available. A read of the DATA register resets RDY = 0. The function of the RDY bit is redundant and is duplicated by RDYB pin.
02  0   DOR     Data overrange bit. DOR = 1 indicates that the conversion result has exceeded the maximum or minimum value and that the result has been clipped or limited to the maximum value. DOR = 0 when the conversion result is within the full-scale range.
03  0   SYSGOR  System gain overrange bit. SYSGOR = 1 indicates that a system gain calibration was overranged. The SGC calibration coefficient maximum value is 1.9999999.
04  1   RATE0   Data rate bits
05  0   RATE1
06  0   RATE2
07  1   RATE3
08  



@author: elena
'''

import pin as GPIO
GPIO.config('./config.json')
import time

'''! @name Registers
@{
'''
MAX11270_REG_STAT     = 0x0
MAX11270_REG_CTRL1    = 0x1
MAX11270_REG_CTRL2    = 0x2
MAX11270_REG_CTRL3    = 0x3
MAX11270_REG_CTRL4    = 0x4
MAX11270_REG_CTRL5    = 0x5
MAX11270_REG_DATA     = 0x6
MAX11270_REG_SOC_SPI  = 0x7
MAX11270_REG_SGC_SPI  = 0x8
MAX11270_REG_SCOC_SPI = 0x9
MAX11270_REG_SCGC_SPI = 0xA
MAX11270_REG_RAM      = 0xC
MAX11270_REG_SYNC_SPI = 0xD
MAX11270_REG_SOC_ADC  = 0x15
MAX11270_REG_SGC_ADC  = 0x16
MAX11270_REG_SCOC_ADC = 0x17
MAX11270_REG_SCGC_ADC = 0x18

'''! @name Conversion Rates Commands
@{ '''
MAX11270_SPS_50    = 0b1000
MAX11270_SPS_62    = 0b0001
MAX11270_SPS_100   = 0b0010
MAX11270_SPS_125   = 0b0011
MAX11270_SPS_200   = 0b0100
MAX11270_SPS_250   = 0b0101
MAX11270_SPS_400   = 0b0110
MAX11270_SPS_500   = 0b0111
MAX11270_SPS_800   = 0b1000
MAX11270_SPS_1000  = 0b1001
MAX11270_SPS_1600  = 0b1010
MAX11270_SPS_2000  = 0b1011
MAX11270_SPS_3200  = 0b1100
MAX11270_SPS_4000  = 0b1101
MAX11270_SPS_6400  = 0b1110
MAX11270_SPS_12800 = 0b1111

def reverseBits(n,width):
    b = '{:0{width}b}'.format(n, width=width)
    return int(b[::-1], 2)

class MAX11270:
  '''
  Constructor of the class, setup the GPIO pin for SPI-communication
  '''
  def __init__(self, CLK_Pin, MOSI_Pin, MISO_Pin, CS_Pin, Sync_Pin, RSTB_Pin, vref=None):
    self.clkPin = CLK_Pin
    self.mosiPin = MOSI_Pin
    self.misoPin = MISO_Pin
    self.csPin = CS_Pin
    self.syncPin = Sync_Pin
    self.rstbPin = RSTB_Pin

    self.softSPI = True
    '''
    if self.clkPin == 11 and self.mosiPin == 10 and self.misoPin == 9:
      self.spidev = 0
      if self.csPin == 8:
        self.spichan = 0
        self.softSPI = False
      elif self.csPin == 7:
        self.spichan == 1
        self.softSPI = False
        
    if self.clkPin == 21 and self.mosiPin == 20 and self.misoPin == 19:
      self.spidev = 1
      if self.csPin == 18:
        self.spichan = 0
        self.softSPI = False
      elif self.csPin == 17:
        self.spichan == 1
        self.softSPI = False
      elif self.csPin == 16:
        self.spichan == 2
        self.softSPI = False
    '''
    if vref is None:
      self.vref = 3
    else:
      self.vref = vref

    GPIO.setmode(GPIO.BCM)

    GPIO.setup(self.clkPin,  GPIO.OUT)
    GPIO.setup(self.mosiPin, GPIO.OUT)
    GPIO.setup(self.csPin,   GPIO.OUT)
    GPIO.setup(self.syncPin,   GPIO.OUT)
    GPIO.setup(self.rstbPin,   GPIO.OUT)
    
    GPIO.setup(self.misoPin, GPIO.IN)

    GPIO.output(self.syncPin, GPIO.LOW)
    #Reset the ADC at creation time
    GPIO.output(self.rstbPin, GPIO.HIGH)
    GPIO.output(self.rstbPin, GPIO.LOW)
    GPIO.output(self.rstbPin, GPIO.HIGH)

    GPIO.output(self.csPin, GPIO.HIGH)
    GPIO.output(self.clkPin, GPIO.LOW)
    GPIO.output(self.mosiPin, GPIO.LOW)
    
    self.ready = 0
    self.mstat = 0
    self.dataOverrange = 0
    self.systemGainOverrange = 0
    self.rate = 0
    self.analogOverrange = 0
    self.dataReadError = 0
    self.powerState = 0
    self.error = 0
    self.inReset = 0
    self.format = 0
    self.dataBytes = 3 
    
  def _toggleClock(self):
    # Toggle clock pin
    GPIO.output(self.clkPin, GPIO.HIGH)
    #time.sleep(0.0001)
    GPIO.output(self.clkPin, GPIO.LOW)
    #time.sleep(0.0001) 

  '''
  Operates the ADC in conversion mode, eather perform calibration or trigger a converison
  '''
  def _conversionMode(self,CAL = 0, IMPD=0, rate=0b1001):
    adc_command = 0b10000000 | CAL<<5 | IMPD<<4 | rate
    #print("{:08b}".format(adc_command))
    # Start com
    GPIO.output(self.csPin, GPIO.LOW)
    for bit in range(8):
      #Set outgoing bit
      if adc_command & 0b10000000:
        GPIO.output(self.mosiPin, GPIO.HIGH)
      else:
        GPIO.output(self.mosiPin, GPIO.LOW)

      #Shift command by 1 bit for next tick
      adc_command <<= 1

      self._toggleClock()

    GPIO.output(self.csPin, GPIO.HIGH)
      
  '''
  Operates the ADC in register access mode, used for configuration and readout
  '''
  def _registerAccessMode(self,rw,register=0x0):
    adc_command = 0b11000000 | register<<1 | rw
    # Start com
    GPIO.output(self.csPin, GPIO.LOW)
    for bit in range(8):
      #Set outgoing bit
      if adc_command & 0b10000000:
        GPIO.output(self.mosiPin, GPIO.HIGH)
      else:
        GPIO.output(self.mosiPin, GPIO.LOW)

      #Shift command by 1 bit for next tick
      adc_command <<= 1

      self._toggleClock()
  
    #GPIO.output(self.csPin, GPIO.HIGH)
  
  
  def _spi_read(self, numbytes):
    retVal = 0

    # Start readout, in most cases cs is already low, because of register access
    GPIO.output(self.csPin, GPIO.LOW)
    #time.sleep(0.0001)

    numBits = numbytes*8
    for bit in range(numBits):
      # Read 1 data bit
      if GPIO.input(self.misoPin):
        retVal |= (1<<(numBits-1-bit))

      self._toggleClock()

    # Set chip select high to end the read process
    GPIO.output(self.csPin, GPIO.HIGH)
    
    print(bin(retVal))
    return retVal
  
  def _spi_write(self,adc_command):
    # Start com
    GPIO.output(self.csPin, GPIO.LOW)
    for bit in range(8):
      #Set outgoing bit
      if adc_command & 0b10000000:
        GPIO.output(self.mosiPin, GPIO.HIGH)
      else:
        GPIO.output(self.mosiPin, GPIO.LOW)

      #Shift command by 1 bit for next tick
      adc_command <<= 1

      self._toggleClock()

    GPIO.output(self.csPin, GPIO.HIGH)
  
  def _readAndChangeRegisterValue(self,register,newBits,firstBit,bits=1):
    print('--------------')
    #First readout register in order to change only one value
    #Select register
    self._registerAccessMode(1,register)
    #Read stored values
    value = self._spi_read(1)
    print('Inital register value')
    print(bin(value))
    
    #Change value in register
    for bit in range(0,bits):
      #Compare register value with new value at position bit, if unequal set new value
      if value >> (firstBit + bit) & 1 != (newBits >> bit) & 1:
        if value >> bit & 1 == 1:
          value = value & ~(1<<firstBit+bit)
        else:
          value = value | (1<<firstBit+bit)
    print('Changed register value')
    print(bin(value))
    #Write back register
    self._registerAccessMode(0,register)
    self._spi_write(value)
    
    #Readout register again to check if values changed
    #Select register
    self._registerAccessMode(1,register)
    #Read stored values
    value = self._spi_read(1)
    print('Final register value')
    print(bin(value))
    print('--------------')
  
  def readStatusRegister(self,disp=0):
    self._registerAccessMode(1,MAX11270_REG_STAT)
    value = self._spi_read(2)

    self.ready = value >> 0 & 1
    self.mstat = value >> 1 & 1
    self.dataOverrange = value >> 2 & 1
    self.systemGainOverrange = value >> 3 & 1
    self.rate = value >> 4 & 0b1111
    self.analogOverrange = value >> 8 & 1
    self.dataReadError = value >> 9 & 1
    self.powerState = value >> 10 & 0b11
    self.error = value >> 14 & 1
    self.inReset = value >> 15 & 1

    if disp:
      if self.ready:
        print('Conversion result available')
      if self.mstat:
        print('Conversion, self-calibration or system-calibration in progress')
      if self.dataOverrange:
        print('Conversion result exceeded the max or min value')
      if self.systemGainOverrange:
        print(' system gain calibration was overranged')
      if self.analogOverrange:
        print(' the modulator detects that the analog input voltage exceeds 1.3 x full-scale range')
      if self.dataReadError:
        print(' result is being written to the DATA register while user is reading from the DATA register.')
      if self.error:
        print('CAL[1:0] bits are set to invalid setting of 11.')
      if self.inReset:
        print('software reset mode')
    
    return value


#%% Control register 1 functions
#The CTRL1 register is an 8-bit read/write register. The byte written to the CTRL1 register determines the clock #setting, synchronization mode, power-down or reset state, input range is unipolar or bipolar, data output is two’s
#complement or offset binary, and conversion mode is in single cycle or continuous.
  def setConversion(self,mode):
    if mode == 'single':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,0,0)
    elif mode == 'continous':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,1,0)
    else:
      raise Exception("No known conversion mode selected") 
  
  def setCycleControl(self,mode):
    if mode == 'single-cycle':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,1,1)
    elif mode == 'continous':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,0,1)
    else:
      raise Exception("No known cycle mode selected") 

  def setDataFormat(self,mode):
    if mode == 'twosComplement':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,0,2)
    elif mode == 'offsetBinary':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,1,2)
    else:
      raise Exception("No known data format selected") 
  
  def setInputRange(self,mode):
    if mode == 'unipolar':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,1,3)
    elif mode == 'bipolar':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,0,3)
    else:
      raise Exception("No known input mode selected") 
  
  def setPowerState(self,mode):
    if mode == 'normal':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,0b00,4,2)
    elif mode == 'sleep':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,0b01,4,2)
    elif mode == 'standy':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,0b10,4,2)
    elif mode == 'reset':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,0b11,4,2)
    else:
      raise Exception("No known power state selected") 
    
  def setSyncMode(self,mode):
    if mode == 'continuousSync':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,1,6)
    elif mode == 'pulseSync':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,0,6)
    else:
      raise Exception("No known input mode selected")  
    
  def setClock(self,mode):
    if mode == 'external':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,1,7)
    elif mode == 'internal':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL1,0,7)
    else:
      raise Exception("No known input mode selected")   
    
#%% Control register 2 functions
#The CTRL2 register is an 8-bit read/write register. The byte written to the CTRL2 register determines the 
#digital and analog gain settings, and whether the buffers or PGA is enabled.
  def setPGAGain(self,gain):
    gainMap = {'x1':0b000,'x2':0b001,'x4':0b010,'x8':0b011,'x16':0b100,'x32':0b101,'x64':0b110,'x128':0b111}
    if gain in gainMap:
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL2,gainMap[gain],0,3)
    else:
      raise Exception("No known gain selected")   
    
  def setPGAenable(self,bit):
    self._readAndChangeRegisterValue(MAX11270_REG_CTRL2,bit,3)
  
  def setPGApower(self,mode):
    if mode == 'low':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL2,1,4)
    elif mode == 'standard':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL2,0,4)
    else:
      raise Exception("No known PGA power mode selected") 
    
  def setBufferEnable(self,bit):
    self._readAndChangeRegisterValue(MAX11270_REG_CTRL2,bit,5)

  def setModulatorGain(self,gain):
    gainMap = {'x1':0b00,'x2':0b01,'x4':0b10,'x8':0b11}
    if gain in gainMap:
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL2,gainMap[gain],6,2)
    else:
      raise Exception("No known gain selected")  

#%% Control register 3 functions
#The CTRL3 register is an 8-bit read/write register. The byte written to the CTRL3 register determines the operation
#of the modulator output.
  def setDataMode(self,mode):
    if mode == '32bit':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL3,1,3)
      self.dataBytes = 4
    elif mode == '24bit':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL3,0,3)
      self.dataBytes = 3
    else:
      raise Exception("No known input mode selected")     

  def setModulatorOutputEnable(self,bit):
    self._readAndChangeRegisterValue(MAX11270_REG_CTRL3,bit,4)
    
  def setModulatorSyncPulseEnable(self,bit):
    self._readAndChangeRegisterValue(MAX11270_REG_CTRL3,bit,5)
    
#%% Control register 5 functions
#The CTRL5 register is an 8-bit read/write register. The byte written to the CTRL5 register determines the 
#MAX11270’s reset, data overflow, and calibration modes.
  def performCalibration(self,mode):
    if mode == 'selfCalibration':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL5,0b00,6,2)
    elif mode == 'offsetCalibration':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL5,0b01,6,2)
    elif mode == 'fullScaleCalibration':
      self._readAndChangeRegisterValue(MAX11270_REG_CTRL5,0b10,6,2)
    else:
      raise Exception("No known input mode selected")     
    
#%% Data register functions
  def readDataRegister(self):
    self._registerAccessMode(1,MAX11270_REG_DATA)
    return self._spi_read(self.dataBytes)
  
#This function is not complete, as I do not understand the data sheet...
  def value_to_voltage(self, adcValue):
    
    if adcValue == 0:
      voltage =  -self.vref
    elif adcValue == 1:
      voltage = 0
    else:
      voltage = self.vref / 2**(self.dataBytes*8-1) *adcValue
    
    return voltage
  
  '''
    Read ADC value in differential mode
  '''          
  def read_differential(self):
    #Triggers conversion
    self._conversionMode()
    time.sleep(0.001)
    adc_code = self.readDataRegister()
    return self.value_to_voltage(adc_code)


