// AD5593R Analog/Digital I/O Device Services // Copyright (C)2020-2023, Philip Munts dba Munts Technologies. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. namespace IO.Devices.AD5593R { /// /// AD5593R I/O Pin Modes. /// public enum PinMode { /// /// Analog input. /// ADC_Input, /// /// Analog output. /// DAC_Output, /// /// GPIO input. /// GPIO_Input, /// /// GPIO output. /// GPIO_Output, /// /// GPIO open drain output. /// GPIO_Output_OpenDrain, } /// /// ADC5593R ADC and DAC reference settings. /// public enum ReferenceMode { /// /// The reference voltage is 2.5V using the internal reference. /// Internalx1, /// /// The reference voltage is 5.0V using the internal reference. /// Internalx2, /// /// The reference voltage is 1.0*Vref, using an external reference. /// Externalx1, /// /// The reference voltage is 2.0*Vref, using an external reference. /// Externalx2, } /// /// Enscapsulates the AD5593R I2C Analog/Digital I/O device. /// public class Device { // Pointer byte modes (high nibble) private const byte MODE_CONFIGURATION = 0x00; private const byte MODE_DAC_WRITE = 0x10; private const byte MODE_ADC_READBACK = 0x40; private const byte MODE_DAC_READBACK = 0x50; private const byte MODE_GPIO_READBACK = 0x60; private const byte MODE_REG_READBACK = 0x70; // Pointer byte register addresses (low nibble) private const byte REG_NOP = 0x00; private const byte REG_ADC_SEQ = 0x02; private const byte REG_CONTROL = 0x03; private const byte REG_ADC_PIN_CONFIG = 0x04; private const byte REG_DAC_PIN_CONFIG = 0x05; private const byte REG_PULLDOWN_CONFIG = 0x06; private const byte REG_LDAC_MODE = 0x07; private const byte REG_GPIO_WRITE_CONFIG = 0x08; private const byte REG_GPIO_WRITE_DATA = 0x09; private const byte REG_GPIO_READ_CONFIG = 0x0A; private const byte REG_POWERDOWN = 0x0B; private const byte REG_OPENDRAIN_CONFIG = 0x0C; private const byte REG_TRISTATE_CONFIG = 0x0D; private const byte REG_SOFTWARE_RESET = 0x0F; // Define some register mask constants private const uint MASK_ADC_RANGE = 0x0020; private const uint MASK_DAC_RANGE = 0x0010; private const uint MASK_EN_REF = 0x0200; // Preallocated I2C bus transaction buffers private byte[] inbuf = { 0, 0 }; private byte[] outbuf = { 0, 0, 0 }; private bool Internal(ReferenceMode x) { return (x >= ReferenceMode.Internalx1) && (x <= ReferenceMode.Internalx2); } private bool External(ReferenceMode x) { return (x >= ReferenceMode.Externalx1) && (x <= ReferenceMode.Externalx2); } // Read 16-bit value from a register private uint ReadRegister(byte mode, byte reg = 0) { outbuf[0] = (byte)(mode | reg); dev.Transaction(outbuf, 1, inbuf, 2); return (uint)((inbuf[0] << 8) | inbuf[1]); } // Write 16-bit value to a register private void WriteRegister(byte mode, byte reg, uint data) { outbuf[0] = (byte)(mode | reg); outbuf[1] = (byte)(data >> 8); outbuf[2] = (byte)data; dev.Write(outbuf, 3); } // I2C device handle private readonly IO.Interfaces.I2C.Device dev; // Shadow some AD5593R registers private uint reg_control = 0x0100; private uint reg_powerdown = 0x02FF; private uint reg_pin_config_adc = 0x00; private uint reg_pin_config_dac = 0x00; private uint reg_pin_config_gpio_read = 0x00; private uint reg_pin_config_gpio_write = 0x00; private uint reg_pin_config_pulldown = 0xFF; private uint reg_pin_config_opendrain = 0x00; private uint reg_pin_config_tristate = 0x00; private uint reg_gpio_write_data = 0x00; /// /// Minimum I/O channel number. /// public const int MinChannel = 0; /// /// Maximum I/O channel number. /// public const int MaxChannel = 7; /// /// ADC resolution in bits. /// public const int ADC_Resolution = 12; /// /// DAC resolution in bits. /// public const int DAC_Resolution = 12; /// /// Constructor for a single AD5593R device. /// /// I2C bus controller object. /// I2C slave address. public Device(IO.Interfaces.I2C.Bus bus, int addr) { // Create an I2C device object dev = new IO.Interfaces.I2C.Device(bus, addr); // Configure the ADC5593R. WriteRegister(MODE_CONFIGURATION, REG_CONTROL, reg_control); WriteRegister(MODE_CONFIGURATION, REG_POWERDOWN, reg_powerdown); WriteRegister(MODE_CONFIGURATION, REG_LDAC_MODE, 0x0000); WriteRegister(MODE_CONFIGURATION, REG_ADC_PIN_CONFIG, reg_pin_config_adc); WriteRegister(MODE_CONFIGURATION, REG_DAC_PIN_CONFIG, reg_pin_config_dac); WriteRegister(MODE_CONFIGURATION, REG_GPIO_READ_CONFIG, reg_pin_config_gpio_read); WriteRegister(MODE_CONFIGURATION, REG_GPIO_WRITE_CONFIG, reg_pin_config_gpio_write); WriteRegister(MODE_CONFIGURATION, REG_PULLDOWN_CONFIG, reg_pin_config_pulldown); WriteRegister(MODE_CONFIGURATION, REG_OPENDRAIN_CONFIG, reg_pin_config_opendrain); WriteRegister(MODE_CONFIGURATION, REG_TRISTATE_CONFIG, reg_pin_config_tristate); WriteRegister(MODE_CONFIGURATION, REG_GPIO_WRITE_DATA, reg_gpio_write_data); } /// /// Write-only property for setting the AD5593R ADC reference mode. /// public ReferenceMode ADC_Reference { set { switch (value) { case ReferenceMode.Internalx1: reg_control &= ~MASK_ADC_RANGE; reg_powerdown |= MASK_EN_REF; break; case ReferenceMode.Internalx2: reg_control |= MASK_ADC_RANGE; reg_powerdown |= MASK_EN_REF; break; case ReferenceMode.Externalx1: reg_control &= ~MASK_ADC_RANGE; reg_powerdown &= ~MASK_EN_REF; break; case ReferenceMode.Externalx2: reg_control |= MASK_ADC_RANGE; reg_powerdown &= ~MASK_EN_REF; break; } WriteRegister(MODE_CONFIGURATION, REG_CONTROL, reg_control); WriteRegister(MODE_CONFIGURATION, REG_POWERDOWN, reg_powerdown); } } /// /// Write-only property for setting the AD5593R DAC reference mode. /// public ReferenceMode DAC_Reference { set { switch (value) { case ReferenceMode.Internalx1: reg_control &= ~MASK_DAC_RANGE; reg_powerdown |= MASK_EN_REF; break; case ReferenceMode.Internalx2: reg_control |= MASK_DAC_RANGE; reg_powerdown |= MASK_EN_REF; break; case ReferenceMode.Externalx1: reg_control &= ~MASK_DAC_RANGE; reg_powerdown &= ~MASK_EN_REF; break; case ReferenceMode.Externalx2: reg_control |= MASK_DAC_RANGE; reg_powerdown &= ~MASK_EN_REF; break; } WriteRegister(MODE_CONFIGURATION, REG_CONTROL, reg_control); WriteRegister(MODE_CONFIGURATION, REG_POWERDOWN, reg_powerdown); } } /// /// Configure a single ADC5593R I/O pin. /// /// ADC5593R I/O channel number (0 to 7). /// ADC5593R I/O pin mode. public void ConfigureChannel(int channel, PinMode mode) { // Validate parameters if ((channel < MinChannel) || (channel > MaxChannel)) throw new System.Exception("Invalid channel number."); uint mask = (uint)(1 << channel); switch (mode) { case PinMode.ADC_Input: reg_pin_config_adc |= mask; reg_pin_config_dac &= ~mask; reg_pin_config_gpio_read &= ~mask; reg_pin_config_gpio_write &= ~mask; reg_pin_config_pulldown &= ~mask; reg_pin_config_opendrain &= ~mask; reg_pin_config_tristate &= ~mask; reg_powerdown &= ~mask; break; case PinMode.DAC_Output: reg_pin_config_adc &= ~mask; reg_pin_config_dac |= mask; reg_pin_config_gpio_read &= ~mask; reg_pin_config_gpio_write &= ~mask; reg_pin_config_pulldown &= ~mask; reg_pin_config_opendrain &= ~mask; reg_pin_config_tristate &= ~mask; reg_powerdown &= ~mask; break; case PinMode.GPIO_Input: reg_pin_config_adc &= ~mask; reg_pin_config_dac &= ~mask; reg_pin_config_gpio_read |= mask; reg_pin_config_gpio_write &= ~mask; reg_pin_config_pulldown &= ~mask; reg_pin_config_opendrain &= ~mask; reg_pin_config_tristate &= ~mask; reg_powerdown &= ~mask; break; case PinMode.GPIO_Output: reg_pin_config_adc &= ~mask; reg_pin_config_dac &= ~mask; reg_pin_config_gpio_read &= ~mask; reg_pin_config_gpio_write |= mask; reg_pin_config_pulldown &= ~mask; reg_pin_config_opendrain &= ~mask; reg_pin_config_tristate &= ~mask; reg_powerdown &= ~mask; break; case PinMode.GPIO_Output_OpenDrain: reg_pin_config_adc &= ~mask; reg_pin_config_dac &= ~mask; reg_pin_config_gpio_read &= ~mask; reg_pin_config_gpio_write |= mask; reg_pin_config_pulldown &= ~mask; reg_pin_config_opendrain |= mask; reg_pin_config_tristate &= ~mask; reg_powerdown &= ~mask; break; } WriteRegister(MODE_CONFIGURATION, REG_ADC_PIN_CONFIG, reg_pin_config_adc); WriteRegister(MODE_CONFIGURATION, REG_DAC_PIN_CONFIG, reg_pin_config_dac); WriteRegister(MODE_CONFIGURATION, REG_GPIO_READ_CONFIG, reg_pin_config_gpio_read); WriteRegister(MODE_CONFIGURATION, REG_GPIO_WRITE_CONFIG, reg_pin_config_gpio_write); WriteRegister(MODE_CONFIGURATION, REG_PULLDOWN_CONFIG, reg_pin_config_pulldown); WriteRegister(MODE_CONFIGURATION, REG_OPENDRAIN_CONFIG, reg_pin_config_opendrain); WriteRegister(MODE_CONFIGURATION, REG_TRISTATE_CONFIG, reg_pin_config_tristate); WriteRegister(MODE_CONFIGURATION, REG_POWERDOWN, reg_powerdown); } /// /// Read from an ADC channel. /// /// ADC channel number (0 to 7). /// ADC input sample data (0 to 4095). public int Read_ADC(int channel) { // Validate parameters if ((channel < MinChannel) || (channel > MaxChannel)) throw new System.Exception("Invalid channel number."); WriteRegister(MODE_CONFIGURATION, REG_ADC_SEQ, (uint)(1 << channel)); return (int)(ReadRegister(MODE_ADC_READBACK, 0) & 0x0FFF); } /// /// Write to a DAC channel. /// /// DAC channel number (0 to 7). /// DAC output sample data (0 to 4095). public void Write_DAC(int channel, int data) { // Validate parameters if ((channel < MinChannel) || (channel > MaxChannel)) throw new System.Exception("Invalid channel number."); if ((data < 0) || (data > 4095)) throw new System.Exception("Invalid DAC data value."); WriteRegister(MODE_DAC_WRITE, (byte)channel, (uint)data); } /// /// GPIO output register state. Any I/O pin that is not configured /// as a GPIO output will be written as zero. /// public byte GPIO_Outputs { get { return (byte)reg_gpio_write_data; } set { reg_gpio_write_data = value & reg_pin_config_gpio_write; WriteRegister(MODE_CONFIGURATION, REG_GPIO_WRITE_DATA, reg_gpio_write_data); } } /// /// GPIO input register state. Any I/O pin that is not configured /// as a GPIO input will read as zero. /// public byte GPIO_Inputs { get { return (byte)(ReadRegister(MODE_GPIO_READBACK) & reg_pin_config_gpio_read); } } /// /// Create an AD5593R ADC input object. /// /// AD5593R ADC channel number (0 to 7). /// ADC input object. public IO.Interfaces.ADC.Sample ADC_Create(int channel) { return new IO.Devices.AD5593R.ADC.Sample(this, channel); } /// /// Create an AD5593R DAC output object. /// /// AD5593R DAC channel number (0 to 7). /// Initial DAC output sample. /// DAC output object. public IO.Interfaces.DAC.Sample DAC_Create(int channel, int sample = 0) { return new IO.Devices.AD5593R.DAC.Sample(this, channel, sample); } /// /// Create an AD5593R GPIO pin object. /// /// AD5593R GPIO channel number (0 to 7). /// GPIO pin data direction. /// Initial GPIO output state. /// GPIO pin object. public IO.Interfaces.GPIO.Pin GPIO_Create(int channel, IO.Interfaces.GPIO.Direction dir, bool state = false) { return new IO.Devices.AD5593R.GPIO.Pin(this, channel, dir, state); } } }