Convert 24-bit ADC serial read data from 3-byte format to signed integer (int32)
40 views (last 30 days)
Show older comments
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
0 Comments
Answers (2)
Jan
on 6 Dec 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 Comments
Jan
on 9 Dec 2016
Perhaps:
ADC_value = typecast(uint32(rawData(startpackets+2)*16777216 + ...
rawData(startpackets+3)*131072 + ...
rawData(startpackets+4)*1024), 'int32');
David Mellinger
on 29 Jun 2023
Edited: David Mellinger
on 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');
0 Comments
See Also
Categories
Find more on Parametric Spectral Estimation in Help Center and File Exchange
Products
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!