import mmap import os import logging import time logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) import spidev from pyfastservo.common import ( CH0_HIGH_WORD_ADDR, CH0_LOW_WORD_ADDR, CH1_HIGH_WORD_ADDR, CH1_LOW_WORD_ADDR, CTRL_ADDR, MAP_MASK, PAGESIZE, ) # /dev/spidev2.0 <=> spidev. MAIN_DAC_BUS = 2 MAIN_DAC_DEVICE = 0 DAC_VERSION = 0x0A def spi_write(spi, address, value): spi.xfer2([address, value]) def spi_read(spi, address): rx_buffer = spi.xfer2([0x80 | address, 0x00]) return rx_buffer[1] def hard_reset(spi): spi_write(spi, 0x00, 0x10) # Software reset spi_write(spi, 0x00, 0x00) # Release software reset spi_read(spi, 0x00) # Read reset address (necessary for reset to take effect) def check_version(spi): version = spi_read(spi, 0x1F) if version == DAC_VERSION: print(f"Verified DAC version: 0x{version:02X}") else: print(f"Unrecognized device version: 0x{version:02X}") def set_data_control_and_output_mode(spi, mode='differential'): try: current_reg = spi_read(spi, 0x02) logger.info(f"Current data control register value: 0x{current_reg:02X}") new_reg = 0xB4 if mode == 'differential': new_reg &= ~0x03 elif mode == 'single_ended': new_reg = (new_reg & ~0x01) | 0x02 else: logger.error(f"Invalid output mode: {mode}") return spi_write(spi, 0x02, new_reg) verify_reg = spi_read(spi, 0x02) if verify_reg == new_reg: logger.info(f"Data control set and output mode successfully set to {mode}") logger.info(f"New data control register value: 0x{verify_reg:02X}") else: logger.error(f"Failed to set data control and output mode. Expected: 0x{new_reg:02X}, Got: 0x{verify_reg:02X}") except Exception as e: logger.error(f"Error setting data control and output mode: {e}") def wait_for_clock_sync(spi): max_attempts = 10 for attempt in range(max_attempts): clkmode = spi_read(spi, 0x14) if not clkmode & 0x10: print(f"Clock synchronized after {attempt + 1} attempts") break if attempt == max_attempts - 1: print("Warning: Clock synchronization not achieved") time.sleep(0.001) def enable_dac_outputs(spi): try: # Read current power-down register value power_down_reg = spi_read(spi, 0x01) logger.info(f"Current power-down register value: 0x{power_down_reg:02X}") if not (power_down_reg & ((1 << 4) | (1 << 3))): logger.info("DAC outputs are already enabled") return new_power_down_reg = power_down_reg & ~((1 << 4) | (1 << 3)) spi_write(spi, 0x01, new_power_down_reg) # Verify the write operation verify_power_down_reg = spi_read(spi, 0x01) if verify_power_down_reg == new_power_down_reg: logger.info("DAC outputs successfully enabled") else: logger.error(f"Failed to enable DAC outputs. Expected: 0x{new_power_down_reg:02X}, Got: 0x{verify_power_down_reg:02X}") except Exception as e: logger.error(f"Error enabling DAC outputs: {e}") def set_dac_constant_output(value=0xFFFF): try: max_value = 0x3FFF # 14-bit maximum (2^14 - 1) value = min(value, max_value) # Split the value into high and low words low_word_value = value & 0xFF high_word_value = (value >> 8) & 0x3F # Write to Channel 0 write_to_memory(CH0_HIGH_WORD_ADDR, high_word_value) write_to_memory(CH0_LOW_WORD_ADDR, low_word_value) # Write to Channel 1 write_to_memory(CH1_HIGH_WORD_ADDR, high_word_value) write_to_memory(CH1_LOW_WORD_ADDR, low_word_value) logger.info(f"DAC outputs set to constant value: 0x{value:04X}") logger.info("Please verify the output using an oscilloscope") except Exception as e: logger.error(f"Error setting DAC constant output: {e}") def configure_dac_for_hardware(spi): try: # Enable internal reference power_down_reg = spi_read(spi, 0x01) power_down_reg &= ~(1 << 0) # Clear EXTREF bit for internal reference spi_write(spi, 0x01, power_down_reg) logger.info("Internal reference enabled") # Set RREF to default 10 kΩ for 1.0V reference spi_write(spi, 0x0D, 0x00) logger.info("RREF set to 10 kΩ for 1.0V reference") # Enable on-chip IRSET and QRSET irset_value = 0xA0 # 10100000 spi_write(spi, 0x04, irset_value) # IRSET spi_write(spi, 0x07, irset_value) # QRSET # Disable internal termination resistors ircml_value = 0x00 spi_write(spi, 0x05, ircml_value) # IRCML spi_write(spi, 0x08, ircml_value) # QRCML spi_write(spi, 0x02, 0xB4) # Verify settings irset_read = spi_read(spi, 0x04) qrset_read = spi_read(spi, 0x07) ircml_read = spi_read(spi, 0x05) qrcml_read = spi_read(spi, 0x08) data_control_read = spi_read(spi, 0x02) if (irset_read == irset_value and qrset_read == irset_value and ircml_read == ircml_value and qrcml_read == ircml_value and data_control_read == 0xB4): logger.info("DAC configured for 20mA output with external termination") else: logger.error(f"DAC configuration failed. IRSET: 0x{irset_read:02X}, QRSET: 0x{qrset_read:02X}, " f"IRCML: 0x{ircml_read:02X}, QRCML: 0x{qrcml_read:02X}, " f"Data Control: 0x{data_control_read:02X}") except Exception as e: logger.error(f"Error configuring DAC: {e}") def read_from_memory(address, n_bytes): assert n_bytes <= 4 addr = address try: f = os.open("/dev/mem", os.O_SYNC | os.O_RDWR) with mmap.mmap( f, PAGESIZE, mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, offset=addr & ~MAP_MASK, ) as mem: start_addr = addr & MAP_MASK stop_addr = start_addr + 4 # print(f"addr: 0x{addr:x}\tstart_addr: 0x{start_addr}\tstop_addr: 0x{stop_addr}") contents = mem[start_addr:stop_addr] read_value = list(contents)[:n_bytes] finally: os.close(f) return read_value def write_to_memory(address, value): value_bytes = value.to_bytes(4, "little") addr = address try: f = os.open("/dev/mem", os.O_SYNC | os.O_RDWR) with mmap.mmap( f, PAGESIZE, mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, offset=addr & ~MAP_MASK, ) as mem: start_addr = addr & MAP_MASK stop_addr = start_addr + 4 mem[start_addr:stop_addr] = value_bytes contents = mem[start_addr:stop_addr] finally: os.close(f) def manual_override(enable=True): reg_contents = read_from_memory(CTRL_ADDR, 1)[0] print(f"REG contents: 0b{reg_contents:03b}") to_write = reg_contents | 0b1 if enable else reg_contents & 0b110 write_to_memory(CTRL_ADDR, to_write) def power_down(channel, power_down=True): assert channel in (0, 1) bitmask = 1 << (channel + 1) & 0b111 reg_contents = read_from_memory(CTRL_ADDR, 1)[0] value = (1 if power_down else 0) << (channel + 1) reg_contents &= ~bitmask to_write = reg_contents | value write_to_memory(CTRL_ADDR, to_write) reg_contents = read_from_memory(CTRL_ADDR, 1)[0] print(f"REG contents: 0b{reg_contents:03b}") def main_dac_init(): spi = spidev.SpiDev() try: spi.open(MAIN_DAC_BUS, MAIN_DAC_DEVICE) spi.max_speed_hz = 5000 spi.mode = 0b00 # CPOL = 0 CPHA = 0 spi.cshigh = False hard_reset(spi) check_version(spi) configure_dac_for_hardware(spi) set_data_control_and_output_mode(spi, 'differential') wait_for_clock_sync(spi) enable_dac_outputs(spi) power_down(0, False) power_down(1, False) manual_override(True) set_dac_constant_output(0x1FFF) # Verify control register reg_contents = read_from_memory(CTRL_ADDR, 1)[0] logger.info(f"Control register contents after setting: 0b{reg_contents:03b}") # Verify channel settings ch0_high = read_from_memory(CH0_HIGH_WORD_ADDR, 1)[0] ch0_low = read_from_memory(CH0_LOW_WORD_ADDR, 1)[0] ch1_high = read_from_memory(CH1_HIGH_WORD_ADDR, 1)[0] ch1_low = read_from_memory(CH1_LOW_WORD_ADDR, 1)[0] logger.info(f"Channel 0: 0x{ch0_high:02X}{ch0_low:02X}, Channel 1: 0x{ch1_high:02X}{ch1_low:02X}") except Exception as e: print(f"Error initializing DAC: {e}") finally: spi.close() def main(): main_dac_init() if __name__ == "__main__": main()