Callbacks: Difference between DAQmxRegisterDoneEvent() and DAQmxEveryNSamplesEvent - callback

Trying to figure out how callback wrappers are called specifically. Our code deals with a slowTask and an onTask. During a slowTask, I deal with the following two lines (specific to this question):
DAQmxCfgSampClkTiming(slowTask, "OnboardClock", GUI_RATE,
DAQmx_Val_Rising, DAQmx_Val_ContSamps, 1);
DAQmxRegisterEveryNSamplesEvent(slowTask, DAQmx_Val_Acquired_Into_Buffer, 1,
0, EveryNCallbackWrapper, this);
I understand that here, everytime the buffer fills up with one sample, EveryNCallbackWrapper will be called.
For an onTask, I have a hardtime understanding how the callback gets called. I consulted the NI documentation but couldn't quite get it.
DAQmxCfgSampClkTiming(onTask, "OnboardClock", ON_RATE, DAQmx_Val_Rising,
DAQmx_Val_FiniteSamps, 100);
DAQmxRegisterDoneEvent(onTask, 0, DoneCallbackWrapper, this);
This one boggles my mind a bit more. I believe that whenever onTask is triggered (with a hardware trigger), the DAQ starts taking and digitizing analog measurements at ON_RATE samples/second and once 100 samples are taken/read into the DAQs buffer, the DoneCallbackWrapper() is called. Depending on how long this hardware trigger stays high, this wrapper will be called every time the DAQ reads 100 samples (while trigger is high) OR will the callback be called only once after 100 samples were read?

The callback is called only once after 100 samples were read
Because slowTask uses DAQmx_Val_ContSamps, the program asks for an infinite (aka continuous) acquisition where data is streamed to the host. Using the EveryNSamples callback allows the program to access and process the newest data that was sent by the device.
Because onTask uses DAQmx_Val_FiniteSamps, the program asks for a single acquisition of 100 samples. Using the Done event allows the program to access and process the complete and full acquisition.
In your comment update, the program uses
DAQmxCfgDigEdgeStartTrig(onTask, "/PXI2Slot4/PXI_Trig0", DAQmx_Val_Rising));
to configure a digital edge start trigger for onTask. When that trigger line has a rising edge, the onTask acquisition begins, captures 100 samples, stops, and invokes the callback.
If the program needs to acquire 100 samples for onTask for every rising edge on /PXI2Slot4/PXI_Trig0, you can use the retriggerable property on the NI 63xx series devices that allows the same task to re-run for each trigger event.
More details are in the X Series User Manual:
The AI Start Trigger is also configurable as retriggerable. The timing engine generates the sample and convert clocks for the configured acquisition in response to each pulse on an AI Start Trigger signal.
The timing engine ignores the AI Start Trigger signal while the clock generation is in progress. After the clock generation is finished, the counter waits for another Start Trigger to begin another clock generation. Figure 4-22 shows a retriggerable analog input with three AI channels and four samples per trigger

Related

Optimize power consumption with STM32L4 ADC

I'm working on a firmware development on a STM32L4. I need to sample an analog signal at around 200Hz. So basically one analog to digital conversion every 5ms.
Up to now, I was starting the ADC in continuous conversion mode, triggered by a timer. However this prevents to put the STM32 in Stop mode in between conversions, which would be very efficient in terms of power consumption since 99%+ ot the time the product has nothing to do.
So my idea is to use the single conversion mode: use a low power timer to wakeup the product from Stop mode every 5ms, launch a single conversion in the LPTIM interrupt handler (waiting for ADC end of conversion in polling), and go back to Stop mode.
Do you think it makes sense or do you see problems to proceed like this ? I'm not sure about polling for a single ADC conversion inside a handler, what do you think ? I think a single conversion on one channel should be pretty fast (I run at 80MHz, the datasheet mentions a maximum sampling time of 8us)
Do I have to disable/enable ADC (the bit ADEN) between each single conversion ?
Also, I have to know how long a single conversion lasts to assess whether the solution is interesting or not. I'm confused about the sampling time (bits SMP). The reference manual states: "This sampling time must be enough for the input voltage source to charge the embedded capacitor to the input voltage level." What is the way to find the right SMP value ?
There are no problems with the general idea, LPTIM1 can generate wakeup events through the EXTI controller even in Stop2 mode.
I'm not sure about polling for a single ADC conversion inside a handler, what do you think ?
You might want to put the MCU in Sleep mode in the timer interrupt, and have the ADC trigger an interrupt when the conversion is complete. So disable SLEEPDEEP in the timer interrupt, and enable it in the ADC interrupt.
What is the way to find the right SMP value ?
Empirical method: start with the longest sampling time, and start decreasing it. When the conversion result significantly changes, go one or two steps back.

DAQ Matlab toolbox: how to count trigger events without an edge counter channel and how to output different value at each successive trigger

I need your help with the session based interface for the Matlab DAQ toolbox. I have not been able to find much help in the MathWorks tutorials or examples. I am currently using a USB-6003 DAQ from NI.
So basically in my system I have 2 analog output channels (ch1 and ch2) and 1 analog input channel (ch3), and what I am trying to do is to drive the output voltage in ch1 from 0V to 10V in steps of 1V, with ch2 constant and then repeat the loop in ch1 for a different voltage in ch2. As for the analog input ch3, I am triggering it some time after triggering the ch1. My triggers are being externally generated by a function generator.
What I have been struggling with is:
1) How to at each successive trigger event output a different value in the ch1.
2) And how after 11 triggers, can I change ch2 output's.
3) How to save the input in a different location between trigger events, so it does not get overwritten by the next event.
My main constraints are:
1) I cannot use an edge-counter channel to count the triggers because I only have two PFI channels and I need both, one to trigger ch1 and the other ch3 (I cannot use only one).
2) I cannot use wait or any other software time function, because I need a high speed acquisition system (it is for a laser microscope)
3) I need two have at least 2 sessions running in parallel because my DAQ does not allow simultaneous tasks in the same session.
I have attached a channel's time diagram of what I am trying to do.
Channels diagram
Caution
"I need a high speed acquisition system"
USB might not be the right option. Using USB as the control/data transport mechanism is slow compared to other computer I/O, like PCIe or EtherCAT. If, after you get this working, you determine that you need lower latency and jitter, my recommendation is to try CompactRIO and LabVIEW Real-Time.
Compounding the performance is the on-demand nature of the USB-6003. While both analong input and analog output are controlled by electrical signals (the start trigger and sample clock) and have their data automatically transferred by the driver, the digital input and counter are only software-timed, which means that reading data isn't automatic and must be prompted by you, the user, with a read command.
Since the only way you can get digital data from a USB-6003 is on-demand, your only option is to wait for it; there is no way to be notified that a new edge has arrived. Other devices (like the PCIe-63xx X Series or cDAQ-940x devices) support digital input change detection, which causes a software event to be sent to the program. If you had one of these devices, then you wouldn't have to wait.
Suggestion
However, if you change your triggering and data strategies a little, I still think you can achieve the kind of I/O you want. You'll then be able to evaluate its speed and reliability to decide if you need to upgrade DAQ hardware.
New triggering and data strategy
The core idea is: instead of keeping the channels on their own "time base", unify them to a single time base and use that to coordinate the voltage updates. By doubling the frequency of your external trigger, all three channels can share the same timing. In other words, both the analog input task and the analog output task use the same external signal as their sample clock.
Double the frequency of the FGEN's trigger signal.
Repeat an analog output sample if the level doesn't need to change.
Throw an analog input sample away if it coincides with an output level change.
The analog output samples would be:
ch1 ch2
0.0 0.0
0.0 0.0
1.0 0.0
1.0 0.0
2.0 0.0
2.0 0.0
0.0 1.0
0.0 1.0
New program strategy
Now that both the analog input and analog output are using the FGEN as their sample clock, the MATLAB routine only needs to prepare the operation and then monitor/feed it. The hardware will be able to generate and acquire without any intervention from the PC, but the PC will need to periodically read analog input data and write more analog output data to keep the driver satisfied.
I don't know how much of the DAQmx API MATLAB exposes, but you can ask the driver how many samples are left in the device's buffer
Analog input is DAQmxGetReadAvailSampPerChan (doc)
Analog output is DAQmxGetWriteSpaceAvail (doc)
Reference
NI USB-6003 Specifications
http://digital.ni.com/manuals.nsf/websearch/666A752FCC177B0186257CD8006C24C8

How to synchronize readout of binary streams on serial port of Matlab

I'm having an issue which is partially Matlab- and partially general programming-related, I'm hoping that somebody can help me brainstorm for solutions.
I have an external microcontroller that generates a large stream of binary data (~40kb) every 400ms and sends it via UART to a PC running Matlab scripts. The data is not encoded in hexa or dec characters, but true binary (hence, there's no terminator defined as all 256 values are possible, valid combinations of data). Baudrate is set at 1024000. In short, it takes roughly 375ms for a whole stream of data to be sent, with 25ms of dead time in between streams
In Matlab, the serial port is configured correctly (also 1024000, 8x bits, 1x stop bit, no parity, no hardware flow control, etc.). I am able to readout the data I'm sending via the microcontroller correctly (i.e. there's no corruption of data), but I'm not being able to synchronize the serial readout on Matlab. My script is as follows:
function data_show = GetDATA
if ~isempty(instrfind)
fclose(instrfind);
end
DATA_TOTAL_SIZE = 38400;
DATA_buffer = uint8(zeros(DATA_TOTAL_SIZE,1));
DATA_show = reshape(DATA_buffer(1:2:end)',[160,120])';
f_data_in = false;
f_data_out = true;
serialport = serial('COM11','BaudRate',1024000,'DataBits',8,'FlowControl','none','Parity','none','StopBits',1,...
'BytesAvailableFcnCount',DATA_TOTAL_SIZE,'BytesAvailableFcnMode','byte','InputBufferSize',DATA_TOTAL_SIZE * 2,...
'BytesAvailableFcn',#GetPortData);
fopen(serialport);
while (get(serialport,'BytesAvailable') ~= 0) % Skip first packet which might be incomplete
fread(serialport,DATA_TOTAL_SIZE,'uint8');
end
f_data_out = true;
while (1)
if (f_data_in)
DATA_buffer = fread(serialport,DATA_TOTAL_SIZE,'uint8');
DATA_show = reshape(DATA_buffer(1:2:end)',[160,120])'; %Reshape array as matrix
DATAsc(DATA_show);
disp('DATA');
end
pause(0.01);
end
fclose(serialport);
delete(serialport);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function GetPortData (obj,~)
if f_data_out
f_data_in = true;
end
end
end
The problem I see is that what I end up reading is always the correct size, but belongs to multiple streams, because I haven't found a way to tell Matlab that these 25ms of no data should be used to synchronize (i.e. data from before and after that blank period should belong to different streams).
Does anyone have any suggestions for this?
Thanks a lot!
For completeness, I would like to post the current implementation I have fixing this issue, which is probably not a suitable solution in all cases but might be useful in some.
The approach I took consists in moving into a bi-directional communication protocol, in which Matlab initiates the streaming by sending a very short command as a trigger (e.g. single, non-printable character). Given the high baudrate it does not add significant delay due to processing in the microcontroller's side.
The microcontroller, upon reception of this trigger, proceeds to transmit only one full package (as opposed to continuously streaming package at a 5Hz rate). By forcing Matlab to pickup a serial package of the known length right after issuing the trigger, it ensures that only one package and without synchronization issues is received.
Then it becomes just a matter of encapsulating the Matlab script in a routine with a 5Hz tick given by a timer, in which the sequence is repeated (send trigger, retrieve package, do whatever processing, and repeat).
Advantages of this:
It solves the synchronization problems
Disadvantages of this:
Having Matlab running on a timer tick does not ensure perfect periodicity, and hence the triggers might not always be sent at exactly 5Hz. If triggers are sent at "inconvenient" times for the microcontroller, packages might need to be skipped in order to avoid that a package is updated in memory while it is still being transmitted (since transmission takes a significant part of the 200ms time slot)
From experience, performance can vary a lot depending on what the PC running Matlab is doing. For example, it works fine when the PC is left on its own to do the acquisition, but if another program is used (e.g. Chrome), Matlab begins to lag and that results in delays in transmission of triggers.
As mentioned above, it's not a complete answer, but it is an approach that might be sufficient in some situations. If someone has a more efficient option, please fell free to share!

Wait using s-function

I am using a s-function built in Simulink and I need to implement a waiting time. For example I need to do this :
send the first frame
wait 20 ms
send the second frame
wait 20 ms
send third frame
How could I establish this waiting time between 2 frames. I am using C language and a Level-1 Matlab S-function.
First of all, it is very difficult to get millisecond accuracy. it depends a lot on your hardware, OS, proccesses running..
you could try to achieve it by simply using the pause command
send the first frame
pause(0.020)
send the second frame
pause(0.020)
send third frame
or using timer objects http://www.mathworks.com/help/matlab/ref/timerclass.html
both solutions are not accurate. the best solution would be to base your timing in external events. is there any event triggered after sending each frame?

How can I parallelize input and display in MATLAB?

I'm using Psychtoolbox in MATLAB to run a behavioral psychology paradigm. As part of the paradigm, users have to view a visual stimulus and respond to it using some input mechanism. For a keyboard, this works as follows:
show stimulus
poll keyboard for response
if no response detected, loop back to 1
if response detected, break and move on with script
This works fine for a keyboard, as step 2 takes between 1-2 ms. The problem comes when I use an alternate input mechanism; in that case, step 2 takes ~20 ms. (I need this alternate input to run the study, and that should be considered immutable fact.) As the stimulus changes with a very short timespan, this added delay breaks the task.
My current thought is to try to use the parallel processing, such that one thread shows the stimulus, and another thread polls the keyboard. I'm currently using the Parallel Computing Toolbox to do this. The problem I'm having is that I don't know how to direct keyboard input to a "parallelized" thread. Does anyone know (1) whether it's possible to direct keyboard input to a thread / have a thread send a visual signal to a monitor, and if yes, (2) how to do it?
Also, if anyone has any better ideas as to how to approach this problem, I'm all ears.
According to this MATLAB newsgroup thread, it appears that threads can't modify graphics objects. Only the desktop MATLAB client can do that. This means that you can't handle updating of graphics from a thread, and I can confirm this as I tried it and wasn't able to modify figures or even the root object from a thread.
However, I think you may be able to do the main graphics updating in MATLAB while a thread handles polling for your input. Here's a sample function for continuously updating a display until a thread waiting for input from KbCheck is finished running:
function varargout = plot_until_input
obj = createJob(); %# Create a job
task = createTask(obj,#get_input,4,{deviceNumber}); %# Create a task
submit(obj); %# Submit the job
waitForState(task,'running'); %# Wait for the task to start running
%# Initialize your stimulus display here
while ~strcmp(get(task,'State'),'finished') %# Loop while the task is running
%# Update your stimulus display here
end
varargout = get(task,'OutputArguments'); %# Get the outputs from the task
destroy(obj); %# Remove the job from memory
%#---Nested functions below---
function [keyIsDown,secs,keyCode,deltaSecs] = get_input(deviceNumber)
keyIsDown = false;
while ~keyIsDown %# Keep looping until a key is pressed
[keyIsDown,secs,keyCode,deltaSecs] = KbCheck(deviceNumber);
end
end
end
I was able to successfully run the above function with some simple plotting routines and replacing the code in get_input with a simple pause statement and a return value. I'm unsure whether KbCheck will work in a thread, but hopefully you will be able to adapt this for your needs.
Here's the documentation for the Parallel Computing Toolbox functions used in the above code: createJob, createTask, submit, waitForState, destroy.
I don't know of a way how you could do this with parallel processing.
However, a feature you might be able to use is the timer object. You would set up the timer object to poll the input mechanism, and, if anything is detected, change the value of a global variable. Then, you start your stimulus routine. In the while-loop in which you're updating the display, you keep checking the global variable for a change from the timer object.
You have to tackle the 20ms latency in your input device. If it's too slow then get another input device. You can get good sub-millisecond timing with proper response boxes.
All this talk about threading is misguided and not applicable to the PTB framework.