Added classes for ICs

This commit is contained in:
Elena Arenskötter 2023-11-04 13:51:50 +01:00
parent 441e51954d
commit a6a0fedfeb
5 changed files with 532 additions and 0 deletions

BIN
datasheets/MAX11270.pdf Normal file

Binary file not shown.

BIN
datasheets/MCP4822.pdf Normal file

Binary file not shown.

421
src/MAX11270.py Normal file
View File

@ -0,0 +1,421 @@
'''
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
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.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)
GPIO.output(self.rstbPin, GPIO.LOW)
GPIO.output(self.csPin, GPIO.HIGH)
GPIO.output(self.clkPin, 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()
'''
Operates the ADC in register access mode, used for configuration and readout
'''
def _registerAccessMode(self,register=0x0,rw)
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()
def _spi_read(self, numbytes):
retVal = 0
# Start readout
GPIO.output(self.csPin, GPIO.LOW)
#time.sleep(0.0001)
#Commucation consists of 32 transfered bits
for bit in range(numbytes*8):
# Read 1 data bit
if GPIO.input(self.misoPin):
retVal |= 0x1
# Advance input to next bit
retVal <<= 1
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()
def _readAndChangeRegisterValue(self,register,value,firstBit,bits=1):
#Select register
self._registerAccessMode(register,1)
#Read stored values
value = self._spi_read(1)
self._registerAccessMode(register,0)
for bit in range(0,bits):
if value >> bit & 1 == 0:
value = value & ~(1<<firstBit+bit)
else:
value = value | (1<<firstBit+bit)
self._spi_write(value)
def readStatusRegister(self):
self._registerAccessMode(MAX11270_REG_STAT,1)
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
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 twos
#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
#MAX11270s 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(MAX11270_REG_DATA,1)
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) *adcValue
return voltage
'''
Read ADC value in differential mode
'''
def read_differential(self):
#Triggers conversion
self._conversionMode(self)
time.sleep(0.001)
adc_code = self.readDataRegister(self)
return self.value_to_voltage(adc_code)

24
src/MCP3204.py Normal file
View File

@ -0,0 +1,24 @@
class MCP3204(object):
def __init__(self,spiDev,cs):
self.spi = SPI.SpiDev(spiDev, cs, max_speed_hz=1000000)
self.spi.set_mode(0)
self.spi.set_bit_order(SPI.MSBFIRST)
def __del__(self):
self.spi.close()
def read(self, ch):
if 4 <= ch <= 0:
raise Exception('MCP3204 channel must be 0-4: ' + str(ch))
cmd = 128 # 1000 0000
cmd += 64 # 1100 0000
cmd += ((ch & 0x07) << 3)
ret = self.spi.transfer([cmd, 0x0, 0x0])
# get the 12b out of the return
val = (ret[0] & 0x01) << 11 # only B11 is here
val |= ret[1] << 3 # B10:B3
val |= ret[2] >> 5 # MSB has B2:B0 ... need to move down to LSB
return (val & 0x0FFF) # ensure we are only sending 12b

87
src/MCP4822.py Normal file
View File

@ -0,0 +1,87 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Jan 10 15:56:30 2018
Driver class for MCP4822: 12-Bit, 2-Channel DAC.
SPI DATA FORMAT (MSB First):
Byte #1 Byte #2
Data In : !A/B BUF !GA !SHDN D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
@author: jan
"""
import pin as GPIO
GPIO.config('./config.json')
import time
class MCP4822:
'''
Constructor of the class, setup the GPIO pin for SPI-communication
'''
def __init__(self, CLK_Pin, MOSI_Pin, CS_Pin, vref=None):
self.clkPin = CLK_Pin
self.mosiPin = MOSI_Pin
self.csPin = CS_Pin
if vref is None:
self.vref = 2.048
else:
self.vref = vref
GPIO.setup(self.clkPin, GPIO.OUT)
GPIO.setup(self.mosiPin, GPIO.OUT)
GPIO.setup(self.csPin, GPIO.OUT)
GPIO.output(self.csPin, GPIO.HIGH)
GPIO.output(self.clkPin, GPIO.LOW)
def U2dac(self,U,DAC):
#U: voltage (V) [0V-4.096V]
#DAC: DACA -> 0
# DACB -> 1
#calculate binary data
if U > 2*self.vref:
print('maximum output voltage overshot')
SPIdata = 666
elif U > self.vref:
U = U/2
#buffered, gain=2x, enabled, data
SPIdata = DAC<<15 | 1<<14 | 1<<12 | int(U/self.vref * ((1<<12)-1))
elif U >= 0:
#buffered, gain=1x, enabled, data
SPIdata = DAC<<15 | 1<<14 | 1<<13 | 1<<12 | int(U/self.vref * ((1<<12)-1))
else:
print('minimum output voltage undershot')
SPIdata = -666
return SPIdata
def write(self,voltage,channel):
#Convert voltage to spi command
command = self.U2dac(voltage,channel)
# Start communication
GPIO.output(self.csPin, GPIO.LOW)
#Commucation consists of 16 transfered bits
for bit in range(16):
#Set outgoing bit
if command & 0x8000:
GPIO.output(self.mosiPin, GPIO.HIGH)
else:
GPIO.output(self.mosiPin, GPIO.LOW)
#Shift command by 1 bit for next tick
command <<= 1
# Toggle clock pin
GPIO.output(self.clkPin, GPIO.HIGH)
#time.sleep(0.00001)
GPIO.output(self.clkPin, GPIO.LOW)
#time.sleep(0.00001)
# Set chip select high to end the write process
GPIO.output(self.csPin, GPIO.HIGH)