Created
October 9, 2019 05:26
-
-
Save theacodes/0714d78c1356eee2b57ac024d436eac6 to your computer and use it in GitHub Desktop.
A helper library to setting a DAC to direct voltage values
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# (c) 2019 Alethea Flowers | |
# Licensed under the MIT license. | |
"""A helper library to setting a DAC to direct voltage values. | |
That is, instead of setting a 16-bit integer value you can set the DAC to a | |
floating-point voltage value. | |
This requires information about the real-world DAC output and any output | |
scaling - this is called **calibration** data. | |
""" | |
import math | |
class VoltageOut: | |
"""Wraps an AnalogOut object to set voltage instead of 16-bit value. | |
Example:: | |
vout = winterbloom_voltage_out.VoltageOut(board.A1) | |
vout.linear_calibration(3.3) | |
vout.voltage = 1.23 | |
""" | |
def __init__(self, analog_out): | |
self._analog_out = analog_out | |
self._calibration = {} | |
def linear_calibration(self, max_voltage): | |
"""Determines intermediate calibration values using the given | |
maximum output voltage (assumes the minimum is 0). This is the | |
simplest way to calibrate the output. It assumes that the DAC | |
and any output scaling op amps have an exactly linear response. | |
Example:: | |
vout.linear_calibration(10.26) | |
""" | |
self._calibration = { | |
voltage: int((voltage * 65535) / max_voltage) | |
for voltage | |
in range(math.ceil(max_voltage) + 1) | |
} | |
print(self._calibration) | |
def direct_calibration(self, calibration): | |
"""Allows you to set the calibration values directly. Generally | |
you'll do this by driving the DAC to each integral voltage in | |
the output range and recording the 16-bit DAC value. Then, you | |
can pass these integrals and associated values into this function. | |
Example:: | |
vout.direct_calibration({ | |
0: 0, | |
1: 1000, | |
2: 2000, | |
3: 3000, | |
... | |
}) | |
""" | |
self._calibration.update(calibration) | |
def _calibrated_value_for_voltage(self, voltage): | |
# TODO: Use a function like take_nearest to allow this to work on non-integral, | |
# non-continuos calibration data. | |
root = int(math.floor(voltage)) | |
next_root = root + 1 | |
offset = voltage - root | |
root_val = self._calibration[root] | |
next_val = self._calibration[next_root] | |
lerped = int(root_val + ((next_val - root_val) * offset)) | |
print(voltage, root, root_val, lerped) | |
return min(lerped, 65535) | |
def _set_voltage(self, voltage): | |
value = self._calibrated_value_for_voltage(voltage) | |
print("Voltage: ", voltage, "Value: ", value) | |
self._analog_out.value = value | |
voltage = property(None, _set_voltage) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment