I would like to present another example code/demo that reads acceleration data from the Xtrinsic MMA8491Q digital accelerometer and visualizes them using the FreeMASTER tool via USBDM interface. I have used recently released Xtrinsic MEMS sensors board that features three types of Xtrinsic sensors including the MMA8491Q and is fully compatible with the Freescale FRDM-KL25Z platform.
In comparison with other Xtrinsic accelerometers, the MMA8491Q is turned on at the rising edge on the EN pin and acquires only one sample for each of the three axes. It does not have any interrupt pins, instead there are three push-pull logic outputs which provide tilt detection at 45 degrees as the original target application was tamper detection. However, it is possible to read the 14-bit output values through the I2C port as demonstrated in my example below.
According to the User Manual, both SCL and SDA lines are connected through the 4.7K pull-up resistors to the I2C1 module (PTE1 and PTE0 pins) on the KL25Z128 MCU and the EN pin is connected to the PTA13 pin. The EN input needs to be kept high until a new data is ready (max. 900us) and read. In my code I use the PIT module to wait 1ms before reading the output values. This timer is also used to read the output data periodically at a fixed rate. The timeout period of the PIT is set to 500us. The MCU is, therefore, configured as follows:
//I2C1 module initialization
SIM_SCGC4 |= SIM_SCGC4_I2C1_MASK; // Turn on clock to I2C1 module
SIM_SCGC5 |= SIM_SCGC5_PORTE_MASK; // Turn on clock to Port E module
PORTE_PCR1 = PORT_PCR_MUX(6); // PTE1 pin is I2C1 SCL line
PORTE_PCR0 = PORT_PCR_MUX(6); // PTE0 pin is I2C1 SDA line
I2C1_F = 0x14; // SDA hold time = 2.125us, SCL start hold time = 4.25us, SCL stop hold time = 5.125us
I2C1_C1 = I2C_C1_IICEN_MASK; // Enable I2C1 module
//Configure the PTA13 pin as an output to drive the EN input of the MMA8491Q
SIM_SCGC5 |= SIM_SCGC5_PORTA_MASK; // Turn on clock to Port A module
PORTA_PCR13 = PORT_PCR_MUX(1); // PTA13 is configured as GPIO
GPIOA_PCOR |= 1<<13; // Set PTA13 pin low
GPIOA_PDDR |= 1<<13; // PTA13 pin is an output
SIM_SCGC6 |= SIM_SCGC6_PIT_MASK; // Turn on clock to to the PIT module
PIT_LDVAL0 = 5240; // Timeout period = 500us
PIT_MCR = PIT_MCR_FRZ_MASK; // Enable clock for PIT, freeze PIT in debug mode
PIT_TCTRL0 = PIT_TCTRL_TIE_MASK | // Enable PIT interrupt
PIT_TCTRL_TEN_MASK; // and PIT
//Enable PIT interrupt on NVIC
NVIC_ICPR |= 1 << ((INT_PIT - 16) % 32);
NVIC_ISER |= 1 << ((INT_PIT - 16) % 32);
In the PIT interrupt service routine (ISR), there is a variable Counter that is increased by one on every PIT interrupt (500us) and its value is then compared with two preset values. The first preset value EN_HIGH_TIME determines how long the EN pin will remain high to ensure a valid reading of a new set of output data. The second preset value DATA_UPDATE_PERIOD corresponds to the desired output data rate. At the end of the ISR, the PIT interrupt flag is cleared.
static int Counter = 0;
Counter++; // Each increment represents 500us
GPIOA_PSOR |= 1<<13; // Set EN pin high
case EN_HIGH_TIME: // 1ms passed
DataReady = 1; // Data is ready
case DATA_UPDATE_PERIOD: // 100ms passed
Counter = 0; // Clear Counter at the end of the sample period
PIT_TFLG0 |= PIT_TFLG_TIF_MASK; // Clear PIT interrupt flag
In the main loop, the DataReady variable is periodically checked and if it is set, the accelerometer data registers 0x01 - 0x06 are read and then the acceleration in units of g is calculated. Finally the EN pin is set low to reduce the current consumption and the DataReady variable is cleared.
if (DataReady) // Is a new set of data ready?
AccData = I2C_ReadRegister(MMA8491Q_I2C_ADDRESS, OUT_X_MSB_REG); // [7:0] are 8 MSBs of the 14-bit X-axis sample
AccData = I2C_ReadRegister(MMA8491Q_I2C_ADDRESS, OUT_X_LSB_REG); // [7:2] are the 6 LSB of 14-bit X-axis sample
AccData = I2C_ReadRegister(MMA8491Q_I2C_ADDRESS, OUT_Y_MSB_REG); // [7:0] are 8 MSBs of the 14-bit Y-axis sample
AccData = I2C_ReadRegister(MMA8491Q_I2C_ADDRESS, OUT_Y_LSB_REG); // [7:2] are the 6 LSB of 14-bit Y-axis sample
AccData = I2C_ReadRegister(MMA8491Q_I2C_ADDRESS, OUT_Z_MSB_REG); // [7:0] are 8 MSBs of the 14-bit Z-axis sample
AccData = I2C_ReadRegister(MMA8491Q_I2C_ADDRESS, OUT_Z_LSB_REG); // [7:2] are the 6 LSB of 14-bit Z-axis sample
Xout_14_bit = ((short) (AccData<<8 | AccData)) >> 2; // Compute 14-bit X-axis output value
Yout_14_bit = ((short) (AccData<<8 | AccData)) >> 2; // Compute 14-bit Y-axis output value
Zout_14_bit = ((short) (AccData<<8 | AccData)) >> 2; // Compute 14-bit Z-axis output value
Xout_g = ((float) Xout_14_bit) / SENSITIVITY; // Compute X-axis output value in g's
Yout_g = ((float) Yout_14_bit) / SENSITIVITY; // Compute Y-axis output value in g's
Zout_g = ((float) Zout_14_bit) / SENSITIVITY; // Compute Z-axis output value in g's
GPIOA_PCOR |= 1<<13; // Set EN pin low
DataReady = 0;
The calculated values can be watched in the "Variables" window on the top right of the Debug perspective or in the FreeMASTER application.
Attached you can find the complete source code written in the CW 10.3 as well as the FreeMASTER project.
If there are any questions regarding this simple application, do not hesitate to ask below. Your feedback or suggestions are also welcome.