Sensors Knowledge Base

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

Sensors Knowledge Base

Discussions

Sort by:
Hi Everyone, In this tutorial I intend to run through my simple bare metal example code I created for the Freescale FRDM-KL25Z platform and the FRDM-STBC-AGM01 board containing a three axis accelerometer + magnetometer (FXOS8700CQ) and a three axis gyroscope (FXAS21002C). I will not cover the Sensor Fusion library and the ISF which also support this board. The FreeMASTER tool is used to visualize all the data that are read from both sensors using an interrupt technique through the I 2 C interface. This example illustrates: 1. Initialization of the MKL25Z128 MCU (mainly I 2 C and PORT modules). 2. I 2 C data write and read operations. 3. Initialization of the FXOS8700CQ and FXAS21002C. 4. Simple accelerometer offset calibration based on the AN4069. 5. Output data reading using an interrupt technique. 6. Conversion of the output raw values to real values in g’s, µT, dps and °C. 7. Visualization of the calculated values in the FreeMASTER tool. 1. As you can see in the FRDM-STBC-AGM01 schematic, both sensors are controlled via I 2 C by default. With jumpers J6 and J7 in their default position (2-3), the I 2 C signals are routed to the I2C1 module (PTC1 and PTC2 pins) of the KL25Z MCU. The INT1_8700 output is connected to the PTD4 pin and the INT1_21002 pin to the PTA5 pin of the KL25Z MCU. These both interrupt pins are configured as push-pull active-low outputs, so the corresponding PTD4/PTA5 pin configuration is GPIO with an interrupt on falling edge. The MCU is, therefore, configured as follows. void MCU_Init(void) {      //I2C1 module initialization      SIM_SCGC4 |= SIM_SCGC4_I2C1_MASK;        // Turn on clock to I2C1 module      SIM_SCGC5 |= SIM_SCGC5_PORTC_MASK;       // Turn on clock to Port C module      PORTC_PCR1 |= PORT_PCR_MUX(0x2);         // PTC1 pin is I2C1 SCL line      PORTC_PCR2 |= PORT_PCR_MUX(0x2);         // PTC2 pin is I2C1 SDA line      I2C1_F  |= I2C_F_ICR(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 PTD4 pin (connected to the INT1 of the FXOS8700CQ) for falling edge interrupts      SIM_SCGC5 |= SIM_SCGC5_PORTD_MASK;       // Turn on clock to Port D module      PORTD_PCR4 |= (0|PORT_PCR_ISF_MASK|      // Clear the interrupt flag                       PORT_PCR_MUX(0x1)|      // PTD4 is configured as GPIO                       PORT_PCR_IRQC(0xA));    // PTD4 is configured for falling edge interrupts                   //Configure the PTA5 pin (connected to the INT1 of the FXAS21002) for falling edge interrupts      SIM_SCGC5 |= SIM_SCGC5_PORTA_MASK;       // Turn on clock to Port A module      PORTA_PCR5 |= (0|PORT_PCR_ISF_MASK|      // Clear the interrupt flag                       PORT_PCR_MUX(0x1)|      // PTA5 is configured as GPIO                       PORT_PCR_IRQC(0xA));    // PTA5 is configured for falling edge interrupts                          //Enable PORTD interrupt on NVIC      NVIC_ICPR |= 1 << ((INT_PORTD - 16)%32);      NVIC_ISER |= 1 << ((INT_PORTD - 16)%32);                //Enable PORTA interrupt on NVIC      NVIC_ICPR |= 1 << ((INT_PORTA - 16)%32);      NVIC_ISER |= 1 << ((INT_PORTA - 16)%32); } 2. The 7-bit I 2 C slave address of the FXOS8700CQ is 0x1E since both SA0 and SA1 pins are shorted to GND. The address of the FXAS21002C is 0x20 since SA0 pin is also shorted to GND. The two screenshots below show the write operation which writes the value 0x25 to the CTRL_REG1 (0x2A) of the FXOS8700CQ and 0x16 to the CTRL_REG1 (0x13) of the FXAS21002C. Here is the single byte read from the WHO_AM_I register. As you can see, it returns the correct value 0xC7 for the FXOS8700CQ and 0xD7 for the FXAS21002C. Finally, a burst read of 12 bytes from the FXOS8700CQ output data registers (0x01 – 0x06 and 0x33 – 0x38) and 6 bytes from the FXAS21002C output data registers (0x01 – 0x06) is shown below. 3. At the beginning of the initialization, all registers are reset to their default values by setting the RST bit of the CTRL_REG2 register. Then the FXOS8700CQ is initialized as shown below. void FXOS8700CQ_Init (void) {      I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG2, 0x40);          // Reset all registers to POR values      Pause(0x631);          // ~1ms delay            I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, XYZ_DATA_CFG_REG, 0x00);   // +/-2g range with 0.244mg/LSB              I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, M_CTRL_REG1, 0x1F);        // Hybrid mode (accelerometer + magnetometer), max OSR      I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, M_CTRL_REG2, 0x20);        // M_OUT_X_MSB register 0x33 follows the OUT_Z_LSB register 0x06 (burst read)                       I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG2, 0x02);          // High Resolution mode      I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG3, 0x00);          // Push-pull, active low interrupt      I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG4, 0x01);          // Enable DRDY interrupt      I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG5, 0x01);          // DRDY interrupt routed to INT1 - PTD4      I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG1, 0x25);          // ODR = 25Hz, Reduced noise, Active mode   } And here is the initialization of the FXAS21002C. void FXAS21002C_Init (void) {      I2C_WriteRegister(FXAS21002C_I2C_ADDRESS, GYRO_CTRL_REG1, 0x40);     // Reset all registers to POR values      Pause(0x631);        // ~1ms delay            I2C_WriteRegister(FXAS21002C_I2C_ADDRESS, GYRO_CTRL_REG0, 0x03);     // High-pass filter disabled, +/-250 dps range -> 7.8125 mdps/LSB = 128 LSB/dps      I2C_WriteRegister(FXAS21002C_I2C_ADDRESS, GYRO_CTRL_REG2, 0x0C);     // Enable DRDY interrupt, routed to INT1 - PTA5, push-pull, active low interrupt      I2C_WriteRegister(FXAS21002C_I2C_ADDRESS, GYRO_CTRL_REG1, 0x16);     // ODR = 25Hz, Active mode        } 4. A simple accelerometer offset calibration method is implemented according to the AN4069. void FXOS8700CQ_Accel_Calibration (void) {      char X_Accel_offset, Y_Accel_offset, Z_Accel_offset;            FXOS8700CQ_DataReady = 0;           while (!FXOS8700CQ_DataReady){}           // Is a first set of data ready?      FXOS8700CQ_DataReady = 0;            I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG1, 0x00);          // Standby mode                 I2C_ReadMultiRegisters(FXOS8700CQ_I2C_ADDRESS, OUT_X_MSB_REG, 6, AccelMagData);          // Read data output registers 0x01-0x06                     Xout_Accel_14_bit = ((short) (AccelMagData[0]<<8 | AccelMagData[1])) >> 2;          // Compute 14-bit X-axis acceleration output value      Yout_Accel_14_bit = ((short) (AccelMagData[2]<<8 | AccelMagData[3])) >> 2;          // Compute 14-bit Y-axis acceleration output value      Zout_Accel_14_bit = ((short) (AccelMagData[4]<<8 | AccelMagData[5])) >> 2;          // Compute 14-bit Z-axis acceleration output value                   X_Accel_offset = Xout_Accel_14_bit / 8 * (-1);          // Compute X-axis offset correction value      Y_Accel_offset = Yout_Accel_14_bit / 8 * (-1);          // Compute Y-axis offset correction value      Z_Accel_offset = (Zout_Accel_14_bit - SENSITIVITY_2G) / 8 * (-1);          // Compute Z-axis offset correction value                   I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, OFF_X_REG, X_Accel_offset);                  I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, OFF_Y_REG, Y_Accel_offset);           I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, OFF_Z_REG, Z_Accel_offset);                        I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG1, 0x25);          // Active mode again } 5. In the ISRs, only the interrupt flags are cleared and the DataReady variables are set to indicate the arrival of new data. void PORTD_IRQHandler() {      PORTD_PCR4 |= PORT_PCR_ISF_MASK;          // Clear the interrupt flag      FXOS8700CQ_DataReady = 1;  } void PORTA_IRQHandler() {      PORTA_PCR5 |= PORT_PCR_ISF_MASK;          // Clear the interrupt flag      FXAS21002C_DataReady = 1;  } 6. The output values from accelerometer registers 0x01 – 0x06 are first converted to signed 14-bit integer values and afterwards to real values in g’s. Similarly, the output values from magnetometer registers 0x33 – 0x38 are first converted to signed 16-bit integer values and afterwards to real values in microtesla (µT). if (FXOS8700CQ_DataReady)          // Is a new set of accel + mag data ready? {                  FXOS8700CQ_DataReady = 0;                                                                                                                          I2C_ReadMultiRegisters(FXOS8700CQ_I2C_ADDRESS, OUT_X_MSB_REG, 12, AccelMagData);         // Read FXOS8700CQ data output registers 0x01-0x06 and 0x33 - 0x38                     // 14-bit accelerometer data      Xout_Accel_14_bit = ((short) (AccelMagData[0]<<8 | AccelMagData[1])) >> 2;        // Compute 14-bit X-axis acceleration output value      Yout_Accel_14_bit = ((short) (AccelMagData[2]<<8 | AccelMagData[3])) >> 2;        // Compute 14-bit Y-axis acceleration output value      Zout_Accel_14_bit = ((short) (AccelMagData[4]<<8 | AccelMagData[5])) >> 2;        // Compute 14-bit Z-axis acceleration output value                              // Accelerometer data converted to g's      Xout_g = ((float) Xout_Accel_14_bit) / SENSITIVITY_2G;        // Compute X-axis output value in g's      Yout_g = ((float) Yout_Accel_14_bit) / SENSITIVITY_2G;        // Compute Y-axis output value in g's      Zout_g = ((float) Zout_Accel_14_bit) / SENSITIVITY_2G;        // Compute Z-axis output value in g's                               // 16-bit magnetometer data                   Xout_Mag_16_bit = (short) (AccelMagData[6]<<8 | AccelMagData[7]);          // Compute 16-bit X-axis magnetic output value      Yout_Mag_16_bit = (short) (AccelMagData[8]<<8 | AccelMagData[9]);          // Compute 16-bit Y-axis magnetic output value      Zout_Mag_16_bit = (short) (AccelMagData[10]<<8 | AccelMagData[11]);        // Compute 16-bit Z-axis magnetic output value                                                         // Magnetometer data converted to microteslas      Xout_uT = (float) (Xout_Mag_16_bit) / SENSITIVITY_MAG;        // Compute X-axis output magnetic value in uT      Yout_uT = (float) (Yout_Mag_16_bit) / SENSITIVITY_MAG;        // Compute Y-axis output magnetic value in uT      Zout_uT = (float) (Zout_Mag_16_bit) / SENSITIVITY_MAG;        // Compute Z-axis output magnetic value in uT              } Similarly, the output values from gyroscope registers 0x01 – 0x06 are first converted to signed 16-bit integer values and afterwards to real values in degrees per second. Temperature is also read out from the 0x12 register. if (FXAS21002C_DataReady)         // Is a new set of gyro data ready? {                  FXAS21002C_DataReady = 0;                                                                                                                                       I2C_ReadMultiRegisters(FXAS21002C_I2C_ADDRESS, GYRO_OUT_X_MSB_REG, 6, GyroData);         // Read FXAS21002C data output registers 0x01-0x06                                   // 16-bit gyro data      Xout_Gyro_16_bit = (short) (GyroData[0]<<8 | GyroData[1]);           // Compute 16-bit X-axis output value      Yout_Gyro_16_bit = (short) (GyroData[2]<<8 | GyroData[3]);           // Compute 16-bit Y-axis output value      Zout_Gyro_16_bit = (short) (GyroData[4]<<8 | GyroData[5]);           // Compute 16-bit Z-axis output value                                           // Gyro data converted to dps      Roll = (float) (Xout_Gyro_16_bit) / SENSITIVITY_250;          // Compute X-axis output value in dps      Pitch = (float) (Yout_Gyro_16_bit) / SENSITIVITY_250;         // Compute Y-axis output value in dps      Yaw = (float) (Zout_Gyro_16_bit) / SENSITIVITY_250;           // Compute Z-axis output value in dps                               // Temperature data      Temp = I2C_ReadRegister(FXAS21002C_I2C_ADDRESS, GYRO_TEMP_REG);                   }   7. The calculated values can be watched in the "Variables" window on the top right of the Debug perspective or in the FreeMASTER application. To open and run the FreeMASTER project, install the FreeMASTER application​ and FreeMASTER Communication Driver. I guess this is enough to let you start experimenting with the FRDM-STBC-AGM01 board. Attached you can find the complete source code written in the CW for MCU's v10.6 including 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. Regards, Tomas
View full article
Hi Everyone, In this article I would like to describe a simple bare-metal example code for the new Xtrinsic FXLS8471Q digital accelerometer. I have used recently released FRDM-FXS-MULTI(-B) sensor expansion board, that features many of the Xtrinsic sensors introduced in 2013 including the FXLS8471Q, in conjunction with the  Freescale FRDM-KL25Z development platform. The FreeMASTER tool is used to visualize the acceleration data that are read from the FXLS8471Q using an interrupt technique through the SPI interface. This example illustrates: 1. Initialization of the MKL25Z128 MCU (mainly SPI and PORT modules). 2. SPI data write and read operations. 3. Initialization of the accelerometer to achieve the highest resolution. 4. Simple offset calibration based on the AN4069. 5. Output data reading using an interrupt technique. 6. Conversion of the output values from registers 0x01 – 0x06 to real acceleration values in g’s. 7. Visualization of the output values in the FreeMASTER tool. 1. As you can see in the FRDM-FXS-MULTI(-B)/FRDM-KL25Z schematics and the image below, SPI signals are routed to the SPI0 module of the KL25Z MCU and the INT1 output is connected to the PTA5 pin (make sure that pins 2-3 of J6 on the sensor board are connected together using a jumper). The PTD0 pin (Chip select) is not controlled automatically by SPI0 module, hence it is configured as a general-purpose output. The INT1 output of the FXLS8471Q is configured as a push-pull active-low output, so the corresponding PTA5 pin configuration is GPIO with an interrupt on falling edge.The core/system clock frequency is 20.97 MHz and SPI clock is 524.25 kHz. The MCU is, therefore, configured as follows. void MCU_Init(void) {      //SPI0 module initialization      SIM_SCGC4 |= SIM_SCGC4_SPI0_MASK;        // Turn on clock to SPI0 module      SIM_SCGC5 |= SIM_SCGC5_PORTD_MASK;       // Turn on clock to Port D module      PORTD_PCR1 = PORT_PCR_MUX(0x02);         // PTD1 pin is SPI0 CLK line      PORTD_PCR2 = PORT_PCR_MUX(0x02);         // PTD2 pin is SPI0 MOSI line      PORTD_PCR3 = PORT_PCR_MUX(0x02);         // PTD3 pin is SPI0 MISO line      PORTD_PCR0 = PORT_PCR_MUX(0x01);         // PTD0 pin is configured as GPIO (CS line driven manually)      GPIOD_PSOR |= GPIO_PSOR_PTSO(0x01);      // PTD0 = 1 (CS inactive)      GPIOD_PDDR |= GPIO_PDDR_PDD(0x01);       // PTD0 pin is GPIO output          SPI0_C1 = SPI_C1_SPE_MASK | SPI_C1_MSTR_MASK;     // Enable SPI0 module, master mode      SPI0_BR = SPI_BR_SPPR(0x04) | SPI_BR_SPR(0x02);     // BaudRate = BusClock / ((SPPR+1) * 2^(SPR+1)) = 20970000 / ((4+1) * 2^(2+1)) = 524.25 kHz                        //Configure the PTA5 pin (connected to the INT1 of the FXLS8471Q) for falling edge interrupts      SIM_SCGC5 |= SIM_SCGC5_PORTA_MASK;       // Turn on clock to Port A module      PORTA_PCR5 |= (0|PORT_PCR_ISF_MASK|      // Clear the interrupt flag                       PORT_PCR_MUX(0x1)|      // PTA5 is configured as GPIO                       PORT_PCR_IRQC(0xA));    // PTA5 is configured for falling edge interrupts                 //Enable PORTA interrupt on NVIC      NVIC_ICPR |= 1 << ((INT_PORTA - 16) % 32);      NVIC_ISER |= 1 << ((INT_PORTA - 16) % 32); } 2. The FXLS8471Q uses the ‘Mode 0′ SPI protocol, which means that an inactive state of clock signal is low and data are captured on the leading edge of clock signal and changed on the falling edge. The falling edge on the SA1/CS_B pin starts the SPI communication. A write operation is initiated by transmitting a 1 for the R/W bit. Then the 8-bit register address, ADDR[7:0] is encoded in the first and second serialized bytes. Data to be written starts in the third serialized byte. The order of the bits is as follows: Byte 0: R/W, ADDR[6], ADDR[5], ADDR[4], ADDR[3], ADDR[2], ADDR[1], ADDR[0] Byte 1: ADDR[7], X, X, X, X, X, X, X Byte 2: DATA[7], DATA[6], DATA[5], DATA[4], DATA[3], DATA[2], DATA[1], DATA[0] The rising edge on the SA1/CS_B pin stops the SPI communication. Below is the write operation which writes the value 0x3D to the CTRL_REG1 (0x3A). Similarly a read operation is initiated by transmitting a 0 for the R/W bit. Then the 8-bit register address, ADDR[7:0] is encoded in the first and second serialized bytes. The data is read from the MISO pin (MSB first). The screenshot below shows the read operation which reads the correct value 0x6A from the WHO_AM_I register (0x0D). Multiple read operations are performed similar to single read except bytes are read in multiples of eight SCLK cycles. The register address is auto incremented so that every eighth next clock edges will latch the MSB of the next register. A burst read of 6 bytes from registers 0x01 to 0x06 is shown below. It also shows how the INT1 pin is automatically cleared by reading the acceleration output data. 3. At the beginning of the initialization, all accelerometer registers should be reset to their default values by setting the RST bit of the CTRL_REG2 register. However, the software reset does not work properly in SPI mode as described in Appendix A of the FXLS8471Q data sheet. Therefore the following piece of the code performing the software reset should not be used. Instead, I have shortened R46 on the FRDM-FXS-MULTI-B board to activate a hardware reset. The dynamic range is set to ±2g and to achieve the highest resolution, the LNOISE bit is set and the lowest ODR (1.56Hz) and the High Resolution mode are selected (more details in AN4075). The DRDY interrupt is enabled and routed to the INT1 interrupt pin that is configured to be a push-pull, active-low output. void FXLS8471Q_Init (void) {      unsigned char reg_val = 0;          /* The software reset does not work properly in SPI mode as described in Appendix A         of the FXLS8471Q data sheet. Therefore the following piece of the code is not used.         I have shortened R46 on the FRDM-FXS-MULTI-B board to activate a hardware reset. */          /*FXLS8471Q_WriteRegister(CTRL_REG2, 0x40);     // Reset all registers to POR values          Pause(0x631);     // ~1ms delay                 do       // Wait for the RST bit to clear      {           reg_val = FXLS8471Q_ReadRegister(CTRL_REG2) & 0x40;      } while (reg_val); */                FXLS8471Q_WriteRegister(XYZ_DATA_CFG_REG, 0x00);          // +/-2g range with ~0.244mg/LSB      FXLS8471Q_WriteRegister(CTRL_REG2, 0x02);            // High Resolution mode      FXLS8471Q_WriteRegister(CTRL_REG3, 0x00);            // Push-pull, active low interrupt      FXLS8471Q_WriteRegister(CTRL_REG4, 0x01);            // Enable DRDY interrupt      FXLS8471Q_WriteRegister(CTRL_REG5, 0x01);            // DRDY interrupt routed to INT1 - PTA5       FXLS8471Q_WriteRegister(CTRL_REG1, 0x3D);            // ODR = 1.56Hz, Reduced noise, Active mode           } 4. A simple offset calibration method is implemented according to the AN4069. void FXLS8471Q_Calibration (void) {      char Xoffset, Yoffset, Zoffset;            DataReady = 0;                while (!DataReady){}      // Is a first set of data ready?      DataReady = 0;            FXLS8471Q_WriteRegister(CTRL_REG1, 0x00);     // Standby mode                   FXLS8471Q_ReadMultiRegisters(OUT_X_MSB_REG, 6, AccData);     // Read data output registers 0x01-0x06                                                      Xout_14_bit = ((short) (AccData[0]<<8 | AccData[1])) >> 2;     // Compute 14-bit X-axis output value      Yout_14_bit = ((short) (AccData[2]<<8 | AccData[3])) >> 2;     // Compute 14-bit Y-axis output value      Zout_14_bit = ((short) (AccData[4]<<8 | AccData[5])) >> 2;     // Compute 14-bit Z-axis output value                                              Xoffset = Xout_14_bit / 8 * (-1);     // Compute X-axis offset correction value      Yoffset = Yout_14_bit / 8 * (-1);     // Compute Y-axis offset correction value      Zoffset = (Zout_14_bit - SENSITIVITY_2G) / 8 * (-1);     // Compute Z-axis offset correction value                                              FXLS8471Q_WriteRegister(OFF_X_REG, Xoffset);                FXLS8471Q_WriteRegister(OFF_Y_REG, Yoffset);         FXLS8471Q_WriteRegister(OFF_Z_REG, Zoffset);                   FXLS8471Q_WriteRegister(CTRL_REG1, 0x3D);     // Active mode again }      5. In the ISR, only the interrupt flag is cleared and the DataReady variable is set to indicate the arrival of new data. void PORTA_IRQHandler() {      PORTA_PCR5 |= PORT_PCR_ISF_MASK;     // Clear the interrupt flag      DataReady = 1;     } 6. The output values from accelerometer registers 0x01 – 0x06 are first converted to signed 14-bit values and afterwards to real values in g’s. if (DataReady)     // Is a new set of data ready? {                  DataReady = 0;                                                                                                                        FXLS8471Q_ReadMultiRegisters(OUT_X_MSB_REG, 6, AccData);     // Read data output registers 0x01-0x06                                                        Xout_14_bit = ((short) (AccData[0]<<8 | AccData[1])) >> 2;     // Compute 14-bit X-axis output value      Yout_14_bit = ((short) (AccData[2]<<8 | AccData[3])) >> 2;     // Compute 14-bit Y-axis output value      Zout_14_bit = ((short) (AccData[4]<<8 | AccData[5])) >> 2;     // Compute 14-bit Z-axis output value                                            Xout_g = ((float) Xout_14_bit) / SENSITIVITY_2G;     // Compute X-axis output value in g's      Yout_g = ((float) Yout_14_bit) / SENSITIVITY_2G;     // Compute Y-axis output value in g's      Zout_g = ((float) Zout_14_bit) / SENSITIVITY_2G;     // Compute Z-axis output value in g's } 7. The calculated values can be watched in the "Variables" window on the top right of the Debug perspective or in the FreeMASTER application. To open and run the FreeMASTER project, install the FreeMASTER 1.4 application and FreeMASTER Communication Driver. Attached you can find the complete source code written in the CW for MCU's v10.5 including the FreeMASTER project. If there are any questions regarding this simple application, please feel free to ask below. Your feedback or suggestions are also welcome. Regards, Tomas
View full article
Hi Everyone,   I would like to share here another simple bare-metal example code/demo for the Xtrinsic MMA8652FC digital accelerometer that I have created while working with the Freescale FRDM-KL25Z development platform and FRDM-FXS-MULTI(-B) sensor expansion board. To visualize the acceleration data that are read from the MMA8652FC using an interrupt technique through the I 2 C interface, I have used the FreeMASTER tool.   This example illustrates:   1. Initialization of the MKL25Z128 MCU (mainly I 2 C and PORT modules). 2. I 2 C data write and read operations. 3. Initialization of the accelerometer to achieve the highest resolution. 4. Simple offset calibration based on the AN4069. 5. Output data reading using an interrupt technique. 6. Conversion of the output values from registers 0x01 – 0x06 to real acceleration values in g’s. 7. Visualization of the output values in the FreeMASTER tool.   1. As you can see in the FRDM-FXS-MULTI(-B)/FRDM-KL25Z schematics and the image below, I2C signals are routed to the I2C1 module (PTC1 and PTC2 pins) of the KL25Z MCU and the INT1 output is connected to the PTA5 pin (make sure that pin #3 of J4 and pin #2 of J6 connector on the sensor expansion board are connected together). The INT1 output of the MMA8652FC is configured as a push-pull active-low output, so the corresponding PTA5 pin configuration is GPIO with an interrupt on falling edge.     The MCU is, therefore, configured as follows. void MCU_Init(void) {      //I2C1 module initialization      SIM_SCGC4 |= SIM_SCGC4_I2C1_MASK;        // Turn on clock to I2C1 module      SIM_SCGC5 |= SIM_SCGC5_PORTC_MASK;       // Turn on clock to Port C module      PORTC_PCR1 |= PORT_PCR_MUX(0x2);         // PTC1 pin is I2C1 SCL line      PORTC_PCR2 |= PORT_PCR_MUX(0x2);         // PTC2 pin is I2C1 SDA line      I2C1_F  |= I2C_F_ICR(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 PTA5 pin (connected to the INT1 of the MMA8652FC) for falling edge interrupts      SIM_SCGC5 |= SIM_SCGC5_PORTA_MASK;       // Turn on clock to Port A module      PORTA_PCR5 |= (0|PORT_PCR_ISF_MASK|      // Clear the interrupt flag                       PORT_PCR_MUX(0x1)|      // PTA5 is configured as GPIO                       PORT_PCR_IRQC(0xA));    // PTA5 is configured for falling edge interrupts                     //Enable PORTA interrupt on NVIC      NVIC_ICPR |= 1 << ((INT_PORTA - 16)%32);      NVIC_ISER |= 1 << ((INT_PORTA - 16)%32); }   2. The 7-bit I 2 C address of the MMA8652FC is fixed value 0x1D. As shown above, the SCL line is connected to the PTC1 pin and SDA line to the PTC2 pin. The I2C clock frequency is 125 kHz. The screenshot below shows the write operation which writes the value 0x39 to the CTRL_REG1 (0x2A).     And here is the single byte read from the WHO_AM_I register 0x0D. As you can see, it returns the correct value 0x4A.     Multiple bytes of data can be read from sequential registers after each MMA8652FC acknowledgment (AK) is received until a no acknowledge (NAK) occurs from the KL25Z followed by a stop condition (SP) signaling an end of transmission. A burst read of 6 bytes from registers 0x01 to 0x06 is shown below. It also shows how the INT1 pin is automatically deasserted by reading the acceleration output data.       3. At the beginning of the initialization, all registers are reset to their default values by setting the RST bit of the CTRL_REG2 register. The dynamic range is set to ±2g and to achieve the highest resolution, the lowest ODR (1.56Hz) and the High Resolution mode are selected (more details in AN4075). The DRDY interrupt is enabled and routed to the INT1 interrupt pin that is configured to be a push-pull, active-low output. void MMA8652FC_Init (void) {      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG2, 0x40);          // Reset all registers to POR values           Pause(0x631);          // ~1ms delay           I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, XYZ_DATA_CFG_REG, 0x00);   // +/-2g range with ~0.977mg/LSB       I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG2, 0x02);          // High Resolution mode      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG3, 0x00);          // Push-pull, active low interrupt      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG4, 0x01);          // Enable DRDY interrupt      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG5, 0x01);          // DRDY interrupt routed to INT1 - PTA5      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG1, 0x39);          // ODR = 1.56Hz, Active mode       }   4. A simple offset calibration method is implemented according to the AN4069.   void MMA8652FC_Calibration (void) {      char X_offset, Y_offset, Z_offset;           DataReady = 0;                while (!DataReady){}          // Is a first set of data ready?      DataReady = 0;           I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG1, 0x00);          // Standby mode                I2C_ReadMultiRegisters(MMA8652FC_I2C_ADDRESS, OUT_X_MSB_REG, 6, AccelData);          // Read data output registers 0x01-0x06                         Xout_12_bit = ((short) (AccelData[0]<<8 | AccelData[1])) >> 4;             // Compute 12-bit X-axis acceleration output value      Yout_12_bit = ((short) (AccelData[2]<<8 | AccelData[3])) >> 4;             // Compute 12-bit Y-axis acceleration output value      Zout_12_bit = ((short) (AccelData[4]<<8 | AccelData[5])) >> 4;             // Compute 12-bit Z-axis acceleration output value                  X_offset = Xout_12_bit / 2 * (-1);          // Compute X-axis offset correction value      Y_offset = Yout_12_bit / 2 * (-1);          // Compute Y-axis offset correction value      Z_offset = (Zout_12_bit - SENSITIVITY_2G) / 2 * (-1);         // Compute Z-axis offset correction value                  I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, OFF_X_REG, X_offset);                  I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, OFF_Y_REG, Y_offset);            I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, OFF_Z_REG, Z_offset);                        I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG1, 0x39);          // Active mode again }   5. In the ISR, only the interrupt flag is cleared and the DataReady variable is set to indicate the arrival of new data.   void PORTA_IRQHandler() {      PORTA_PCR5 |= PORT_PCR_ISF_MASK;          // Clear the interrupt flag      DataReady = 1;    }   6. The output values from accelerometer registers 0x01 – 0x06 are first converted to signed 12-bit values and afterwards to real values in g’s.   if (DataReady)             // Is a new set of data ready? {                  DataReady = 0;                                                                                                                         I2C_ReadMultiRegisters(MMA8652FC_I2C_ADDRESS, OUT_X_MSB_REG, 6, AccelData);          // Read data output registers 0x01-0x06                    // 12-bit accelerometer data      Xout_12_bit = ((short) (AccelData[0]<<8 | AccelData[1])) >> 4;             // Compute 12-bit X-axis acceleration output value      Yout_12_bit = ((short) (AccelData[2]<<8 | AccelData[3])) >> 4;             // Compute 12-bit Y-axis acceleration output value      Zout_12_bit = ((short) (AccelData[4]<<8 | AccelData[5])) >> 4;             // Compute 12-bit Z-axis acceleration output value                             // Accelerometer data converted to g's      Xout_g = ((float) Xout_12_bit) / SENSITIVITY_2G;              // Compute X-axis output value in g's      Yout_g = ((float) Yout_12_bit) / SENSITIVITY_2G;              // Compute Y-axis output value in g's      Zout_g = ((float) Zout_12_bit) / SENSITIVITY_2G;              // Compute Z-axis output value in g's               }     7. The calculated values can be watched in the "Variables" window on the top right of the Debug perspective or in the FreeMASTER application. To open and run the FreeMASTER project, install the FreeMASTER application and FreeMASTER Communication Driver.       Attached you can find the complete source code written in the CW for MCU's (Eclipse IDE) including 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.   Regards, Tomas
View full article
Hello Community,   One of the main features of the NXP accelerometers is the Auto-WAKE/SLEEP mode.   I would like to share this project in order to demonstrate the feasibility of using the Low-power and auto-WAKE/SLEEP modes for reducing current consumption in the different NXP accelerometers such as the MMA845x and MMA865x series.   I created this project using the FRDM-KL25Z platform and the MMA8652FC accelerometer (You may find the breakout board files here). The complete source code is written in CodeWarrior v10.x IDE.   This document gives you an introduction of the MMA8652FC accelerometer as well as the different power consumptions and guides you through the initialization process and how to appreciate the demonstration:   Initialization of the MKL25Z128 MCU. Initialization of the MMA8652FC. Auto-WAKE/SLEEP mode. MMA8652FC Embedded functions. Interrupt handlers. Evaluation of the interrupt source. Summarizing the application and Macros definition. Visualization of the current consumption.   1. As you can see in the FRDM-KL25Z schematic and the image below, the I2C signals are routed to the I2C1 module (PTC1 and PTC2 pins) of the KL25Z MCU and the INT1 and INT2 outputs are connected to the PTD5 and PTA5 pins. The INT1 and INT2 outputs of the MMA8652FC are configured as a push-pull active-low outputs, so the corresponding PTD5 and PTA5 pins configuration are GPIOs with an interrupt on falling edge.   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_PORTC_MASK;       // Turn on clock to Port C module      PORTC_PCR1 |= PORT_PCR_MUX(0x2);         // PTC1 pin is I2C1 SCL line      PORTC_PCR2 |= PORT_PCR_MUX(0x2);         // PTC2 pin is I2C1 SDA line      I2C1_F  |= I2C_F_ICR(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 PTD5 and PTA5 pin (connected to the INT1 and INT2 of the MMA8652FC) for falling edge interrupts */      SIM_SCGC5 |= SIM_SCGC5_PORTD_MASK;       // Turn on clock to Port D module      PORTD_PCR5 |= (0|PORT_PCR_ISF_MASK|      // Clear the interrupt flag                           PORT_PCR_MUX(0x1)|  // PTD5 is configured as GPIO                           PORT_PCR_IRQC(0xA));// PTD5 is configured for falling edge interrupts        SIM_SCGC5 |= SIM_SCGC5_PORTA_MASK;       // Turn on clock to Port A module      PORTA_PCR5 |= (0|PORT_PCR_ISF_MASK|      // Clear the interrupt flag                           PORT_PCR_MUX(0x1)|  // PTA5 is configured as GPIO                           PORT_PCR_IRQC(0xA));// PTA5 is configured for falling edge interrupts        /* Enable PORTD interrupt on NVIC */      NVIC_ICPR |= 1 << ((INT_PORTD - 16)%32);      NVIC_ISER |= 1 << ((INT_PORTD - 16)%32);      /* Enable PORTA interrupt on NVIC */      NVIC_ICPR |= 1 << ((INT_PORTA - 16)%32);      NVIC_ISER |= 1 << ((INT_PORTA - 16)%32);     2. The MMA8652FC is an intelligent, low-power, three-axis, capacitive micromachined accelerometer with 12 bits of resolution.   At the beginning of the initialization, all registers are reset to their default values by setting the RST bit of the CTRL_REG2 register. The Normal and Low Power modes are set in the same register. The MODS[1:0] bits select which Oversampling mode is to be used. The Oversampling modes are available in both WAKE Mode MOD[1:0] and also in the SLEEP Mode SMOD[1:0].   Then the MMA8652FC is initialized as shown below:        I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG2, 0x40);        // Reset all registers to POR values      Pause(0x631);        // ~1ms delay      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, XYZ_DATA_CFG_REG, 0x00); // +/-2g range with ~0.977mg/LSB              /* Power Mode Configuration */      If LOW power mode is selected:      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG2, 0x1B);        // Low Power mode        If NORMAL power mode is selected:      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG2, 0x00);        // Normal mode     3. As I mentioned, one of the main features of the MMA8652FC is the Auto-WAKE/SLEEP mode.   The advantage of using the Auto-WAKE/SLEEP is that the system can automatically transition to a higher sample rate (higher current consumption) when needed, but spends the majority of the time in the SLEEP mode (lower current) when the device does not require higher sampling rates.   • Auto-WAKE refers to the device being triggered by one of the interrupt functions to transition to a higher sample rate. This may also interrupt the processor to transition from a SLEEP mode to a higher power mode. • SLEEP mode occurs after the accelerometer has not detected an interrupt for longer than the user-definable timeout period.       At the ASLP_COUNT register, you can set the minimum time period of inactivity required to switch the part between Wake and Sleep status, in this case, 5 seconds.   The Auto-WAKE/SLEEP mode, therefore, is configured as follow:        read_reg = I2C_ReadRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG2);      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG2, read_reg|0x04);  // Auto-SLEEP enable      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, ASLP_COUNT_REG, 0x10);      // 5 seconds (16 * 320ms)      read_reg = I2C_ReadRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG4);      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG4, read_reg|0x80);  // Enable AutoSleep interrupt, INT2 - PTD5      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG1, 0xC1);           // ODR=800Hz and Sleep mode ODR=1.56Hz, Active mode     4. The device can be configured to generate inertial wake-up interrupt signals from any combination of the configurable embedded functions, enabling the MMA8652FC to monitor inertial events while remaining in a low-power mode during periods of inactivity.   The Interrupts that can WAKE the device from SLEEP are: Tap Detection, Orientation Detection, Motion/Freefall, and Transient Detection.       In this project, the TAP (Pulse) or Transient interrupts are used to wake up the device from the SLEEP. In order to get more information about the TAP detection, please click here.   The MMA8652FC is configured as below:        If Transient interrupt is selected:      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, TRANSIENT_THS_REG, 0x84);         // Set threshold to 252mg (4 x 63mg )      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, TRANSIENT_COUNT_REG, 0x02);       // Set debounce timer period to 40ms (low power mode) / 2.5ms (normal mode)-Table 66      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, TRANSIENT_CFG_REG, 0x17);         // Enable transient detection for X and Y axis, latch enabled         I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG3, 0x40);                 // Wake from Transient interrupt, Push-pull, active low interrupt      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG4, 0x20);                 // Enable Transient detection interrupt      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG5, 0x20);                 // Transient interrupt routed to INT1 - PTA5          If TAP interrupt is selected:      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, PULSE_CFG_REG, 0x15);             // Enable X, Y and Z Single Pulse      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, PULSE_THSX_REG, 0x20);            // Set X Threshold to 2.016g      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, PULSE_THSY_REG, 0x20);            // Set Y Threshold to 2.016g      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, PULSE_THSZ_REG, 0x2A);            // Set Z Threshold to 2.646g      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, PULSE_TMLT_REG, 0x28);            // Set Time Limit for Tap Detection to 25 ms      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, PULSE_LTCY_REG, 0x28);            // Set Latency Time to 50 ms. During this time interval, all pulses are ignored          I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG3, 0x10);                 // Wake from Pulse interrupt, Push-pull, active low interrupt      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG4, 0x08);                 // Pulse detection interrupt enabled      I2C_WriteRegister(MMA8652FC_I2C_ADDRESS, CTRL_REG5, 0x08);                 // Pulse interrupt routed to INT1 - PTA5     5. As I mentioned above, the TAP (Pulse) or Transient interrupts are used to wake up the device from the SLEEP. Besides, if Auto-SLEEP interrupt is enabled, then transitioning from ACTIVE mode to Auto-SLEEP mode (or vice versa) generates an interrupt.   In this project, the Auto-SLEEP, the TAP (Pulse) or the Transient interrupts are enable. The MKL25Z128 responds to these interrupts reading the INT_SOURCE (0x0C) register, in order to determine the appropriate sources of the interrupt.   Every source of interrupt has its own way to clear the interrupt flag. Please refer to the comments of each ISR:        Transient interrupt handler      void PORTA_IRQHandler()      {         PORTA_PCR5 |= PORT_PCR_ISF_MASK;                // Clear the PTC interrupt         int_source = I2C_ReadRegister(MMA8652FC_I2C_ADDRESS, INT_SOURCE_REG); // Clear interrupt Source Register           if(int_source&0x20)  // Transient interrupt ?         {            i = I2C_ReadRegister(MMA8652FC_I2C_ADDRESS, TRANSIENT_SRC_REG); // Read the TRANSIENT_SRC register to clear the SRC_TRANS flag in the INT_SOURCE register             transient_int = 1;         }      }        TAP interrupt handler      void PORTA_IRQHandler()      {         PORTA_PCR5 |= PORT_PCR_ISF_MASK;                // Clear the PTC interrupt flag         int_source = I2C_ReadRegister(MMA8652FC_I2C_ADDRESS, INT_SOURCE_REG); // Clear interrupt Source Register            if(int_source&0x08)  // Pulse interrupt ?         {            i = I2C_ReadRegister(MMA8652FC_I2C_ADDRESS, PULSE_SRC_REG); // Read the PULSE_SRC register to clear the SRC_TRANS flag in the INT_SOURCE register             pulse_int = 1;         }      }        Auto WAKE/SLEEP interrupt handler      void PORTD_IRQHandler()      {          PORTD_PCR5 |= PORT_PCR_ISF_MASK;                // Clear the PTD interrupt flag             int_source = I2C_ReadRegister(MMA8652FC_I2C_ADDRESS, INT_SOURCE_REG); // Clear interrupt Source Register             if (int_source&0x80) // Auto Sleep/Wake interrupt ?          {             i = I2C_ReadRegister(MMA8652FC_I2C_ADDRESS, SYSMOD_REG);      // Read the SYSMOD register to clear the SRC_ASLP flag in the INT_SOURCE register             sleep_int = 1;          }      }     6. At this point, the configuration of the MCU and the accelerometer is done. The RGB LED contained on the FRDM-KL25Z board is configured in order to help showing the behavior of the application. This behavior is configured as follow:        #if TRANSIENT_DETECTION                    if (transient_int){                             transient_int = 0;                 TURN_BLUE_ON(); TURN_RED_OFF();}      #elif TAP_DETECTION                    if (pulse_int){                 pulse_int = 0;                 TURN_BLUE_ON(); TURN_RED_OFF();}      #endif                    if (sleep_int){                  sleep_int = 0;                  TURN_RED_ON(); TURN_BLUE_OFF();}     7. In summary, the FRDM-KL25Z will be interfacing with the MMA8652FC. The power mode will be set and the interrupts will be enabled. The macros at the top of the source code will allow us to select between the different power modes, the different embedded functions and to select the Auto-WAKE/SLEEP function.   If the Auto-WAKE/SLEEP function is enabled, the MMA8652FC will go into the SLEEP mode (ODR=1.56Hz) after 5 seconds of inactivity. The RED LED will be set. When an interrupt from the embedded functions is generated, the MMA8652FC will be awakened (ODR=800Hz) and so on. The BLUE LED will be set.        /* Select the Power Mode - Table 101 from datasheet */      #define NORMAL_MODE        1      #define LOW_POWER_MODE     0         /* Select the Embedded Function */      #define TRANSIENT_DETECTION       1      #define TAP_DETECTION             0        /* Auto-WAKE/SLEEP Function */      #define AUTO_SLEEP   1     8. The Table 5 from the datasheet shows the expected current consumption in regard with the power mode and ODR selected:   As I mentioned before, the Sleep mode allow us to change between different Output Data Rates (ODR) dynamically so we can reduce the current consumption.   In order to verify if the accelerometer is consuming the current mentioned on the datasheet, I measured the MMA8652FC current consumption using the project mentioned.   Please refer to the results below:           9. The advantage of using the Auto-WAKE/SLEEP mode is that the system can automatically transition to a higher sample rate (higher current consumption) when needed, but spends the majority of the time in the SLEEP mode (lower current) when the device does not require higher sampling rates.   In the manner we have come to expect of the MMA8652FC, the current consumption decreases when the ODR is changed from 800Hz to 1.56Hz, in both normal and low power mode.   The information mentioned on the datasheet is now confirmed.     Please find attached the complete source code.   I hope you find useful and funny this sample project. Any suggestion will be appreciated.   You are invited to take part of the NXP community where you can post all your questions and you may find useful material for your projects.   Best Regards, David
View full article
Hi Everyone, As I am frequently asked for a simple bare metal example code for the Xtrinsic MMA8451Q digital accelerometer, I would like to share here one of my examples I have created for this part while working with the Freescale FRDM-KL25Z platform. This example illustrates: 1. Initialization of the MKL25Z128 MCU (mainly I2C and PORT modules). 2. Initialization of the accelerometer to achieve the highest resolution. 3. Simple offset calibration based on the AN4069. 4. Output data reading using an interrupt technique. 5. Conversion of the output values from registers 0x01 – 0x06 to real acceleration values in g’s. 6. Visualization of the output values in the FreeMASTER tool. 1. According to the schematic, the INT1 output of the MMA8451Q is connected to the PTA14 pin of the KL25Z MCU and both SCL and SDA lines are connected to the I2C0 module (PTE24 and PTE25 pins). The MCU is, therefore, configured as follows: void MCU_Init(void) {      //I2C0 module initialization      SIM_SCGC4 |= SIM_SCGC4_I2C0_MASK;        // Turn on clock to I2C0 module      SIM_SCGC5 |= SIM_SCGC5_PORTE_MASK;       // Turn on clock to Port E module      PORTE_PCR24 = PORT_PCR_MUX(5);           // PTE24 pin is I2C0 SCL line      PORTE_PCR25 = PORT_PCR_MUX(5);           // PTE25 pin is I2C0 SDA line      I2C0_F  = 0x14;                          // SDA hold time = 2.125us, SCL start hold time = 4.25us, SCL stop hold time = 5.125us *      I2C0_C1 = I2C_C1_IICEN_MASK;             // Enable I2C0 module           //Configure the PTA14 pin (connected to the INT1 of the MMA8451Q) for falling edge interrupts      SIM_SCGC5 |= SIM_SCGC5_PORTA_MASK;       // Turn on clock to Port A module      PORTA_PCR14 |= (0|PORT_PCR_ISF_MASK|     // Clear the interrupt flag                        PORT_PCR_MUX(0x1)|     // PTA14 is configured as GPIO                        PORT_PCR_IRQC(0xA));   // PTA14 is configured for falling edge interrupts           //Enable PORTA interrupt on NVIC      NVIC_ICPR |= 1 << ((INT_PORTA - 16)%32);      NVIC_ISER |= 1 << ((INT_PORTA - 16)%32); } 2. At the beginning of the initialization, all accelerometer registers are reset to their default values by setting the RST bit of the CTRL_REG2 register. The dynamic range is set to ±2g and to achieve the highest resolution, the LNOISE bit is set and the lowest ODR (1.56Hz) and the High Resolution mode are selected (more details in AN4075). void Accelerometer_Init (void) {      unsigned char reg_val = 0;        I2C_WriteRegister(MMA845x_I2C_ADDRESS, CTRL_REG2, 0x40);           // Reset all registers to POR values          do            // Wait for the RST bit to clear      {         reg_val = I2C_ReadRegister(MMA845x_I2C_ADDRESS, CTRL_REG2) & 0x40;      }  while (reg_val);        I2C_WriteRegister(MMA845x_I2C_ADDRESS, XYZ_DATA_CFG_REG, 0x00);    // +/-2g range -> 1g = 16384/4 = 4096 counts      I2C_WriteRegister(MMA845x_I2C_ADDRESS, CTRL_REG2, 0x02);           // High Resolution mode      I2C_WriteRegister(MMA845x_I2C_ADDRESS, CTRL_REG1, 0x3D);           // ODR = 1.56Hz, Reduced noise, Active mode   } 3. A simple offset calibration method is implemented according to the AN4069. At the end of the calibration routine, the DRDY interrupt is enabled and routed to the INT1 interrupt pin that is configured to be a push-pull, active-low output. void Calibrate (void) {      unsigned char reg_val = 0;            while (!reg_val)           // Wait for a first set of data               {         reg_val = I2C_ReadRegister(MMA845x_I2C_ADDRESS, STATUS_REG) & 0x08;      }               I2C_ReadMultiRegisters(MMA845x_I2C_ADDRESS, OUT_X_MSB_REG, 6, AccData);           // Read data output registers 0x01-0x06                                                 Xout_14_bit = ((short) (AccData[0]<<8 | AccData[1])) >> 2;           // Compute 14-bit X-axis output value      Yout_14_bit = ((short) (AccData[2]<<8 | AccData[3])) >> 2;           // Compute 14-bit Y-axis output value      Zout_14_bit = ((short) (AccData[4]<<8 | AccData[5])) >> 2;           // Compute 14-bit Z-axis output value                                          Xoffset = Xout_14_bit / 8 * (-1);        // Compute X-axis offset correction value      Yoffset = Yout_14_bit / 8 * (-1);        // Compute Y-axis offset correction value      Zoffset = (Zout_14_bit - SENSITIVITY_2G) / 8 * (-1);          // Compute Z-axis offset correction value                                          I2C_WriteRegister(MMA845x_I2C_ADDRESS, CTRL_REG1, 0x00);             // Standby mode to allow writing to the offset registers       I2C_WriteRegister(MMA845x_I2C_ADDRESS, OFF_X_REG, Xoffset);              I2C_WriteRegister(MMA845x_I2C_ADDRESS, OFF_Y_REG, Yoffset);       I2C_WriteRegister(MMA845x_I2C_ADDRESS, OFF_Z_REG, Zoffset);       I2C_WriteRegister(MMA845x_I2C_ADDRESS, CTRL_REG3, 0x00);             // Push-pull, active low interrupt      I2C_WriteRegister(MMA845x_I2C_ADDRESS, CTRL_REG4, 0x01);             // Enable DRDY interrupt      I2C_WriteRegister(MMA845x_I2C_ADDRESS, CTRL_REG5, 0x01);             // DRDY interrupt routed to INT1 - PTA14      I2C_WriteRegister(MMA845x_I2C_ADDRESS, CTRL_REG1, 0x3D);             // ODR = 1.56Hz, Reduced noise, Active mode  } 4. In the ISR, only the interrupt flag is cleared and the DataReady variable is set to indicate the arrival of new data. void PORTA_IRQHandler() {      PORTA_PCR14 |= PORT_PCR_ISF_MASK;            // Clear the interrupt flag      DataReady = 1;      } 5. The output values from accelerometer registers 0x01 – 0x06 are first converted to signed 14-bit values and afterwards to real values in g’s. if (DataReady)             // Is a new set of data ready? {                 DataReady = 0;                                                                                                                      I2C_ReadMultiRegisters(MMA845x_I2C_ADDRESS, OUT_X_MSB_REG, 6, AccData);           // Read data output registers 0x01-0x06             Xout_14_bit = ((short) (AccData[0]<<8 | AccData[1])) >> 2;           // Compute 14-bit X-axis output value      Yout_14_bit = ((short) (AccData[2]<<8 | AccData[3])) >> 2;           // Compute 14-bit Y-axis output value      Zout_14_bit = ((short) (AccData[4]<<8 | AccData[5])) >> 2;           // Compute 14-bit Z-axis output value                      Xout_g = ((float) Xout_14_bit) / SENSITIVITY_2G;              // Compute X-axis output value in g's      Yout_g = ((float) Yout_14_bit) / SENSITIVITY_2G;              // Compute Y-axis output value in g's      Zout_g = ((float) Zout_14_bit) / SENSITIVITY_2G;              // Compute Z-axis output value in g's                                   } 6. The calculated values can be watched in the "Variables" window on the top right of the Debug perspective or in the FreeMASTER application. To view both the 14-bit and real values in the FreeMASTER application, some USBDM drivers need to be first installed on your computer. They are available for download from SourceForge. Erich Styger described their installation in this tutorial. In addition to that, the USBDM_OpenSDA application that provides both debugging and a virtual serial port needs to be loaded into the MK20 debugger chip on the FRDM-KL25Z board. This installation follows the usual FRDM-KL25Z bootloader process: Unplug the FRDM-KL25Z board. Whilst holding the SW1/RST switch depressed plug in the FRDM-KL25Z board. The green LED should start blinking at a rate of about 1Hz. Open a file explorer and locate the USB drive that has now appeared. It will have the drive name "BOOTLOADER". Drag the file USBDM_OpenSDA.sx to the USB drive and wait a short while. The OpenSDA firmware on the FRDM-KL25Z board will program the USBDM firmware into the MK20 debugger chip on the board. Remove and re-plug the FRDM-KL25Z board. The board will now appear as a USBDM device. Attached you can find the complete source code written in the CW for MCU's v10.5 as well as the FreeMASTER project. So make it, test it and keep in touch... Regards, Tomas
View full article
Hi Everyone,   As I am often asked for a simple bare metal example code for the Xtrinsic FXOS8700CQ 6-axis sensor, I would like to share here one of my examples I have created for this part while working with the Freescale FRDM-KL25Z platform and FRDM-FXS-MULTI(-B) sensor expansion board that features many of the Xtrinsic sensors including the FXOS8700CQ. The FreeMASTER tool is used to visualize both the acceleration and magnetic data that are read from the FXOS8700CQ using an interrupt technique through the I 2 C interface.   This example illustrates:   1. Initialization of the MKL25Z128 MCU (mainly I2C and PORT modules). 2. I 2 C data write and read operations. 3. Initialization of the FXOS8700CQ. 4. Simple accelerometer offset calibration based on the AN4069. 5. Simple magnetic hard-iron offset calibration. 6. Output data reading using an interrupt technique. 7. Conversion of the output values from registers 0x01 – 0x06 and 0x33 – 0x38 to real values in g’s and µT and simple heading angle calculation. 8. Visualization of the calculated values in the FreeMASTER tool.   1. As you can see in the FRDM-FXS-MULTI(-B)/FRDM-KL25Z schematics and the image below, I2C signals are routed to the I2C1 module (PTC1 and PTC2 pins) of the KL25Z MCU and the INT1 output is connected to the PTD4 pin (make sure that pins 1-2 of J3 on the sensor expansion board are connected together using a jumper). The INT1 output of the FXOS8700CQ is configured as a push-pull active-low output, so the corresponding PTD4 pin configuration is GPIO with an interrupt on falling edge.   The MCU is, therefore, configured as follows.   void MCU_Init(void) {      //I2C1 module initialization      SIM_SCGC4 |= SIM_SCGC4_I2C1_MASK;        // Turn on clock to I2C1 module      SIM_SCGC5 |= SIM_SCGC5_PORTC_MASK;       // Turn on clock to Port C module      PORTC_PCR1 |= PORT_PCR_MUX(0x2);         // PTC1 pin is I2C1 SCL line      PORTC_PCR2 |= PORT_PCR_MUX(0x2);         // PTC2 pin is I2C1 SDA line      I2C1_F  |= I2C_F_ICR(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 I2C0 module               //Configure the PTD4 pin (connected to the INT1 of the FXOS8700CQ) for falling edge interrupts      SIM_SCGC5 |= SIM_SCGC5_PORTD_MASK;       // Turn on clock to Port A module      PORTD_PCR4 |= (0|PORT_PCR_ISF_MASK|      // Clear the interrupt flag                      PORT_PCR_MUX(0x1)|       // PTD4 is configured as GPIO                      PORT_PCR_IRQC(0xA));     // PTD4 is configured for falling edge interrupts                   //Enable PORTD interrupt on NVIC      NVIC_ICPR |= 1 << ((INT_PORTD - 16)%32);      NVIC_ISER |= 1 << ((INT_PORTD - 16)%32); }   2. The 7-bit I2C address of the FXOS8700CQ is 0x1E since both SA0 and SA1 lines are shorted to GND using jumpers J21 and J23 on the sensor board. As shown above, the SCL line is connected to the PTC1 pin and SDA line to the PTC2 pin. The I2C clock frequency is 125 kHz.   The screenshot below shows the write operation which writes the value 0x35 to the CTRL_REG1 (0x2A).     And here is the single byte read from the WHO_AM_I register 0x0D. As you can see, it returns the correct value 0xC7.     Multiple bytes of data can be read from sequential registers after each FXOS8700CQ acknowledgment (AK) is received until a no acknowledge (NAK) occurs from the KL25Z followed by a stop condition (SP) signaling an end of transmission. A burst read of 6 bytes from registers 0x33 to 0x38, that is performed in the calibration routine “FXOS8700CQ_Mag_Calibration()”, is shown below. It also shows when the INT1 pin is automatically cleared.     3. At the beginning of the initialization, all registers are reset to their default values by setting the RST bit of the CTRL_REG2 register. Then the FXOS8700CQ is initialized as shown below.   void FXOS8700CQ_Init (void) {      I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG2, 0x40);          // Reset all registers to POR values        Pause(0x631);        // ~1ms delay        I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, XYZ_DATA_CFG_REG, 0x00);   // +/-2g range with 0.244mg/LSB          I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, M_CTRL_REG1, 0x1F);        // Hybrid mode (accelerometer + magnetometer), max OSR      I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, M_CTRL_REG2, 0x20);        // M_OUT_X_MSB register 0x33 follows the OUT_Z_LSB register 0x06 (used for burst read)                   I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG2, 0x02);          // High Resolution mode      I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG3, 0x00);          // Push-pull, active low interrupt      I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG4, 0x01);          // Enable DRDY interrupt      I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG5, 0x01);          // DRDY interrupt routed to INT1 - PTD4      I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG1, 0x35);          // ODR = 3.125Hz, Reduced noise, Active mode   }   4. A simple accelerometer offset calibration method is implemented according to the AN4069.   void FXOS8700CQ_Accel_Calibration (void) {      char X_Accel_offset, Y_Accel_offset, Z_Accel_offset;        DataReady = 0;                while (!DataReady){}       // Is a first set of data ready?      DataReady = 0;        I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG1, 0x00);          // Standby mode             I2C_ReadMultiRegisters(FXOS8700CQ_I2C_ADDRESS, OUT_X_MSB_REG, 6, AccelMagData);          // Read data output registers 0x01-0x06                 Xout_Accel_14_bit = ((short) (AccelMagData[0]<<8 | AccelMagData[1])) >> 2;        // Compute 14-bit X-axis acceleration output value      Yout_Accel_14_bit = ((short) (AccelMagData[2]<<8 | AccelMagData[3])) >> 2;        // Compute 14-bit Y-axis acceleration output value      Zout_Accel_14_bit = ((short) (AccelMagData[4]<<8 | AccelMagData[5])) >> 2;        // Compute 14-bit Z-axis acceleration output value               X_Accel_offset = Xout_Accel_14_bit / 8 * (-1);         // Compute X-axis offset correction value      Y_Accel_offset = Yout_Accel_14_bit / 8 * (-1);         // Compute Y-axis offset correction value      Z_Accel_offset = (Zout_Accel_14_bit - SENSITIVITY_2G) / 8 * (-1);          // Compute Z-axis offset correction value               I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, OFF_X_REG, X_Accel_offset);                  I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, OFF_Y_REG, Y_Accel_offset);           I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, OFF_Z_REG, Z_Accel_offset);                    I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG1, 0x35);          // Active mode again }   5. A simple software calibration of magnetic hard-iron offset consists of recording the minimum and maximum magnetometer readings while rotating the board horizontally and vertically and then computing the calibration values from their average. The calibration time is defined by the number of samples taken during calibration (local variable “i” in the while loop) and selected ODR. In my example I use 94 samples at 3.125Hz, so the calibration routine takes 30s.   void FXOS8700CQ_Mag_Calibration (void) {      short Xout_Mag_16_bit_avg, Yout_Mag_16_bit_avg, Zout_Mag_16_bit_avg;          short Xout_Mag_16_bit_max, Yout_Mag_16_bit_max, Zout_Mag_16_bit_max;          short Xout_Mag_16_bit_min, Yout_Mag_16_bit_min, Zout_Mag_16_bit_min;      char i = 0;        DataReady = 0;            while (i < 94)             // This takes ~30s (94 samples * 1/3.125)      {               if (DataReady)             // Is a new set of data ready?         {                          DataReady = 0;                                                                                                                                                                I2C_ReadMultiRegisters(FXOS8700CQ_I2C_ADDRESS, MOUT_X_MSB_REG, 6, AccelMagData);         // Read data output registers 0x33 - 0x38                                   Xout_Mag_16_bit = (short) (AccelMagData[0]<<8 | AccelMagData[1]);        // Compute 16-bit X-axis magnetic output value              Yout_Mag_16_bit = (short) (AccelMagData[2]<<8 | AccelMagData[3]);        // Compute 16-bit Y-axis magnetic output value              Zout_Mag_16_bit = (short) (AccelMagData[4]<<8 | AccelMagData[5]);        // Compute 16-bit Z-axis magnetic output value                                            // Assign first sample to maximum and minimum values                        if (i == 0)              {                   Xout_Mag_16_bit_max = Xout_Mag_16_bit;                   Xout_Mag_16_bit_min = Xout_Mag_16_bit;                                                Yout_Mag_16_bit_max = Yout_Mag_16_bit;                   Yout_Mag_16_bit_min = Yout_Mag_16_bit;                                                Zout_Mag_16_bit_max = Zout_Mag_16_bit;                   Zout_Mag_16_bit_min = Zout_Mag_16_bit;              }                                       // Check to see if current sample is the maximum or minimum X-axis value                       if (Xout_Mag_16_bit > Xout_Mag_16_bit_max)    {Xout_Mag_16_bit_max = Xout_Mag_16_bit;}                       if (Xout_Mag_16_bit < Xout_Mag_16_bit_min)    {Xout_Mag_16_bit_min = Xout_Mag_16_bit;}                         // Check to see if current sample is the maximum or minimum Y-axis value                       if (Yout_Mag_16_bit > Yout_Mag_16_bit_max)    {Yout_Mag_16_bit_max = Yout_Mag_16_bit;}                       if (Yout_Mag_16_bit < Yout_Mag_16_bit_min)    {Yout_Mag_16_bit_min = Yout_Mag_16_bit;}                                       // Check to see if current sample is the maximum or minimum Z-axis value                       if (Zout_Mag_16_bit > Zout_Mag_16_bit_max)    {Zout_Mag_16_bit_max = Zout_Mag_16_bit;}                       if (Zout_Mag_16_bit < Zout_Mag_16_bit_min)    {Zout_Mag_16_bit_min = Zout_Mag_16_bit;}                              i++;          }           }        Xout_Mag_16_bit_avg = (Xout_Mag_16_bit_max + Xout_Mag_16_bit_min) / 2;            // X-axis hard-iron offset      Yout_Mag_16_bit_avg = (Yout_Mag_16_bit_max + Yout_Mag_16_bit_min) / 2;            // Y-axis hard-iron offset      Zout_Mag_16_bit_avg = (Zout_Mag_16_bit_max + Zout_Mag_16_bit_min) / 2;            // Z-axis hard-iron offset        // Left-shift by one as magnetometer offset registers are 15-bit only, left justified      Xout_Mag_16_bit_avg <<= 1;             Yout_Mag_16_bit_avg <<= 1;       Zout_Mag_16_bit_avg <<= 1;        I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG1, 0x00);          // Standby mode to allow writing to the offset registers        I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, MOFF_X_LSB_REG, (char) (Xout_Mag_16_bit_avg & 0xFF));             I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, MOFF_X_MSB_REG, (char) ((Xout_Mag_16_bit_avg >> 😎 & 0xFF));            I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, MOFF_Y_LSB_REG, (char) (Yout_Mag_16_bit_avg & 0xFF));      I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, MOFF_Y_MSB_REG, (char) ((Yout_Mag_16_bit_avg >> 😎 & 0xFF));                   I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, MOFF_Z_LSB_REG, (char) (Zout_Mag_16_bit_avg & 0xFF));      I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, MOFF_Z_MSB_REG, (char) ((Zout_Mag_16_bit_avg >> 😎 & 0xFF));              I2C_WriteRegister(FXOS8700CQ_I2C_ADDRESS, CTRL_REG1, 0x35);          // Active mode again       }   6. In the ISR, only the interrupt flag is cleared and the DataReady variable is set to indicate the arrival of new data.   void PORTD_IRQHandler() {      PORTD_PCR4 |= PORT_PCR_ISF_MASK;                // Clear the interrupt flag      DataReady = 1;       }   7. The output values from accelerometer registers 0x01 – 0x06 are first converted to signed 14-bit integer values and afterwards to real values in g’s. Similarly, the output values from magnetometer registers 0x33 – 0x38 are first converted to signed 16-bit integer values and afterwards to real values in microtesla (µT).   If the board remains flat, then the compass heading can be simply computed from the arctangent of the ratio of the two horizontal magnetic field components. I have used the atan2 function which returns the result in radians (-π and π), so I multiply it by 180/π to end up with degrees.   If you are interested in more complex algorithms for a tilt-compensated e-compass with soft-iron calibration, please refer to our eCompass software.   if (DataReady)             // Is a new set of data ready? {                  DataReady = 0;                                                                                                                      I2C_ReadMultiRegisters(FXOS8700CQ_I2C_ADDRESS, OUT_X_MSB_REG, 12, AccelMagData);         // Read data output registers 0x01-0x06 and 0x33 - 0x38                 // 14-bit accelerometer data      Xout_Accel_14_bit = ((short) (AccelMagData[0]<<8 | AccelMagData[1])) >> 2;        // Compute 14-bit X-axis acceleration output value      Yout_Accel_14_bit = ((short) (AccelMagData[2]<<8 | AccelMagData[3])) >> 2;        // Compute 14-bit Y-axis acceleration output value      Zout_Accel_14_bit = ((short) (AccelMagData[4]<<8 | AccelMagData[5])) >> 2;        // Compute 14-bit Z-axis acceleration output value                          // Accelerometer data converted to g's      Xout_g = ((float) Xout_Accel_14_bit) / SENSITIVITY_2G;        // Compute X-axis output value in g's      Yout_g = ((float) Yout_Accel_14_bit) / SENSITIVITY_2G;        // Compute Y-axis output value in g's      Zout_g = ((float) Zout_Accel_14_bit) / SENSITIVITY_2G;        // Compute Z-axis output value in g's                           // 16-bit magnetometer data                   Xout_Mag_16_bit = (short) (AccelMagData[6]<<8 | AccelMagData[7]);          // Compute 16-bit X-axis magnetic output value      Yout_Mag_16_bit = (short) (AccelMagData[8]<<8 | AccelMagData[9]);          // Compute 16-bit Y-axis magnetic output value      Zout_Mag_16_bit = (short) (AccelMagData[10]<<8 | AccelMagData[11]);        // Compute 16-bit Z-axis magnetic output value                                                     // Magnetometer data converted to microteslas      Xout_uT = (float) (Xout_Mag_16_bit) / SENSITIVITY_MAG;        // Compute X-axis output magnetic value in uT      Yout_uT = (float) (Yout_Mag_16_bit) / SENSITIVITY_MAG;        // Compute Y-axis output magnetic value in uT      Zout_uT = (float) (Zout_Mag_16_bit) / SENSITIVITY_MAG;        // Compute Z-axis output magnetic value in uT                      Heading = atan2 (Yout_uT, Xout_uT) * 180 / PI;         // Compute Yaw angle }     8. The calculated values can be watched in the "Variables" window on the top right of the Debug perspective or in the FreeMASTER application. To open and run the FreeMASTER project, install the FreeMASTER 1.4 application and FreeMASTER Communication Driver.       Attached you can find the complete source code written in the CW for MCU's v10.5 including the FreeMASTER project.   If there are any questions regarding this simple application, please feel free to ask below. Your feedback or suggestions are also welcome.   Regards, Tomas Original Attachment has been moved to: FRDM-KL25Z-FXOS8700CQ-Basic-read-using-I2C-and-interrupt.zip Original Attachment has been moved to: FreeMASTER---FRDM-KL25Z-FXOS8700CQ-Basic-read-using-I2C-and-interrupt.zip
View full article
Video clip associated with "Android as a Platform for Sensor Fusion Education and Evaluation" presented at 2013 Sensors Expo & Conference by Michael Stanley.
View full article
"Android as a Platform for Sensor Fusion Education and Evaluation" presented at 2013 Sensors Expo & Conference by Michael Stanley.
View full article
Here is the Installer file for the revision 4.2.0.8 of the Sensor Toolbox GUI
View full article
The attached is a copy of a presentation given 24 June 2014 at the Sensors Expo Conference in Rosemont IL.
View full article
My friend Matt Muddiman of Freescale gave this presentation as part of the MEMS Education Series (hosted by Arizona Technology Council and MEMS Industry Group) in Scottsdale Arizona earlier this week.
View full article
The following video shows how to run the FRDM 6DOF Bare Board eCompass using the FRDM-K22. This algorithm uses the FXOS8700 contained on the Freedom Board. In order to get more information about the Sensor Fusion Library for Kinetis MCU's 5.0, please refer to the following link: Sensor Fusion|Freescale I hope this material will be useful for you. David
View full article
The MMA690x, a SafeAssure solution, is a dual axis, Low g, XY, Sensorbased on Freescale’s HARMEMS technology, with an embedded DSP ASIC, allowing for additional processing of the digital signals. Here is a Render of the MMA690x Breakout Board downloaded from OSH park: Layout Design for this board: In the attachments section, you can find the Schematic Source File (SCH), Schematic PDF File, Layout Source File (BRD), Gerber Files (GTL, GBL, GTS, GBS, GTO, GBO, GKO, XLN) and BOM files.   If you're interested in more designs like this breakout board for other sensors, please go to Freescale Sensors Breakout Boards Designs – HOME
View full article
This is the 9 December 2014 build of Vibration Monitoring program written by Mark Pedley in the Sensors Solutions Division systems/algorithms team.  It is compatible with Freescale FRDM-KL25Z/KL26Z/KL46Z/K64F Freedom development platforms.  You can flash your board using the File->Flash pull-down menu.    The application contains an option for controlling motor bias and feedback via optional motor control shield to be discussed in an upcoming Freescale blog.  Use the View->Motor Controls pull-down to enable those functions.
View full article
The MMA8491Q is a low voltage, 3-axis low-g accelerometer housed in a 3 mm x 3 mm QFN package. The device can accommodate two accelerometer configurations, acting as either a 45° tilt sensor or a digital output accelerometer with I2C bus.      • As a 45° Tilt Sensor, the MMA8491Q device offers extreme ease of implementation by using a single line output per axis.      • As a digital output accelerometer, the 14-bit ±8g accelerometer data can be read from the device with a 1 mg/LSB sensitivity. The extreme low power capabilities of the MMA8491Q will reduce the low data rate current consumption to less than 400 nA per Hz. Here is a Render of the MMA8491 Breakout Board downloaded from OSH park: Layout Design for this board: If you're interested in more designs like this breakout board for other sensors, please go to Freescale Sensors Breakout Boards Designs – HOME
View full article
Hi Everyone, In my previous tutorial, I demonstrated how to import an ISSDK based example project into MCUXpresso IDE, build and run it on the Freedom board (FRDM-KL27Z). If you want to visualize/log sensor data, easily change sensor settings (ODR, Range, Power Mode) or directly read and write sensor registers, you can use the Freedom Sensor Toolbox-Community Edition (STB-CE) as described below or in the STBCEUG. 1. Connect the SDA port (J13) on the FRDM-KL27Z board to a USB port on your computer. 2. Open STB-CE GUI by double clicking the Freedom Sensor Toolbox (CE) shortcut located on your desktop. 3. Select "Out of Box Sensor Demonstration". 4. Select the Project to be launched and click on Continue. Base Board Name – FRDM-KL27Z Shield Board Name – OnBoard Project Name – MMA8451 Accelerometer Demo 5. The ISSDK-based MMA8451 Accelerometer Demo firmware is loaded to the KL27Z MCU and the MMA8451 Accelerometer Demo v1.0 GUI launched. 6. In the Main screen you can change basic MMA8451Q accelerometer settings (ODR, Range, Power Mode), enable embedded functions (Landsacpe/Portrait, Pulse/Tap, Freefall, Transient), start/stop accelerometer data streaming and/or logging.   7. The Register screen (MMA8451) provides low-level access (R/W) to the MMA8451Q registers along with a detailed description of the selected register. 8. To change the bit value, simply click on the corresponding cell (make sure you selected the Standby mode before writing a new value to the selected register). I hope you find this simple document useful. f there are any questions, please feel free to ask below.  Regards, Tomas
View full article
The FXLN83XX is a 3-axis, low-power, low-g accelerometer along with a CMOS signal conditioning and control ASIC in a small 3 x 3 x 1 mm QFN package. The analog outputs for the X, Y, and Z axes are internally compensated for zero-g offset and sensitivity, and then buffered to the output pads. The outputs have a fixed 0 g offset of 0.75 V, irrespective of the VDD supply voltage. The bandwidth of the output signal for each axis may be independently set using external capacitors. The host can place the FXLN83XXQ into a low-current shutdown mode to conserve power. Here is a Render of the FXLN83XX Breakout Board downloaded from OSH park: Layout Design for this board: In the attachments section, you can find the Schematic Source File (SCH), Schematic PDF File, Layout Source File (BRD), Gerber Files (GTL, GBL, GTS, GBS, GTO, GBO, GKO, XLN) and BOM files.    If you're interested in more designs like this breakout board for other sensors, please go to Freescale Sensors Breakout Boards Designs – HOMEFreescale Sensors Breakout Boards Designs – HOME
View full article
Hi, The MMA865x, 3-axis, 10-bit/12-bit accelerometer that has industry leading performance in a small 2 x 2 x 1 mm DFN package. This accelerometer is packed with embedded functions that include flexible user-programmable options and two configurable interrupt pins. Overall power savings is achieved through inertial wake-up interrupt signals that monitor events and remain in a low-power mode during periods of inactivity. Here is a Render of the MMA865x Breakout- Board downloaded from OSH park: Layout Design for this board: In the attachments section, you can find the Schematic Source File (SCH), Schematic PDF File, Layout Source File (BRD), Gerber Files (GTL, GBL, GTS, GBS, GTO, GBO, GKO, XLN) and BOM files. If you're interested in more designs like this breakout board for other sensors, please go to Freescale Sensors Breakout Boards Designs – HOME
View full article
Hi Everyone, As I am often asked for a simple bare metal example code illustrating the use of the accelerometer vector-magnitude function, I have decided to share here one of my examples I created for the FXLS8471Q accelerometer while working with the Freescale FRDM-KL25Z platform and FRDM-FXS-MULT2-B sensor expansion board. This example code complements the Python code snippet from the AN4692. The FXLS8471Q is set for detection of a change in tilt angle exceeding 17.25° from the horizontal plane. Once an event is triggered, an interrupt will be generated on the INT1 pin: void FXLS8471Q_Init (void) {      FXLS8471Q_WriteRegister(A_VECM_THS_MSB_REG, 0x84);            // Threshold value set to 300mg or ~17.25°    FXLS8471Q_WriteRegister(A_VECM_THS_LSB_REG, 0xCC);          FXLS8471Q_WriteRegister(A_VECM_CNT_REG, 0x01);                // Debounce timer period set to 80ms          FXLS8471Q_WriteRegister(A_VECM_INITX_MSB_REG, 0x00);    FXLS8471Q_WriteRegister(A_VECM_INITX_LSB_REG, 0x00);    FXLS8471Q_WriteRegister(A_VECM_INITY_MSB_REG, 0x00);    FXLS8471Q_WriteRegister(A_VECM_INITY_LSB_REG, 0x00);    FXLS8471Q_WriteRegister(A_VECM_INITZ_MSB_REG, 0x10);          // Set Z-axis to 1g  as a reference value    FXLS8471Q_WriteRegister(A_VECM_INITZ_LSB_REG, 0x00);          FXLS8471Q_WriteRegister(A_VECM_CFG_REG, 0x78);                // Event latch enabled, A_VECM_INITX/Y/Z used as initial reference, acceleration vector-magnitude detection feature enabled          FXLS8471Q_WriteRegister(CTRL_REG4, 0x02);                     // Acceleration vector-magnitude interrupt enabled    FXLS8471Q_WriteRegister(CTRL_REG5, 0x02);                     // Acceleration vector-magnitude interrupt routed to INT1 - PTA5          FXLS8471Q_WriteRegister(CTRL_REG1, 0x29);                     // ODR = 12.5Hz, Active mode } In the ISR, only the interrupt flag is cleared and the  INT_SOURCE (0x0C) register is read in order to clear the SRC_A_VECM status bit and deassert the INT1 pin, as shown on the screenshot below. void PORTA_IRQHandler() {    PORTA_PCR5 |= PORT_PCR_ISF_MASK;                              // Clear the interrupt flag    IntSource = FXLS8471Q_ReadRegister(INT_SOURCE_REG);           // Read the INT_SOURCE register to clear the SRC_A_VECM bit   } Attached you can find the complete source code. If there are any questions regarding this simple example code, please feel free to ask below. Your feedback or suggestions are also welcome. Regards, Tomas
View full article
Hi Everyone, In this document I would like to go through a simple example code I created for the FRDMKL25-A8471 kit using the KDS 3.0.2 and KSDK 2.0. I will not cover the Sensor Toolbox – CE and Intelligent Sensing Framework (ISF) which primarily support this kit. The FreeMASTER tool is used to visualize the acceleration data that are read from the FXLS8471Q using an interrupt technique through the SPI interface. This example illustrates: 1. Initialization of the MKL25Z128 MCU (mainly PORT and SPI modules). 2. SPI data write and read operations. 3. Initialization of the FXLS8471Q to achieve the highest resolution. 4. Output data reading using an interrupt technique. 5. Conversion of the output values from registers 0x01 – 0x06 to real acceleration values in g’s. 6. Visualization of the output values in the FreeMASTER tool. 1. As you can see in the FRDMSTBC-A8471/FRDM-KL25Z schematics and the image below, SPI signals are routed to the SPI0 module of the KL25Z MCU and the INT1 output is connected to the PTD4 pin. The PTD0 pin (Chip Select) is not controlled automatically by SPI0 module, hence it is configured as a general-purpose output. The INT1 output of the FXLS8471Q is configured as a push-pull active-low output, so the corresponding PTD4 pin configuration is GPIO with an interrupt on falling edge. The configuration is done in the BOARD_InitPins() function using the NXP Pins Tool for Kinetis MCUs. void BOARD_InitPins(void) {    CLOCK_EnableClock(kCLOCK_PortD);                                          /* Port D Clock Gate Control: Clock enabled */    CLOCK_EnableClock(kCLOCK_Spi0);                                           /* SPI0 Clock Gate Control: Clock enabled */    PORT_SetPinMux(PORTD, PIN1_IDX, kPORT_MuxAlt2);                           /* PORTD1 (pin 74) is configured as SPI0_SCK */    PORT_SetPinMux(PORTD, PIN2_IDX, kPORT_MuxAlt2);                           /* PORTD2 (pin 75) is configured as SPI0_MOSI */    PORT_SetPinMux(PORTD, PIN3_IDX, kPORT_MuxAlt2);                           /* PORTD3 (pin 76) is configured as SPI0_MISO */    PORT_SetPinMux(PORTD, PIN0_IDX, kPORT_MuxAsGpio);                         /* PORTD0 (pin 73) is configured as PTD0 */    GPIO_PinInit(GPIOD, PIN0_IDX, &CS_config);                                /* PTD0 = 1 (Chip Select inactive) */       PORT_SetPinMux(PORTD, PIN4_IDX , kPORT_MuxAsGpio);                        /* PORTD4 (pin 77) is configured as PTD4 */    PORT_SetPinInterruptConfig(PORTD, PIN4_IDX, kPORT_InterruptFallingEdge);  /* PTD4 is configured for falling edge interrupts */      NVIC_EnableIRQ(PORTD_IRQn);                                               /* Enable PORTD interrupt on NVIC */ } The SPI_INIT() function is used to enable and configure the SPI0 module. The FXLS8471Q uses the ‘Mode 0′ SPI protocol, which means that an inactive state of clock signal is low and data are captured on the leading edge of clock signal and changed on the falling edge. The SPI clock is 500 kHz. void SPI_Init(void) {    uint32_t sourceClock = 0U;    sourceClock = CLOCK_GetFreq(kCLOCK_BusClk);    spi_master_config_t masterConfig = {    .enableMaster = true,    .enableStopInWaitMode = false,    .polarity = kSPI_ClockPolarityActiveHigh,    .phase = kSPI_ClockPhaseFirstEdge,    .direction = kSPI_MsbFirst,    .outputMode = kSPI_SlaveSelectAsGpio,    .pinMode = kSPI_PinModeNormal,    .baudRate_Bps = 500000U     };    SPI_MasterInit(SPI0, &masterConfig, sourceClock); } 2. The falling edge on the CS pin starts the SPI communication. A write operation is initiated by transmitting a 1 for the R/W bit. Then the 8-bit register address, ADDR[7:0] is encoded in the first and second serialized bytes. Data to be written starts in the third serialized byte. The order of the bits is as follows: Byte 0: R/W, ADDR[6], ADDR[5], ADDR[4], ADDR[3], ADDR[2], ADDR[1], ADDR[0] Byte 1: ADDR[7], X, X, X, X, X, X, X Byte 2: DATA[7], DATA[6], DATA[5], DATA[4], DATA[3], DATA[2], DATA[1], DATA[0] The rising edge on the CS pin stops the SPI communication. Below is the write operation which writes the value 0x3D to the CTRL_REG1 (0x3A). Similarly a read operation is initiated by transmitting a 0 for the R/W bit. Then the 8-bit register address, ADDR[7:0] is encoded in the first and second serialized bytes. The data is read from the MISO pin (MSB first). The screenshot below shows the read operation which reads the correct value 0x6A from the WHO_AM_I register (0x0D). Multiple read operations are performed similar to single read except bytes are read in multiples of eight SCLK cycles. The register address is auto incremented so that every eighth next clock edges will latch the MSB of the next register. A burst read of 6 bytes from registers 0x01 to 0x06 is shown below. It also shows how the INT1 pin is automatically cleared by reading the acceleration output data. 3. At the beginning of the initialization, all FXLS8471Q registers are reset to their default values by setting the RST bit of the CTRL_REG2 register. The dynamic range is set to ±2g and to achieve the highest resolution, the LNOISE bit is set and the lowest ODR (1.56Hz) and the High Resolution mode are selected (more details in AN4075). The DRDY interrupt is enabled and routed to the INT1 interrupt pin that is configured to be a push-pull, active-low output. void FXLS8471Q_Init (void) {    FXLS8471Q_WriteRegister(CTRL_REG2, 0x40);            /* Reset all registers to POR values */    Pause(0xC62);                                        /* ~1ms delay */    FXLS8471Q_WriteRegister(CTRL_REG2, 0x02);            /* High Resolution mode */    FXLS8471Q_WriteRegister(CTRL_REG3, 0x00);            /* Push-pull, active low interrupt */    FXLS8471Q_WriteRegister(CTRL_REG4, 0x01);            /* Enable DRDY interrupt */    FXLS8471Q_WriteRegister(CTRL_REG5, 0x01);            /* DRDY interrupt routed to INT1 - PTD4 */    FXLS8471Q_WriteRegister(CTRL_REG1, 0x3D);            /* ODR = 1.56Hz, Reduced noise, Active mode */ } 4. In the ISR, only the interrupt flag is cleared and the DataReady variable is set to indicate the arrival of new data. void PORTD_IRQHandler(void) {    PORT_ClearPinsInterruptFlags(PORTD, 1<<4);           /* Clear the interrupt flag */    DataReady = 1; } 5. In the main loop, the DataReady variable is periodically checked and if it is set, the accelerometer registers 0x01 – 0x06 are read and then converted to signed 14-bit values and real values in g’s. if (DataReady)                                                        /* Is a new set of data ready? */ {    DataReady = 0;    FXLS8471Q_ReadMultiRegisters(OUT_X_MSB_REG, 6, AccData);           /* Read data output registers 0x01-0x06 */    Xout_14_bit = ((int16_t) (AccData[0]<<8 | AccData[1])) >> 2;       /* Compute 14-bit X-axis output value */    Yout_14_bit = ((int16_t) (AccData[2]<<8 | AccData[3])) >> 2;       /* Compute 14-bit Y-axis output value */    Zout_14_bit = ((int16_t) (AccData[4]<<8 | AccData[5])) >> 2;       /* Compute 14-bit Z-axis output value */    Xout_g = ((float) Xout_14_bit) / SENSITIVITY_2G;                   /* Compute X-axis output value in g's */    Yout_g = ((float) Yout_14_bit) / SENSITIVITY_2G;                   /* Compute Y-axis output value in g's */    Zout_g = ((float) Zout_14_bit) / SENSITIVITY_2G;                   /* Compute Z-axis output value in g's */ } 6. The calculated values can be watched in the Debug perspective or in the FreeMASTER application. To open and run the FreeMASTER project, install the FreeMASTER 2.0 application and FreeMASTER Communication Driver. Attached you can find the complete source code written in the KDS 3.0.2 including 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. Best regards, Tomas
View full article
clicktaleID