Reputation: 1
I'd like to convert 8.24 fixed point numbers from within range of\
0.000000000000000 -> 1.000000000000000 to uint32_t
Do I multiply decimal places or add or bitshift ?
I am receiving the 8.24 format fixed point numbers as 4 bytes
uint8_t meterDataRX[4];
// read 4 bytes from DSP channel
HAL_I2C_Master_Receive(&I2cHandle,bbboxDsp_address,meterDataRX,4,1);
uint32_t a;
a = (meterDataRX[0] << 24) | (meterDataRX[1] << 16) | (meterDataRX[2] << 8) | meterDataRX[3];
But not sure this is correct to start with!
The goal is to make values between uint8_t
of 0x00
to 0xFF
but should I make uint32_t
values from 4 bytes 1st? the cast
uint8_t b;
b = (uint8_t)a;
Upvotes: 0
Views: 1120
Reputation: 17503
You need to read the 4 byte, 8.24 fixed byte number as a 32-bit number. For a real number in the range 0 to 1 inclusive, the '8.24' fixed point number will be represented as a 32-bit number in the range 0 to 0x01000000 (integer part is 1, fractional part is 0). You wish to scale this to a number the range 0 to 0xFF.
Optional step: clamp out-of-range input number to a maximum value of 0x01000000:
if (a > 0x01000000) a = 0x01000000;
Multiply by 0xFF to give a number in the range 0x00000000 to 0xFF000000:
a *= 0xFF;
Optional step: for rounding rather than truncating, add the '8.24' fixed point representation of the real value 0.5, which is 0x00800000:
a += 0x00800000;
Shift right by 24 bits to strip the fractional part:
a >>= 24;
You will be left with a number in the range 0 to 0xFF.
Note that if you skip step 1 (clamping out-of-range numbers), inputs greater than 0x01008080 (representing the real value 1.00196075439453125) will result in arithmetic overflow. If you skip both steps 1 (clamping) and 3 (rounding), inputs greater than 0x01010101 (representing the real value 1.003921568393707275390625) will result in arithmetic overflow.
Upvotes: 1
Reputation: 37222
For 8.24 fixed point, each uint32_t
(or equivelent) would hold a number in the range from 00000000.000000000000000000000000b to 11111111.111111111111111111111111b.
For a floating point number in the range 0.000000000000000 to 1.000000000000000, you'd have to multiply it by (1 << 24)
before converting it to an unsigned integer; so that you end up with 8.24 fixed point.
For a uint8_t
where 0x00 represent 0.0 and 0xFF represents 0.996; you'd have to multiply it by (1 << (24-8))
(or shift it left 16 places) to convert it to 8.24 fixed point.
For a uint8_t
where 0x00 represent 0.0 and 0xFF represents 1.0; you'd have to multiply it by (1 << 24)
(or shift it left 24 places) and then divide by 0xFF to convert it to 8.24 fixed point.
To convert 8.24 fixed point back to either of the cases above, you'd do the reverse (e.g. multiply by 0xFF then shift right by 24 places to get back to uint8_t
where 0xFF represents 1.0).
Upvotes: 0
Reputation: 67820
if the value is between 0 and 1 you need to scale it.
but if you want to just store it in the uint32 variable you need to change the order as now (in your code) your data is big endian but stm32 uCs use little endian.
a = ((uint32_t )meterDataRX[0] ) | ((uint32_t )meterDataRX[1] << 8) | ((uint32_t )meterDataRX[2] << 16) | ((uint32_t )meterDataRX[3] <<24);
you van also use union pinning for it
Upvotes: 0