Convert 24-bit ADC serial read data from 3-byte format to signed integer (int32)

15 Ansichten (letzte 30 Tage)
I am receiving EEG data from a 24 bit ADC over serial. The ADC data is transmitting in 3 bytes from MSB to LSB. The full packet is 21 bytes:
  • The first byte is the start byte - 0xFF (255 in decimal)
  • Then packet number byte.
  • Then the next 3 bytes are the 24 bit ADC value broken into MSB LSB2 LSB1
I can parse the data fine, but re-constructing a 2's complement signed int32 number is causing issues. The values I am getting out certainly don't reflect what the ADC should be giving out.
Below are the lines to read and parse the 504 samples (which gives me 24 ADC values (504samples/21bytes = 24 values)). I have tried uint8 instead of uchar with similar results (when I try int8 I get a invalid specified precision error).
comEEGSMT = serial(com,'BaudRate',3000000);
fopen(comEEGSMT);
rawData(1:504) = fread(comEEGSMT, 504, 'uchar');
fclose(comEEGSMT);
startPackets = find(rawData == 255);
bytes = rawData([startpackets+2 startpackets+3 startpackets+4]);
I have tried the following method to reconstruct the value:
ADC_value = bytes(:,1)*256^2 + bytes(:,2)*256 + bytes(:,3);
and the following line is the formula to convert the above number to volts:
ADC_value_volts = ADC_value*(5/3)*(1/(2^32));
The values are in the range of 4000 - 8000 microvolts with large jumps in value. The values SHOULD be in the range of 200 - 600 microvolts with small changes.
I have found other questions relating to similar issues, but have had no success trying the proposed solutions such as in the link below: https://uk.mathworks.com/matlabcentral/answers/137965-concatenate-3-bytes-array-of-real-time-serial-data-into-single-precision
Any help would be very much appreciated as I've been stuck on this for quite long.
Thanks Mark

Antworten (2)

Jan
Jan am 6 Dez. 2016
Isn't this insecure:
startPackets = find(rawData == 255);
What happens, if a 255 appears in the data?
This replies the wrong order, as far as I can see:
bytes = rawData([startpackets+2 startpackets+3 startpackets+4]);
Try:
ADC_value = rawData(startpackets+2)*256^2 + rawData(startpackets+3)*256 + ...
rawData(startpackets+4);
  6 Kommentare
Mark O'Sullivan
Mark O'Sullivan am 9 Dez. 2016
I don't understand why the packet number is meaningless? Presume it's just a handy way to show if you're dropping packets or whatever.
The documentation states:
The formula for reconstructing to a 2's compliment, signed int32 is:
data = (MSB<<24 + LSB2<<17 + LSB1<<10).
The conversion factor for EEG channels to volts is:
EEG = data*(5/3)*(1/2^32)
I haven't been able to get meaningful data using the parsing method above with these formula. The expected values are in the hundreds of microvolts range, so I should be getting values around 0.0005V (5e-4 V).
Jan
Jan am 9 Dez. 2016
Perhaps:
ADC_value = typecast(uint32(rawData(startpackets+2)*16777216 + ...
rawData(startpackets+3)*131072 + ...
rawData(startpackets+4)*1024), 'int32');

Melden Sie sich an, um zu kommentieren.


David Mellinger
David Mellinger am 29 Jun. 2023
Bearbeitet: David Mellinger am 29 Jun. 2023
There's an additional problem if the file has signed 3-byte numbers. After a LOT of trial and error, I ended up doing it like this.
ch = fread(fp, [3 n], 'uint8'); % n is the number of data values to read
val = int32(ch(3,:)*2^16 + ch(2,:)*2^8 + ch(1,:));
ix = (ch(3,:) >= 0x80); % indices of negative values
val(ix) = typecast(bitor(val(ix), -(2^24)), 'int32');
This is for little-endian data in the file (fp) being read from. For big-endian data, which the person asking the question has, use this:
ch = fread(fp, [3 n], 'uint8'); % n is the number of data values to read
val = int32(ch(1,:)*2^16 + ch(2,:)*2^8 + ch(3,:));
ix = (ch(1,:) >= 0x80); % indices of negative values
val(ix) = typecast(bitor(val(ix), -(2^24)), 'int32');

Kategorien

Mehr zu EEG/MEG/ECoG finden Sie in Help Center und File Exchange

Produkte

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by