FRDM - KL02Z - MMA8451Q 3-axis accelerometer

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

FRDM - KL02Z - MMA8451Q 3-axis accelerometer

2,468 Views
markadamson
Contributor II

Hi,

I have managed to get the 3 - axis accelerometer working, using an 8 bit measurement. I actually followed a guide online: http://mcuoneclipse.com/2012/09/21/tutorial-accelerating-the-kl25z-freedom-board/

Its been very helpful. I wish to now use a 14 bit measurement using the same setup. I'm however having difficulties. I have setup a terminal using Putty, to display my xyz values so far for the 8 bit measurement.

I have been trying to edit the code myself, with the help of the data sheet for the accelerometer however the values I'm getting are incorrect.

My understanding from the online guide for the 8 bit measurement is that #define MMA8451_OUT_X_MSB 0x01, #define MMA8451_OUT_Y_MSB 0x03,#define MMA8451_OUT_Z_MSB 0x05 are being used to get the correct 8 bit measurement.

However, to get the 14 bit measurement, I must use the LSB values as well. I will end up having a 16 bit number, so I have to concatenate or mask (not sure if my terminology is correct) the values to get a 14 bit measurement.

Am I on the right track so far?

Any ideas how to get the 14 bit measurement to work??

Mark

Labels (1)
0 Kudos
20 Replies

1,413 Views
BlackNight
NXP Employee
NXP Employee

Hi Mark,

you might have a look at the project on mcuoneclipse/Examples/FRDM-KL25Z/Freedom_Accel at master · ErichStyger/mcuoneclipse · GitHub

The project shows reading values in non-fast (F_MODE disabled), so you can get more than 8bit values.

Switching off fast mode is done with

  res = MMA1_SetFastMode(FALSE);

Then you can use for example MMA1_GetX() (or the Y or Z counterparts) to get all the bits.

Regards,

Erich

0 Kudos

1,413 Views
markadamson
Contributor II

@ Erich, Your method seems easier to understand for me. However, it seems like I need a different Processor Expert component. I have gone to the github link, under the beans folder, however I don't see a download zip option like a normally do. Could you help me with this?

Also, is there a way I can get the 14-bit data, without having to use a different processor expert component?

0 Kudos

1,413 Views
BlackNight
NXP Employee
NXP Employee

Hi Mark,

the components are as *.PEupd files in mcuoneclipse/PEupd at master · ErichStyger/mcuoneclipse · GitHub, and you need to download and install both files (see Processor Expert Component *.PEupd Files on GitHub | MCU on Eclipse).

And if you want the 14-bit data, without Processor Expert component: you need to configure the F_MODE register as outlined by Mark Butcher.

Erich

0 Kudos

1,413 Views
markadamson
Contributor II

How do I go about getting, F_Mode not set?

The F_Mode is confusing me if I'm honest. How can I understand this?

0 Kudos

1,413 Views
BlackNight
NXP Employee
NXP Employee

Acutally it is the F_READ bit. From the reference manual:

5.2 8-bit or 14-bit Data

The measured acceleration data is stored in the OUT_X_MSB, OUT_X_LSB, OUT_Y_MSB, OUT_Y_LSB, OUT_Z_MSB, and

OUT_Z_LSB registers as 2’s complement 14-bit numbers. The most significant 8-bits of each axis are stored in OUT_X (Y,

Z)_MSB, so applications needing only 8-bit results can use these 3 registers and ignore OUT_X,Y, Z_LSB. To do this, the

F_READ bit in CTRL_REG1 must be set. When the F_READ bit is cleared, the fast read mode is disabled.

The F_READ bit is in CTRL_REG1:

So with the F_READ bit set, reading continuously from the memory map of the accelerometer will skip every second byte for the X, Y, Z values.

With that bit cleared, I can read the 16bit registers.

Below is my code how I write that register:

/*

** ===================================================================

**     Method      :  MMA1_SetFastMode (component MMA8451Q)

**     Description :

**         Turns the F_READ (Fast Read Mode) on or off

**     Parameters  :

**         NAME            - DESCRIPTION

**         on              - if to turn the F_READ mode on or off

**     Returns     :

**         ---             - Error code

** ===================================================================

*/

byte MMA1_SetFastMode(bool on)

{

  uint8_t val, res;

  res = GI2C1_ReadByteAddress8(MMA8451_I2C_ADDR, MMA8451_CTRL_REG_1, &val);

  if (res!=ERR_OK) {

    return res;

  }

  if (on) {

    val |= MMA8451_F_READ_BIT_MASK; /* enable F_READ: Fast read mode, data format limited to single byte (auto increment counter will skip LSB) */

  } else {

    val &= ~MMA8451_F_READ_BIT_MASK; /* disable F_READ: Fast read mode, data format limited to single byte (auto increment counter will skip LSB) */

  }

  return GI2C1_WriteByteAddress8(MMA8451_I2C_ADDR, MMA8451_CTRL_REG_1, val);

}

I hope this helps,

Erich

0 Kudos

1,413 Views
markadamson
Contributor II

@ Erich

I have downloaded the *.PEupd files (although a download zip option is still not visible, I right clicked and saved link as. Only option I had to download these files) but when I import components, I get an error saying  " Import Processor Expert Package" has encountered a problem.....Corrupted update package. Unknown LZH header.

I have imported components before (the LED components for example, which I have been using)

0 Kudos

1,413 Views
BlackNight
NXP Employee
NXP Employee

Yes, unfortunately some browsers or proxies can cause corrupted files :-(

There is a zip file link on the GitHub root (https://github.com/ErichStyger/mcuoneclipse/archive/master.zip), but be aware that will download the full repository snapshot (around 60 MB).

Probably the easiest way.

Otherwise you can download the raw files (click on the file, then select the raw button). I have pasted the links below:

https://github.com/ErichStyger/mcuoneclipse/raw/master/PEupd/Part1_Beans_24.06.2014.PEupd

https://github.com/ErichStyger/mcuoneclipse/raw/master/PEupd/Part2_Beans_24.06.2014.PEupd

I hope this helps,

Erich

0 Kudos

1,413 Views
mjbcswitzerland
Specialist V

Erich

I find that when I change modes (not just the 8/14 bit mode but also the sampling rate) it is necessary to power cycle the device before it accepts the change - do you find the same thing?

Also it looks like the PE generated code is only fully usable together with a pre-emptive operating system since it is blocking and so uses up the complete processor power for the I2C transfers. This allows the demo to operate but it couldn't work together in projects with various interfaces operating in parallel, especially when the accelerometer is being continuously read.

Regards

Mark

0 Kudos

1,413 Views
BlackNight
NXP Employee
NXP Employee

Hi Mark,

I have not noticed that it is necessary to power cycle the device, but I have not verified it neither (yet).

As for the driver code: it works fine for me in RTOS preemptive and bare metal way, as I can specify if I use it either interrupt driven (non-blocking) or in blocking/polling mode. I have used it in both ways, but typically for larger systems I'm using it with a pre-emptive operating system.

Erich

0 Kudos

1,413 Views
markadamson
Contributor II

Yeah, I have noticed that you do have to power cycle the device. I thought it was just me for a while, but thanks for confirming that.

As for the results, with the 8 bit output, I was getting XYZ values of maximum 127 and minimum -128.

Now, trying the 14 bit output, I am getting XYZ values of maximum 255. My understanding is that a 14 bit output, I should be getting a maximum value of 16383??

Another strange observation:

X =  -5 Y =   7 Z =  63 , these were results I obtained with the 8 bit output when the board is flat. This seems fine to me. But now, with the 14 bit output I am getting results: X =  253 Y =   3 Z =  63 when the board is flat.

EDIT: Here are an updated set of results I have obtained.

X = -12035 Y = -22523 Z = 4159

X = -12035 Y = 28677 Z = 4159

X = -12035 Y = 16389 Z = -10178

X = 26877 Y = -32763 Z = -8130

X = -32515 Y = 16389 Z = 8255

X = -32515 Y = 20485 Z = 18495

X = -16131 Y = -24571 Z = -4034

X = 4350 Y = 28677 Z =  63

X = -20227 Y = 28677 Z = 22591

It must be noted that these results are when the board is flat! Actually, I tried moving the board and the results I was obtaining wasn't changing to the orientation of the board. So these changing have no effect of the orientation of the board.

My code is clearly wrong and my understanding is most probably incorrect too unfortunately. I will have a further look this evening.

0 Kudos

1,413 Views
mjbcswitzerland
Specialist V

Mark

The 14 bit range is +8191/-8192 - but if left shifted (in 16 bits) it becomes (+32764/-32768).

It is fastest if you zip the complete project and post it as attachment (activate advanced editor to use attachements) since it can be loaded to a FRDM-KL02Z and then the error immediately seen, so that the reason can be explained.

Posting snippets of code often doesn't help because the actual error is usually elsewhere.

Regards

Mark

0 Kudos

1,413 Views
markadamson
Contributor II

Hi,

I know the title of the folder is pulse_width_modulation, but its the project that I have been working on fairly recently, including trying to add pulse width modulation functionality. That isn't important right now though.

Let me know your thoughts, I'm sure there will be plenty! :smileylaugh:

Mark

0 Kudos

1,413 Views
mjbcswitzerland
Specialist V

Hi Mark

The problems that you have are general coding issues and nothing to do with the processor/accelerometer.

I made some corrections as follows so that it looks to basically work (RGB LED responds to tilt of board and the values printed out may be about what are expected).

      res = MMA8451_ReadReg(MMA8451_OUT_X_MSB, xyz, 6); // read 6 bytes (3 x 16 bit X,Y,Z results)

      usXYZ[0] = ((xyz[0] << 8) | xyz[1]); // convert X to 16 bit value (with 14 bit resolution)

      usXYZ[1] = ((xyz[2] << 8) | xyz[3]); // convert Y to 16 bit value (with 14 bit resolution)

        usXYZ[2] = ((xyz[4] << 8) | xyz[5]); // convert Z to 16 bit value (with 14 bit resolution)

    

      RLED_Put(xyz[0]>50);

      GLED_Put(xyz[2]>50);

      BLED_Put(xyz[4]>50);

    

        printf("X = %3d Y = %3d Z = %3d \n", usXYZ[0], usXYZ[1], usXYZ[2]);

1. You were only reading 3 byte (you must read 6 if in 14 bit mode)

2. You were reading the bytes to usXYZ which means that the values were stored in wrong format

3. Added the conversion between the byte values and the 16 bit value

4. The PWM can work with the 8 bit value as before (note the 0,2,4 for passed location to the R/G/B routines)

Regards

Mark

0 Kudos

1,413 Views
markadamson
Contributor II

I have implemented the changes you suggested, however, the values I am outputting on Putty terminal seem very random, and don't seem to be following the orientation of the board.

For example, when the board is flat on the table I get the following results,

X = -96 Y = -96 Z = 16144

X = -112 Y = -112 Z = 16200

X = -56 Y = 864 Z = -40

X = -64 Y = -112 Z = 16168

X = -128 Y = -112 Z = 16408


Surely these values should be more consistent, i.e.

X =  -5 Y =   7 Z =  63

X =  -5 Y =   7 Z =  63

X =  -5 Y =   6 Z =  63

X =  -5 Y =   6 Z =  63

X =  -5 Y =   7 Z =  63

X =  -5 Y =   6 Z =  62

when flat on the table. These are results obtained with the 8 bit output.

Having said this, as I tilt the board in the XYZ orientations, the RGB outputs the correct colors. So what I did was change the printf statement to printf("X = %3d Y = %3d Z = %3d \n", xyz[0], xyz[2], xyz[4]); and I get the following results when the board is flat on the table:

X =  -1 Y =   3 Z =  63

X =  -1 Y =   3 Z =  63

X =  -1 Y =   3 Z =  63

X =  -1 Y =   3 Z =  63

So I guess that makes sense. But in terms of outputting the 14 bit values on Putty terminal, its not working. Did you say you tested this?

0 Kudos

1,413 Views
mjbcswitzerland
Specialist V

Mark

I wrote

"..and the values printed out may be about what are expected".

I would print the values out as hexadecimal since as decimal it is difficult to see exactly what is happening - see my original example.

Since this detail is more general coding I'll leave it to you to sort out because the accelerometer seems to be doing its job and also the I2C operation looks realistic.

If you want to do some general improvements I would suggest reading the status register as well as just the result values since this will also show whether there were errors and that the conversion has completed, etc. It also means that the reads can be made without always first commanding the address to be read from since the accelerometer's internal, address pointer wraps around to the status register after the conversion results have been read.

As reference I have shown the code to do this in the uTasker project below. It allows simulation of the KL02, I2C and the accelerometer so also makes it much easier to test and debug (using VisualStudio).

Regards

Mark

static const unsigned char ucReadAccelerometerState[] = {RESULT_LENGTH, MMA8451Q_READ_ADDRESS, OWN_TASK}; // command to start a read the defined amount of bytes with the task scheduled when the read has completed

static const unsigned char ucSetAccelerometerRead[] = {MMA8451Q_WRITE_ADDRESS, 0}; // command to set address to read to the first register address (status)

static const unsigned char ucSetAccelerometerMode[] = {MMA8451Q_WRITE_ADDRESS, ACC_CONTROL_REGISTER, (ACC_CONTROL_REGISTER_ACTIVE | ACC_CONTROL_REGISTER_DATA_RATE_50Hz | ACC_CONTROL_REGISTER_SLEEP_RATE_6_25Hz | ACC_CONTROL_REGISTER_LNOISE)}; // command to set the 14-bit resolution mode

#define RESULT_LENGTH 7                   // 14 bit mode 1 status byte and 6 result bytes

// Initialisation

//

fnWrite(IICPortID, (unsigned char *)ucSetAccelerometerMode, sizeof(ucSetAccelerometerMode)); // write the operating mode

// Followed by the first status read

//

fnWrite(IICPortID, (unsigned char *)ucSetAccelerometerRead, sizeof(ucSetAccelerometerRead)); // write the register address to read

fnRead(IICPortID, (unsigned char *)ucReadAccelerometerState, 0); // start the read process of the status

...

// I2C reception

//

if (fnRead(IICPortID, ucInputMessage, RESULT_LENGTH) != 0) {     // if the result read has completed

    int i = 0;

    fnSetColor((signed char)ucInputMessage[1], (signed char)ucInputMessage[3]); // set the colour to the LED

    fnDebugMsg("3-axis state:");                         // display the status on a regular basis

    while (i < RESULT_LENGTH) {                          // display 4 values

        if (i == 0) {

            fnDebugHex(ucInputMessage[i], (sizeof(ucInputMessage[i]) | WITH_LEADIN | WITH_SPACE)); // display the received register contents

        }

        else {

            unsigned short usValue = ucInputMessage[i++];

            usValue <<= 8;

            usValue |= ucInputMessage[i];

            fnDebugHex(usValue, (sizeof(usValue) | WITH_LEADIN | WITH_SPACE)); // display the received register contents

        }

        i++;

    }

    fnDebugMsg("\r\n");

    fnRead(IICPortID, (unsigned char *)ucReadAccelerometerState, 0); // start the read process of the next value

}

0 Kudos

1,413 Views
mjbcswitzerland
Specialist V

Mark

OK. I just saw that the code was using things like

while (!deviceData.dataReceivedFlg) {} /* Wait until data is received received */

and so is presumably configured to use the blocking case, which would only be suiable for simple systems.

Regards

Mark

0 Kudos

1,413 Views
markadamson
Contributor II

@ Mark

I am getting strange values when outputting the xyz values. I am following your method of :

unsigned short usX = ((array[0] << 8) | array[1]);

unsigned short usY = ((array[2] << 8) | array[3]);

unsigned short usZ = ((array[4] << 8) | array[5]);


I am not clear on this process. Why are we shifting by 8 and ORing the shifted vales with the LSB?

0 Kudos

1,413 Views
mjbcswitzerland
Specialist V

Mark

What values do you read from the 6 registers ? [X_MSB, X_LSB, Y_MSB, Y_LSB, and Z_MSB, Z_LSB]

If these are not corect also the 16 bit representations of them won't be good.

Also try a power cycle of the board before testing (if previously 8 bit mode was operating). I have found that this seems to be necessary otherwise the accelerometer is still operating in the old mode even though the new settings have been written to it.

Regards

Mark

0 Kudos

1,413 Views
BlackNight
NXP Employee
NXP Employee

Mark,

the data on the accelerometer is big endian, while the ARM core is little endian. The code swaps the bytes for the ARM.

Erich

0 Kudos

1,413 Views
mjbcswitzerland
Specialist V

Mark

In 8 bit mode the accelerometer is set up in F_MODE and reading three values from address 0x01 returns the contents of the internal registers 0x01, 0x03, 0x05 which are the 8 MSBits of the conversion result (X_MSB, Y_LSB, Z_MSB).

To read in 14 bit mode the F_MODE is not set and then instead of reading 3 bytes from the address 0x01, 6 bytes need to be read which contain X_MSB, X_LSB, Y_MSB, Y_LSB, and Z_MSB, Z_LSB,

To convert the two bytes to a single 16 bit value (with 14 bits of resolution) do something like (assuming the 6 bytes from the accelerometer have been read to array[6])

unsigned short usX = ((array[0] << 8) | array[1]);

unsigned short usY = ((array[2] << 8) | array[3]);

unsigned short usZ = ((array[4] << 8) | array[5]);

In the uTasker project setting the define MMA8451Q_14BIT_RES operates the I2c/accelerometer interface in 14 bit mode (16 bit result with 14 byte resolution) and without it operates in 8 bit mode (8 bit result with 8 bit resolution).

As reference I have attached two binaries for the FRDM-KL02Z built for 8 bit and 14 bit modes. In each case the RGB LED's colour is controlled by the tilt of the board and the accelerometer values can be seen on the UART (virtual COM connection at 115200 Baud) when enabled in the I2C menu (menu 7) with the command "acc_on" ["acc_off" disables]. For example in 14 bit mode:

[first byte is state, followed by X,Y,Z]

3-axis state: 0x00 0xfde4 0x0054 0x3f78

3-axis state: 0x00 0xfdd8 0x0048 0x3fa8

and compared in 8 bit mode:

3-axis state: 0x00 0xfe 0xff 0x3f

3-axis state: 0x00 0xfe 0x00 0x3f

Note that a power cycle seems to be necessary when changing the mode of operation.

Regards

Mark

0 Kudos