Version:0.9 StartHTML:0000000105 EndHTML:0000070303 StartFragment:0000001499 EndFragment:0000070287
/*
* File: MPU6050.c
* Author: Armstrong Subero
* uC: STM32F103C8T6 w/PLL OSC @ 72 MHz, 3.3v
* Program: I22_MPU6050
* Compiler: MikroC Pro v6.0.0
* Program Version: 1.0
*
* Program Description: This Program Allows STM32F103C8T6 to be interfaced with
* the MPU6050 and reads the IMU and performs calculations
* using the Madgwick filter to produce unit quaternion
* values that can be used to determine spatial orientation
*
* Hardware Description: A MPU6050 is connected to the I2C bus and a CP2104 is
* connected to UART1
*
* Created April 13th, 2018, 1:27 AM
*/
/******************************************************************************
* Includes and Defines
******************************************************************************/
#include "stdbool.h"
#include <built_in.h>
#include <stdio.h>
#include <stdlib.h>
#include "MPU6050.h"
#include "MadgwickAHRS.h"
// Volatile Variables
volatile int GYRO_XOUT, GYRO_YOUT, GYRO_ZOUT;
volatile int ACCEL_XOUT, ACCEL_YOUT, ACCEL_ZOUT;
// Global variables
double acc_x;
double acc_y;
double acc_z;
double gyro_x;
double gyro_y;
double gyro_z;
int info;
char buf[25];
int cal_var = 0;
double gyro_x_offset;
double gyro_y_offset;
double gyro_z_offset;
char gyro_x_out[10];
char gyro_y_out[10];
char gyro_z_out[10];
char accel_x_out[10];
char accel_y_out[10];
char accel_z_out[10];
// quaternion values
char q0_t[10];
char q1_t[10];
char q2_t[10];
char q3_t[10];
// Writes to the IMU via I2C1
void MPU6050_Write(unsigned char addr, int raddr, int rdata)
{
buf[0] = raddr; // register address
buf[1] = rdata; // register data
I2C1_Start(); // issue I2C start signal
I2C1_Write(addr,buf,2,END_MODE_STOP); // write data via I2C bus
}
// Test to ensure MP6050 is connected
int MPU6050_Test(void)
{
buf[0] = MPU6050_RA_WHO_AM_I;
I2C1_Start();
I2C1_Write(MPU6050_ADDRESS, buf, 1, END_MODE_RESTART);
I2C1_Read(MPU6050_ADDRESS, buf, 1, END_MODE_STOP);
if(buf[0] == 0x68)
{
return 1;
}
else
{
return 0;
}
}
// Read the IMU via I2C1
int MPU6050_Read(unsigned short addr, int raddr)
{
buf[0] = raddr;
I2C1_Start();
I2C1_Write(addr, buf, 1, END_MODE_RESTART);
I2C1_Read (addr, buf, 1, END_MODE_STOP);
return buf[0];
}
// Initializes the IMU
void MPU6050_Init(void)
{
// Wakeup from sleep
MPU6050_Write(MPU6050_ADDRESS, MPU6050_RA_PWR_MGMT_1, 0x00);
// sample rate = 8kHz / 1 + 109 = 72.7Hz
MPU6050_Write(MPU6050_ADDRESS, MPU6050_RA_SMPLRT_DIV, 109);
// Write Accelerometer to +\- 4g
MPU6050_Write(MPU6050_ADDRESS, MPU6050_RA_ACCEL_CONFIG, 0x00);
// gyro scale of 2000 degrees/s
MPU6050_Write(MPU6050_ADDRESS, MPU6050_RA_GYRO_CONFIG, 0x18);
// Disable Frame Sync, Set Digital Low pass filter to 256Hz DLPF
MPU6050_Write(MPU6050_ADDRESS, MPU6050_RA_CONFIG, 0x00);
// Disable sensor output to FIFO buffer
MPU6050_Write(MPU6050_ADDRESS, MPU6050_RA_FIFO_EN, 0x00);
// AUX I2C setup, single master control
MPU6050_Write(MPU6050_ADDRESS, MPU6050_RA_I2C_MST_CTRL, 0x00);
// Setup INT pin and AUX I2C pass through
MPU6050_Write(MPU6050_ADDRESS, MPU6050_RA_INT_PIN_CFG, 0x00);
// Enable data ready interrupt
MPU6050_Write(MPU6050_ADDRESS, MPU6050_RA_INT_ENABLE, 0x00);
}
// Read the raw accel vlaue from the IMU
int MPU6050_Accel_Raw(char axis)
{
//Variables for high and low accel values
int ACCEL_XOUT_H, ACCEL_XOUT_L, ACCEL_YOUT_H;
int ACCEL_YOUT_L, ACCEL_ZOUT_H, ACCEL_ZOUT_L;
// Read accelerometer values out from H and L registers
ACCEL_XOUT_H = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_ACCEL_XOUT_H);
ACCEL_XOUT_L = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_ACCEL_XOUT_L);
ACCEL_YOUT_H = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_ACCEL_YOUT_H);
ACCEL_YOUT_L = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_ACCEL_YOUT_L);
ACCEL_ZOUT_H = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_ACCEL_ZOUT_H);
ACCEL_ZOUT_L = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_ACCEL_ZOUT_L);
// Convert received data
ACCEL_XOUT = ( ACCEL_XOUT_H<<8 | ACCEL_XOUT_L );
ACCEL_YOUT = ( ACCEL_YOUT_H<<8 | ACCEL_YOUT_L );
ACCEL_ZOUT = ( ACCEL_ZOUT_H<<8 | ACCEL_ZOUT_L );
// Return value to user based on reqiested axis
switch (axis)
{
case 'X':
return ACCEL_XOUT; // Accel X Axis
break;
case 'Y':
return ACCEL_YOUT; // Accel Y Axis
break;
case 'Z':
return ACCEL_ZOUT; // Accel Z Axis
break;
default:
return 0;
}
}
// Read the accel value and convert to G's
double MPU6050_Read_Accel(char axis)
{
// Variables for high and low accel values
int ACCEL_XOUT_H, ACCEL_XOUT_L, ACCEL_YOUT_H;
int ACCEL_YOUT_L, ACCEL_ZOUT_H, ACCEL_ZOUT_L;
float GFORCEX;
float GFORCEY;
float GFORCEZ;
// Read accelerometer values out from H and L registers
ACCEL_XOUT_H = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_ACCEL_XOUT_H);
ACCEL_XOUT_L = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_ACCEL_XOUT_L);
ACCEL_YOUT_H = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_ACCEL_YOUT_H);
ACCEL_YOUT_L = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_ACCEL_YOUT_L);
ACCEL_ZOUT_H = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_ACCEL_ZOUT_H);
ACCEL_ZOUT_L = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_ACCEL_ZOUT_L);
// Convert received data
ACCEL_XOUT = ( ACCEL_XOUT_H<<8 | ACCEL_XOUT_L );
ACCEL_YOUT = ( ACCEL_YOUT_H<<8 | ACCEL_YOUT_L );
ACCEL_ZOUT = ( ACCEL_ZOUT_H<<8 | ACCEL_ZOUT_L );
// Calculate G-Force Data
GFORCEX = (ACCEL_XOUT / G_CALC_VAL);
GFORCEY = (ACCEL_YOUT / G_CALC_VAL);
GFORCEZ = (ACCEL_ZOUT / G_CALC_VAL);
// Return G's value to user
switch (axis)
{
case 'X':
return GFORCEX; // Gforce X Axis
break;
case 'Y':
return GFORCEY; // Gforce Y Axis
break;
case 'Z':
return GFORCEZ; // Gforce Z Axis
break;
default:
return 0;
}
}
// Read the raw gyro data from the IMU
int MPU6050_Gyro_Raw(char axis)
{
int GYRO_XOUT_H, GYRO_XOUT_L;
int GYRO_YOUT_H, GYRO_YOUT_L;
int GYRO_ZOUT_H ,GYRO_ZOUT_L;
/* Read data from gyroscope */
GYRO_XOUT_H = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_GYRO_XOUT_H);
GYRO_XOUT_L = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_GYRO_XOUT_L);
GYRO_YOUT_H = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_GYRO_YOUT_H);
GYRO_YOUT_L = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_GYRO_YOUT_L);
GYRO_ZOUT_H = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_GYRO_ZOUT_H);
GYRO_ZOUT_L = MPU6050_Read(MPU6050_ADDRESS, MPU6050_RA_GYRO_ZOUT_L);
// Convert received data
GYRO_XOUT = ( GYRO_XOUT_H << 8 | GYRO_XOUT_L );
GYRO_YOUT = ( GYRO_YOUT_H << 8 | GYRO_YOUT_L );
GYRO_ZOUT = ( GYRO_ZOUT_H << 8 | GYRO_ZOUT_L );
// Return value to user
switch (axis)
{
case 'X':
return GYRO_XOUT; // Gyro X Axis
break;
case 'Y':
return GYRO_YOUT; // Gyro Y Axis
break;
case 'Z':
return GYRO_ZOUT; // Gyro Z Axis
break;
default:
return 0;
}
}
// Main
void main() {
// Initialize the module at 230400 baud with no parity bit, one stop bit at UART1
UART1_Init(9600);
// Wait for UART module to stabilize
Delay_ms(100);
//Initialize the MPU6050 module with 400Kbps speed on PORTB
I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67);
Delay_ms(1000);
// Let us know everything is OK
UART1_Write_Text("Pre Init Okay");
UART1_Write(13);
// Initialize the MPU6050 module
MPU6050_Init();
info = MPU6050_Test();
if (info == 1)
{
UART1_Write_Text("MPU6050 OK");
UART1_Write(0x0A); //Line Feed LF
UART1_Write(0x0D); //Return Car CR
}
else
{
UART1_Write_Text("MPU6050 Failed");
UART1_Write(0x0A); //Line Feed LF
UART1_Write(0x0D); //Return Car CR
}
// give sensor a second to stabilize
Delay_ms(1000);
// Calculate gyro offset
for (cal_var = 0; cal_var <= 64; cal_var++)
{
gyro_x_offset += MPU6050_Gyro_Raw('X');
gyro_y_offset += MPU6050_Gyro_Raw('Y');
gyro_z_offset += MPU6050_Gyro_Raw('Z');
Delay_ms(1);
}
// Determine gyro offset for 64 read values
gyro_x_offset /= 64;
gyro_y_offset /= 64;
gyro_z_offset /= 64;
while(1)
{
// Read gyro values and convert to Radians per second
gyro_x = (MPU6050_Gyro_Raw('X') - gyro_x_offset) / 939.650784f;
gyro_y = (MPU6050_Gyro_Raw('Y') - gyro_y_offset) / 939.650784f;
gyro_z = (MPU6050_Gyro_Raw('Z') - gyro_z_offset) / 939.650784f;
// Read accel values and convert relative to 1G
acc_x = MPU6050_Read_Accel('X') / 2.0f;
acc_y = MPU6050_Read_Accel('Y') / 2.0f;
acc_z = MPU6050_Read_Accel('Z') / 2.0f;
//////////////////////////////
// Display Accel Values
///////////////////////////////
/*
UART1_Write_Text(" AccelX: ");
sprintf(accel_x_out, "%7.2f", acc_x);
UART1_Write_Text(accel_x_out);
UART1_Write_Text(" AccelY: ");
sprintf(accel_y_out, "%7.2f", acc_y);
UART1_Write_Text(accel_y_out);
UART1_Write_Text(" AccelZ: ");
sprintf(accel_z_out, "%7.2f", acc_z);
UART1_Write_Text(accel_z_out);
UART1_Write(0x0A); //Line Feed LF
UART1_Write(0x0D); //Return Car CR
*/
/////////////////////////////////
// Display Gyro Values
/////////////////////////////////
/*
UART1_Write_Text(" GyroX: ");
sprintf(gyro_x_out, "%7.2f", gyro_x);
UART1_Write_Text(gyro_x_out);
UART1_Write_Text(" GyroY: ");
sprintf(gyro_y_out, "%7.2f", gyro_y);
UART1_Write_Text(gyro_y_out);
UART1_Write_Text(" GyroZ: ");
sprintf(gyro_z_out, "%7.2f", gyro_z);
UART1_Write_Text(gyro_z_out);
UART1_Write(0x0A); //Line Feed LF
UART1_Write(0x0D); //Return Car CR
*/
/////////////////////////////////////
// Apply Madgwick filter on values
////////////////////////////////////
MadgwickAHRSupdateIMU(gyro_x, gyro_y, gyro_z, acc_x, acc_y, acc_z);
///////////////////////////////////////
// Send unit quaternion values as csv
///////////////////////////////////////
sprintf(q3_t, "%7.2f,%7.2f,%7.2f,%7.2f\n", q0, q1, q2,q3);
UART1_Write_Text(q3_t);
Delay_ms(1);
}
}