03-28-2013 01:50 PM
Hi Michael,
It makes sense only to store and read only 1 sample, containing your count, in order to avoid filling up the buffer with the samples that you don't even need.
I am glad we were finally able to get to the bottom of this. Feel free to post on the forums if you have any additional questions and good luck with your project!
Regards,
03-28-2013 02:08 PM - edited 03-28-2013 02:08 PM
Here is the code for anyone else that needs it. It will require some modifications to work in your software.
Public Sub CreateTimerTasks()
' ***********************************************************************************
' Subroutine: CreateTimerTasks
' Parameters: None
' Author: Michael Chadwell
' Date: 03/28/13
' Version: 6.1.7
' Tested: 03/28/13
' Notes: This subroutine configures a counter and a clock to run a hardware
' timed Interrupt Service Routine (ISR)
' ***********************************************************************************
Try
' This code creates a task to call a function on the rising edge of an input clock.
With DCChannels(ptrLoopTimer.Value)
counterReadTask = New Task()
' .Device is a variable which holds a NI device identifier like Dev1
' .Channel is a variable which holds a NI channel identifier like ctr0
' Note that PFI8 is the CTR0 Source pin for many E-series devices
counterReadTask.CIChannels.CreateCountEdgesChannel(.Device & "/" & .Channel, "Input", _
edgeType, 0, countDirection)
' The 10 in this statement is the desired frequency of your ISR
counterReadTask.Timing.ConfigureSampleClock("/" & .Device & "/PFI8", _
10, SampleClockActiveEdge.Rising, _
SampleQuantityMode.ContinuousSamples, 1)
runningCTRTask = counterReadTask
myCounterReader = New CounterReader(counterReadTask.Stream)
' CounterReadCallback is the ISR
myCallBack = New AsyncCallback(AddressOf CounterReadCallback)
myCounterReader.SynchronizeCallbacks = True
myCounterReader.BeginReadSingleSampleInt32(myCallBack, counterReadTask)
End With
' This code creates the clock that the other counter will count. You do not need this
' clock if you use some other clock source, but for my case it was convient to use
' two counter/timers from one DAQ card.
With DCChannels(ptrLoopClock.Value)
counterWriteTask = New Task
' The 10 in this statement is the frequency of the clock
counterWriteTask.COChannels.CreatePulseChannelFrequency(.Device & "/" & .Channel, _
"Output", COPulseFrequencyUnits.Hertz, COPulseIdleState.Low, 0.0, _
10, 0.5)
counterWriteTask.Timing.ConfigureImplicit(SampleQuantityMode.ContinuousSamples, 1)
counterWriteTask.Start()
End With
Catch exception As DaqException
counterReadTask.Dispose()
runningTask = Nothing
LoopTimerFrequency = 8
' Fall back to a software timer on error.
Dim timerDelegate As TimerCallback = AddressOf ControlCode
TenHzLoopTimer = New System.Threading.Timer(timerDelegate, inProcess, 0, 125)
End Try
End Sub
Public Sub CounterReadCallback(ByVal ar As IAsyncResult)
' ***********************************************************************************
' Subroutine: CounterReadCallback
' Parameters: ByVal ar As IAsyncResult
' Author: Michael Chadwell
' Date: 03/28/13
' Version: 6.1.7
' Tested: 03/28/13
' Notes: This callback function is called on the rising edge of an input clock.
' It then calls the function that you want to run as a "hardware-timed"
' loop.
' ***********************************************************************************
Try
If runningCTRTask Is ar.AsyncState Then
HWTimerData = myCounterReader.EndReadSingleSampleInt32(ar)
myCounterReader.BeginReadSingleSampleInt32(myCallBack, counterReadTask)
' Control code is the 10 Hz process that I want to run.
ControlThread = New Thread(AddressOf ControlCode)
ControlThread.Priority = ThreadPriority.AboveNormal
ControlThread.Start()
End If
Catch exception As DaqException
counterReadTask.Dispose()
runningTask = Nothing
' Fall back to a software timer on an error
Dim timerDelegate As TimerCallback = AddressOf ControlCode
LoopTimerFrequency = 8
TenHzLoopTimer = New System.Threading.Timer(timerDelegate, inProcess, 0, 125)
End Try
End Sub
03-28-2013 02:12 PM
Thanks for contributing!
12-20-2013 08:07 AM
I hope you guys respond to updates on old threads...
I am having trouble with the timing using this method. If you'll recall, I have a 10 Hz loop and a 1 Hz loop. Throughout this thread I was only trying to get the 10 Hz loop on a hardware timer. Recently I wanted to synchronize them, so I got rid of the software timer with 1000 ms period and added a loop counter to my 10 Hz loop (which is fired by the hardware timer). When the loop counter = 0 (can be 0-9 before resetting to 0), I call my 1 Hz code as a thread. I though it would be as easy as that, but I was wrong. It appears as though this method is losing time. If I run a cycle long enough, I notice it because the period is actually 1000.07 ms.
If my hardware timer is running at exactly 10 Hz, and every zeroth iteration (of 10) is calling some code that takes less than 1000 ms to complete, how am I losing time? Even if there is a delay in windows responding to the callback, it should still be called once per second.
I hope this explanation makes sense.
Thank you.
12-20-2013 01:28 PM
There could be a number of reasons. Just because a while loop is set to run every second, does not mean that it will run once every second. There could be another process finishing up before the resource is released for the while loop to tick. This could be why you are seeing that 0.07 ms difference. If you require something to be this specific, you might want to go to a Real-Time or FPGA solution.
12-20-2013 01:34 PM
Would you agree that the hardware timer set for 10 Hz will be 10.0000000 Hz? If so, the hardware timer is calling the procedure every tenth iteration. If that is the case, and as long as the procedure takes less than 1000 ms, then the procedure should keep time. The ACTUAL time that the procedure occurs will dither, but the overall period should average around 1.000000 Hz, correct?
Worded differently, if a hardware timer that is the golden standard is calling a procedure, why would the frequency of the procedure call be something other than 1 Hz?
12-23-2013 11:06 AM
Hi Michael,
Windows doesn't have the determinism available that is possible on a Real-Time or FPGA system. Even if the 1 Hz loop is given priority with its own thread, there will be other processes in the background that Windows will deem a higher priority. The disparity between the set 1 Hz and the actual frequency is most likely due to the other processes that are being performed in the background.
Regards,
Jordan G.
12-23-2013 01:43 PM
I think I must explain the architecture a little better.
I have an asyncronous callback function that is tied to a hardware timer coming from an NI board. On every rising edge an interrupt is called which runs the code in my "loop." Regardless of how busy windows is, 10 times per second this function is called. The part of my code that runs 10x/s takes far less than 100ms to complete, so there is no trouble with it. The part that runs 1x/s takes less than 1000 ms to run (less than 500 ms). I am fully aware of the differences between Windows sharing/allocating resources and a dedicated real time system (I have a PXI system sitting in my office).
My point is that this call back function is happening 10 times per second coming from a hardware timer that should be running at 10.000000 Hz. My question is how is is possible to lose time? How is it possible for the period to be greater than one second if the hardware timed loop is calling it and the hardware timed loop is in fact running at one second. I may need to put a scope on the 10 Hz loop as well to see what its true period is, although my scope my not have enough resolution to capture such a tiny offset.
Perhaps I should create an output file with the timing for each 10 Hz iteration and the timing for each 1 Hz iteration.
12-23-2013 07:11 PM
Hi Michael,
Could you provide a little more information? Is the "lost time" you're seeing consistently .07 ms every second, or does the amount of time lost vary?
70 microseconds is a fairly small error, and my instinct is that it can be attributed to jitter. Are you still using the PCI-6173 that you mentioned earlier in the thread?
I would recommend that you go ahead and try generating that output report and that you post the results here. The more information we have, the more likely it is that we can give you a more concrete answer.
Regards,