Matlab Serial Errors with Psychtoolbox using RS232 device and fast screen updates
Show older comments
(Previously posted on Mathworks/Matlab answers: Serial Reading Continuous Data in Matlab - Inconsistent Responses 6/10/2005, http://au.mathworks.com/matlabcentral/newsreader/view_thread/342780#939295 )
Apologies for the length of the question, but best to start at the beginning...
BACKGROUND
I have set up a user-reaction response system for psychology students based on what is known as a KSS response scale. The script is written in Matlab and uses the Psychtoolbox functions to control the display, and a response box, a USB-connected RS232 device, that continually transmits characters designed around an Arduino device.
This system consists of a response box which consists of two buttons and continually transmits a character. If no button is pressed, a '32' char is transmitted, if button one is pressed it transmits a '33' char, and if button two is pressed it transmits a '34' char. This unit transmits characters at approximately 1800 chars per second (or 0.000625 secs between characters). This device is connected to the PC via a USB-RS232 adapter.
The script starts by asks few questions of the users who responds by a scale 1-9 on the keyboard, then the reaction test starts. The reaction test is basically a stopwatch which displays a blank screen for a few seconds, then the stopwatch appears that counts upward. The script check the incoming characters from the response box (COM port) for a 'key press' and the stopwatch will stop when the first one is seen. The time for the stopwatch display is obtained by the Psychtoolbox 'GetSecs' command. When a timeout time is reached, the stopwatch will stop and pause, sound a beep error, then reset for the next trial. After a total test time has elapsed, the system stops and saves the data to file. The typical total test time is 10 minutes.
I have setup two versions of the script:
- one that polls the serial port during the reaction tests (DIRECTREAD),
- one that uses the Matlab recommended continuous serial read (BUFFERREAD).
CURRENT TEST METHOD
As the participant reaction time can be variable, I have rigged-up a 'robot' participant that looks that the screen and when the stopwatch numbers appear (or hit a certain number) it triggers the response box to transmit the keypress character - this is by displaying a circle on the screen and sensed by an LDR sensor.
Hence the current round-trip sequence is:
- Response Box - Transmit '32' character
- PC - Display BLANK for 2-10 sec (random selection)
- Sensor - no trigger
- PC - Display stopwatch value (from GetSecs)
- PC - When 200msec is reached, display circle under sensor, SKIP to 7.
- REPEAT to 4.
- Sensor - detect circle and trigger to Response Box
- Response Box - Transmit '33' character
- PC - Senses the '33' character and STOPS counting
- PC - save data
- REPEAT until time is up.
THE ISSUE
With the DIRECTREAD version, the time between the display of the circle and the STOP of the PC is fairly consistent. => Generally between 9 and 17 msec. Each update of the stopwatch on the screen is done by Psychtoolbox Screen('FLIP') command, this can take up to 15ms.
With the BUFFER_READ version, the time between the display of the circle and the STOP of is INCONSISTENT. You can have tests that record a 15 to 35msec difference, but to others can be 90 to 120msec out! As the BytesAvailableFcnCount size used is 32, it could be an additional 16msec before the buffer is full and ready to read by the READ function as BUFFER_READ only deals with complete buffers of integral size BytesAvailableFcnCount. However, this does not explain why the numbers can occasionally be so high!
Could it be another process in the PC is preventing the serial callback from executing when it is triggered and hence data could be lost? (TBC)
If the serial callback-generated interrupt is missed, is it re-queued or is it ignored?
Can this serial callback be safely executed as fast as possible (by say reducing the BytesAvailableFcnCount value to say 32) and reliably not miss any data? i.e. for a character speed of 1800cps, the time between each callback executing could be as fast as ~16msec.
(BTW I have been experiencing some random 'crash' issues with Matlab and the Psychtoolbox in the DIRECTREAD version, which is why I moved temporarily to the BUFFER_READ version. But it could be that the BUFFER_READ version is losing data! I have not been successful in isolating these 'crash' issues - looking like due to Psychtoolbox on certain machines).
Serial Port Open:
%Open Port
if strcmp(Selection, 'OPEN') %Open Port
if isempty(objPSTPort)
Clear_PSTPort_Serial(COMPort);
end
objPSTPort=serial(COMport);
objPSTPort.BaudRate=19200;
objPSTPort.Terminator=' ';
objPSTPort.ByteOrder='bigEndian';
objPSTPort.InputBufferSize=2^18; %262144
objPSTPort.BytesAvailableFcnMode='byte';
objPSTPort.BytesAvailableFcnCount=readbuffersize; %512; 2^10 = 1024 -> 0.5s data
objPSTPort.UserData.newData=[];
objPSTPort.UserData.isNew=0;
objPSTPort.UserData.keydetected=0;
%open serial port (if not open)
arg1='a';
if strcmp(objPSTPort.Status,'closed'),
fopen(objPSTPort);
else
disp('port is open')
end
%Can only assign BytesAvailableFcn AFTER fopen
objPSTPort.BytesAvailableFcn={@getNewData,arg1};
Endtime = GetSecs-StartTime;
%return values
retValue1 = 0;
retValue2 = [ 0 ];
retRoutineTimeTaken=Endtime;
end
Direct Read Code:
if strcmp(Selection, 'DIRECTREAD')
bytecount = 0;
dummy = [ 0 ];
if objPSTPort.BytesAvailable
[ dummy, count ] = fread(objPSTPort, objPSTPort.BytesAvailable, 'char');
bytecount = bytecount + count;
end
for (iCount = 1:bytecount)
if ( (dummy(iCount)) == 33 || (dummy(iCount) == 34) )
break;
end
end
%translate 32, 33, 34 char to [1 0] or [0 1] or [0 0]
CharsToReturn = [];
if dummy(iCount) == 33
CharsToReturn = [ 1 0 ];
elseif dummy(iCount) == 34
CharsToReturn = [ 0 1 ];
else
CharsToReturn = [ 0 0 ];
end
Endtime = GetSecs-StartTime;
%return values
retValue1 = [ bytecount ];
retValue2 = [ CharsToReturn ];
retRoutineTimeTaken=Endtime;
end
Buffer Read Code:
if strcmp(Selection, 'BUFFERREAD')
%Read buffer saved by callback fcnGetNewData
retValue1 = 0;
newData=0;
if objPSTPort.UserData.isNew==1
newData=objPSTPort.UserData.newData';
%clear newdata flag
objPSTPort.UserData.isNew=0;
end
values=objPSTPort.ValuesReceived;
Endtime = GetSecs-StartTime;
%return values
retValue1 = objPSTPort.BytesAvailable;
retValue2 = newData;
retRoutineTimeTaken=Endtime;
end
The callback used is:
%getNewData, the serial Callback (i.e. BytesAvailableFcn)
function getNewData(objPSTPort,event,arg1)
global readbuffersize
[Dnew, Dcount, Dmsg]=fread(objPSTPort, readbuffersize, 'char'); %%1024 array? fscanf
if objPSTPort.UserData.isNew==0
%copy Dnew to UserData.newData (i.e. start newData)
objPSTPort.UserData.newData=Dnew; %copy data first
objPSTPort.UserData.isNew=1; %set data new flag
else
%append Dnew to UserData.newData
objPSTPort.UserData.newData=[objPSTPort.UserData.newData Dnew];
end
return
1 Comment
David Barker
on 11 Apr 2016
Edited: David Barker
on 11 Apr 2016
Answers (0)
Categories
Find more on Installation and Operational Settings in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!