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