MATLAB serial port connection is too slow

2019-08-25 02:32发布

问题:

I've got a detector that spits out two numbers at a high rate when I turn it on. I've been capturing my data using HyperTerminal, which seems to be able to keep up with the device.

I wanted to automate the process and control the device entirely through Matlab, but discovered that less than half of the data gets through to Matlab. Are there any known issues with Matlab's speed in this area?

Here's what I'm using to read in data:

s = serial('COM1', 'BaudRate', 115200, 'DataBits', 8, 'Terminator','CR/LF', 'InputBufferSize', 1024);
T1 = 1;       % Initial T1, T2 values
T2 = 10000;
timer = 300;

% Inputs to serial device: T1, T2, runtime (seconds)    
fprintf(s, sprintf('%d %d %d\r', T1, T2, timer));
tdata = zeros(1e5,2,'uint16');
data = fopen(sprintf('%s.txt',date_and_trial),'w');
tic;
while toc <= timer
% Read data into an array, and write to file.
    if s.BytesAvailable >= 13
            line = fgets(s);
            if length(line) == 13
                a = sscanf(line, '%u %u');
                if length(a) == 2
                    tdata(i,1) = a(1);
                    tdata(i,2) = a(2);
                    fprintf(data, sprintf('%d %d\r', tdata(i,1), tdata(i,2)));
                    i++;
                end
            end
        else
            pause(0.01);
        end       
    end
    disp(toc);
    fclose(s);
    fclose(data);
    fprintf('Finished!\r');

I've been thinking that the conditionals might be slowing it down, but they also seem to be necessary to keep things in the '%i %i\n' format that I need. Maybe there's some way to read in all the data and process it after completion?

回答1:

Since you pass 'Terminator', 'CR/LF' in the serial port open call, you can replace

if s.BytesAvailable >= 13
        line = fgets(s);
        if length(line) == 13

with line = fscanf(s);. fscanf will wait for the terminator, and return a whole line (assuming there aren't errors on the wire). You can also remove the else pause part. These changes should make the loop run fast enough to keep up with the serial data. I would guess that s.BytesAvailable and pause actually take much more time than you'd expect - the former because it calls out to the OS and the latter because the pause could go much longer than you specify depending on timeslicing.

Making this change introduces a new problem though: fscanf will block waiting for the terminator, meaning if the device stops sending before your toc >= timer condition becomes true, the program will hang. So you should make sure to set a sensible timeout in the serial call.

You can make two minor speedups in the body of the loop: tdata(i,:) = a'; will fill the row in one shot, and fprintf(data, '%s\r', line); will skip printf. So putting it all together:

s = serial('COM1', 'BaudRate', 115200, 'DataBits', 8, 'Terminator','CR/LF', 'InputBufferSize', 1024, 'Timeout', 3);
T1 = 1;       % Initial T1, T2 values
T2 = 10000;
timer = 300;

% Inputs to serial device: T1, T2, runtime (seconds)    
fprintf(s, sprintf('%d %d %d\r', T1, T2, timer));
tdata = zeros(1e5,2,'uint16');
data = fopen(sprintf('%s.txt',date_and_trial),'wt');
tic;
while toc <= timer
% Read data into an array, and write to file.
    line = fscanf(s); %# waits for CR/LF terminator
    a = sscanf(line, '%u %u');
    if length(a) == 2
        tdata(i,:) = a'; %# ' assumes sscanf returned a column vector
        fprintf(data, '%s\r', line);
        i++;
    end
end
disp(toc);
fclose(data);
fclose(s);
fprintf('Finished!\r');