Working with STM32 and sensors: MPU6050

In this guide, we shall see how to interface MPU6050 with STM32F4xx.

In this guide, we shall cover the following:

  • What is MPU6050.
  • Connection.
  • Developing the driver.
  • Code.
  • Results.

1. What is MPU6050:

The MPU-6050™ parts are the world’s first MotionTracking devices designed for the low power, low cost, and high-performance requirements of smartphones, tablets and wearable sensors.

The MPU-6050 incorporates InvenSense’s MotionFusion™ and run-time calibration firmware that enables manufacturers to eliminate the costly and complex selection, qualification, and system level integration of discrete devices in motion-enabled products, guaranteeing that sensor fusion algorithms and calibration procedures deliver optimal performance for consumers.

The MPU-6050 devices combine a 3-axis gyroscope and a 3-axis accelerometer on the same silicon die, together with an onboard Digital Motion Processor™ (DMP™), which processes complex 6-axis MotionFusion algorithms. The device can access external magnetometers or other sensors through an auxiliary master I²C bus, allowing the devices to gather a full set of sensor data without intervention from the system processor. The devices are offered in a 4 mm x 4 mm x 0.9 mm QFN package.

The InvenSense MotionApps™ Platform that comes with the MPU-6050 abstracts motion-based complexities, offloads sensor management from the operating system, and provides a structured set of APIs for application development.

For precision tracking of both fast and slow motions, the parts feature a user-programmable gyro full-scale range of ±250, ±500, ±1000, and ±2000 °/sec (dps), and a user-programmable accelerometer full-scale range of ±2g, ±4g, ±8g, and ±16g. Additional features include an embedded temperature sensor and an on-chip oscillator with ±1% variation over the operating temperature range.

Applications

  • BlurFree™ technology (for Video/Still Image Stabilization)
  • AirSign™ technology (for Security/Authentication)
  • TouchAnywhere™ technology (for “no touch” UI Application Control/Navigation)
  • MotionCommand™ technology (for Gesture Short-cuts)
  • Motion-enabled game and application framework
  • InstantGesture™ iG™ gesture recognition
  • Location based services, points of interest, and dead reckoning
  • Handset and portable gaming
  • Motion-based game controllers
  • 3D remote controls for Internet connected DTVs and set top boxes, 3D mice
  • Wearable sensors for health, fitness and sports
  • Toys

Features:

Gyroscope Features:

The triple-axis MEMS gyroscope in the MPU-60X0 includes a wide range of features:

  • Digital-output X-, Y-, and Z-Axis angular rate sensors (gyroscopes) with a user-programmable full-scale range of ±250, ±500, ±1000, and ±2000°/sec
  • External sync signal connected to the FSYNC pin supports image, video and GPS synchronization
  • Integrated 16-bit ADCs enable simultaneous sampling of gyros
  • Enhanced bias and sensitivity temperature stability reduces the need for user calibration
  • Improved low-frequency noise performance
  • Digitally-programmable low-pass filter
  • Gyroscope operating current: 3.6mA
  • Standby current: 5µA
  • Factory calibrated sensitivity scale factor
  • User self-test

Accelerometer Features:

The triple-axis MEMS accelerometer in MPU-60X0 includes a wide range of features:

  • Digital-output triple-axis accelerometer with a programmable full scale range of ±2g, ±4g, ±8g and ±16g
  • Integrated 16-bit ADCs enable simultaneous sampling of accelerometers while requiring no external multiplexer
  • Accelerometer normal operating current: 500µA
  • Low power accelerometer mode current: 10µA at 1.25Hz, 20µA at 5Hz, 60µA at 20Hz, 110µA at 40Hz
  • Orientation detection and signaling
  • Tap detection
  • User-programmable interrupts
  • High-G interrupt
  • User self-test

2. Connection:

The connection as following:

MPU6050STM32F411
Vcc5V
GNDGND
SDAPB9
SCLPB8

3. Developing the driver:

Before we starting developing the driver, we need the following:

  • Writing single byte using i2c bus (here).
  • Multibyte read using i2c bus (here).
  • Time base (here).

Also, we need uart to transmit data over uart (uart , retargeting printf ).

We start of by creating new source and header file with name of mpu6050.c and mpu6050.h respectively.

Within the header:

Include the following header files

//Header Files
#include "i2c.h"
#include <string.h>
#include <stdbool.h>	//Boolean
#include <math.h>			//Pow()

Define the registers:

//Define Registers
#define MPU_ADDR          0x68

#define WHO_AM_I_REG			0x75
#define PWR_MAGT_1_REG		0x6B
#define CONFIG_REG				0x1A
#define GYRO_CONFIG_REG		0x1B
#define ACCEL_CONFIG_REG	0x1C
#define SMPLRT_DIV_REG		0x19
#define INT_STATUS_REG		0x3A
#define ACCEL_XOUT_H_REG	0x3B
#define TEMP_OUT_H_REG		0x41
#define GYRO_XOUT_H_REG		0x43
#define FIFO_EN_REG 			0x23
#define INT_ENABLE_REG 		0x38
#define I2CMACO_REG 			0x23
#define USER_CNT_REG			0x6A
#define FIFO_COUNTH_REG 	0x72
#define FIFO_R_W_REG 			0x74

Define the struct for MPU configuration:

//1- MPU Configuration 
typedef struct
{
	uint8_t ClockSource;
	uint8_t Gyro_Full_Scale;
	uint8_t Accel_Full_Scale;
	uint8_t CONFIG_DLPF;
	bool 		Sleep_Mode_Bit; 
	
}MPU_ConfigTypeDef;

Enum for clock source:

//2- Clock Source ENUM
enum PM_CLKSEL_ENUM
{
	Internal_8MHz 	= 0x00,
	X_Axis_Ref			= 0x01,
	Y_Axis_Ref			= 0x02,
	Z_Axis_Ref			= 0x03,
	Ext_32_768KHz		= 0x04,
	Ext_19_2MHz			= 0x05,
	TIM_GENT_INREST	= 0x07
};

Gyro full scale:

//3- Gyro Full Scale Range ENUM (deg/sec)
enum gyro_FullScale_ENUM
{
	FS_SEL_250 	= 0x00,
	FS_SEL_500 	= 0x01,
	FS_SEL_1000 = 0x02,
	FS_SEL_2000	= 0x03
};

Accelerometer scale:

//4- Accelerometer Full Scale Range ENUM (1g = 9.81m/s2)
enum accel_FullScale_ENUM
{
	AFS_SEL_2g	= 0x00,
	AFS_SEL_4g,
	AFS_SEL_8g,
	AFS_SEL_16g
};

Digital low pass filter:

enum DLPF_CFG_ENUM
{
	DLPF_260A_256G_Hz = 0x00,
	DLPF_184A_188G_Hz = 0x01,
	DLPF_94A_98G_Hz 	= 0x02,
	DLPF_44A_42G_Hz 	= 0x03,
	DLPF_21A_20G_Hz 	= 0x04,
	DLPF_10_Hz 				= 0x05,
	DLPF_5_Hz 				= 0x06
};

External frame sync:

/6- e external Frame Synchronization ENUM
enum EXT_SYNC_SET_ENUM
{
	input_Disable = 0x00,
	TEMP_OUT_L		= 0x01,
	GYRO_XOUT_L		= 0x02,
	GYRO_YOUT_L		= 0x03,
	GYRO_ZOUT_L		= 0x04,
	ACCEL_XOUT_L	= 0x05,
	ACCEL_YOUT_L	= 0x06,
	ACCEL_ZOUT_L	= 0x07
};

Raw data struct:

//7. Raw data typedef
typedef struct
{
	int16_t x;
	int16_t y;
	int16_t z;
}RawData_Def;

Scaled data struct:

//8. Scaled data typedef
typedef struct
{
	float x;
	float y;
	float z;
}ScaledData_Def;

Function prototypes:

//Function Prototype

//2- i2c Read
void I2C_Read(uint8_t ADDR, uint8_t *i2cBuf, uint8_t NofData);
//3- i2c Write 8 Bit
void I2C_Write8(uint8_t ADDR, uint8_t data);
//4- MPU6050 Initialaztion Configuration 
void MPU6050_Config(MPU_ConfigTypeDef *config);
//5- Get Sample Rate Divider
uint8_t MPU6050_Get_SMPRT_DIV(void);
//6- Set Sample Rate Divider
void MPU6050_Set_SMPRT_DIV(uint8_t SMPRTvalue);
//7- External Frame Sync.
uint8_t MPU6050_Get_FSYNC(void);
//8- Set External Frame Sync.
void MPU6050_Set_FSYNC(enum EXT_SYNC_SET_ENUM ext_Sync);
//9- Get Accel Raw Data
void MPU6050_Get_Accel_RawData(RawData_Def *rawDef);//************
//10- Get Accel scaled data
void MPU6050_Get_Accel_Scale(ScaledData_Def *scaledDef);//***********
//11- Get Accel calibrated data
void MPU6050_Get_Accel_Cali(ScaledData_Def *CaliDef);
//12- Get Gyro Raw Data
void MPU6050_Get_Gyro_RawData(RawData_Def *rawDef);
//13- Get Gyro scaled data
void MPU6050_Get_Gyro_Scale(ScaledData_Def *scaledDef);
//14- Accel Calibration
void _Accel_Cali(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max);

Hence, the entire header file:

//Header Files
#include "i2c.h"
#include <string.h>
#include <stdbool.h>	//Boolean
#include <math.h>			//Pow()

//Define Registers
#define MPU_ADDR          0x68

#define WHO_AM_I_REG			0x75
#define PWR_MAGT_1_REG		0x6B
#define CONFIG_REG				0x1A
#define GYRO_CONFIG_REG		0x1B
#define ACCEL_CONFIG_REG	0x1C
#define SMPLRT_DIV_REG		0x19
#define INT_STATUS_REG		0x3A
#define ACCEL_XOUT_H_REG	0x3B
#define TEMP_OUT_H_REG		0x41
#define GYRO_XOUT_H_REG		0x43
#define FIFO_EN_REG 			0x23
#define INT_ENABLE_REG 		0x38
#define I2CMACO_REG 			0x23
#define USER_CNT_REG			0x6A
#define FIFO_COUNTH_REG 	0x72
#define FIFO_R_W_REG 			0x74

//TypeDefs and Enums
//1- MPU Configuration 
typedef struct
{
	uint8_t ClockSource;
	uint8_t Gyro_Full_Scale;
	uint8_t Accel_Full_Scale;
	uint8_t CONFIG_DLPF;
	bool 		Sleep_Mode_Bit; 
	
}MPU_ConfigTypeDef;

//2- Clock Source ENUM
enum PM_CLKSEL_ENUM
{
	Internal_8MHz 	= 0x00,
	X_Axis_Ref			= 0x01,
	Y_Axis_Ref			= 0x02,
	Z_Axis_Ref			= 0x03,
	Ext_32_768KHz		= 0x04,
	Ext_19_2MHz			= 0x05,
	TIM_GENT_INREST	= 0x07
};

//3- Gyro Full Scale Range ENUM (deg/sec)
enum gyro_FullScale_ENUM
{
	FS_SEL_250 	= 0x00,
	FS_SEL_500 	= 0x01,
	FS_SEL_1000 = 0x02,
	FS_SEL_2000	= 0x03
};


//4- Accelerometer Full Scale Range ENUM (1g = 9.81m/s2)
enum accel_FullScale_ENUM
{
	AFS_SEL_2g	= 0x00,
	AFS_SEL_4g,
	AFS_SEL_8g,
	AFS_SEL_16g
};

//5- Digital Low Pass Filter ENUM
enum DLPF_CFG_ENUM
{
	DLPF_260A_256G_Hz = 0x00,
	DLPF_184A_188G_Hz = 0x01,
	DLPF_94A_98G_Hz 	= 0x02,
	DLPF_44A_42G_Hz 	= 0x03,
	DLPF_21A_20G_Hz 	= 0x04,
	DLPF_10_Hz 				= 0x05,
	DLPF_5_Hz 				= 0x06
};
//6- e external Frame Synchronization ENUM
enum EXT_SYNC_SET_ENUM
{
	input_Disable = 0x00,
	TEMP_OUT_L		= 0x01,
	GYRO_XOUT_L		= 0x02,
	GYRO_YOUT_L		= 0x03,
	GYRO_ZOUT_L		= 0x04,
	ACCEL_XOUT_L	= 0x05,
	ACCEL_YOUT_L	= 0x06,
	ACCEL_ZOUT_L	= 0x07
};

//7. Raw data typedef
typedef struct
{
	int16_t x;
	int16_t y;
	int16_t z;
}RawData_Def;

//8. Scaled data typedef
typedef struct
{
	float x;
	float y;
	float z;
}ScaledData_Def;


//Function Prototype

//2- i2c Read
void I2C_Read(uint8_t ADDR, uint8_t *i2cBuf, uint8_t NofData);
//3- i2c Write 8 Bit
void I2C_Write8(uint8_t ADDR, uint8_t data);
//4- MPU6050 Initialaztion Configuration 
void MPU6050_Config(MPU_ConfigTypeDef *config);
//5- Get Sample Rate Divider
uint8_t MPU6050_Get_SMPRT_DIV(void);
//6- Set Sample Rate Divider
void MPU6050_Set_SMPRT_DIV(uint8_t SMPRTvalue);
//7- External Frame Sync.
uint8_t MPU6050_Get_FSYNC(void);
//8- Set External Frame Sync.
void MPU6050_Set_FSYNC(enum EXT_SYNC_SET_ENUM ext_Sync);
//9- Get Accel Raw Data
void MPU6050_Get_Accel_RawData(RawData_Def *rawDef);//************
//10- Get Accel scaled data
void MPU6050_Get_Accel_Scale(ScaledData_Def *scaledDef);//***********
//11- Get Accel calibrated data
void MPU6050_Get_Accel_Cali(ScaledData_Def *CaliDef);
//12- Get Gyro Raw Data
void MPU6050_Get_Gyro_RawData(RawData_Def *rawDef);
//13- Get Gyro scaled data
void MPU6050_Get_Gyro_Scale(ScaledData_Def *scaledDef);
//14- Accel Calibration
void _Accel_Cali(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max);

Thats all for header file.

The source file:

Include the following:

#include "MPU6050.h"
#include "i2c.h"
#include "time_base.h"

Static variable for the library:

//2- Accel & Gyro Scaling Factor
static float accelScalingFactor, gyroScalingFactor;
//3- Bias variables
static float A_X_Bias = 0.0f;
static float A_Y_Bias = 0.0f;
static float A_Z_Bias = 0.0f;

static int16_t GyroRW[3];

MPU configuration:

void MPU6050_Config(MPU_ConfigTypeDef *config)
{
	uint8_t Buffer = 0;
	//Clock Source 
	//Reset Device

	i2c_writeByte(MPU_ADDR, PWR_MAGT_1_REG, 0x80);

	delay(100);

	Buffer = config ->ClockSource & 0x07; //change the 7th bits of register

	Buffer |= (config ->Sleep_Mode_Bit << 6) &0x40; // change only the 7th bit in the register
	
	i2c_writeByte(MPU_ADDR,PWR_MAGT_1_REG, Buffer);

	delay(100); // should wait 10ms after changeing the clock setting.

	//Set the Digital Low Pass Filter
	Buffer = 0;

	Buffer = config->CONFIG_DLPF & 0x07;
	
	i2c_writeByte(MPU_ADDR,CONFIG_REG, Buffer);

	//Select the Gyroscope Full Scale Range
	Buffer = 0;

	Buffer = (config->Gyro_Full_Scale << 3) & 0x18;
	
	i2c_writeByte(MPU_ADDR,GYRO_CONFIG_REG, Buffer);

	//Select the Accelerometer Full Scale Range 
	Buffer = 0; 

	Buffer = (config->Accel_Full_Scale << 3) & 0x18;

	i2c_writeByte(MPU_ADDR,ACCEL_CONFIG_REG, Buffer);

	//Set SRD To Default
	MPU6050_Set_SMPRT_DIV(0x04);
	
	
	//Accelerometer Scaling Factor, Set the Accelerometer and Gyroscope Scaling Factor
	switch (config->Accel_Full_Scale)
	{
		case AFS_SEL_2g:
			accelScalingFactor = (2000.0f/32768.0f);
			break;
		
		case AFS_SEL_4g:
			accelScalingFactor = (4000.0f/32768.0f);
				break;
		
		case AFS_SEL_8g:
			accelScalingFactor = (8000.0f/32768.0f);
			break;
		
		case AFS_SEL_16g:
			accelScalingFactor = (16000.0f/32768.0f);
			break;
		
		default:
			break;
	}
	//Gyroscope Scaling Factor 
	switch (config->Gyro_Full_Scale)
	{
		case FS_SEL_250:
			gyroScalingFactor = 250.0f/32768.0f;
			break;
		
		case FS_SEL_500:
				gyroScalingFactor = 500.0f/32768.0f;
				break;
		
		case FS_SEL_1000:
			gyroScalingFactor = 1000.0f/32768.0f;
			break;
		
		case FS_SEL_2000:
			gyroScalingFactor = 2000.0f/32768.0f;
			break;
		
		default:
			break;
	}
	
}

Get the sample rate:

//5- Get Sample Rate Divider
uint8_t MPU6050_Get_SMPRT_DIV(void)
{
	uint8_t Buffer = 0;
	
	i2c_readByte(MPU_ADDR,SMPLRT_DIV_REG,&Buffer);

	return Buffer;
}

Set the sample rate:

//6- Set Sample Rate Divider
void MPU6050_Set_SMPRT_DIV(uint8_t SMPRTvalue)
{
	i2c_writeByte(MPU_ADDR,SMPLRT_DIV_REG, SMPRTvalue);
}

Get eh external FYSNC:

uint8_t MPU6050_Get_FSYNC(void)
{
	uint8_t Buffer = 0;
	
	i2c_readByte(MPU_ADDR,CONFIG_REG, &Buffer);
	Buffer &= 0x38; 
	return (Buffer>>3);
}

Set the FYSNC:

//8- Set External Frame Sync. 
void MPU6050_Set_FSYNC(enum EXT_SYNC_SET_ENUM ext_Sync)
{
	uint8_t Buffer = 0;
	i2c_readByte(MPU_ADDR,CONFIG_REG, &Buffer);
	Buffer &= ~0x38;
	
	Buffer |= (ext_Sync <<3); 
	i2c_writeByte(MPU_ADDR,CONFIG_REG, Buffer);
	
}

Get the raw acceleration data:

//9- Get Accel Raw Data
void MPU6050_Get_Accel_RawData(RawData_Def *rawDef)
{
	uint8_t state;
	uint8_t AcceArr[6], GyroArr[6];
	
	i2c_readByte(MPU_ADDR,INT_STATUS_REG, &state);


	if((state&&0x01))
	{
		i2c_ReadMulti(MPU_ADDR,ACCEL_XOUT_H_REG, 6,AcceArr);
		
		//Accel Raw Data
		rawDef->x = ((AcceArr[0]<<8) + AcceArr[1]); // x-Axis
		rawDef->y = ((AcceArr[2]<<8) + AcceArr[3]); // y-Axis
		rawDef->z = ((AcceArr[4]<<8) + AcceArr[5]); // z-Axis
		//Gyro Raw Data
		i2c_ReadMulti(MPU_ADDR,GYRO_XOUT_H_REG, 6,GyroArr);
		GyroRW[0] = ((GyroArr[0]<<8) + GyroArr[1]);
		GyroRW[1] = (GyroArr[2]<<8) + GyroArr[3];
		GyroRW[2] = ((GyroArr[4]<<8) + GyroArr[5]);
	}
}

Get the acceleration data:

//10- Get Accel scaled data (g unit of gravity, 1g = 9.81m/s2)
void MPU6050_Get_Accel_Scale(ScaledData_Def *scaledDef)
{

	RawData_Def AccelRData;
	MPU6050_Get_Accel_RawData(&AccelRData);
	
	//Accel Scale data 
	scaledDef->x = ((AccelRData.x+0.0f)*accelScalingFactor)/1000;
	scaledDef->y = ((AccelRData.y+0.0f)*accelScalingFactor)/1000;
	scaledDef->z = ((AccelRData.z+0.0f)*accelScalingFactor)/1000;
}

Get the acceleration calibrated data:

//11- Get Accel calibrated data
void MPU6050_Get_Accel_Cali(ScaledData_Def *CaliDef)
{
	ScaledData_Def AccelScaled;
	MPU6050_Get_Accel_Scale(&AccelScaled);
	
	//Accel Scale data 
	CaliDef->x = (AccelScaled.x) - A_X_Bias; // x-Axis
	CaliDef->y = (AccelScaled.y) - A_Y_Bias;// y-Axis
	CaliDef->z = (AccelScaled.z) - A_Z_Bias;// z-Axis
}

Get the Gyro data:

//12- Get Gyro Raw Data
void MPU6050_Get_Gyro_RawData(RawData_Def *rawDef)
{
	
	//Accel Raw Data
	rawDef->x = GyroRW[0];
	rawDef->y = GyroRW[1];
	rawDef->z = GyroRW[2];
	
}

//13- Get Gyro scaled data
void MPU6050_Get_Gyro_Scale(ScaledData_Def *scaledDef)
{
	RawData_Def myGyroRaw;
	MPU6050_Get_Gyro_RawData(&myGyroRaw);
	
	//Gyro Scale data 
	scaledDef->x = (myGyroRaw.x)*gyroScalingFactor; // x-Axis
	scaledDef->y = (myGyroRaw.y)*gyroScalingFactor; // y-Axis
	scaledDef->z = (myGyroRaw.z)*gyroScalingFactor; // z-Axis
}

Calibrate the acceleration:

//14- Accel Calibration
void _Accel_Cali(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max)
{
	//1* X-Axis calibrate
	A_X_Bias		= (x_max + x_min)/2.0f;
	
	//2* Y-Axis calibrate
	A_Y_Bias		= (y_max + y_min)/2.0f;
	
	//3* Z-Axis calibrate
	A_Z_Bias		= (z_max + z_min)/2.0f;
}

In main.c:

#include "i2c.h"
#include "time_base.h"
#include "uart.h"
#include "stdio.h"
#include "MPU6050.h"

#define rate 300
uint32_t previous;


int main(void)
{
	Ticks_Init(16000000);

	uart2_rxtx_init();

	i2c_init();

	MPU_ConfigTypeDef myConfig;

	myConfig.Accel_Full_Scale = AFS_SEL_4g;
	myConfig.ClockSource = Internal_8MHz;
	myConfig.CONFIG_DLPF = DLPF_184A_188G_Hz;
	myConfig.Sleep_Mode_Bit = 0;
	myConfig.Gyro_Full_Scale = FS_SEL_500;

	MPU6050_Config(&myConfig);

	ScaledData_Def meAccel;

	while(1)
	{
		MPU6050_Get_Accel_Scale(&meAccel);
		if(get_Ticks()-previous >rate)
		{
		    printf("Accel: X = %.2f Y = %.2f Z = %.2f\r\n", meAccel.x, meAccel.y, meAccel.z);
		    printf("==============================\r\n");
			previous=get_Ticks();
		}
	}

}

For the configuration:

First, Set thee acceleration scale for 4G:

myConfig.Accel_Full_Scale = AFS_SEL_4g;

Set the clock to be internal 8MHz:

myConfig.ClockSource = Internal_8MHz;

Configure the filter:

myConfig.CONFIG_DLPF = DLPF_184A_188G_Hz;

Exit from sleep mode:

myConfig.Sleep_Mode_Bit = 0;

Set the gyro scale:

myConfig.Gyro_Full_Scale = FS_SEL_500;

Pass the data structure to MPU configuration function as following:

MPU6050_Config(&myConfig);

4. Code:

You may download the code from here:

5. Results:

Upload the code to your F411RE Nucleo and open serial monitor and set the buadrare to 115200:

Happy coding 🙂

Add Comment

Your email address will not be published. Required fields are marked *