Skip to content

Commit 1e08c2f

Browse files
committed
Update power_supply instrument for firmware v3.0
1 parent e0da861 commit 1e08c2f

File tree

3 files changed

+52
-147
lines changed

3 files changed

+52
-147
lines changed

pslab/instrument/power_supply.py

Lines changed: 50 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,14 @@
66
>>> ps = PowerSupply()
77
>>> ps.pv1 = 4.5
88
>>> ps.pv1
9-
4.499389499389499
9+
4.5
1010
1111
>>> ps.pcs = 2e-3
1212
>>> ps.pcs
13-
0.00200014652014652
13+
0.002
1414
"""
1515

16-
import numpy as np
17-
18-
from pslab.bus.i2c import I2CSlave
16+
import pslab.protocol as CP
1917
from pslab.serial_handler import SerialHandler
2018

2119

@@ -34,42 +32,73 @@ class PowerSupply:
3432
instance will be created automatically if not specified.
3533
"""
3634

37-
_ADDRESS = 0x60
35+
_REFERENCE = 3300
36+
_PV1_CH = 3
37+
_PV1_RANGE = (-5, 5)
38+
_PV2_CH = 2
39+
_PV2_RANGE = (-3.3, 3.3)
40+
_PV3_CH = 1
41+
_PV3_RANGE = (0, 3.3)
42+
_PCS_CH = 0
43+
_PCS_RANGE = (3.3e-3, 0)
3844

3945
def __init__(self, device: SerialHandler = None):
4046
self._device = device if device is not None else SerialHandler()
41-
self._mcp4728 = I2CSlave(self._ADDRESS, self._device)
42-
self._pv1 = VoltageSource(self._mcp4728, "PV1")
43-
self._pv2 = VoltageSource(self._mcp4728, "PV2")
44-
self._pv3 = VoltageSource(self._mcp4728, "PV3")
45-
self._pcs = CurrentSource(self._mcp4728)
47+
self._pv1 = None
48+
self._pv2 = None
49+
self._pv3 = None
50+
self._pcs = None
51+
52+
def _set_power(self, channel, output):
53+
self._device.send_byte(CP.DAC)
54+
self._device.send_byte(CP.SET_POWER)
55+
print(channel)
56+
self._device.send_byte(channel)
57+
self._device.send_int(output)
58+
print(output)
59+
return self._device.get_ack()
60+
61+
@staticmethod
62+
def _bound(value, output_range):
63+
return max(min(value, max(output_range)), min(output_range))
64+
65+
def _scale(self, value, output_range):
66+
scaled = (value - output_range[0]) / (output_range[1] - output_range[0])
67+
return int(scaled * self._REFERENCE)
4668

4769
@property
4870
def pv1(self):
4971
"""float: Voltage on PV1; range [-5, 5] V."""
50-
return self._pv1.voltage
72+
return self._pv1
5173

5274
@pv1.setter
5375
def pv1(self, value: float):
54-
self._pv1.voltage = value
76+
value = self._bound(value, self._PV1_RANGE)
77+
ret = self._set_power(self._PV1_CH, self._scale(value, self._PV1_RANGE))
78+
self._pv1 = value
79+
return ret
5580

5681
@property
5782
def pv2(self):
5883
"""float: Voltage on PV2; range [-3.3, 3.3] V."""
59-
return self._pv2.voltage
84+
return self._pv2
6085

6186
@pv2.setter
6287
def pv2(self, value: float):
63-
self._pv2.voltage = value
88+
value = self._bound(value, self._PV2_RANGE)
89+
self._set_power(self._PV2_CH, self._scale(value, self._PV2_RANGE))
90+
self._pv2 = value
6491

6592
@property
6693
def pv3(self):
6794
"""float: Voltage on PV3; range [0, 3.3] V."""
68-
return self._pv3.voltage
95+
return self._pv3
6996

7097
@pv3.setter
7198
def pv3(self, value: float):
72-
self._pv3.voltage = value
99+
value = self._bound(value, self._PV3_RANGE)
100+
self._set_power(self._PV3_CH, self._scale(value, self._PV3_RANGE))
101+
self._pv3 = value
73102

74103
@property
75104
def pcs(self):
@@ -93,136 +122,10 @@ def pcs(self):
93122
current will be only a few hundred µA instead of the maximum available
94123
1.65 mA.
95124
"""
96-
return self._pcs.current
125+
return self._pcs
97126

98127
@pcs.setter
99128
def pcs(self, value: float):
100-
self._pcs.current = value
101-
102-
@property
103-
def _registers(self):
104-
"""Return the contents of the MCP4728's input registers and EEPROM."""
105-
return self._mcp4728.read(24)
106-
107-
108-
class Source:
109-
"""Base class for voltage/current/power sources."""
110-
111-
_RANGE = {
112-
"PV1": (-5, 5),
113-
"PV2": (-3.3, 3.3),
114-
"PV3": (0, 3.3),
115-
"PCS": (3.3e-3, 0),
116-
}
117-
_CHANNEL_NUMBER = {
118-
"PV1": 3,
119-
"PV2": 2,
120-
"PV3": 1,
121-
"PCS": 0,
122-
}
123-
_RESOLUTION = 2**12 - 1
124-
_MULTI_WRITE = 0b01000000
125-
126-
def __init__(self, mcp4728: I2CSlave, name: str):
127-
self._mcp4728 = mcp4728
128-
self.name = name
129-
self.channel_number = self._CHANNEL_NUMBER[self.name]
130-
slope = self._RANGE[self.name][1] - self._RANGE[self.name][0]
131-
intercept = self._RANGE[self.name][0]
132-
self._unscale = np.poly1d(
133-
[self._RESOLUTION / slope, -self._RESOLUTION * intercept / slope]
134-
)
135-
self._scale = np.poly1d([slope / self._RESOLUTION, intercept])
136-
137-
def unscale(self, voltage: float) -> int:
138-
"""Return integer representation of a voltage.
139-
140-
Parameters
141-
----------
142-
voltage : float
143-
Voltage in Volt.
144-
145-
Returns
146-
-------
147-
raw : int
148-
Integer represention of the voltage.
149-
"""
150-
return int(round(self._unscale(voltage)))
151-
152-
def scale(self, raw: int) -> float:
153-
"""Convert an integer value to a voltage value.
154-
155-
Parameters
156-
----------
157-
raw : int
158-
Integer representation of a voltage value.
159-
160-
Returns
161-
-------
162-
voltage : float
163-
Voltage in Volt.
164-
"""
165-
return self._scale(raw)
166-
167-
def _multi_write(self, raw: int):
168-
channel_select = self.channel_number << 1
169-
command_byte = self._MULTI_WRITE | channel_select
170-
data_byte1 = (raw >> 8) & 0x0F
171-
data_byte2 = raw & 0xFF
172-
self._mcp4728.write([data_byte1, data_byte2], register_address=command_byte)
173-
174-
175-
class VoltageSource(Source):
176-
"""Helper class for interfacing with PV1, PV2, and PV3."""
177-
178-
def __init__(self, mcp4728: I2CSlave, name: str):
179-
self._voltage = 0
180-
super().__init__(mcp4728, name)
181-
182-
@property
183-
def voltage(self):
184-
"""float: Most recent voltage set on PVx in Volt.
185-
186-
The voltage on PVx can be set by writing to this attribute.
187-
"""
188-
return self._voltage
189-
190-
@voltage.setter
191-
def voltage(self, value: float):
192-
raw = self.unscale(value)
193-
raw = int(np.clip(raw, 0, self._RESOLUTION))
194-
self._multi_write(raw)
195-
self._voltage = self.scale(raw)
196-
197-
198-
class CurrentSource(Source):
199-
"""Helper class for interfacing with PCS."""
200-
201-
def __init__(self, mcp4728: I2CSlave):
202-
self._current = 0
203-
super().__init__(mcp4728, "PCS")
204-
205-
@property
206-
def current(self):
207-
"""float: Most recent current value set on PCS in Ampere.
208-
209-
The current on PCS can be set by writing to this attribute.
210-
"""
211-
return self._current
212-
213-
@current.setter
214-
def current(self, value: float):
215-
# Output current is a function of the voltage set on the MCP4728's V+
216-
# pin (assuming negligible load resistance):
217-
# I(V) = 3.3e-3 - V / 1000
218-
# I.e. the lower the voltage the higher the current. However, the
219-
# function is discontinuous in V = 0:
220-
# I(0) = 0
221-
if value == 0:
222-
self._multi_write(0)
223-
self._current = 0
224-
else:
225-
raw = self.unscale(value)
226-
raw = int(np.clip(raw, 0, self._RESOLUTION))
227-
self._multi_write(raw)
228-
self._current = self.scale(raw)
129+
value = self._bound(value, self._PCS_RANGE)
130+
self._set_power(self._PCS_CH, self._scale(value, self._PCS_RANGE))
131+
self._pcs = value

pslab/protocol.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
DAC = Byte.pack(6)
8787
SET_DAC = Byte.pack(1)
8888
SET_CALIBRATED_DAC = Byte.pack(2)
89+
SET_POWER = Byte.pack(3)
8990

9091
# /*--------WAVEGEN-----*/
9192
WAVEGEN = Byte.pack(7)

pslab/sciencelab.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
if you need to use several at once the ScienceLab class provides a convenient
55
collection.
66
"""
7+
78
import time
89
from typing import Iterable, List
910

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy