Pulse width triggering using V2 advanced trigger functions

Post general discussions on using our drivers to write your own software here
Post Reply
jwallace91
Newbie
Posts: 0
Joined: Wed Feb 17, 2021 3:14 pm

Pulse width triggering using V2 advanced trigger functions

Post by jwallace91 »

I am having some difficulty understanding how exactly to implement pulse width triggering using the V2 advanced functions.

I'd like to implement the following trigger on a P5242D (using the python wrapper):
Trigger mode: Normal
Type: Pulse width
Source: A
Pre-trigger: 25%
Threshold: 2V
Hysterisis: 1.5%
Pulse direction: Either
Time condition: Inside range 1.3us-5us

I am unsure how to use all six of the V2 and PWQ functions to achieve what I need.

For example…

Conditions there are two separate functions to set the trigger conditions that take the same parameters:
ps5000aSetTriggerChannelConditionsV2
ps5000aSetPulseWidthQualifierConditions

Do I need to use them both? And if so, what PS500A condition should be passed to each?

Directions Same again, there are two different functions that take the same parameters:
ps5000aSetTriggerChannelDirectionsV2
ps5000aSetPulseWidthQualifierDirections

Do I need them both, and what should I pass to them?

Parameters This seems more intuitive to me, I need to call each function once:
ps5000aSetTriggerChannelPropertiesV2 <- I use this to set the voltage threshold on channel A
ps5000aSetPulseWidthQualifierProperties <- I use this to set the time conditions (inside rang 1.3us-5us)

Does any example code exist for setting up a pulse width trigger using the V2 functions?

Thanks in advance.

jwallace91
Newbie
Posts: 0
Joined: Wed Feb 17, 2021 3:14 pm

Re: Pulse width triggering using V2 advanced trigger functions

Post by jwallace91 »

Below (and attached) is my initial attempt at triggering on a positive going pulse on channel A that is at least 1.296uS long with a 1V triggering threshold (based on the 5000ABlockAdvancedTriggerExample). When I test this, it just appears to auto trigger randomly. I have included comments to illustrate what I believe is happening.

I guess how I have set the thresholds is incorrect as I read in the advanced triggering guidance that "Dual thresholds are used for pulse-width triggering, when one threshold applies to the level trigger and the other to the pulse-width qualifier". I do not really understand this. Which threshold applies to the level trigger, the upper or the lower? and how does the other one relate to the pulse-width qualifier?

I also had the problem of the enum PS5000A_PULSE_WIDTH_TYPE not being present in picosdk.ps5000a, so I had to create it in myself.

if anyone can give me some pointers, it would be much appreciated.

Source code:

Code: Select all

import ctypes
import numpy as np
from picosdk.ps5000a import ps5000a as ps
import matplotlib.pyplot as plt
from picosdk.functions import adc2mV, assert_pico_ok, mV2adc
from picosdk.constants import make_enum #Needs this to make missing PWQ enum

# This enumerator is missing from the picoSDK? create it here. 
PS5000A_PULSE_WIDTH_TYPE = make_enum([
    "PS5000A_PW_TYPE_NONE",
    "PS5000A_PW_TYPE_LESS_THAN",
    "PS5000A_PW_TYPE_GREATER_THAN",
    "PS5000A_PW_TYPE_IN_RANGE",
    "PS5000A_PW_TYPE_OUT_OF_RANGE",
    "PS5000A_DIGITAL_DIRECTION_RISING_OR_FALLING",
    "PS5000A_PULSE_WIDTH_TYPE"
])

# Create chandle and status ready for use
chandle = ctypes.c_int16()
status = {}

# picoSDK can not adjust for probe attenuation, a 1:1 probe should be used where possible.
# If not, set the probe attenuation here so the attenuation can be applied after aquisition.
probeAttenuation = 10


# Open 5000 series PicoScope========================================================================================================
# Resolution set to 12 Bit
resolution =ps.PS5000A_DEVICE_RESOLUTION["PS5000A_DR_12BIT"]
# Returns handle to chandle for use in future API functions
status["openunit"] = ps.ps5000aOpenUnit(ctypes.byref(chandle), None, resolution)

try:
    assert_pico_ok(status["openunit"])
except: # PicoNotOkError:

    powerStatus = status["openunit"]

    if powerStatus == 286:
        status["changePowerSource"] = ps.ps5000aChangePowerSource(chandle, powerStatus)
    elif powerStatus == 282:
        status["changePowerSource"] = ps.ps5000aChangePowerSource(chandle, powerStatus)
    else:
        raise

    assert_pico_ok(status["changePowerSource"])

# Set up channels================================================================================================================
channelA = ps.PS5000A_CHANNEL["PS5000A_CHANNEL_A"]
channelB = ps.PS5000A_CHANNEL["PS5000A_CHANNEL_B"]
enabled = 1
coupling_type = ps.PS5000A_COUPLING["PS5000A_DC"]
chARange = ps.PS5000A_RANGE["PS5000A_1V"] #volts per division (10 divisions total)
chBRange = ps.PS5000A_RANGE["PS5000A_1V"] #volts per division (10 divisions total)
analogue_offset = 0 #(Volts)

status["setChA"] = ps.ps5000aSetChannel(chandle, channelA, enabled, coupling_type, chARange, analogue_offset)
assert_pico_ok(status["setChA"])

status["setChB"] = ps.ps5000aSetChannel(chandle, channelB, enabled, coupling_type, chBRange, analogue_offset)
assert_pico_ok(status["setChB"])

# find maximum ADC count value===============================================================================================
maxADC = ctypes.c_int16()
status["maximumValue"] = ps.ps5000aMaximumValue(chandle, ctypes.byref(maxADC))
assert_pico_ok(status["maximumValue"])

# Set up a ulse width trigger==============================================================================================================
#Set Conditions----------------------------------------------------------------------------------------------------------------------------
clear = 1
add = 2	
num_conditions = 1
#Set Channel A as the trigger channel
triggerConditions = ps.PS5000A_CONDITION(   ps.PS5000A_CHANNEL["PS5000A_CHANNEL_A"],
                                            ps.PS5000A_TRIGGER_STATE["PS5000A_CONDITION_TRUE"]
                                        )
status["ps5000aSetTriggerChannelConditionsV2"] = ps.ps5000aSetTriggerChannelConditionsV2(chandle,ctypes.byref(triggerConditions), num_conditions, clear)
assert_pico_ok(status["ps5000aSetTriggerChannelConditionsV2"])

#Apply the pulse width qualifier to channel A
status["ps5000aSetPulseWidthQualifierConditions"] = ps.ps5000aSetPulseWidthQualifierConditions(chandle,ctypes.byref(triggerConditions), num_conditions, clear)
assert_pico_ok(status["ps5000aSetPulseWidthQualifierConditions"])

#Set Directions---------------------------------------------------------------------------------------------------------------------------
#For positive pulse detection, we need to trigger on the falling edge and set the PWQ to reset/start counting on the rising edge.
num_directions = 1
triggerDirection = ps.PS5000A_DIRECTION(   ps.PS5000A_CHANNEL["PS5000A_CHANNEL_A"], 
                                            ps.PS5000A_THRESHOLD_DIRECTION["PS5000A_FALLING"], 
                                            ps.PS5000A_THRESHOLD_MODE["PS5000A_LEVEL"]
                                        )
status["ps5000aSetTriggerChannelDirectionsV2"] = ps.ps5000aSetTriggerChannelDirectionsV2(chandle, ctypes.byref(triggerDirection), num_directions)
assert_pico_ok(status["ps5000aSetTriggerChannelDirectionsV2"])

PWQDirection = ps.PS5000A_DIRECTION(   ps.PS5000A_CHANNEL["PS5000A_CHANNEL_A"], 
                                            ps.PS5000A_THRESHOLD_DIRECTION["PS5000A_RISING"], 
                                            ps.PS5000A_THRESHOLD_MODE["PS5000A_LEVEL"]
                                        )
status["ps5000aSetPulseWidthQualifierDirections"] = ps.ps5000aSetPulseWidthQualifierDirections(chandle, ctypes.byref(triggerDirection), num_directions)
assert_pico_ok(status["ps5000aSetPulseWidthQualifierDirections"])

#Set Properties---------------------------------------------------------------------------------------------------------------------------
triggerLevelmV = 1000
triggerLevel = triggerLevelmV/probeAttenuation
adcTriggerLevel = mV2adc(triggerLevel, chARange, maxADC)

thresholdUpperHysteresis = 10
thresholdLower = 0 #not used for rising or falling edge directions
thresholdLowerHysteresis = 10
triggerProperties = ps.PS5000A_TRIGGER_CHANNEL_PROPERTIES_V2(   adcTriggerLevel, # Threhold upper
                                                                thresholdUpperHysteresis,
								thresholdLower, #Threshold lower
								thresholdLowerHysteresis,
                                                                ps.PS5000A_CHANNEL["PS5000A_CHANNEL_A"]
                                                            )															
status["ps5000aSetTriggerChannelPropertiesV2"] = ps.ps5000aSetTriggerChannelPropertiesV2(chandle, ctypes.byref(triggerProperties), 1, 0)
assert_pico_ok(status["ps5000aSetTriggerChannelPropertiesV2"])

lower = 162 #time base of 3 at 12bit gives a 8ns sample time. 8ns*162= 1.296us
upper = 0 # Not used for greater than trigger, only used for in-range or out-of-range triggers
PWQType = PS5000A_PULSE_WIDTH_TYPE["PS5000A_PW_TYPE_GREATER_THAN"]
status["ps5000aSetPulseWidthQualifierProperties"] = ps.ps5000aSetPulseWidthQualifierProperties(chandle, lower, upper, PWQType)
assert_pico_ok(status["ps5000aSetPulseWidthQualifierProperties"])

#Configure timebase======================================================================================================================================
preTriggerSamples = 1000
postTriggerSamples = 5000
maxSamples = preTriggerSamples + postTriggerSamples
timebase = 3 #8ns at 12-bit
segment_index = 0
returnedTimeIntervalns = ctypes.c_float()
returnedMaxSamples = ctypes.c_int32()
#pass in pointers to maxIntervalns and maxSamples and rturns actual max values
status["getTimebase2"] = ps.ps5000aGetTimebase2(chandle, timebase, maxSamples, ctypes.byref(returnedTimeIntervalns), ctypes.byref(returnedMaxSamples), segment_index)
assert_pico_ok(status["getTimebase2"])

# Run block capture======================================================================================================================================
status["runBlock"] = ps.ps5000aRunBlock(chandle, preTriggerSamples, postTriggerSamples, timebase, None, segment_index, None, None)
assert_pico_ok(status["runBlock"])

# Check for data collection to finish using ps5000aIsReady
ready = ctypes.c_int16(0)
check = ctypes.c_int16(0)
while ready.value == check.value:
    status["isReady"] = ps.ps5000aIsReady(chandle, ctypes.byref(ready))


# Create buffers ready for assigning pointers for data collection==========================================================================================================
bufferAMax = (ctypes.c_int16 * maxSamples)()
bufferAMin = (ctypes.c_int16 * maxSamples)() # used for downsampling which isn't used
bufferBMax = (ctypes.c_int16 * maxSamples)()
bufferBMin = (ctypes.c_int16 * maxSamples)() # used for downsampling which isn't used

# Set data buffer location for data collection
segment_index = 0
ratio_mode = 0 #PS5000A_RATIO_MODE_NONE

status["setDataBuffersA"] = ps.ps5000aSetDataBuffers(chandle, channelA, ctypes.byref(bufferAMax), ctypes.byref(bufferAMin), maxSamples, segment_index, ratio_mode)
assert_pico_ok(status["setDataBuffersA"])

status["setDataBuffersB"] = ps.ps5000aSetDataBuffers(chandle, channelB, ctypes.byref(bufferBMax), ctypes.byref(bufferBMin), maxSamples, segment_index, ratio_mode)
assert_pico_ok(status["setDataBuffersB"])

# create overflow loaction
overflow = ctypes.c_int16()
# create converted type maxSamples
cmaxSamples = ctypes.c_int32(maxSamples)

# Retrive data from scope to buffers assigned above=====================================================================================================================
startIndex = 0
downsampleRatio = 0
downsampleRatioMode = 0 #PS5000A_RATIO_MODE_NONE
status["getValues"] = ps.ps5000aGetValues(chandle, startIndex, ctypes.byref(cmaxSamples), downsampleRatio, downsampleRatioMode, segment_index, ctypes.byref(overflow))
assert_pico_ok(status["getValues"])

# Plot the data========================================================================================================================================================
# convert ADC counts data to mV
adc2mVChAMax =  adc2mV(bufferAMax, chARange, maxADC)
adc2mVChBMax =  adc2mV(bufferBMax, chBRange, maxADC)
# Apply probe attenuation
attenuatedValuesA = [i * probeAttenuation for i in adc2mVChAMax]
attenuatedValuesB = [i * probeAttenuation for i in adc2mVChBMax]
# Create time data
time = np.linspace(0, (cmaxSamples.value - 1) * returnedTimeIntervalns.value, cmaxSamples.value)
# plot data from channel A and B
plt.plot(time, attenuatedValuesA[:], label='A')
plt.plot(time, attenuatedValuesB[:], label='B')
plt.legend()
plt.xlabel('Time (ns)')
plt.ylabel('Voltage (mV)')
plt.show()

# Stop the scope
status["stop"] = ps.ps5000aStop(chandle)
assert_pico_ok(status["stop"])

# Close unit Disconnect the scope
status["close"]=ps.ps5000aCloseUnit(chandle)
assert_pico_ok(status["close"])

# display status returns
print(status)

Attachments
BlockAdvancedTriggerPulseWidth.py
(10.32 KiB) Downloaded 9 times

bennog
Advanced User
Advanced User
Posts: 187
Joined: Mon Nov 26, 2012 9:16 am
Location: Netherlands

Re: Pulse width triggering using V2 advanced trigger functions

Post by bennog »

Have you tried to do this in picoscope software.
And get a working trigger ?

Then post a psdata file with the working trigger and some data.

That is far easier than looking at code and guess what you want trigger on.

Benno

Martyn
Site Admin
Site Admin
Posts: 4461
Joined: Fri Jun 10, 2011 8:15 am
Location: St. Neots

Re: Pulse width triggering using V2 advanced trigger functions

Post by Martyn »

The pulse width type enums can be found here
https://github.com/picotech/picosdk-pyt ... ceEnums.py

For a simple pulse width trigger

Channel Properties for Channel A to the voltage and hysteresis required with mode Level
Channel Conditions for Channel A need to be False, and for PWQ True

PWQ directions need to be Channel A, Above, and Level
PWQ Properties as desired for your pulse timing and trigger requirement

This will start the pulse timer when the signal rises above the set level and will then trigger if the time condition of the pulse is met, this is the way we use the control in PicoScope 6/7

The additional threshold can be use to set different levels applicable to the start and end of the pulse.
Martyn
Technical Support Manager

jwallace91
Newbie
Posts: 0
Joined: Wed Feb 17, 2021 3:14 pm

Re: Pulse width triggering using V2 advanced trigger functions

Post by jwallace91 »

Thank you very much for the responses. I've attached the psdata of the waveform I am trying to trigger on.

Martyn, I have attempted to implement your guidance for a simple pulse trigger, but it still looks like it auto triggers randomly.

Here's what I've tried to implement:

Channel Properties: Channel A, Upper Voltage and Upper Hysteresis are set (lower values set to 0 as unused)
Channel Direction: Channel A, Rising, Level
Channel Conditions: Channel A=False AND PWQ=True (combined them into an array so that they are ANDed

PWQ Properties: Set as Greater Than and Lower=1.3us (upper set to 0 as unused)
PWQ Directions: Channel A, Above, Level
PWQ Conditions: Not set.

and Here's the trigger source code:

Code: Select all

# Set up a pulse width trigger==================================================================================================
# Set channel A voltage and hysteresis
triggerLevelmV = 1000
triggerLevel = triggerLevelmV/probeAttenuation
upperThreshold = mV2adc(triggerLevel, chARange, maxADC)
thresholdUpperHysteresis = 10
thresholdLower = 0 #not used for rising or falling edge directions
thresholdLowerHysteresis = 0
triggerProperties = ps.PS5000A_TRIGGER_CHANNEL_PROPERTIES_V2(   upperThreshold, # Threhold upper
                                                                thresholdUpperHysteresis,
								thresholdLower, #Threshold lower
								thresholdLowerHysteresis,
                                                                ps.PS5000A_CHANNEL["PS5000A_CHANNEL_A"]
                                                            )															
status["ps5000aSetTriggerChannelPropertiesV2"] = ps.ps5000aSetTriggerChannelPropertiesV2(chandle, ctypes.byref(triggerProperties), 1, 0)
assert_pico_ok(status["ps5000aSetTriggerChannelPropertiesV2"])

# Set channel A direction as rising and level
num_directions = 1
ChannelDirection = ps.PS5000A_DIRECTION(   ps.PS5000A_CHANNEL["PS5000A_CHANNEL_A"], 
                                            ps.PS5000A_THRESHOLD_DIRECTION["PS5000A_RISING"], 
                                            ps.PS5000A_THRESHOLD_MODE["PS5000A_LEVEL"]
                                        )
status["ps5000aSetPulseWidthQualifierDirections"] = ps.ps5000aSetPulseWidthQualifierDirections(chandle, ctypes.byref(ChannelDirection), num_directions)
assert_pico_ok(status["ps5000aSetPulseWidthQualifierDirections"])

# Set channel conditions as A=FALSE & PWQ=TRUE
clear = 1
add = 2	
triggerConditionsA = ps.PS5000A_CONDITION(   ps.PS5000A_CHANNEL["PS5000A_CHANNEL_A"],
                                            ps.PS5000A_TRIGGER_STATE["PS5000A_CONDITION_FALSE"]
                                        )

triggerConditionsPWQ = ps.PS5000A_CONDITION(   ps.PS5000A_CHANNEL["PS5000A_PULSE_WIDTH_SOURCE"],
                                            ps.PS5000A_TRIGGER_STATE["PS5000A_CONDITION_TRUE"]
                                        )
trigCond = [triggerConditionsA, triggerConditionsPWQ]
num_conditions = 2
Conditions = ps.PS5000A_CONDITION*num_conditions
trigConditions = Conditions(*trigCond) # Combine the condition structs into an array
status["setTriggerChannelConditions"] = ps.ps5000aSetTriggerChannelConditionsV2(chandle,ctypes.byref(trigConditions), num_conditions, clear)
assert_pico_ok(status["setTriggerChannelConditions"])

# Set PWQ direction as channel A, above and level
num_directions = 1
PWQDirection = ps.PS5000A_DIRECTION(   ps.PS5000A_CHANNEL["PS5000A_CHANNEL_A"], 
                                            ps.PS5000A_THRESHOLD_DIRECTION["PS5000A_ABOVE"], 
                                            ps.PS5000A_THRESHOLD_MODE["PS5000A_LEVEL"]
                                        )
status["ps5000aSetPulseWidthQualifierDirections"] = ps.ps5000aSetPulseWidthQualifierDirections(chandle, ctypes.byref(PWQDirection), num_directions)
assert_pico_ok(status["ps5000aSetPulseWidthQualifierDirections"])

# Set pulse timing and trigger requirement
lower = 162 #time base of 3 at 12bit gives a 8ns sample time. 8ns*162= 1.296us
upper = 0 # Not used for greater than trigger, only used for in-range or out-of-range triggers
PWQType = PS5000A_PULSE_WIDTH_TYPE["PS5000A_PW_TYPE_GREATER_THAN"]
status["ps5000aSetPulseWidthQualifierProperties"] = ps.ps5000aSetPulseWidthQualifierProperties(chandle, lower, upper, PWQType)
assert_pico_ok(status["ps5000aSetPulseWidthQualifierProperties"])
I have also attached the full source code for ref.

Thanks again.
Attachments
BlockAdvancedTriggerPulseWidth.py
(9.97 KiB) Downloaded 8 times
20240216-0001.psdata
(1.26 MiB) Downloaded 8 times

Martyn
Site Admin
Site Admin
Posts: 4461
Joined: Fri Jun 10, 2011 8:15 am
Location: St. Neots

Re: Pulse width triggering using V2 advanced trigger functions

Post by Martyn »

The section at line 86 is wrong, as it is setting the PulseWidth when it should set the Channel, PulseWidth is set correctly in the section at line 112,

Code: Select all

# Set channel A direction as rising and level
num_directions = 1
ChannelDirection = ps.PS5000A_DIRECTION(   ps.PS5000A_CHANNEL["PS5000A_CHANNEL_A"], 
                                            ps.PS5000A_THRESHOLD_DIRECTION["PS5000A_RISING"], 
                                            ps.PS5000A_THRESHOLD_MODE["PS5000A_LEVEL"]
                                        )
status["ps5000aSetPulseWidthQualifierDirections"] = ps.ps5000aSetPulseWidthQualifierDirections(chandle, ctypes.byref(ChannelDirection), num_directions)
assert_pico_ok(status["ps5000aSetPulseWidthQualifierDirections"])
and you don't need to set a direction for Channel A as you are only checking for a pulse width so you need to use PS5000A_NONE for all channels by using ps5000aSetTriggerChannelDirectionsV2.

Additonally your psdtata file shows the range set to 500mV (scaled by 10 to show as 5V on the screen) with a trigger voltage of 1.9V so to match this chARange should be 500mV and triggerLevelmV should be 1900
Martyn
Technical Support Manager

Post Reply