|
|
|
@ -1,26 +1,59 @@ |
|
|
|
|
import gex |
|
|
|
|
|
|
|
|
|
CMD_STOP = 0 |
|
|
|
|
CMD_PWM_CONT_START = 1 |
|
|
|
|
CMD_PWM_BURST_START = 2 |
|
|
|
|
CMD_PWM_CONT_READ = 10 |
|
|
|
|
|
|
|
|
|
# Measuring a waveform |
|
|
|
|
CMD_INDIRECT_CONT_START = 1 # keep measuring, read on demand |
|
|
|
|
CMD_INDIRECT_BURST_START = 2 # wait and reply |
|
|
|
|
|
|
|
|
|
# Counting pulses |
|
|
|
|
CMD_DIRECT_CONT_START = 3 # keep measuring, read on demand |
|
|
|
|
CMD_DIRECT_BURST_START = 4 # wait and reply |
|
|
|
|
CMD_FREECOUNT_START = 5 # keep counting pulses until stopped, read on reply |
|
|
|
|
|
|
|
|
|
CMD_MEASURE_SINGLE_PULSE = 6 |
|
|
|
|
CMD_FREECOUNT_CLEAR = 7 |
|
|
|
|
|
|
|
|
|
# Results readout for continuous modes |
|
|
|
|
CMD_INDIRECT_CONT_READ = 10 |
|
|
|
|
CMD_DIRECT_CONT_READ = 11 |
|
|
|
|
CMD_FREECOUNT_READ = 12 |
|
|
|
|
|
|
|
|
|
CMD_SET_POLARITY = 20 |
|
|
|
|
CMD_SET_DIR_PRESC = 21 |
|
|
|
|
CMD_SET_INPUT_FILTER = 22 |
|
|
|
|
CMD_SET_DIR_MSEC = 23 |
|
|
|
|
|
|
|
|
|
CMD_RESTORE_DEFAULTS = 30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FCAP_Report: |
|
|
|
|
def __init__(self): |
|
|
|
|
self.period = 0 |
|
|
|
|
self.ontime = 0 |
|
|
|
|
self.frequency = 0 |
|
|
|
|
self.duty = 0 |
|
|
|
|
self.period = None # s |
|
|
|
|
self.ontime = None # s |
|
|
|
|
self.frequency = None # Hz |
|
|
|
|
self.duty = None # [-] |
|
|
|
|
|
|
|
|
|
# Raw data (can be used to avoid distortion by float arithmetics) |
|
|
|
|
self.period_raw = 0 |
|
|
|
|
self.ontime_raw = 0 |
|
|
|
|
self.sample_count = 0 |
|
|
|
|
self.clock_freq = 0 |
|
|
|
|
self.period_raw = None |
|
|
|
|
self.ontime_raw = None |
|
|
|
|
self.sample_count = None |
|
|
|
|
self.clock_freq = None # Hz |
|
|
|
|
self.meas_time_ms = None |
|
|
|
|
|
|
|
|
|
def __str__(self): |
|
|
|
|
return "{\n f = %f Hz\n T = %f s\n Ton = %f s\n duty = %f\n}" % \ |
|
|
|
|
(self.frequency, self.period, self.ontime, self.duty) |
|
|
|
|
s = "{\n" |
|
|
|
|
if self.frequency is not None: |
|
|
|
|
s += " f = %f Hz\n" % self.frequency |
|
|
|
|
if self.period is not None: |
|
|
|
|
s += " T = %f s\n" % self.period |
|
|
|
|
if self.ontime is not None: |
|
|
|
|
s += " Ton = %f s\n" % self.ontime |
|
|
|
|
if self.duty is not None: |
|
|
|
|
s += " duty = %f\n" % self.duty |
|
|
|
|
s += "}" |
|
|
|
|
|
|
|
|
|
return s |
|
|
|
|
|
|
|
|
|
class FCAP(gex.Unit): |
|
|
|
|
""" |
|
|
|
@ -34,17 +67,95 @@ class FCAP(gex.Unit): |
|
|
|
|
""" Stop any ongoing capture """ |
|
|
|
|
self._send(CMD_STOP, confirm=confirm) |
|
|
|
|
|
|
|
|
|
def configure(self, |
|
|
|
|
polarity=None, |
|
|
|
|
presc=None, |
|
|
|
|
filter=None, |
|
|
|
|
msec=None, |
|
|
|
|
confirm=True): |
|
|
|
|
""" |
|
|
|
|
Re-configure some capture parameters. None = unchanged |
|
|
|
|
|
|
|
|
|
polarity: 0,1 active level |
|
|
|
|
presc: 1,2,4,8 pulse counter prescaller |
|
|
|
|
filter: 0-15 digital input filter |
|
|
|
|
msec: <65535 milliseconds for direct capture |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
if polarity is not None: |
|
|
|
|
pb = gex.PayloadBuilder() |
|
|
|
|
pb.u8(polarity) # 0,1 |
|
|
|
|
self._send(CMD_SET_POLARITY, pld=pb.close(), confirm=confirm) |
|
|
|
|
|
|
|
|
|
if presc is not None: |
|
|
|
|
pb = gex.PayloadBuilder() |
|
|
|
|
pb.u8(presc) |
|
|
|
|
self._send(CMD_SET_DIR_PRESC, pld=pb.close(), confirm=confirm) |
|
|
|
|
|
|
|
|
|
if filter is not None: |
|
|
|
|
pb = gex.PayloadBuilder() |
|
|
|
|
pb.u8(filter) |
|
|
|
|
self._send(CMD_SET_INPUT_FILTER, pld=pb.close(), confirm=confirm) |
|
|
|
|
|
|
|
|
|
if msec is not None: |
|
|
|
|
pb = gex.PayloadBuilder() |
|
|
|
|
pb.u16(msec) |
|
|
|
|
self._send(CMD_SET_DIR_MSEC, pld=pb.close(), confirm=confirm) |
|
|
|
|
|
|
|
|
|
def config_reset(self, confirm=True): |
|
|
|
|
""" Reset all config to persistent defaults and switch to IDLE mode. """ |
|
|
|
|
self._send(CMD_RESTORE_DEFAULTS, confirm=confirm) |
|
|
|
|
|
|
|
|
|
def indirect_start(self, confirm=True): |
|
|
|
|
""" Start continuous PWM measurement """ |
|
|
|
|
self._send(CMD_PWM_CONT_START, confirm=confirm) |
|
|
|
|
self._send(CMD_INDIRECT_CONT_START, confirm=confirm) |
|
|
|
|
|
|
|
|
|
def counter_start(self, presc=None, confirm=True): |
|
|
|
|
""" Start the free-running counter """ |
|
|
|
|
|
|
|
|
|
pb = gex.PayloadBuilder() |
|
|
|
|
pb.u8(presc or 0) |
|
|
|
|
self._send(CMD_FREECOUNT_START, pld=pb.close(), confirm=confirm) |
|
|
|
|
|
|
|
|
|
def counter_read(self): |
|
|
|
|
""" Read the free counter value """ |
|
|
|
|
|
|
|
|
|
resp = self._query(CMD_FREECOUNT_READ) |
|
|
|
|
pp = gex.PayloadParser(resp.data) |
|
|
|
|
return pp.u32() |
|
|
|
|
|
|
|
|
|
def counter_clear(self): |
|
|
|
|
""" |
|
|
|
|
Restart the free-running counter, returns current value before the clear. |
|
|
|
|
This should lose at most 1 tick for signals where f < core clock speed |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
resp = self._query(CMD_FREECOUNT_CLEAR) |
|
|
|
|
pp = gex.PayloadParser(resp.data) |
|
|
|
|
return pp.u32() |
|
|
|
|
|
|
|
|
|
def direct_start(self, msec=None, presc=None, confirm=True): |
|
|
|
|
""" |
|
|
|
|
Start continuous PWM measurement |
|
|
|
|
|
|
|
|
|
msec - measurement time (ms), <65535 |
|
|
|
|
presc - pre-divider, 1,2,4,8. |
|
|
|
|
|
|
|
|
|
arg None = unchanged |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
pb = gex.PayloadBuilder() |
|
|
|
|
pb.u16(msec or 0) |
|
|
|
|
pb.u8(presc or 0) |
|
|
|
|
self._send(CMD_DIRECT_CONT_START, pld=pb.close(), confirm=confirm) |
|
|
|
|
|
|
|
|
|
def indirect_read(self): |
|
|
|
|
""" |
|
|
|
|
Read the current continuous measurement values |
|
|
|
|
Returns value of the last measurement in continuous mode |
|
|
|
|
Read the current indirect continuous measurement values |
|
|
|
|
Returns value of the last measurement in continuous indirect mode |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
resp = self._query(CMD_PWM_CONT_READ) |
|
|
|
|
resp = self._query(CMD_INDIRECT_CONT_READ) |
|
|
|
|
pp = gex.PayloadParser(resp.data) |
|
|
|
|
|
|
|
|
|
mhz = pp.u16() |
|
|
|
@ -65,11 +176,59 @@ class FCAP(gex.Unit): |
|
|
|
|
# returned in microseconds |
|
|
|
|
return rp |
|
|
|
|
|
|
|
|
|
def indirect_single(self, timeout=5): |
|
|
|
|
def _process_direct_resp(self, resp): |
|
|
|
|
pp = gex.PayloadParser(resp.data) |
|
|
|
|
|
|
|
|
|
presc = pp.u8() |
|
|
|
|
msec = pp.u16() |
|
|
|
|
count = pp.u32() * presc |
|
|
|
|
|
|
|
|
|
rp = FCAP_Report() |
|
|
|
|
|
|
|
|
|
if count > 0: |
|
|
|
|
sec = msec / 1000 |
|
|
|
|
freq = count / sec |
|
|
|
|
period = 1 / freq |
|
|
|
|
|
|
|
|
|
rp.period = period |
|
|
|
|
rp.frequency = freq |
|
|
|
|
|
|
|
|
|
rp.sample_count = count * presc |
|
|
|
|
rp.meas_time_ms = msec |
|
|
|
|
|
|
|
|
|
return rp |
|
|
|
|
|
|
|
|
|
def direct_read(self): |
|
|
|
|
""" |
|
|
|
|
Perform a burst measure with averaging (sum/count) |
|
|
|
|
Read the current direct continuous measurement values |
|
|
|
|
Returns value of the last measurement in continuous direct mode |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
resp = self._query(CMD_DIRECT_CONT_READ) |
|
|
|
|
return self._process_direct_resp(resp) |
|
|
|
|
|
|
|
|
|
def measure_pulse(self, polarity=None, timeout=5): |
|
|
|
|
""" |
|
|
|
|
Measure a pulse. Optionally set polarity |
|
|
|
|
""" |
|
|
|
|
return self.indirect_burst(count=1, timeout=timeout) |
|
|
|
|
|
|
|
|
|
if polarity is not None: |
|
|
|
|
self.configure(polarity=polarity) |
|
|
|
|
|
|
|
|
|
resp = self._query(CMD_MEASURE_SINGLE_PULSE, timeout=timeout) |
|
|
|
|
pp = gex.PayloadParser(resp.data) |
|
|
|
|
|
|
|
|
|
mhz = pp.u16() |
|
|
|
|
ontime = pp.u32() |
|
|
|
|
|
|
|
|
|
rp = FCAP_Report() |
|
|
|
|
rp.ontime = ontime / (mhz * 1e6) # in seconds |
|
|
|
|
|
|
|
|
|
rp.clock_freq = mhz * 1e6 |
|
|
|
|
rp.sample_count = 1 |
|
|
|
|
rp.ontime_raw = ontime |
|
|
|
|
|
|
|
|
|
return rp |
|
|
|
|
|
|
|
|
|
def indirect_burst(self, count, timeout=5): |
|
|
|
|
""" |
|
|
|
@ -79,7 +238,7 @@ class FCAP(gex.Unit): |
|
|
|
|
pb = gex.PayloadBuilder() |
|
|
|
|
pb.u16(count) |
|
|
|
|
|
|
|
|
|
resp = self._query(CMD_PWM_BURST_START, pld=pb.close(), timeout=timeout) |
|
|
|
|
resp = self._query(CMD_INDIRECT_BURST_START, pld=pb.close(), timeout=timeout) |
|
|
|
|
pp = gex.PayloadParser(resp.data) |
|
|
|
|
|
|
|
|
|
mhz = pp.u16() |
|
|
|
@ -100,3 +259,16 @@ class FCAP(gex.Unit): |
|
|
|
|
|
|
|
|
|
return rp |
|
|
|
|
|
|
|
|
|
def direct_burst(self, msec=1000, presc=None): |
|
|
|
|
""" |
|
|
|
|
Perform direct burst measurement |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
pb = gex.PayloadBuilder() |
|
|
|
|
pb.u16(msec) |
|
|
|
|
pb.u8(presc or 0) |
|
|
|
|
|
|
|
|
|
resp = self._query(CMD_DIRECT_BURST_START, |
|
|
|
|
pld=pb.close(), |
|
|
|
|
timeout=(msec/1000)+1) |
|
|
|
|
return self._process_direct_resp(resp) |
|
|
|
|