In “Tutorial: Accelerating the KL25Z Freedom Board” I used the MMA8451Q accelerometer on the FRDM-KL25Z board in a very primitive way: I’m reading directly some low-level registers from the device through an I2C low-level component. No calibrating, no special device feature setting, only raw values. Since then, things have been evolved: In “Tutorial: Creating a Processor Expert Component for an Accelerometer” I started to create a driver for this accelerometer, and since then a lot more functionality has been added.
MMA7260Q and MMA8451Q
I’m using a my Processor Expert driver for the Freescale MMA7260Q accelerometer for multiple years in multiple projects. That MMA7260Q is present on many Freescale evaluation boards, including many of the Tower Boards. The MMA7260Q is an accelerometer with an analog interface, and the driver I have created for it is available on GitHub.
So when I was thinking to extend the MMA8451Q driver, I wanted to have the connection to be compatible as much as possible. That way I easily can switch my existing software to the MMA8451Q. The MMA8451Q has a nice digital I2C interface, with a lot of cool features (tap detection, orientation detection) I consider to use.
MMA8451Q Properties
The component properties have been extended with calibration values and Shell support:
💡 The MMA8451Q is factory calibrated, and has the ability to store calibration values on the device. Right now I store the calibration values in the driver, and not on the device yet.
The optional Shell interface is something I recently have added to the MMA7260Q too: it allows me to inspect/configure the device. More about this later in this post.
💡 I continue to add a command line (Shell) interface to my components, as feedback has been that this is very useful. And I’m using it all the time too: it allows me to work with a device using a command line interface. And with the modular way of Processor Expert components I can enable/disable it on a component by component base.
MMA8451Q Methods
The following picture shows the currently implemented interface:
The ParseCommand()
method is only enabled if the Shell is enabled in the component properties. This is done in the component .CHG (Change) script:
%ifndef Shell %set ParseCommand Selection never %else %set ParseCommand Selection always %endifI2C
To simplify the communication with I2C to the device, two more methods (
WriteByteAddress8()
andReadByteAddress8()
) have been added to the GenericI2C component. This makes e.g. the driver method to enable the accelerometer really easy:byte %'ModuleName'%.%Enable(void) { uint8_t val, res; res = inherited.I2C.ReadByteAddress8(MMA8451_I2C_ADDR, MMA8451_CTRL_REG_1, &val); if (res!=ERR_OK) { return res; } val |= MMA8451_ACTIVE_BIT_MASK; /* enable device */ return inherited.I2C.WriteByteAddress8(MMA8451_I2C_ADDR, MMA8451_CTRL_REG_1, val); }Little Or Big Endian
One thing (again!) I was running into was that the Kinetis/ARM is Little Endian, while my brain is thinking (always? most of the time?) in Big Endian mode :-(. As I want the driver to work both with LE (Little Endian) and BE (Big Endian) cores, I created a macro to the driver code to deal with both worlds:
%if (CPUfamily = "Kinetis") #define %'ModuleName'_CPU_IS_LITTLE_ENDIAN 1 /* Cpu is little endian */ %else #define %'ModuleName'_CPU_IS_LITTLE_ENDIAN 0 /* Cpu is big endian */ %endif❓ Note sure if there is a better way in Processor Expert CDE to know if the CPU is BE or LE?
Then I can use this in my driver code like this:
%-************************************************************************************************************ %-BW_METHOD_BEGIN MeasureGetRawX %ifdef MeasureGetRawX %define! RetVal %include Common\MMA8451QMeasureGetRawX.Inc word %'ModuleName'%.%MeasureGetRawX(void) { union { uint8_t buf[2]; /* value from device is in big endian */ uint16_t be; } val; static const uint8_t addr = MMA8451_OUT_X_MSB; if(inherited.I2C.ReadAddress(MMA8451_I2C_ADDR, (uint8_t*)&addr, sizeof(addr), &val.buf[0], sizeof(val.buf))!=ERR_OK) { return 0; /* failure */ } #if %'ModuleName'_CPU_IS_LITTLE_ENDIAN return (uint16_t)((val.buf[0]<<8)|val.buf[1]); /* transform into LE value */ #else return val.be; /* already in BE */ #endif } %endif %- MeasureGetRawX %-BW_METHOD_END MeasureGetRawXShell Interface
The Shell interface offers following commands:
With the status command I get the following information:
- raw: the raw (14bit, left shifted) values, both in hexdecimal and signed decimal
- calibOffset: the calibration offsets (as defined in the component properties or set by the calibration methods).
- calib 1g: The value the device uses for 1 g acceleration.
- GetX, Y, Z: the values returned by the
GetX()
,GetY()
andGetZ()
methods. These values are compensated with the calibration values - mg X, Y, Z: the values returned by
GetXmg()
,GetYmg()
andGetZmg()
, in milli-g
As I have added the I2CSpy component as a bonus to my project, it allows me to read/write/dump the I2C memory map of the accelerometer:
💡 Using the I2CSpy I can explore the device settings without the need to download/write special code: I can inspect the bits and settings, write to the device and immediately see the impact. Really cool (at least I think this :-))
Sources
As always, things are available on GitHub. This link points to the driver code.
Summary and Outlook
The new Processor Expert driver works very well for me. It does not cover all the features of the device yet (tap detection, low power modes, orientation detection, different g sensitivity levels). So there is still a lot of room for extensions, and this will be added as soon as I find the time :-). But for now, I think it is a good starting point.
Happy Accelerating 🙂
Can you give me the link to the component.
Please.
LikeLike
The link to it is at the end of the post, under ‘Sources’. The ‘beans’ folder is here: https://github.com/ErichStyger/McuOnEclipse_PEx
See this link how you can import the component(s):
https://github.com/ErichStyger/mcuoneclipse/wiki/Getting-Started
LikeLike
I’m a fan of yours since I’ve started playing with freedom board a couple weeks ago. I’m following along with this article and start noticing when I enable the MMA8451Q; It only allow a selection to create new component with Shell. But I’ve already used FSShell with other components up to now. And I’d really like to access the MMA8451Q with all the above command lines with FSShell. Any suggestion ? Thank you for a great library.
LikeLike
Hi,
I had to redesign the FSShell, as it had too much lumped into it. The shell part is now in the ‘Shell’ component, while now FatFS has an interface to it. What I suggest is:
a) disable the FSShell (so you have it as backup)
b) Add the Shell component. You can link it to your serial interface (e.g. Asynchroserial)
c) link accelerometer/FatFS/FreeRTOS or whatever you have to the new Shell
Should be fairly easy. Otherwise let me know.
LikeLike
Pingback: Tutorial: FreeMASTER Visualization and Run-Time Debugging | MCU on Eclipse
Pingback: USB for the Freescale ARM Kinetis KL46Z and K21D50M | MCU on Eclipse
Pingback: Tutorial: Using the FRDM-KL25Z as Low Power Board | MCU on Eclipse
Hi. It’s possible to use this with the MMA8452 accelerometer? Thanks!
LikeLike
Hi Juan,
I did a quick data sheet comparison, and it looks the MMA8452 has only different number of A/D bits, and everything else is the same. So you should be able to use the driver with it. Same for the MMA8453.
LikeLike
Hi Erich, I´m using MMA8453QR1 in a custom board of mine and I´d like to know where I can change the resolution (14 to 10 bits from MMA8451 bean) to port this project? Another question is: what is the full-scale value range? Is it set to 8g?
LikeLike
Hi Marcio,
you would have to use the accelerometer with SetFastMode(FALSE). Setting the ScaleRange requires that the sensor is disabled.
I have now extended the component with methods to set and get the range (GetScaleRangeG() and SetScaleRangeG()). Or you could use the following code below:
uint8_t MMA1_GetScaleRangeG(uint8_t *gScale)
{
uint8_t val, res;
res = GI2C1_ReadByteAddress8(MMA1_I2C_ADDR, MMA1_XYZ_DATA_CFG, &val);
if (res!=ERR_OK) {
return res;
}
switch(val&0x3) { /* check FS1 and FS2 bits */
case 0: *gScale = 2; /* 00: 2g */
return ERR_OK;
case 1: *gScale = 4; /* 01: 4g */
return ERR_OK;
case 2: *gScale = 8; /* 10: 8g */
return ERR_OK;
default: break;
} /* switch */
*gScale = 0; /* error case */
return ERR_RANGE;
}
There is an updated component available on SourceForge too: https://sourceforge.net/projects/mcuoneclipse/files/PEx%20Components/ and an example project using it is on https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/KDS/FRDM-KL25Z/FRDM-KL25Z_Accel
I hope this helps?
Erich
LikeLike
Right now I´m getting a ERR_BUSY code when I try to use SetFastMode(FALSE) method. I do that right after I call MMA1_Init() with no error, so unfortunately I couldn´t test GetScaleRangeG() yet : (
Did you faced this problem before or have some tip to help me to solve it?
LikeLike
I have found that the accelerometer after writing some settings, needs some delay time (because the sensor is busy storing the settings?).
Try to add a delay after the MMA1_Init(), e.g. with
WAIT1_Waitms(10);
LikeLike
In fact, even in the FRDM-KL25Z board I`m getting this ERR_BUSY error and I don´t know why.
LikeLike
With the project I have posted on GitHub, or your own version of it?
ERR_BUSY means that the device does not respond with an ACK when the driver wants to talk to it.
LikeLike
It happens with your project too, but it seems to work properly when I turn off and then turn on the board again (FRDM-KL25Z or my board).
LikeLike
Hi Marcio,
yes, I noticed that as well: if the accelerometer somehow internally is screwed up, it only can be brougth back with a power cycle. That’s why in my designs I usually add a FET to the Vcc/supply pin of the accelerometer so the microcontroller can reset it.
LikeLike
Ok Erich. I´d like to use the shell to test the commands, but I´m little confused here: When I enable it on MMA8451Q component I need to choose one default serial, which one? Why I can use the FreeMaster bean that is already on project?
LikeLike
Hi Marcio,
I have added shell support to the example on GitHub (https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/KDS/FRDM-KL25Z/FRDM-KL25Z_Accel), so you can use that as an example how to use it. That example uses the OpenSDA serial connection.
I hope this helps,
Erich
LikeLike
Oh, it´s true! I have forgot this step, sorry about that. I already make this changes and now it´s working, thanks Erich!
The only problem now it´s about the strange axis values I´m reading here when I´m doing the calibration.
LikeLike
Doing my tests I found some weird (errors?) here: right after calling MMA1_SetFastMode(FALSE) I used MMA1_CalibrateX1g() method and I found that in fact this function is switched with MMA1_CalibrateY1g(), in other words the X and Y axis are inverted and for MMA1_CalibrateZ1g() method positioning the FRDM-KL25Z in horizontal position I got a 0g and turning it up side down I found -2g (near 8192 in decimal). Did you find the same result at your side? Obs: The scale range for your project came with 2g.
LikeLike
Hi Erich,
Another issue I´m facing here is that it seems I can´t change the scale range using MMA1_SetScaleRangeG method. It was set to 2g and until now I couldn´t change it to 4g or 8g, even with no error codes. I used this code below:
uint8_t res=ERR_OK, ret, scale;
res = MMA1_Init();
WAIT1_Waitms(10);
res = MMA1_SetFastMode(FALSE);
WAIT1_Waitms(10);
ret = MMA1_SetScaleRangeG(8);
And then I used this code to read the scale range:
res = MMA1_Init();
WAIT1_Waitms(10);
// res = MMA1_SetFastMode(FALSE);
// WAIT1_Waitms(10);
ret = MMA1_GetScaleRangeG(&scale);
Using MMA1_SetFastMode(FALSE) or not doesn´t make difference here and when I call MMA1_GetScaleRangeG, I allways read scale equals to 2. Did you find the same problem there?
LikeLike
Hi Marcio,
as noted earlier (and in the documentation of the component too) you have to disable the sensor if you want to do change the settings.
So have you called MMA1_Disable() first?
LikeLike
Hi Marcio,
as noted in the comments of these functions, you have to place the board with that respective axis in a 1g position. So if you do the Z calibration, you have to put the board flat (the sensor is exposed 1g). In a smilar way, you have to change the board orientation if you do the other calibration functions.
I hope this helps,
Erich
LikeLike
Yes, that is correct Erich. I just would like to know if you have tested there this calibrations and got the same results or behavior I´m got here at my side.
LikeLike
Below is the output of my calibration, and that looks ok to me?
------------------------
FRDM-KL25Z Accel
------------------------
Device is enabled, disable it before applying settings...
Setting G mode to 2 g.
Enable device.
X axis calibration (1g for X): place the board with the USB connector pointing to the sky and press a key...
Y axis calibration (1g for Y): place the board with the right arduino rows down and press a key...
Z axis calibration (1g for Z): place the board flat on a table and press a key...
done!
x: -11 y: -11, z: 3969
x: 17 y: 21, z: 4005
x: 21 y: 3, z: 3995
x: 19 y: -7, z: 4007
x: 21 y: 17, z: 4007
x: 19 y: 29, z: 4021
x: 29 y: 13, z: 4019
x: 25 y: 15, z: 4003
x: 19 y: 13, z: 3995
x: 39 y: -1, z: 4003
x: -3 y: -7, z: 4017
x: 9 y: 21, z: 4017
x: 15 y: 9, z: 4003
x: 23 y: -1, z: 4021
x: 17 y: -1, z: 4027
x: 15 y: 25, z: 4051
x: 21 y: 25, z: 4025
x: 23 y: 13, z: 4003
x: 33 y: 13, z: 4043
LikeLike
For 2g I think is correct. Are you using Shell? Can you please tell me how to set it on this project to do this tests either?
LikeLike
Yes, it is using the shell. See https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/KDS/FRDM-KL25Z/FRDM-KL25Z_Accel
LikeLike
Hello Erich,
Thanks for great posts on pretty much a huge number of topics….
I tried to use the component with the Shell interface but I do not understand
which API should be called regularly to parse commands entered in the termite shell…
I did not find any example code using the extended driver (with non OS)…
Could you please give me a hint ?
Thanks
Vince
LikeLike
Hi Vince,
Just put the code from the Shell task into a loop:
static void ShellLoop(void) {
unsigned char buf[48];
buf[0] = ”;
(void)CLS1_ParseWithCommandTable((unsigned char*)CLS1_CMD_HELP, CLS1_GetStdio(), CmdParserTable);
for(;;) {
(void)CLS1_ReadAndParseWithCommandTable(buf, sizeof(buf), CLS1_GetStdio(), CmdParserTable);
LEDG_Neg();
}
}
instead of its own loop, call it from your main() loop. There is really nothing special with or without an RTOS.
LikeLike
Pingback: First Steps with the Freescale TWR-K64F120M | MCU on Eclipse
Sorry for posting on an old topic however I’m struggling with the GenericI2C component in Kinetis Design Studio 3.0.0.
I’m trying to adapt this code to work for an LSM9DS0 combined acc, mag and gyro device however I’m having difficulty when trying to do multi-byte reads. As an example, reading the 3×16 bit accelerometer values:
static const uint8_t addr = LSM9DS0_REG_OUT_X_L_A;
int16_t xyz[3];
GI2C1_ReadAddress(LSM9DS0_ADDR_XM, (uint8_t*)&addr, sizeof(addr), (uint8_t*)xyz, 6);
When inspecting the “xyz” array, each element has the same value. The value is different each time the ReadAddress function is called but still the value in each array element is identical.
Am I using this ReadAddress function incorrectly?
Thanks in advance,
Kevin
LikeLike
Hi Kevin,
your usage looks ok. I think you have a different problem down the wire. Could you use a logic analyzer or scope to check what is sent and received over SCL and SDA and matches what your LSM9DS0 needs? It might be that there is something wrong.
I hope this helps,
Erich
LikeLike
Hi Erich,
Thank you for your quick reply.
I haven’t used a logic analyzer as of yet as the WHO_AM_I and single-byte reads all seem to work correctly – returning the expected values.
I shall investigate with a logic analyzer later this evening.
Kind regards,
Kevin
LikeLike
Hi Erich,
I’ve been working on FRDM kl25z to interface MMA8451q via I2C0.
My code is something like this
https://github.com/sunsided/frdm-kl25z-marg-fusion/blob/milestone/mma8451q-serial-stream/frdm-kl25z-acc-uart/Sources/i2c/i2c.c
As soon as I initialize the I2C0, I’m read WHO_AM_I continuously with 3 seconds delay, but after 1st read, SCL is pulled low forever. SDA seems to be fine.
What might be the possible problem? Can you please suggest something which I can try?
Regards,
Bharath
LikeLike
Hi Bharath,
can you try the code I have used in that post instead? There are so many things which could go wrong with your code example. What you could try is to do single stepping through your code while watching with a logic analyzer the SDA and SCL line. I hope this helps, good luck!
LikeLike
Hi Erich,
Yes I’ve tried stepping through the code and the controller wasn’t able to generate the STOP condition hence the SCL remained low. I used the different frdm kl25z board, and things started working. I believe the board I was using has some problem. Thank you for your input.
Regards,
Bharath
LikeLike
Erich
If I want to use the (GetXmg, GetYmg, GetZmg) and send those values to freemaster I have to change those three lines in “Appliction.c”:
#if USE_PEX_COMPONENT
xyz[0] = MMA1_GetX()>>8; to xyz[0] = MMA1_GetXmg()>>8;
xyz[1] = MMA1_GetY()>>8; to xyz[1] = MMA1_GetYmg()>>8;
xyz[2] = MMA1_GetZ()>>8; to xyz[2] = MMA1_GetZmg()>>8;
or do I have to do something more?
LikeLike
That depends what values you want to show? The shift by 8 is simply to only send the MSBits, but that’s up to you.
LikeLike
Hi,
I have used the Generic I2C Component and Accelerometer code downloaded from GitHub for making the LEDs blink according to tilt. This is my main() program.
/*for(;;)
{
LED1_On();
LED2_On();
LED3_On();
res = MMA1_Init();
while (res==ERR_OK) {
res = MMA1_GetRaw8XYZ(&xyz[0]);
LED1_Put(xyz[0]>50);
LED2_Put(xyz[1]>50);
LED3_Put(xyz[2]>50);
}
LED1_Off();
LED2_Off();
LED3_Off();
}*/
I am not sure if I need to include other methods on that are available on the accelerometer driver like the writereg8/readreg8 etc. In a previous tutorial where you built separate events for the accelerometer you had used such methods. I would like to get some pointers with regard to that.
LikeLike
The GenericI2C driver is a wrapper to deal with different I2C driver implementations (LDD, non-LDD, bitbanging, etc). Your posted example make sense, and you should not need any other functions. The writereg8/readreg8 methods are there for you to read/write registers directly in case you need them.
LikeLike
Pingback: McuOnEclipse Components: 30-Sept-2018 Release | MCU on Eclipse