I am trying to get tap detection working using an MMA8451QR1 and a Kinetis K22. I have used this combination in a number of other products and haven't had any problems. This is the first time I'm trying to get tap detection working.
I am running the MEMS at 200 Hz, trying to get interrupts for data and tap detection. I get interrupts for data, and the first one for tap detection, but after that, nothing.
Unfortunately, I can't post all my code, so I'll try to show what is going on.
I have a number of structs which are unions of bit fields and "Byte" data. Here is my configuration of the MEMS:
void mma8451qr1_configure_tap(void)
{
MMA_CTRL_REG1 ctrl_reg1;
MMA_CTRL_REG2 ctrl_reg2;
MMA_CTRL_REG3 ctrl_reg3;
MMA_CTRL_REG4 ctrl_reg4;
MMA_CTRL_REG5 ctrl_reg5;
MMA_XYZDATACFG ctrl_reg_xyz_data_cfg;
MMA_PULSE_CFG pulse_cfg;
MMA_PULSE_THSX pulse_thsx;
MMA_PULSE_THSY pulse_thsy;
MMA_PULSE_THSZ pulse_thsz;
MMA_PULSE_TMLT pulse_tmlt;
MMA_PULSE_LTCY pulse_ltcy;
MMA_PULSE_WIND pulse_wind;
ctrl_reg2.Bits.RST = true;
mma8451qr1_set_register(MMA8451QR1_REG_CTRL_REG2, ctrl_reg2.Byte);
do
{
ctrl_reg2.Byte = mma8451qr1_get_register(MMA8451QR1_REG_CTRL_REG2);
} while (ctrl_reg2.Bits.RST);
/* CTRL_REG1 */
ctrl_reg1.Byte = mma8451qr1_get_register(MMA8451QR1_REG_CTRL_REG1);
ctrl_reg1.Bits.DATA_RATE = MMA8451QR1_DATA_RATE_200;
mma8451qr1_set_register(MMA8451QR1_REG_CTRL_REG1, ctrl_reg1.Byte);
ctrl_reg_xyz_data_cfg.Bits.FS = MMA8451QR1_FSRANGE_2G;
mma8451qr1_set_register(MMA8451QR1_REG_XYZ_DATA_CFG, ctrl_reg_xyz_data_cfg.Byte);
/* CTRL_REG2 */
ctrl_reg2.Byte = mma8451qr1_get_register(MMA8451QR1_REG_CTRL_REG2);
ctrl_reg2.Bits.MODS = MMA8451QR1_OVS_MODE_HRES;
mma8451qr1_set_register(MMA8451QR1_REG_CTRL_REG2, ctrl_reg2.Byte);
/* CTRL_REG4 */
ctrl_reg4.Byte = mma8451qr1_get_register(MMA8451QR1_REG_CTRL_REG4);
ctrl_reg4.Bits.INT_EN_PULSE = true;
ctrl_reg4.Bits.INT_EN_DRDY = true;
mma8451qr1_set_register(MMA8451QR1_REG_CTRL_REG4, ctrl_reg4.Byte);
/* CTRL_REG5 */
ctrl_reg5.Byte = mma8451qr1_get_register(MMA8451QR1_REG_CTRL_REG5);
ctrl_reg5.Bits.INT_CFG_PULSE = false; /* false == INT2 */
ctrl_reg5.Bits.INT_CFG_DRDY = false;
mma8451qr1_set_register(MMA8451QR1_REG_CTRL_REG5, ctrl_reg5.Byte);
pulse_cfg.Bits.XSPEFE = 1;
pulse_cfg.Bits.XDPEFE = 0;
pulse_cfg.Bits.YSPEFE = 1;
pulse_cfg.Bits.YDPEFE = 0;
pulse_cfg.Bits.ZSPEFE = 1;
pulse_cfg.Bits.ZDPEFE = 0;
pulse_cfg.Bits.ELE = 1;
pulse_cfg.Bits.DPA = 0;
mma8451qr1_set_register(MMA8451QR1_REG_PULSE_CFG, pulse_cfg.Byte);
pulse_thsx.Byte = 0x20;
mma8451qr1_set_register(MMA8451QR1_REG_PULSE_THSX, pulse_thsx.Byte);
pulse_thsy.Byte = 0x20;
mma8451qr1_set_register(MMA8451QR1_REG_PULSE_THSY, pulse_thsy.Byte);
pulse_thsz.Byte = 0x2A;
mma8451qr1_set_register(MMA8451QR1_REG_PULSE_THSZ, pulse_thsz.Byte);
pulse_tmlt.Byte = 0x28;
mma8451qr1_set_register(MMA8451QR1_REG_PULSE_TMLT, pulse_tmlt.Byte);
pulse_ltcy.Byte = 0x28;
mma8451qr1_set_register(MMA8451QR1_REG_PULSE_LTCY, pulse_ltcy.Byte);
pulse_wind.Byte = 0x0F;
mma8451qr1_set_register(MMA8451QR1_REG_PULSE_WIND, pulse_wind.Byte);
mma8451qr1_set_active();
}
I have the INT1 and INT2 connected to two pins on the K22 and the interrupts work. The pins on the K22 are configured as GPIO, with interrupts. The handler is here:
void PORTD_IRQHandler(void)
{
if (PORTD_PCR4 & PORT_PCR_ISF_MASK)
{
g_main_event_flags.bits.do_mems = true;
PORTD_PCR4 |= PORT_PCR_ISF_MASK;
}
if (PORTD_PCR6 & PORT_PCR_ISF_MASK)
{
g_main_event_flags.bits.do_mems = true;
PORTD_PCR6 |= PORT_PCR_ISF_MASK;
}
}
The g_main_event_flags struct is also a union and declared globally.
In my main processing loop (main.c), I have the following:
/*
* Handle MEMS events
*/
if (g_main_event_flags.bits.do_mems)
{
MMA_INT_SOURCE int_src;
MMA_PULSE_SRC pulse_src;
int_src.Byte = mma8451qr1_get_int_source();
if (int_src.Bits.SRC_PULSE)
{
pulse_src.Byte = mma8451qr1_read_pulse();
ports_led(true);
}
if (int_src.Bits.SRC_DRDY)
{
mma8451qr1_read_data();
}
int_src.Byte = mma8451qr1_get_int_source();
g_main_event_flags.bits.do_mems = false;
}
My understanding from reading the spec is that I should read the SRC_PULSE register to clear the interrupt and read the data from the MEMS to clear the SRC_DRDY interrupt. All the routines appear to work, I just never get a second interrupt. It makes me think I'm not clearing something I should.
ports_led() sets a GPIO to turn on an LED. It also sets a clock counter to keep it on for about 200 ms (interrupt driven) so that I can see the taps and then the LED is turned off.
I have gotten as many as three taps before it stops working. I can pause the debugger and it the K22 is not frozen (still executes my main event loop).
Thanks for the help.
I saw that page. I finally disabled the DRDY interrupt and I have it working. That seems a bit extreme though. Shouldn't I be able to get both interrupts (PULSE and DRDY) so that I can use the data and get taps?
Thanks!
Hello James,
I am glad to know your application works properly.
Indeed, you can enable both interrupt sources at the CTRL_REG4 register.
I recommend reading the INT_SOURCE register once an interrupt is triggered in order to know the source of the interrupt. Please note that the bits are set by a low to high transition and are cleared by reading the appropriate interrupt source register.
Please let me know if this information helps.
Best regards,
David
I did that. Please note that it is not working properly. If I enable the DRDY interrupt, I only get a few tap interrupts. This is what I stated above and showed in the initial code. As I said later, the tapping only works if I disable the DRDY interrupt.
Hello James,
I noticed that you are using an ODR of 200Hz. It means that every 5ms an interrupt will be triggered due to a new set of data is ready (DRDY interrupt).
I assume that is the reason you are not able to generate a PULSE interrupt.
In this case, I recommend using a slower ODR in order to have enough time between the every new set of data.
Please let me know your results.
Best Regards,
David
I tried it at a slower rate and it worked better, but not completely.
I removed the calls to read the INT_SOURCE register and just set my flags based on the interrupts (INT1 for pulse and INT2 for data) and it appears to work fine. Not sure why reading the INT_SOURCE register makes things not work, I'm sure it's something I'm doing somewhere, but I can't find it.
I can read data at 200 Hz and get tap interrupts. I don't think that 5 ms is a short time for my interrupts considering all I'm doing is setting a flag. This is running on a K22 at 40 MHz. Just to verify, I stepped the speed up to 800 Hz and it still works (without reading the INT_SOURCE register).
Thanks for the help everybody!
Hello James,
Thank you very much for writing.
In this case, I recommend to use the example project below as a reference for your application.
MMA8451Q -Single Tap Detection Bare metal example project
The Kinetis KL25Z is used in the project mentioned but it is useful for you. Please refer to the registers configuration.
Please let me know if you have any further question and I will be glad to help.
Have a great day,
David
I've done a little more experimenting. If I turn off interrupts for data ready (INT_EN_DRDY = 0), it works. I find it hard to believe that you can't do both at once, so it must be something I'm doing.