Look at the datasheet. Essentially, there are two separate devices in a single package.
You need the accelerometer/gyroscope device registers:
21 and 22 (0x15 and 0x16) for temperature
24 and 25 (0x18 and 0x19) for gyroscope X axis
26 and 27 (0x1A and 0x1B) for gyroscope Y axis
28 and 29 (0x1C and 0x1D) for gyroscope Z axis
40 and 41 (0x28 and 0x29) for accelerometer X axis
42 and 43 (0x2A and 0x2B) for accelerometer X axis
44 and 45 (0x2C and 0x2D) for accelerometer Z axis
For the magnetic field, you need the magnetic sensor registers:
40 and 41 (0x28 and 0x29) for X axis field strength
42 and 43 (0x2A and 0x2B) for Y axis field strength
44 and 45 (0x2C and 0x2D) for Z axis field strength
These are all in two's complement format, with least significant byte first.
Since practically all microcontrollers use two's complement format for signed integers (STM32 definitely does, as do ARMs and AVRs, including all Arduinos), you can write five simple helper functions. In pseudo-C:
static int16_t temperature;
static int16_t gyro_x, gyro_y, gyro_z;
static int16_t accel_x, accel_y, accel_z;
static int16_t mag_x, mag_y, mag_z;
static uint8_t buf[6];
static void update_temperature(void)
{
/* Read 2 bytes from registers 0x15 and 0x16 (21 and 22)
from the accelerometer/gyroscope device, to buf[] */
temperature = (int16_t)((((unsigned int)buf[1]) << 8) | (unsigned int)(buf[0]));
}
static void update_gyro(void)
{
/* Read 6 bytes from registers 0x18..0x1D (24 through 29)
from the accelerometer/gyroscope device, to buf[] */
gyro_x = (int16_t)((((unsigned int)buf[1]) << 8) | (unsigned int)(buf[0]));
gyro_y = (int16_t)((((unsigned int)buf[3]) << 8) | (unsigned int)(buf[2]));
gyro_z = (int16_t)((((unsigned int)buf[5]) << 8) | (unsigned int)(buf[4]));
}
static void update_accel(void)
{
/* Read 6 bytes from registers 0x28..0x2D (40 through 45)
from the accelerometer/gyroscope device, to buf[] */
accel_x = (int16_t)((((unsigned int)buf[1]) << 8) | (unsigned int)(buf[0]));
accel_y = (int16_t)((((unsigned int)buf[3]) << 8) | (unsigned int)(buf[2]));
accel_z = (int16_t)((((unsigned int)buf[5]) << 8) | (unsigned int)(buf[4]));
}
static void update_mag(void)
{
/* Read 6 bytes from registers 0x28..0x2D (40 through 45)
from the magnetic sensor device, to buf[] */
mag_x = (int16_t)((((unsigned int)buf[1]) << 8) | (unsigned int)(buf[0]));
mag_y = (int16_t)((((unsigned int)buf[3]) << 8) | (unsigned int)(buf[2]));
mag_z = (int16_t)((((unsigned int)buf[5]) << 8) | (unsigned int)(buf[4]));
}
static void update_all(void)
{
update_temperature();
update_gyro();
update_accel();
update_mag();
}
The assignments look funky, but the compiler should optimize them to sane machine code.
You'll probably want to write a helper function to do the actual reads (or perhaps three functions; one for reading 2 bytes from the accelerometer/gyroscope device to buf[], one for reading 6 bytes from the accelerometer/gyroscope device to buf[], and one for reading 6 bytes from the magnetic sensor device to buf[], all from consecutive registers given as a parameter to the function).
This approach uses 26 bytes of RAM, and should be suitable for all microcontrollers, both 8-bit (AVRs) and 32-bit (STM32, ARMs). The above code is not sensitive to the byte order (endianness) of the microcontroller.
If your device sends the gyroscope, accelerometer, and magnetic sensor values via USB or serial to your computer, you need to know the byte order (endianness) of the output values is.
The sensor itself provides them in least significant byte first. STM32 is, as far as I know, little-endian too, so it should also provide them least significant byte first.
So, lets assume you receive 18*N
bytes from the device, with gyroscope axes first, then accelerometer axes, and finally magnetic sensor axis readings. The following should convert those values to signed integers for easy manipulation:
#include <stdlib.h>
#include <inttypes.h>
static inline void extract16toint(int *const to,
const unsigned char *const from,
const size_t n,
const size_t stride)
{
const unsigned char *const end = from + n * stride;
const unsigned char *src = from;
int *dst;
while (src < end) {
*(dst++) = (int16_t)( (unsigned int)(src[0])
| ((unsigned int)(src[1]) << 8) );
src += stride;
}
}
static void extract_gyroX(int *const to,
const unsigned char *const data,
const size_t n)
{
extract16toint(to, data + 0, n, 18);
}
static void extract_gyroY(int *const to,
const unsigned char *const data,
const size_t n)
{
extract16toint(to, data + 2, n, 18);
}
static void extract_gyroZ(int *const to,
const unsigned char *const data,
const size_t n)
{
extract16toint(to, data + 4, n, 18);
}
static void extract_accelX(int *const to,
const unsigned char *const data,
const size_t n)
{
extract16toint(to, data + 6, n, 18);
}
static void extract_accelY(int *const to,
const unsigned char *const data,
const size_t n)
{
extract16toint(to, data + 8, n, 18);
}
static void extract_accelZ(int *const to,
const unsigned char *const data,
const size_t n)
{
extract16toint(to, data + 10, n, 18);
}
static void extract_magX(int *const to,
const unsigned char *const data,
const size_t n)
{
extract16toint(to, data + 12, n, 18);
}
static void extract_magY(int *const to,
const unsigned char *const data,
const size_t n)
{
extract16toint(to, data + 14, n, 18);
}
static void extract_magZ(int *const to,
const unsigned char *const data,
const size_t n)
{
extract16toint(to, data + 16, n, 18);
}
In OP's case, calling something like
int *accelX = malloc(300 * sizeof (int));
extract_accelX(accelX, rawData, 300);
should work.