06-10-2020 09:52 AM
Hi everyone ! I am a student and very new to this and am still getting familiar with the basics. I am working on a project with an electronic card.
I have to send some commands on the serial link of the card to activate and deactivate some I/O in order to measure different voltages on the card and to validate some tests. I want to automate these tests using python and the NI-DAQmx Python API.
Currently, I have 28 analog voltages to measure and 2 analog voltages to send that's why I am using the DAQ USB-6218 with its 32 analog inputs and its 2 analog outputs. Also, the input range of 10 V suits me perfectly due to the fact that most of my voltages are between 0 and 10 V.
I need also to measure a current of 241 mA, but it would be better to measure its duty cycle which is at a frequency of 5 kHz, Pulse width = 111 μs, Period = 199 μs, so duty cycle = Pulse width / Period = 111/199 = 0.56.
Even if I managed to code the acquisition of my analog voltages and to send the analog voltages I wanted, I am currently struggling for the duty cyle part ...
For the voltage acquisition I am currently using this sample which is working very well :
##################################################################
# imports
##################################################################
import nidaqmx
import time
from builtins import input
##################################################################
# Function definition
##################################################################
def first_test():
#Function for first test
voltage_ai0 = task_ai0.read()
print("Voltage on port AI 0 : ", voltage_ai0)
if 2.475 < voltage_ai0 and voltage_ai0 < 2.525:
First_test = True
else :
First_test = False
return First_test
##################################################################
# main function
##################################################################
if __name__ == '__main__':
print("------------------------------------")
print("Starting Voltage Acquisition..")
print("------------------------------------")
input("Press Enter to continue...")
print("------------------------------------")
#First test
task_ai0 = nidaqmx.Task()
task_ai0.ai_channels.add_ai_voltage_chan("Dev1/ai0",terminal_config=nidaqmx.constants.TerminalConfiguration.RSE)
time.sleep(2.0)
first_test()
For the duty cycle acquisition, I tried this :
import nidaqmx
import pprint
pp = pprint.PrettyPrinter(indent=4)
with nidaqmx.Task() as task:
task.ci_channels.add_ci_duty_cycle_chan("Dev1/ctr0")
print('1 Channel 1 Sample Read: ')
data = task.read()
pp.pprint(data)
print('1 Channel N Samples Read: ')
data = task.read(number_of_samples_per_channel=8)
pp.pprint(data)
But I get this error ... :
I read on this link : https://knowledge.ni.com/KnowledgeArticleDetails?id=kA00Z000000kHAPSA2&l=fr-FR that there are three new counter pulse measurements available in DAQmx 9.0 and later that are only supported on X Series devices, 661x devices and newer cDAQ devices. If you attempt to create these tasks with unsupported hardware, you will receive the above error.
I tried to use the Task Type : "CI Pulse Freq" with the resulting measurement values :" Frequency and Duty Cycle" by testing this example : https://github.com/ni/nidaqmx-python/blob/master/nidaqmx_examples/ci_pulse_freq.py, but I get the same error message ...
That's why I am wondering if I am using the good measurement type and if not, can you tell me which one should I use and how to program it please ...
Any help would be greatly appreciated!! Thank you.
Solved! Go to Solution.
06-10-2020 01:47 PM
First off, any signals you connect to the counters on your board should be *digital logic*, typically 0-5V TTL though 3.3V logic will often work too. In your description, it sounded like you might be trying to measure the duty cycle of a signal that behaves like a current source. So the first thing I advise is that you make sure you have the right kind of signal to make counter-based measurements *possible*.
Beyond that, I don't know the python API for DAQmx to be able to give detailed guidance, but I would look for a counter measurement type known as "semiperiod measurement." It's more widely supported.
With semiperiod measurement, you'll get an array of values that alternate between high time and low time. On your M-series device, the very 1st measurement will be the sorta random time from when the task starts until the first active edge - it's likely to be meaningless and will be safe to ignore. After that, you can figure out 1 duty cycle measurement result for every 2 interval times in your data array.
Note that there's a way to specify the digital edge polarity (rising by default, falling if you configure it) so you can know know whether your pairs of intervals are in (High, Low) or (Low, High) order, which is important for your duty cycle calculation High/(High + Low).
-Kevin P
06-11-2020 10:27 AM
Hi Mr Price,
Thank you a lot for your time and your answer, as you advised me, I checked my PWM signal, it's a digital logic signal, I can see the signal slots on the oscilloscope, it's a square wave signal. The only thing I am worry about is the maximum voltage the digital inputs of the USB-6218 can receive ... I will probably need to use a voltage divider bridge (I read the maximum voltage was 5.25 V but I am not sure ...)
You were right about the choice of the counter measurement "semiperiod", it's supported by my USB-6218 but in order to calculate my duty cycle, I decided to use a combination of the counter "period" and the counter "pulsewidth", here the code I am currently using, it might help someone someday :
import nidaqmx
period = nidaqmx.Task()
period.ci_channels.add_ci_period_chan("Dev1/ctr0")
data_period = period.read()
PulseWidth = nidaqmx.Task()
PulseWidth.ci_channels.add_ci_pulse_width_chan("Dev1/ctr0")
data_pulsewidth = PulseWidth.read()
duty_cycle = (data_pulsewidth/data_period)*100
print("duty cycle = ", duty_cycle)
(it's also possible to use the counter "semiperiod" instead of the counter "period" and to take two samples in order to get the period of the signal)
I am generating a square wave signal on the PFI1 port of the USB-6218 with a Sefram device at a frequency of 50Hz and a duty cycle of 40% and I am getting these results :
I will be delighted to have your opinion on this method, I think it might work.
Quertsy
06-11-2020 12:43 PM
Your method is really only valid for an input signal that will not vary. Your pulsewidth measurement comes from a different cycle than your period measurement. So the duty cycle characterization is only valid if the pulse timing doesn't change.
I would expect that you aim to *measure* duty cycle because it *can* change. Thus, you really should be doing semiperiod measurement.
-Kevin P
06-11-2020 05:46 PM
You are right, I need to use the semiperiod measurement.
However, in the definition of this measurement method in the python API, it doesn't seem possible to specify the edge polarity :
So it seems difficult to know the configuration of my pairs, if they are in "High, low" or in "low,High", that's why I decided to use the combination of the "pulsewidth" et the "period" measurement methods. Do you have any clue on how to configure it by any chance ?
06-11-2020 07:19 PM
The main API functions tend to focus on exposing the most commonly changed parameters. Less common ones are often accessed as DAQmx properties. Or at least that's how it works in LabVIEW.
To set the edge polarity in LabVIEW, I would need to use a "DAQmx Channel property node". Then I'd navigate its menu to select my channel type (counter input), measurement type (semiperiod), and there I'd find a property named "Starting Edge" that I can either query or set.
I don't know what this would look like in python, but I'd expect it to be available *somewhere*. Probably some kind of channel property. The function name might also have some reference to the measurement type, i.e., semi-period.
-Kevin P
06-12-2020 05:16 AM
Unfortunately I can't find it in the documentation of the python API, maybe I am doing something wrong.
Anyway thank you a lot for your help and for your answers.
Quertsy
06-12-2020 06:00 AM
Found it. I can't tell whether it's a separate function call or an optional argument when adding the channel to the task. But you probably can.
-Kevin P
06-12-2020 07:20 AM
I didn't see this definition, it looks like it's an argument when adding the channel to the task.
I wrote this code and it seems working :
import nidaqmx
SemiPeriod = nidaqmx.Task()
SemiPeriod.ci_semi_period_starting_edge = nidaqmx.constants.Edge.RISING
SemiPeriod.ci_channels.add_ci_semi_period_chan("Dev1/ctr0")
data_semiperiod = SemiPeriod.read(number_of_samples_per_channel=2)
duty_cycle = (data_semiperiod[0]/(data_semiperiod[0]+data_semiperiod[1]))*100
print("duty cycle = ", duty_cycle)
Thanks for your help.
Quertsy
08-26-2024 01:56 PM
I am new to nidaqmx python, and I was wondering if you could help me with an issue. I am using your code but I am getting errors about 'function' object has no attribute 'Edge'. It also seems to have issues with your variable data_semiperiod. Not sure why. I am using your code on a USB-6001. I figured it wouldn't be that much different. I tried to rem out the statement but I just got more errors. Task Name: _unnamedTask
SemiPeriod.ci_semi_period_starting_edge = nidaqmx.constants.Edge("Rising")
Thank you for your help.
import nidaqmx
SemiPeriod = nidaqmx.Task()
SemiPeriod.ci_semi_period_starting_edge = nidaqmx.constants.Edge.RISING
SemiPeriod.ci_channels.add_ci_semi_period_chan("Dev1/ctr0")
data_semiperiod = SemiPeriod.read(number_of_samples_per_channel=2)
duty_cycle = (data_semiperiod[0]/(data_semiperiod[0]+data_semiperiod[1]))*100
print("duty cycle = ", duty_cycle)