Counter/Timer

cancel
Showing results for 
Search instead for 
Did you mean: 

Measuring duty cycle with NI-DAQmx Python on USB-6218

Solved!
Go to solution

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 ... :

error_duty_cycle.png

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.

0 Kudos
Message 1 of 9
(4,714 Views)

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

 

 

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
Message 2 of 9
(4,680 Views)

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 :

result.png

 

I will be delighted to have your opinion on this method, I think it might work.

 

Quertsy

0 Kudos
Message 3 of 9
(4,651 Views)

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

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
Message 4 of 9
(4,644 Views)

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 :semiperiod.png

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 ?

0 Kudos
Message 5 of 9
(4,634 Views)

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

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
0 Kudos
Message 6 of 9
(4,628 Views)

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

0 Kudos
Message 7 of 9
(4,602 Views)

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

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
Message 8 of 9
(4,597 Views)
Solution
Accepted by topic author Quertsy

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

0 Kudos
Message 9 of 9
(4,592 Views)