forked from M-Labs/artiq
parent
74517107f0
commit
1452cd7447
|
@ -0,0 +1,174 @@
|
||||||
|
from numpy import int32, int64
|
||||||
|
|
||||||
|
from artiq.language.core import kernel, delay, portable
|
||||||
|
from artiq.language.units import us, ns
|
||||||
|
from artiq.coredevice.ad9912_reg import *
|
||||||
|
|
||||||
|
from artiq.coredevice import spi2 as spi
|
||||||
|
from artiq.coredevice import urukul
|
||||||
|
|
||||||
|
|
||||||
|
SPI_CONFIG = (0*spi.SPI_OFFLINE | 0*spi.SPI_END |
|
||||||
|
0*spi.SPI_INPUT | 0*spi.SPI_CS_POLARITY |
|
||||||
|
0*spi.SPI_CLK_POLARITY | 0*spi.SPI_CLK_PHASE |
|
||||||
|
0*spi.SPI_LSB_FIRST | 0*spi.SPI_HALF_DUPLEX)
|
||||||
|
|
||||||
|
|
||||||
|
SPI_CS_ADC = 1
|
||||||
|
SPI_CS_SR = 2
|
||||||
|
|
||||||
|
|
||||||
|
@portable
|
||||||
|
def adc_ctrl(channel=1, softspan=0b111, valid=1):
|
||||||
|
"""Build a LTC2335-16 control word"""
|
||||||
|
return (valid << 7) | (channel << 3) | softspan
|
||||||
|
|
||||||
|
|
||||||
|
@portable
|
||||||
|
def adc_softspan(data):
|
||||||
|
"""Return the softspan configuration index from a result packet"""
|
||||||
|
return data & 0x7
|
||||||
|
|
||||||
|
|
||||||
|
@portable
|
||||||
|
def adc_channel(data):
|
||||||
|
"""Return the channel index from a result packet"""
|
||||||
|
return (data >> 3) & 0x7
|
||||||
|
|
||||||
|
|
||||||
|
@portable
|
||||||
|
def adc_data(data):
|
||||||
|
"""Return the ADC value from a result packet"""
|
||||||
|
return (data >> 8) & 0xffff
|
||||||
|
|
||||||
|
|
||||||
|
@portable
|
||||||
|
def adc_value(data, v_ref=5.):
|
||||||
|
"""Convert a ADC result packet to SI units (Volt)"""
|
||||||
|
softspan = adc_softspan(data)
|
||||||
|
data = adc_data(data)
|
||||||
|
if softspan & 4:
|
||||||
|
g3 = 2
|
||||||
|
else:
|
||||||
|
g3 = 1
|
||||||
|
if softspan & 2:
|
||||||
|
g2 = 1 << 15
|
||||||
|
else:
|
||||||
|
g2 = 1 << 16
|
||||||
|
if softspan & 1:
|
||||||
|
g1 = 1000
|
||||||
|
else:
|
||||||
|
g1 = 1023
|
||||||
|
data = -(data & g2) + (data & ~g2)
|
||||||
|
v_per_lsb = v_ref*(1250*g3)/(g1*g2)
|
||||||
|
return data*v_per_lsb
|
||||||
|
|
||||||
|
|
||||||
|
class Novogorny:
|
||||||
|
"""Novogorny ADC.
|
||||||
|
|
||||||
|
Controls the LTC2335-16 8 channel ADC with SPI interface and
|
||||||
|
the switchable gain instrumentation amplifiers using a shift
|
||||||
|
register.
|
||||||
|
|
||||||
|
:param spi_device: SPI bus device name
|
||||||
|
:param conv_device: CONV RTIO TTLOut channel name
|
||||||
|
:param div: SPI clock divider (default: 8)
|
||||||
|
:param core_device: Core device name
|
||||||
|
"""
|
||||||
|
kernel_invariants = {"bus", "core", "conv", "div", "v_ref"}
|
||||||
|
|
||||||
|
def __init__(self, dmgr, spi_device, conv_device, div=8,
|
||||||
|
core_device="core"):
|
||||||
|
self.bus = dmgr.get(spi_device)
|
||||||
|
self.core = dmgr.get(core_device)
|
||||||
|
self.conv = dmgr.get(conv_device)
|
||||||
|
self.div = div
|
||||||
|
self.gains = 0x0000
|
||||||
|
self.v_ref = 5. # 5 Volt reference
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_gain_mu(self, channel, gain):
|
||||||
|
"""Set instrumentation amplifier gain of a channel.
|
||||||
|
|
||||||
|
The four gain settings (0, 1, 2, 3) corresponds to gains of
|
||||||
|
(1, 10, 100, 1000) respectively.
|
||||||
|
|
||||||
|
:param channel: Channel index
|
||||||
|
:param gain: Gain setting
|
||||||
|
"""
|
||||||
|
self.gains &= ~(0b11 << (channel*2))
|
||||||
|
self.gains |= gain << (channel*2)
|
||||||
|
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END,
|
||||||
|
2, self.div, SPI_CS_SR)
|
||||||
|
self.bus.write(self.gains << 16)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def configure(self, data):
|
||||||
|
"""Set up the ADC sequencer.
|
||||||
|
|
||||||
|
:param data: List of 8 bit control words to write into the sequencer
|
||||||
|
table.
|
||||||
|
"""
|
||||||
|
if len(data) > 1:
|
||||||
|
self.bus.set_config_mu(SPI_CONFIG,
|
||||||
|
8, self.div, SPI_CS_ADC)
|
||||||
|
for i in range(len(data) - 1):
|
||||||
|
self.bus.write(data[i] << 24)
|
||||||
|
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END,
|
||||||
|
8, self.div, SPI_CS_ADC)
|
||||||
|
self.bus.write(data[len(data) - 1] << 24)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def sample_mu(self, next_ctrl=0):
|
||||||
|
"""Acquire a sample:
|
||||||
|
|
||||||
|
Perform a conversion and transfer the sample.
|
||||||
|
|
||||||
|
:param next_ctrl: ADC control word for the next sample
|
||||||
|
:return: The ADC result packet (machine units)
|
||||||
|
"""
|
||||||
|
self.conv.pulse(40*ns) # t_CNVH
|
||||||
|
delay(560*ns) # t_CONV max
|
||||||
|
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_INPUT | spi.SPI_END,
|
||||||
|
24, self.div, SPI_CS_ADC)
|
||||||
|
self.bus.write(next_ctrl << 24)
|
||||||
|
return self.bus.read()
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def sample(self, next_ctrl=0):
|
||||||
|
"""Acquire a sample
|
||||||
|
|
||||||
|
.. seealso:: :meth:`sample_mu`
|
||||||
|
|
||||||
|
:param next_ctrl: ADC control word for the next sample
|
||||||
|
:return: The ADC result packet (Volt)
|
||||||
|
"""
|
||||||
|
return adc_value(self.sample_mu(), self.v_ref)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def burst_mu(self, data, dt_mu, ctrl=0):
|
||||||
|
"""Acquire a burst of samples.
|
||||||
|
|
||||||
|
If the burst is too long and the sample rate too high, there will be
|
||||||
|
RTIO input overflows.
|
||||||
|
|
||||||
|
High sample rates lead to gain errors since the impedance between the
|
||||||
|
instrumentation amplifier and the ADC is high.
|
||||||
|
|
||||||
|
:param data: List of data values to write result packets into.
|
||||||
|
In machine units.
|
||||||
|
:param dt: Sample interval in machine units.
|
||||||
|
:param ctrl: ADC control word to write during each result packet
|
||||||
|
transfer.
|
||||||
|
"""
|
||||||
|
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_INPUT | spi.SPI_END,
|
||||||
|
24, self.div, SPI_CS_ADC)
|
||||||
|
for i in range(len(data)):
|
||||||
|
t0 = now_mu()
|
||||||
|
self.conv.pulse(40*ns) # t_CNVH
|
||||||
|
delay(560*ns) # t_CONV max
|
||||||
|
self.bus.write(ctrl << 24)
|
||||||
|
at_mu(t0 + dt_mu)
|
||||||
|
for i in range(len(data)):
|
||||||
|
data[i] = self.bus.read()
|
|
@ -16,7 +16,7 @@ __all__ = [
|
||||||
"SPI_OFFLINE", "SPI_END", "SPI_INPUT",
|
"SPI_OFFLINE", "SPI_END", "SPI_INPUT",
|
||||||
"SPI_CS_POLARITY", "SPI_CLK_POLARITY", "SPI_CLK_PHASE",
|
"SPI_CS_POLARITY", "SPI_CLK_POLARITY", "SPI_CLK_PHASE",
|
||||||
"SPI_LSB_FIRST", "SPI_HALF_DUPLEX",
|
"SPI_LSB_FIRST", "SPI_HALF_DUPLEX",
|
||||||
"SPIMaster", "NRTSPIMaster"
|
"SPIMaster"
|
||||||
]
|
]
|
||||||
|
|
||||||
SPI_DATA_ADDR = 0
|
SPI_DATA_ADDR = 0
|
||||||
|
|
|
@ -269,7 +269,7 @@ class Opticlock(_StandaloneBase):
|
||||||
phy = spi2.SPIMaster(self.platform.request("eem3_spi_p"),
|
phy = spi2.SPIMaster(self.platform.request("eem3_spi_p"),
|
||||||
self.platform.request("eem3_spi_n"))
|
self.platform.request("eem3_spi_n"))
|
||||||
self.submodules += phy
|
self.submodules += phy
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=16))
|
||||||
|
|
||||||
for signal in "conv".split():
|
for signal in "conv".split():
|
||||||
pads = platform.request("eem3_{}".format(signal))
|
pads = platform.request("eem3_{}".format(signal))
|
||||||
|
|
|
@ -75,6 +75,12 @@ These drivers are for the core device and the peripherals closely integrated int
|
||||||
.. automodule:: artiq.coredevice.sawg
|
.. automodule:: artiq.coredevice.sawg
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
:mod:`artiq.coredevice.novogorny` module
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: artiq.coredevice.novogorny
|
||||||
|
:members:
|
||||||
|
|
||||||
:mod:`artiq.coredevice.urukul` module
|
:mod:`artiq.coredevice.urukul` module
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue