Counter/Timer

cancel
Showing results for 
Search instead for 
Did you mean: 

How do I read Continuously from 8 PWM channels? PCI-6602 using VB 6

I need to take data from 8 PWM channels at the same time, for a long time (3 hours).

 

I have this piece of code working, but I am only asking for 1 minute of data - and there are some strange erroneous values that I am seeing in a fraction of the data.

  What I am seeing is when I read just 1 PWM channel (validbits=1 or 2^n [n=0..7]) there are no bad values, but when I add more channels (any number more, 2 causes bad data too)

I get an occasional glitch, like .001 for the low time and .320 for the high time (my PWM freq is 500hz, the total low and high should never be much different than .002)

 

I am using the semiperiod and calculating the pwm value from that, which is historically what we have done here.   It seems like I would have to know the frequency to get the PWM value otherwise, and 

using the createCIsemiperiodchannel I get both the pwm and frequency if I do a little math.

 

 

Brynn

 

''''''''''''''''''''''''''''''''''''''''''''''''''''''

Public Function AquireAllPWMs(validbits As Integer, samples As Long, ByRef data() As Double) As Boolean

    Dim TaskHandle(8)    As Long
    Dim TaskIsRunning(8) As Boolean
    Dim ReadCount     As Long
    Dim strCounterString As String
    Dim Index, dmacnt As Long
 
    Dim lngChannelCount As Long
   
    Static ErrorCount As Long
    Static ErrorBoxDisplayed As Boolean

    On Error GoTo ErrorHandler
    dmacnt = 0
    
    For Index = 0 To 7
        If (validbits And 2 ^ Index) = 2 ^ Index Then
       
            DAQmxErrChk DAQmxCreateTask("", TaskHandle(Index))
            TaskIsRunning(Index) = True
       
            Select Case Index
              Case 0 To 5
                    strCounterString = "Dev1/ctr" & (Index + 2)       ' Like "Dev1/ctr2"  The EOL tester uses counters 2-7
                Case 6, 7
                  strCounterString = "Dev1/ctr" & (Index - 6)      ' Like "Dev1/ctr2"  The EOL tester uses counters 2-7
            End Select
       
            DAQmxErrChk DAQmxCreateCISemiPeriodChan(TaskHandle(Index), strCounterString, "", 0.0000001, 0.006, DAQmx_Val_TimeUnits_Seconds, "" )
            DAQmxErrChk DAQmxCfgImplicitTiming(TaskHandle(Index), DAQmx_Val_AcquisitionType_FiniteSamps, samples)
            DAQmxErrChk DAQmxSetCISemiPeriodStartingEdge(TaskHandle(Index), strCounterString, DAQmx_Val_Rising)
       
            ' Set up a simultaneous Trigger arm by a digital edge (PFI3)
            DAQmxErrChk DAQmxSetArmStartTrigType(TaskHandle(Index), DAQmx_Val_TriggerType4_DigEdge)
            DAQmxErrChk DAQmxSetDigEdgeArmStartTrigSrc(TaskHandle(Index), "/Dev1/PFI3" )

            ' Since there are only 3 DMA channels, we have to set the rest to use Interrupts as the transfer mechanism
            dmacnt = dmacnt + 1
            If dmacnt > 3 Then
                DAQmxErrChk DAQmxSetCIDataXferMech(TaskHandle(Index), strCounterString, DAQmx_Val_DataTransferMechanism_Interrupts)
            End If
        End If
    Next Index
   
  
    For Index = 0 To 7
        If (validbits And 2 ^ Index) = 2 ^ Index Then
            DAQmxErrChk DAQmxStartTask(TaskHandle(Index))   ' Start the task, get ready for the hardware trigger
        End If
    Next Index
   
    Call WriteDIO("Dev1/Line3", True)               ' GO! this is the hardware trigger for simultanous readings   
    For Index = 0 To 7
       If (validbits And 2 ^ Index) = 2 ^ Index Then      '  validbits is a bitmap of which of the 8 PWM channels to read, think of it as 255 to read all 8 channels    
        ' DAQmx Read Code
        DAQmxErrChk DAQmxReadCounterF64(TaskHandle(Index), samples, 0.005 * samples, data(0, Index), samples, ReadCount, ByVal 0& )
   
        ' All done! StopTask
        DAQmxErrChk DAQmxStopTask(TaskHandle(Index))
        DAQmxErrChk DAQmxClearTask(TaskHandle(Index))
        TaskIsRunning(Index) = False
       
'        dblPWMValue(Index) = 100# * (data(1) / (data(1) + data(2)))    '   this would be how to calculate the pwm value,  it gets done when I write it out to an .csv excel file later

       End If
    Next Index
   
    Call WriteDIO("Dev1/Line3", False)              ' Reset the trigger line
   
    AquireAllPWMs = True
   
    Exit Function

ErrorHandler:
'   MsgBox "Error: " & Err.Number & " " & Err.Description, , "Error"
    mlngPWM_ErrorCount(Index) = mlngPWM_ErrorCount(Index) + 1
    If Index = 0 Then
        ErrorCount = ErrorCount
    End If
    ErrorCount = ErrorCount + 1
    'MainForm.PWMErrorCount = "Errors(" & lngIndex & "): " & mlngPWM_ErrorCount(Index)
       
    Resume Next
End Function

0 Kudos
Message 1 of 5
(4,765 Views)

Hello Brynn,

 

 I have looked into your code and I don't see anything glaringly wrong, but I have a few questions.  You say that you are getting goo reading on one channel, does it matter which channel it is?  What I mean is have you tried reading all of your channels by themselves?

 

Also, how long do you run your experiment with only 1 channel?  If you haven't run several sequences in a row, it may be useful to try.  This may be pretty time consuming, but testing each channel in this manner could bring to light connection/noise problems.

 

Lastly, it isn't a big deal, but the proper naming convention for your Write to Digital Line should be Dev1/port0/line3, not Dev1/line3.  I'm not sure if this is taken care of in the driver or in your code, but the port is usually specified, even on devices with a single port.

Regards,
Dan King

0 Kudos
Message 2 of 5
(4,755 Views)

Hi Dan_K,

   I believe I tried reading all the channels by themselves, individually.  If I didn't I did do most of them - and I definatly did ones that I had seen flakey values on.

 

   I have run for 10 minutes on one channel with no errors that I can see.        I am getting a bit suspicious of the connectors (which are actually very high quality robust connectors) because of some spikes I have seen.

 

I was wondering if the problem is that I am trying to get more data than can be transferred over the PCI bus.

My PWM's are all 500 hz

If I use the semiperiod I should get 250 low times and 250 high times, 500 per second.  a Double is 8 bytes. 8 channels is 8.  so I am trying to get 500*8*8 = 32000 bytes per second.

That doesn't seem like too much,  but it is 8 seperate tasks, only the first 3 of which can be DMA.  You can see the code that changes the tasks to IO for the rest of the channels.

So each channel is trying to get 4000 bytes per second, which, again, doesn't sound like very much.

 

I also tried setting up the tasks as pulsewidth, on the theory that I would only get 250 low times per second, halving the data.   This does seem to work better, but still has some issues with reading times that I disbelieve.   Yes, I should get a storage scope on there to look and see if these spikes are real, but we have been making this product for years and our big customer would be screaming blody murder if they ever saw such a spike - when I have time I will set up the scope to prove that I am seeing bad counts from the nidaq card, but right now I don't see them when only capturing one channel, and I do see them when capturing more, and that is enough evidence for me right now.

 

For my test (Tomorrow) I'll be happy if I can just discard bad data and keep capturing good data.  Is there a way that I can just continue on with my continuous capture task even if I get an error?

 

Brynn

 

0 Kudos
Message 3 of 5
(4,750 Views)

A couple thoughts/observations, possibly of dubious worth.

 

1. I'm rusty in text language syntax, but wondered what is meant by the "data(0, Index)" argument in the DAQmxRead function call.  Is that a 2D array indexing syntax?  Later you appear to want to reference your data as "data(1)" which looks more like 1D.  Also the function prototype calls out "data()" in a way that reminds me more of 1D, though I'm not knowledgeable about VB syntax.

    It appears you are likely trying to pass a pointer that points to the beginning of a particular row or column of a 2D array.  Does VB even allow that?  And if so, are you sure the actual memory layout for 2D arrays is compatible with your indexing scheme?  It appears you expect the memory layout to put consecutive bytes into consecutive rows of a particular column.  Are you sure the DAQmx call doesn't fill in consecutive columns of a particular row?

 

2. Once you work that part out for *certain*, there's another catch (I think).  You should ignore the first measured value from each counter task.  I'm pretty sure that the first value will be a random-ish time from when you triggered the counter task until the first active edge.

 

3. You can reduce your data rate by a factor of 2 if you read the count value as an 32-bit unsigned integer.  (Offhand, it doesn't sound like data rate is the likely problem, because data rate problems would cause data acq error messages, not bad data.)

 

-Kevin P.

Message Edited by Kevin Price on 11-14-2008 09:36 AM
ALERT! LabVIEW's subscription-only policy came to an end (finally!). Unfortunately, pricing favors the captured and committed over new adopters -- so tread carefully.
0 Kudos
Message 4 of 5
(4,732 Views)

Hi Kevin,  thanks for the response...

 

1)  I am a C programmer so getting this right in VB was some work, but what I posted is working code.  the data(0,index) is sending a row to the DAQmxread call, like it appears.  I did have to swap the (index,0) around to get this to work.   VB does allow it, and it does work, but as you noticed it sure looks stupid (stupid VB).   

    What I am seeing right now is that for the 3 DMA channels I get all the data, but the remaining 5 interrupt channels puke out about 3 minutes into the aquisition with 'data was overwritten' errors from the read.

 

2) yes, we have been ignoring the first data point because it is garbage.

 

3)  What I did do to halve the data (which did help a lot) was to switch from the semiperiod which gives both a low time and a high time, to using the pulsewidth instead, where I now get 500 samples per second per channel instead of the 1000 I got with the semiperiod.

in the VB CAPI there is no 'DAQmxreadU32', but there is a DAQmxreadraw which seems like it does the same thing.

So I tried the read raw and I still got the same amount of samples before it puked.

     Once I switched to the pulsewidth I do indeed get data acq errors and not bad data. (it appears)

 

 

What I surmise is that the DAQmx drivers are not able to get the data from the hardware fast enough, and the error message I see (data is overwritten before being read) is actually telling me the data in the PCI-6602 card got overwritten before the drivers were able to pull it out.

 

So my current plan of attack is to try to just drop any data that I couldn't read fast enough - using the  line

           DAQmxErrChk DAQmxSetReadOverWrite(taskhandle(index), DAQmx_Val_OverwriteMode1_OverwriteUnreadSamps)
 which should tell it not to care if the data gets overwritten,  but I see that I also have to play with the relativeto and offset pointers as the documentation says:

 

When an acquisition encounters unread data in the buffer, the acquisition continues and overwrites the unread samples with new ones. You can read the new samples by setting RelativeTo to DAQmx_Val_MostRecentSamp and setting Offset to the appropriate number of samples.

 

 Brynn
0 Kudos
Message 5 of 5
(4,723 Views)