Source code for artiq.coredevice.novogorny

from artiq.language.core import kernel, delay, portable
from artiq.language.units import ns

from artiq.coredevice import spi2 as spi


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


[docs] @portable def adc_ctrl(channel=1, softspan=0b111, valid=1): """Build a LTC2335-16 control word.""" return (valid << 7) | (channel << 3) | softspan
[docs] @portable def adc_softspan(data): """Return the softspan configuration index from a result packet.""" return data & 0x7
[docs] @portable def adc_channel(data): """Return the channel index from a result packet.""" return (data >> 3) & 0x7
[docs] @portable def adc_data(data): """Return the ADC value from a result packet.""" return (data >> 8) & 0xffff
[docs] @portable def adc_value(data, v_ref=5.): """Convert a ADC result packet to SI units (volts).""" softspan = adc_softspan(data) data = adc_data(data) g = 625 if softspan & 4: g *= 2 if softspan & 2: h = 1 << 15 else: h = 1 << 16 data = -(data & h) + (data & ~h) if softspan & 1: h *= 500 else: h *= 512 v_per_lsb = v_ref*g/h return data*v_per_lsb
[docs] 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 cnv_device: CNV RTIO TTLOut channel name :param div: SPI clock divider (default: 8) :param gains: Initial value for PGIA gains shift register (default: 0x0000). Knowledge of this state is not transferred between experiments. :param core_device: Core device name """ kernel_invariants = {"bus", "core", "cnv", "div", "v_ref"} def __init__(self, dmgr, spi_device, cnv_device, div=8, gains=0x0000, core_device="core"): self.bus = dmgr.get(spi_device) self.core = dmgr.get(core_device) self.cnv = dmgr.get(cnv_device) self.div = div self.gains = gains self.v_ref = 5. # 5 Volt reference
[docs] @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 """ gains = self.gains gains &= ~(0b11 << (channel*2)) gains |= gain << (channel*2) self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END, 16, self.div, SPI_CS_SR) self.bus.write(gains << 16) self.gains = gains
[docs] @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)
[docs] @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.cnv.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()
[docs] @kernel def sample(self, next_ctrl=0): """Acquire a sample. See also :meth:`Novogorny.sample_mu`. :param next_ctrl: ADC control word for the next sample :return: The ADC result packet (volts) """ return adc_value(self.sample_mu(), self.v_ref)
[docs] @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 :exc:RTIOOverflow exceptions. 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.cnv.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()