Wednesday, June 17, 2015

MPU-6050 Configuration and Operation

In this post we will discuss MPU library responsible for configuration and enabling synchronous and asynchronous communication with up to two MPU-6050 sensors. If you followed my other posts, you will notice that the structure of this library is very similar to the one supporting another I2C device on my board – namely the MPL library, which we discussed thoroughly in several posts starting with this one. As such I will not go into the minute details of the MPU library - the code is available and you may review it at your leisure. I will just focus on some aspects which are specific to this library – specifically the implementation details allowing this library to manage up to two sensors at the same time. I have two MPU-6050 sensors on the board and did not want practically repeating the code to create two libraries – one for each device.

As was discussed with regards to other libraries, configuration of the MPU library itself is contained in the MPU-Profile.h header. There are several definitions in this header which specify how many sensors are available on the board and should be managed by the library, as well as what are the addresses of these sensors, what I2C modules they are connected to, and what MCU pins are allocated to capture sensors’ interrupts. This header also provides two inline functions, MPUSetIF(…) and MPUSetIE(…) to control interrupt lines associated with each sensor. _MPUInitPinMap() configures interrupt pin(s) based upon other definitions in this header.

To enable the MPU library working with two sensors we need to be able to specify for each function the details of the sensor that it will be working on. Unfortunately the MPLab-X XC-16 compiler supports only “C” language – otherwise we would just create an instance of the library code for each of the sensor from the same class. As we are limited to plain “C”, I had to emulate the object-oriented class-based approach by creating a structure containing all pertinent information for each sensor and explicitly passing a pointer to the instance of this structure to every internal library function emulating the “this” pointer. This structure, MPU_CB, defined in the MPU_Local.h header file.

MPU_CB structure is critical for correct operation of the library, so the pointer(s) to this structure is not exposed to the user code. Instead interface functions of the MPU library accept a numeric parameter identifying which of the sensors (1 or 2) the operation should be directed to. MPU_Local.h header provides definition for the MPUpCB(…) internal function (not exposed outside the library), which converts the sensor number to the pointer to respective MPU_CB structure for subsequent use by the internal library functions.

Two helper functions, _MPU_ConvertReadBuffer(…) and _MPU_ApplyCalibration(…), also defined in the MPU_Local.h header. The former is used by low-level synchronous and asynchronous read routines to convert data stream from the sensor into a set of values representing acceleration, rotation, and temperature measurement. The latter one is used by the read routines exposing measurements to the user to apply rotation and calibration.

As you may recall from the discussion of the board, it contains two MPU-6050 sensors rotated 22.5 degree left and right with respect to the X axis of the board (45 degree rotation respective to each other sensor). This orientation of the sensors guarantee that at minimum three measured values from 2 sensors will contribute to calculation of the pitch and roll of the board with the expectation that this would reduce sensitivity to vibration and improve precision of the measurements. However this orientation of the sensors require that measurements from each sensor need to be “rotated” to match the axes of the board.

The rest of the function is dealing with applying temperature and axis calibration values – we will talk about calibrating MPU-6050 in a subsequent post.

Finally the MPU_Local.h header maps addresses of the control registers of the MPU-6050 to mnemonic names, which are being used by various routines of the library.

Instances of the MPU_CB structure (one for each sensor) are initialized in the MPUInit(…) function and its extensions defined in the MPU_Init.c code file. Some of the fields in MPU_CB structure, like sensor sensitivities, are filled in by the MPURest(…) function based upon the configuration parameters provided by the user. MPURest(…) function invoked either from MPUInit(…) for initial configuration or directly by the user code after initialization to change sensor configuration. MPURest(…) function defined in the MPU_Reset.c code file.

MPU.h header file provides definitions of functions and objects comprising the MPU library API – this is the only header file required by the user programs to access MPU-6050 functionality. This header provides definitions for error codes, enums defining MPU-6050 configuration parameters (like gyro and accelerometer sensitivities), and the MPUData structure through which measurements are communicated to the user program. The rest of the header provides prototypes for the MPU library interface functions, which are primarily defined in MPU_Sync.c, MPU_Async.c, and MPU_Mgmt.c code files.

Code file MPU_Isr.c defines interrupt routines processing Data Ready interrupts from respective sensors and common for both sensors I2C callback function _MPUCallBack(…). If you recall from our discussion of the I2C Library in one of the previous posts, one of the parameters passed to the I2CAsyncStart (…) function is the I2CAsyncRqst structure, which includes the “client parameter” field. This field becomes very useful for the MPU library as it uses it to pass the pointer to the respective MPU_CB control block. When I2C Library invokes the _MPUCallBack(…) function from the I2C interrupt routine it passes this “client parameter” to the callback so that the callback routine knows which sensor it operates upon.

Initialization routine puts MPU-6050 into the “automatic sampling mode” at defined sampling frequency. In this mode every time that new sample is obtained the Data Ready line receives a 50 usec pulse. This architecture significantly simplifies interrupt and I2C callback routines as we do not have to deal with the “missing interrupt” (it will always be repeated at the next sample) and do not have to trigger next sample acquisition – things we had to do while working with MPL-3115A barometric altitude sensor.


This concludes the review of the MPU library - the code has detailed comments and should be rather easy to read following the review above. In the next post we will see how the three libraries we have been discussing lately (MPU library, MPL Library, and I2C Library) work together to allow sharing of the I2C bus between two devices in the interrupt-driven IO.

No comments:

Post a Comment