forked from M-Labs/artiq
ad5360: add documentation and an example
This commit is contained in:
parent
18ccac717b
commit
f2b4b975a3
|
@ -44,6 +44,12 @@ class AD5360:
|
||||||
"""
|
"""
|
||||||
Support for the Analog devices AD53[67][0123]
|
Support for the Analog devices AD53[67][0123]
|
||||||
multi-channel Digital to Analog Converters
|
multi-channel Digital to Analog Converters
|
||||||
|
|
||||||
|
:param spi_device: Name of the SPI bus this device is on.
|
||||||
|
:param ldac_device: Name of the TTL device that LDAC is connected to
|
||||||
|
(optional). Needs to be explicitly initialized to high.
|
||||||
|
:param chip_select: Value to drive on the chip select lines
|
||||||
|
during transactions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, dmgr, spi_device, ldac_device=None, chip_select=1):
|
def __init__(self, dmgr, spi_device, ldac_device=None, chip_select=1):
|
||||||
|
@ -55,6 +61,16 @@ class AD5360:
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def setup_bus(self, write_div=4, read_div=7):
|
def setup_bus(self, write_div=4, read_div=7):
|
||||||
|
"""Configure the SPI bus and the SPI transaction parameters
|
||||||
|
for this device. This method has to be called before any other method
|
||||||
|
if the bus has been used to access a different device in the meantime.
|
||||||
|
|
||||||
|
This method advances the timeline by the duration of two
|
||||||
|
RTIO-to-Wishbone bus transactions.
|
||||||
|
|
||||||
|
:param write_div: Write clock divider.
|
||||||
|
:param read_div: Read clock divider.
|
||||||
|
"""
|
||||||
# write: 2*8ns >= 10ns = t_6 (clk falling to cs_n rising)
|
# write: 2*8ns >= 10ns = t_6 (clk falling to cs_n rising)
|
||||||
# read: 4*8*ns >= 25ns = t_22 (clk falling to miso valid)
|
# read: 4*8*ns >= 25ns = t_22 (clk falling to miso valid)
|
||||||
self.bus.set_config_mu(_AD5360_SPI_CONFIG, write_div, read_div)
|
self.bus.set_config_mu(_AD5360_SPI_CONFIG, write_div, read_div)
|
||||||
|
@ -62,23 +78,56 @@ class AD5360:
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
|
"""Write 24 bits of data.
|
||||||
|
|
||||||
|
This method advances the timeline by the duration of the SPI transfer
|
||||||
|
and the required CS high time.
|
||||||
|
"""
|
||||||
self.bus.write(data << 8)
|
self.bus.write(data << 8)
|
||||||
delay_mu(self.bus.ref_period_mu) # get to 20ns min cs high
|
delay_mu(self.bus.ref_period_mu) # get to 20ns min cs high
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def write_offsets(self, value=0x1fff):
|
def write_offsets(self, value=0x1fff):
|
||||||
|
"""Write the OFS0 and OFS1 offset DACs.
|
||||||
|
|
||||||
|
This method advances the timeline by twice the duration of
|
||||||
|
:meth:`write`.
|
||||||
|
|
||||||
|
:param value: Value to set both offset registers to.
|
||||||
|
"""
|
||||||
value &= 0x3fff
|
value &= 0x3fff
|
||||||
self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_OFS0 | value)
|
self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_OFS0 | value)
|
||||||
self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_OFS1 | value)
|
self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_OFS1 | value)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def write_channel(self, channel=0, value=0, op=_AD5360_CMD_DATA):
|
def write_channel(self, channel=0, value=0, op=_AD5360_CMD_DATA):
|
||||||
|
"""Write to a channel register.
|
||||||
|
|
||||||
|
This method advances the timeline by the duration of :meth:`write`.
|
||||||
|
|
||||||
|
:param channel: Channel number to write to.
|
||||||
|
:param value: 16 bit value to write to the register.
|
||||||
|
:param op: Operation to perform, one of :const:`_AD5360_CMD_DATA`,
|
||||||
|
:const:`_AD5360_CMD_OFFSET`, :const:`_AD5360_CMD_GAIN`
|
||||||
|
(default: :const:`_AD5360_CMD_DATA`).
|
||||||
|
"""
|
||||||
channel &= 0x3f
|
channel &= 0x3f
|
||||||
value &= 0xffff
|
value &= 0xffff
|
||||||
self.write(op | _AD5360_WRITE_CHANNEL(channel) | value)
|
self.write(op | _AD5360_WRITE_CHANNEL(channel) | value)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def read_channel_sync(self, channel=0, op=_AD5360_READ_X1A):
|
def read_channel_sync(self, channel=0, op=_AD5360_READ_X1A):
|
||||||
|
"""Read a channel register.
|
||||||
|
|
||||||
|
This method advances the timeline by the duration of :meth:`write` plus
|
||||||
|
three RTIO-to-Wishbone transactions.
|
||||||
|
|
||||||
|
:param channel: Channel number to read from.
|
||||||
|
:param op: Operation to perform, one of :const:`_AD5360_READ_X1A`,
|
||||||
|
:const:`_AD5360_READ_X1B`, :const:`_AD5360_READ_OFFSET`,
|
||||||
|
:const:`_AD5360_READ_GAIN` (default: :const:`_AD5360_READ_X1A`).
|
||||||
|
:return: The 16 bit register value.
|
||||||
|
"""
|
||||||
channel &= 0x3f
|
channel &= 0x3f
|
||||||
self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_READ | op |
|
self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_READ | op |
|
||||||
_AD5360_READ_CHANNEL(channel))
|
_AD5360_READ_CHANNEL(channel))
|
||||||
|
@ -90,6 +139,10 @@ class AD5360:
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def load(self):
|
def load(self):
|
||||||
|
"""Pulse the LDAC line.
|
||||||
|
|
||||||
|
This method advances the timeline by two RTIO clock periods (16 ns).
|
||||||
|
"""
|
||||||
self.ldac.off()
|
self.ldac.off()
|
||||||
# t13 = 10ns ldac pulse width low
|
# t13 = 10ns ldac pulse width low
|
||||||
delay_mu(2*self.bus.ref_period_mu)
|
delay_mu(2*self.bus.ref_period_mu)
|
||||||
|
@ -97,6 +150,17 @@ class AD5360:
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set(self, values, op=_AD5360_CMD_DATA):
|
def set(self, values, op=_AD5360_CMD_DATA):
|
||||||
|
"""Write to several channels and pulse LDAC to update the channels.
|
||||||
|
|
||||||
|
This method does not advance the timeline. Write events are scheduled
|
||||||
|
in the past. The DACs will synchronously start changing their output
|
||||||
|
levels `now`.
|
||||||
|
|
||||||
|
:param values: List of 16 bit values to write to the channels.
|
||||||
|
:param op: Operation to perform, one of :const:`_AD5360_CMD_DATA`,
|
||||||
|
:const:`_AD5360_CMD_OFFSET`, :const:`_AD5360_CMD_GAIN`
|
||||||
|
(default: :const:`_AD5360_CMD_DATA`).
|
||||||
|
"""
|
||||||
# compensate all delays that will be applied
|
# compensate all delays that will be applied
|
||||||
delay_mu(-len(values)*(self.bus.xfer_period_mu +
|
delay_mu(-len(values)*(self.bus.xfer_period_mu +
|
||||||
self.bus.write_period_mu +
|
self.bus.write_period_mu +
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
from artiq.experiment import *
|
||||||
|
|
||||||
|
|
||||||
|
class AD5360Test(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
self.setattr_device("core")
|
||||||
|
self.dac = self.get_device("dac0")
|
||||||
|
self.setattr_device("led")
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def run(self):
|
||||||
|
self.dac.setup_bus(write_div=30, read_div=40)
|
||||||
|
self.dac.write_offsets()
|
||||||
|
self.led.on()
|
||||||
|
delay(400*us)
|
||||||
|
self.led.off()
|
||||||
|
self.dac.set([i << 10 for i in range(40)])
|
Loading…
Reference in New Issue