FRDM-STBC-AGM01 - Bare metal example project

Document created by Tomas Vaverka Employee on Jul 22, 2015Last modified by Tomas Vaverka Employee on Jul 22, 2015
Version 1Show Document
  • View in full screen mode

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 I2C interface.

 

This example illustrates:

 

1. Initialization of the MKL25Z128 MCU (mainly I2C and PORT modules).

2. I2C 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 I2C by default. With jumpers J6 and J7 in their default position (2-3), the I2C 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.

 

Boards1.JPG

 

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 I2C 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.

 

Write combo.JPG

Write gyro.JPG

 

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.

 

Read Who_Am_I Combo.JPG

Read Who_Am_I Gyro.JPG

 

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.

 

Burst read combo.JPG

Burst read gyro.JPG

 

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.

 

CW.JPG

 

Freemaster.JPG

 

 

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

1 person found this helpful

Outcomes