Commit 8676a13c authored by Zhuoyu Zhang's avatar Zhuoyu Zhang
Browse files

first commit

parent 598fb2e3
/*
* inv_mpu.c
*
* Created on: Jul 11, 2023
* Author: zzy
*/
/*
$License:
Copyright (C) 2011-2012 InvenSense Corporation, All Rights Reserved.
See included License.txt for License information.
$
*/
/**
* @addtogroup DRIVERS Sensor Driver Layer
* @brief Hardware drivers to communicate with sensors via I2C.
*
* @{
* @file inv_mpu.c
* @brief An I2C-based driver for Invensense gyroscopes.
* @details This driver currently works for the following devices:
* MPU6050
* MPU6500
* MPU9150 (or MPU6050 w/ AK8975 on the auxiliary bus)
* MPU9250 (or MPU6500 w/ AK8963 on the auxiliary bus)
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
#include "MPU6050.h"
#include "usart.h"
#define MPU6050 // 定义我们使用的传感器为MPU6050
#define MOTION_DRIVER_TARGET_MSP430 // 定义驱动部分,采用MSP430的驱动(移植到STM32F1)
/* The following functions must be defined for this platform:
* i2c_write(unsigned char slave_addr, unsigned char reg_addr,
* unsigned char length, unsigned char const *data)
* i2c_read(unsigned char slave_addr, unsigned char reg_addr,
* unsigned char length, unsigned char *data)
* delay_ms(unsigned long num_ms)
* get_ms(unsigned long *count)
* reg_int_cb(void (*cb)(void), unsigned char port, unsigned char pin)
* labs(long x)
* fabsf(float x)
* min(int a, int b)
*/
#if defined MOTION_DRIVER_TARGET_MSP430
// #include "msp430.h"
// #include "msp430_i2c.h"
// #include "msp430_clock.h"
// #include "msp430_interrupt.h"
#define i2c_write MPU_Write_Len
#define i2c_read MPU_Read_Len
#define delay_ms delay_ms
#define get_ms mget_ms
// static inline int reg_int_cb(struct int_param_s *int_param)
//{
// return msp430_reg_int_cb(int_param->cb, int_param->pin, int_param->lp_exit,
// int_param->active_low);
// }
#define log_i printf // 打印信息
#define log_e printf // 打印信息
/* labs is already defined by TI's toolchain. */
/* fabs is for doubles. fabsf is for floats. */
#define fabs fabsf
#define min(a, b) ((a < b) ? a : b)
#elif defined EMPL_TARGET_MSP430
#include "msp430.h"
#include "msp430_i2c.h"
#include "msp430_clock.h"
#include "msp430_interrupt.h"
#include "log.h"
#define i2c_write msp430_i2c_write
#define i2c_read msp430_i2c_read
#define delay_ms msp430_delay_ms
#define get_ms msp430_get_clock_ms
static inline int reg_int_cb(struct int_param_s *int_param)
{
return msp430_reg_int_cb(int_param->cb, int_param->pin, int_param->lp_exit,
int_param->active_low);
}
#define log_i MPL_LOGI
#define log_e MPL_LOGE
/* labs is already defined by TI's toolchain. */
/* fabs is for doubles. fabsf is for floats. */
#define fabs fabsf
#define min(a, b) ((a < b) ? a : b)
#elif defined EMPL_TARGET_UC3L0
/* Instead of using the standard TWI driver from the ASF library, we're using
* a TWI driver that follows the slave address + register address convention.
*/
#include "twi.h"
#include "delay.h"
#include "sysclk.h"
#include "log.h"
#include "sensors_xplained.h"
#include "uc3l0_clock.h"
#define i2c_write(a, b, c, d) twi_write(a, b, d, c)
#define i2c_read(a, b, c, d) twi_read(a, b, d, c)
/* delay_ms is a function already defined in ASF. */
#define get_ms uc3l0_get_clock_ms
static inline int reg_int_cb(struct int_param_s *int_param)
{
sensor_board_irq_connect(int_param->pin, int_param->cb, int_param->arg);
return 0;
}
#define log_i MPL_LOGI
#define log_e MPL_LOGE
/* UC3 is a 32-bit processor, so abs and labs are equivalent. */
#define labs abs
#define fabs(x) (((x) > 0) ? (x) : -(x))
#else
#error Gyro driver is missing the system layer implementations.
#endif
#if !defined MPU6050 && !defined MPU9150 && !defined MPU6500 && !defined MPU9250
#error Which gyro are you using? Define MPUxxxx in your compiler options.
#endif
/* Time for some messy macro work. =]
* #define MPU9150
* is equivalent to..
* #define MPU6050
* #define AK8975_SECONDARY
*
* #define MPU9250
* is equivalent to..
* #define MPU6500
* #define AK8963_SECONDARY
*/
#if defined MPU9150
#ifndef MPU6050
#define MPU6050
#endif /* #ifndef MPU6050 */
#if defined AK8963_SECONDARY
#error "MPU9150 and AK8963_SECONDARY cannot both be defined."
#elif !defined AK8975_SECONDARY /* #if defined AK8963_SECONDARY */
#define AK8975_SECONDARY
#endif /* #if defined AK8963_SECONDARY */
#elif defined MPU9250 /* #if defined MPU9150 */
#ifndef MPU6500
#define MPU6500
#endif /* #ifndef MPU6500 */
#if defined AK8975_SECONDARY
#error "MPU9250 and AK8975_SECONDARY cannot both be defined."
#elif !defined AK8963_SECONDARY /* #if defined AK8975_SECONDARY */
#define AK8963_SECONDARY
#endif /* #if defined AK8975_SECONDARY */
#endif /* #if defined MPU9150 */
#if defined AK8975_SECONDARY || defined AK8963_SECONDARY
#define AK89xx_SECONDARY
#else
/* #warning "No compass = less profit for Invensense. Lame." */
#endif
static int set_int_enable(unsigned char enable);
/* Hardware registers needed by driver. */
struct gyro_reg_s
{
unsigned char who_am_i;
unsigned char rate_div;
unsigned char lpf;
unsigned char prod_id;
unsigned char user_ctrl;
unsigned char fifo_en;
unsigned char gyro_cfg;
unsigned char accel_cfg;
// unsigned char accel_cfg2;
// unsigned char lp_accel_odr;
unsigned char motion_thr;
unsigned char motion_dur;
unsigned char fifo_count_h;
unsigned char fifo_r_w;
unsigned char raw_gyro;
unsigned char raw_accel;
unsigned char temp;
unsigned char int_enable;
unsigned char dmp_int_status;
unsigned char int_status;
// unsigned char accel_intel;
unsigned char pwr_mgmt_1;
unsigned char pwr_mgmt_2;
unsigned char int_pin_cfg;
unsigned char mem_r_w;
unsigned char accel_offs;
unsigned char i2c_mst;
unsigned char bank_sel;
unsigned char mem_start_addr;
unsigned char prgm_start_h;
#if defined AK89xx_SECONDARY
unsigned char s0_addr;
unsigned char s0_reg;
unsigned char s0_ctrl;
unsigned char s1_addr;
unsigned char s1_reg;
unsigned char s1_ctrl;
unsigned char s4_ctrl;
unsigned char s0_do;
unsigned char s1_do;
unsigned char i2c_delay_ctrl;
unsigned char raw_compass;
/* The I2C_MST_VDDIO bit is in this register. */
unsigned char yg_offs_tc;
#endif
};
/* Information specific to a particular device. */
struct hw_s
{
unsigned char addr;
unsigned short max_fifo;
unsigned char num_reg;
unsigned short temp_sens;
short temp_offset;
unsigned short bank_size;
#if defined AK89xx_SECONDARY
unsigned short compass_fsr;
#endif
};
/* When entering motion interrupt mode, the driver keeps track of the
* previous state so that it can be restored at a later time.
* TODO: This is tacky. Fix it.
*/
struct motion_int_cache_s
{
unsigned short gyro_fsr;
unsigned char accel_fsr;
unsigned short lpf;
unsigned short sample_rate;
unsigned char sensors_on;
unsigned char fifo_sensors;
unsigned char dmp_on;
};
/* Cached chip configuration data.
* TODO: A lot of these can be handled with a bitmask.
*/
struct chip_cfg_s
{
/* Matches gyro_cfg >> 3 & 0x03 */
unsigned char gyro_fsr;
/* Matches accel_cfg >> 3 & 0x03 */
unsigned char accel_fsr;
/* Enabled sensors. Uses same masks as fifo_en, NOT pwr_mgmt_2. */
unsigned char sensors;
/* Matches config register. */
unsigned char lpf;
unsigned char clk_src;
/* Sample rate, NOT rate divider. */
unsigned short sample_rate;
/* Matches fifo_en register. */
unsigned char fifo_enable;
/* Matches int enable register. */
unsigned char int_enable;
/* 1 if devices on auxiliary I2C bus appear on the primary. */
unsigned char bypass_mode;
/* 1 if half-sensitivity.
* NOTE: This doesn't belong here, but everything else in hw_s is const,
* and this allows us to save some precious RAM.
*/
unsigned char accel_half;
/* 1 if device in low-power accel-only mode. */
unsigned char lp_accel_mode;
/* 1 if interrupts are only triggered on motion events. */
unsigned char int_motion_only;
struct motion_int_cache_s cache;
/* 1 for active low interrupts. */
unsigned char active_low_int;
/* 1 for latched interrupts. */
unsigned char latched_int;
/* 1 if DMP is enabled. */
unsigned char dmp_on;
/* Ensures that DMP will only be loaded once. */
unsigned char dmp_loaded;
/* Sampling rate used when DMP is enabled. */
unsigned short dmp_sample_rate;
#ifdef AK89xx_SECONDARY
/* Compass sample rate. */
unsigned short compass_sample_rate;
unsigned char compass_addr;
short mag_sens_adj[3];
#endif
};
/* Information for self-test. */
struct test_s
{
unsigned long gyro_sens;
unsigned long accel_sens;
unsigned char reg_rate_div;
unsigned char reg_lpf;
unsigned char reg_gyro_fsr;
unsigned char reg_accel_fsr;
unsigned short wait_ms;
unsigned char packet_thresh;
float min_dps;
float max_dps;
float max_gyro_var;
float min_g;
float max_g;
float max_accel_var;
};
/* Gyro driver state variables. */
struct gyro_state_s
{
const struct gyro_reg_s *reg;
const struct hw_s *hw;
struct chip_cfg_s chip_cfg;
const struct test_s *test;
};
/* Filter configurations. */
enum lpf_e
{
INV_FILTER_256HZ_NOLPF2 = 0,
INV_FILTER_188HZ,
INV_FILTER_98HZ,
INV_FILTER_42HZ,
INV_FILTER_20HZ,
INV_FILTER_10HZ,
INV_FILTER_5HZ,
INV_FILTER_2100HZ_NOLPF,
NUM_FILTER
};
/* Full scale ranges. */
enum gyro_fsr_e
{
INV_FSR_250DPS = 0,
INV_FSR_500DPS,
INV_FSR_1000DPS,
INV_FSR_2000DPS,
NUM_GYRO_FSR
};
/* Full scale ranges. */
enum accel_fsr_e
{
INV_FSR_2G = 0,
INV_FSR_4G,
INV_FSR_8G,
INV_FSR_16G,
NUM_ACCEL_FSR
};
/* Clock sources. */
enum clock_sel_e
{
INV_CLK_INTERNAL = 0,
INV_CLK_PLL,
NUM_CLK
};
/* Low-power accel wakeup rates. */
enum lp_accel_rate_e
{
#if defined MPU6050
INV_LPA_1_25HZ,
INV_LPA_5HZ,
INV_LPA_20HZ,
INV_LPA_40HZ
#elif defined MPU6500
INV_LPA_0_3125HZ,
INV_LPA_0_625HZ,
INV_LPA_1_25HZ,
INV_LPA_2_5HZ,
INV_LPA_5HZ,
INV_LPA_10HZ,
INV_LPA_20HZ,
INV_LPA_40HZ,
INV_LPA_80HZ,
INV_LPA_160HZ,
INV_LPA_320HZ,
INV_LPA_640HZ
#endif
};
#define BIT_I2C_MST_VDDIO (0x80)
#define BIT_FIFO_EN (0x40)
#define BIT_DMP_EN (0x80)
#define BIT_FIFO_RST (0x04)
#define BIT_DMP_RST (0x08)
#define BIT_FIFO_OVERFLOW (0x10)
#define BIT_DATA_RDY_EN (0x01)
#define BIT_DMP_INT_EN (0x02)
#define BIT_MOT_INT_EN (0x40)
#define BITS_FSR (0x18)
#define BITS_LPF (0x07)
#define BITS_HPF (0x07)
#define BITS_CLK (0x07)
#define BIT_FIFO_SIZE_1024 (0x40)
#define BIT_FIFO_SIZE_2048 (0x80)
#define BIT_FIFO_SIZE_4096 (0xC0)
#define BIT_RESET (0x80)
#define BIT_SLEEP (0x40)
#define BIT_S0_DELAY_EN (0x01)
#define BIT_S2_DELAY_EN (0x04)
#define BITS_SLAVE_LENGTH (0x0F)
#define BIT_SLAVE_BYTE_SW (0x40)
#define BIT_SLAVE_GROUP (0x10)
#define BIT_SLAVE_EN (0x80)
#define BIT_I2C_READ (0x80)
#define BITS_I2C_MASTER_DLY (0x1F)
#define BIT_AUX_IF_EN (0x20)
#define BIT_ACTL (0x80)
#define BIT_LATCH_EN (0x20)
#define BIT_ANY_RD_CLR (0x10)
#define BIT_BYPASS_EN (0x02)
#define BITS_WOM_EN (0xC0)
#define BIT_LPA_CYCLE (0x20)
#define BIT_STBY_XA (0x20)
#define BIT_STBY_YA (0x10)
#define BIT_STBY_ZA (0x08)
#define BIT_STBY_XG (0x04)
#define BIT_STBY_YG (0x02)
#define BIT_STBY_ZG (0x01)
#define BIT_STBY_XYZA (BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA)
#define BIT_STBY_XYZG (BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)
#if defined AK8975_SECONDARY
#define SUPPORTS_AK89xx_HIGH_SENS (0x00)
#define AK89xx_FSR (9830)
#elif defined AK8963_SECONDARY
#define SUPPORTS_AK89xx_HIGH_SENS (0x10)
#define AK89xx_FSR (4915)
#endif
#ifdef AK89xx_SECONDARY
#define AKM_REG_WHOAMI (0x00)
#define AKM_REG_ST1 (0x02)
#define AKM_REG_HXL (0x03)
#define AKM_REG_ST2 (0x09)
#define AKM_REG_CNTL (0x0A)
#define AKM_REG_ASTC (0x0C)
#define AKM_REG_ASAX (0x10)
#define AKM_REG_ASAY (0x11)
#define AKM_REG_ASAZ (0x12)
#define AKM_DATA_READY (0x01)
#define AKM_DATA_OVERRUN (0x02)
#define AKM_OVERFLOW (0x80)
#define AKM_DATA_ERROR (0x40)
#define AKM_BIT_SELF_TEST (0x40)
#define AKM_POWER_DOWN (0x00 | SUPPORTS_AK89xx_HIGH_SENS)
#define AKM_SINGLE_MEASUREMENT (0x01 | SUPPORTS_AK89xx_HIGH_SENS)
#define AKM_FUSE_ROM_ACCESS (0x0F | SUPPORTS_AK89xx_HIGH_SENS)
#define AKM_MODE_SELF_TEST (0x08 | SUPPORTS_AK89xx_HIGH_SENS)
#define AKM_WHOAMI (0x48)
#endif
#if defined MPU6050
// const struct gyro_reg_s reg = {
// .who_am_i = 0x75,
// .rate_div = 0x19,
// .lpf = 0x1A,
// .prod_id = 0x0C,
// .user_ctrl = 0x6A,
// .fifo_en = 0x23,
// .gyro_cfg = 0x1B,
// .accel_cfg = 0x1C,
// .motion_thr = 0x1F,
// .motion_dur = 0x20,
// .fifo_count_h = 0x72,
// .fifo_r_w = 0x74,
// .raw_gyro = 0x43,
// .raw_accel = 0x3B,
// .temp = 0x41,
// .int_enable = 0x38,
// .dmp_int_status = 0x39,
// .int_status = 0x3A,
// .pwr_mgmt_1 = 0x6B,
// .pwr_mgmt_2 = 0x6C,
// .int_pin_cfg = 0x37,
// .mem_r_w = 0x6F,
// .accel_offs = 0x06,
// .i2c_mst = 0x24,
// .bank_sel = 0x6D,
// .mem_start_addr = 0x6E,
// .prgm_start_h = 0x70
// #ifdef AK89xx_SECONDARY
// ,.raw_compass = 0x49,
// .yg_offs_tc = 0x01,
// .s0_addr = 0x25,
// .s0_reg = 0x26,
// .s0_ctrl = 0x27,
// .s1_addr = 0x28,
// .s1_reg = 0x29,
// .s1_ctrl = 0x2A,
// .s4_ctrl = 0x34,
// .s0_do = 0x63,
// .s1_do = 0x64,
// .i2c_delay_ctrl = 0x67
// #endif
// };
const struct gyro_reg_s reg = {
0x75, // who_am_i
0x19, // rate_div
0x1A, // lpf
0x0C, // prod_id
0x6A, // user_ctrl
0x23, // fifo_en
0x1B, // gyro_cfg
0x1C, // accel_cfg
0x1F, // motion_thr
0x20, // motion_dur
0x72, // fifo_count_h
0x74, // fifo_r_w
0x43, // raw_gyro
0x3B, // raw_accel
0x41, // temp
0x38, // int_enable
0x39, // dmp_int_status
0x3A, // int_status
0x6B, // pwr_mgmt_1
0x6C, // pwr_mgmt_2
0x37, // int_pin_cfg
0x6F, // mem_r_w
0x06, // accel_offs
0x24, // i2c_mst
0x6D, // bank_sel
0x6E, // mem_start_addr
0x70 // prgm_start_h
};
// const struct hw_s hw = {
// .addr = 0x68,
// .max_fifo = 1024,
// .num_reg = 118,
// .temp_sens = 340,
// .temp_offset = -521,
// .bank_size = 256
// #if defined AK89xx_SECONDARY
// ,.compass_fsr = AK89xx_FSR
// #endif
// };
const struct hw_s hw = {
0x68, // addr
1024, // max_fifo
118, // num_reg
340, // temp_sens
-521, // temp_offset
256 // bank_size
};
// const struct test_s test = {
// .gyro_sens = 32768/250,
// .accel_sens = 32768/16,
// .reg_rate_div = 0, /* 1kHz. */
// .reg_lpf = 1, /* 188Hz. */
// .reg_gyro_fsr = 0, /* 250dps. */
// .reg_accel_fsr = 0x18, /* 16g. */
// .wait_ms = 50,
// .packet_thresh = 5, /* 5% */
// .min_dps = 10.f,
// .max_dps = 105.f,
// .max_gyro_var = 0.14f,
// .min_g = 0.3f,
// .max_g = 0.95f,
// .max_accel_var = 0.14f
// };
const struct test_s test = {
32768 / 250, // gyro_sens
32768 / 16, // accel_sens
0, // reg_rate_div
1, // reg_lpf
0, // reg_gyro_fsr
0x18, // reg_accel_fsr
50, // wait_ms
5, // packet_thresh
10.0f, // min_dps
105.0f, // max_dps
0.14f, // max_gyro_var
0.3f, // min_g
0.95f, // max_g
0.14f // max_accel_var
};
// static struct gyro_state_s st = {
// .reg = &reg,
// .hw = &hw,
// .test = &test
// };
static struct gyro_state_s st = {
&reg,
&hw,
{0},
&test};
#elif defined MPU6500
const struct gyro_reg_s reg = {
.who_am_i = 0x75,
.rate_div = 0x19,
.lpf = 0x1A,
.prod_id = 0x0C,
.user_ctrl = 0x6A,
.fifo_en = 0x23,
.gyro_cfg = 0x1B,
.accel_cfg = 0x1C,
.accel_cfg2 = 0x1D,
.lp_accel_odr = 0x1E,
.motion_thr = 0x1F,
.motion_dur = 0x20,
.fifo_count_h = 0x72,
.fifo_r_w = 0x74,
.raw_gyro = 0x43,
.raw_accel = 0x3B,
.temp = 0x41,
.int_enable = 0x38,
.dmp_int_status = 0x39,
.int_status = 0x3A,
.accel_intel = 0x69,
.pwr_mgmt_1 = 0x6B,
.pwr_mgmt_2 = 0x6C,
.int_pin_cfg = 0x37,
.mem_r_w = 0x6F,
.accel_offs = 0x77,
.i2c_mst = 0x24,
.bank_sel = 0x6D,
.mem_start_addr = 0x6E,
.prgm_start_h = 0x70
#ifdef AK89xx_SECONDARY
,
.raw_compass = 0x49,
.s0_addr = 0x25,
.s0_reg = 0x26,
.s0_ctrl = 0x27,
.s1_addr = 0x28,
.s1_reg = 0x29,
.s1_ctrl = 0x2A,
.s4_ctrl = 0x34,
.s0_do = 0x63,
.s1_do = 0x64,
.i2c_delay_ctrl = 0x67
#endif
};
const struct hw_s hw = {
.addr = 0x68,
.max_fifo = 1024,
.num_reg = 128,
.temp_sens = 321,
.temp_offset = 0,
.bank_size = 256
#if defined AK89xx_SECONDARY
,
.compass_fsr = AK89xx_FSR
#endif
};
const struct test_s test = {
.gyro_sens = 32768 / 250,
.accel_sens = 32768 / 16,
.reg_rate_div = 0, /* 1kHz. */
.reg_lpf = 1, /* 188Hz. */
.reg_gyro_fsr = 0, /* 250dps. */
.reg_accel_fsr = 0x18, /* 16g. */
.wait_ms = 50,
.packet_thresh = 5, /* 5% */
.min_dps = 10.f,
.max_dps = 105.f,
.max_gyro_var = 0.14f,
.min_g = 0.3f,
.max_g = 0.95f,
.max_accel_var = 0.14f};
static struct gyro_state_s st = {
.reg = &reg,
.hw = &hw,
.test = &test};
#endif
#define MAX_PACKET_LENGTH (12)
#ifdef AK89xx_SECONDARY
static int setup_compass(void);
#define MAX_COMPASS_SAMPLE_RATE (100)
#endif
/**
* @brief Enable/disable data ready interrupt.
* If the DMP is on, the DMP interrupt is enabled. Otherwise, the data ready
* interrupt is used.
* @param[in] enable 1 to enable interrupt.
* @return 0 if successful.
*/
static int set_int_enable(unsigned char enable)
{
unsigned char tmp;
if (st.chip_cfg.dmp_on)
{
if (enable)
tmp = BIT_DMP_INT_EN;
else
tmp = 0x00;
if (i2c_write(st.hw->addr, st.reg->int_enable, 1, &tmp))
return -1;
st.chip_cfg.int_enable = tmp;
}
else
{
if (!st.chip_cfg.sensors)
return -1;
if (enable && st.chip_cfg.int_enable)
return 0;
if (enable)
tmp = BIT_DATA_RDY_EN;
else
tmp = 0x00;
if (i2c_write(st.hw->addr, st.reg->int_enable, 1, &tmp))
return -1;
st.chip_cfg.int_enable = tmp;
}
return 0;
}
/**
* @brief Register dump for testing.
* @return 0 if successful.
*/
int mpu_reg_dump(void)
{
unsigned char ii;
unsigned char data;
for (ii = 0; ii < st.hw->num_reg; ii++)
{
if (ii == st.reg->fifo_r_w || ii == st.reg->mem_r_w)
continue;
if (i2c_read(st.hw->addr, ii, 1, &data))
return -1;
log_i("%#5x: %#5x\r\n", ii, data);
}
return 0;
}
/**
* @brief Read from a single register.
* NOTE: The memory and FIFO read/write registers cannot be accessed.
* @param[in] reg Register address.
* @param[out] data Register data.
* @return 0 if successful.
*/
int mpu_read_reg(unsigned char reg, unsigned char *data)
{
if (reg == st.reg->fifo_r_w || reg == st.reg->mem_r_w)
return -1;
if (reg >= st.hw->num_reg)
return -1;
return i2c_read(st.hw->addr, reg, 1, data);
}
/**
* @brief Initialize hardware.
* Initial configuration:\n
* Gyro FSR: +/- 2000DPS\n
* Accel FSR +/- 2G\n
* DLPF: 42Hz\n
* FIFO rate: 50Hz\n
* Clock source: Gyro PLL\n
* FIFO: Disabled.\n
* Data ready interrupt: Disabled, active low, unlatched.
* @param[in] int_param Platform-specific parameters to interrupt API.
* @return 0 if successful.
*/
int mpu_init(void)
{
unsigned char data[6], rev;
/* Reset device. */
data[0] = BIT_RESET;
if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 1, data))
return -1;
HAL_Delay(100);
/* Wake up chip. */
data[0] = 0x00;
if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 1, data))
return -1;
#if defined MPU6050
/* Check product revision. */
if (i2c_read(st.hw->addr, st.reg->accel_offs, 6, data))
return -1;
rev = ((data[5] & 0x01) << 2) | ((data[3] & 0x01) << 1) |
(data[1] & 0x01);
if (rev)
{
/* Congrats, these parts are better. */
if (rev == 1)
st.chip_cfg.accel_half = 1;
else if (rev == 2)
st.chip_cfg.accel_half = 0;
else
{
log_e("Unsupported software product rev %d.\n", rev);
return -1;
}
}
else
{
if (i2c_read(st.hw->addr, st.reg->prod_id, 1, data))
return -1;
rev = data[0] & 0x0F;
if (!rev)
{
log_e("Product ID read as 0 indicates device is either "
"incompatible or an MPU3050.\n");
return -1;
}
else if (rev == 4)
{
log_i("Half sensitivity part found.\n");
st.chip_cfg.accel_half = 1;
}
else
st.chip_cfg.accel_half = 0;
}
#elif defined MPU6500
#define MPU6500_MEM_REV_ADDR (0x17)
if (mpu_read_mem(MPU6500_MEM_REV_ADDR, 1, &rev))
return -1;
if (rev == 0x1)
st.chip_cfg.accel_half = 0;
else
{
log_e("Unsupported software product rev %d.\n", rev);
return -1;
}
/* MPU6500 shares 4kB of memory between the DMP and the FIFO. Since the
* first 3kB are needed by the DMP, we'll use the last 1kB for the FIFO.
*/
data[0] = BIT_FIFO_SIZE_1024 | 0x8;
if (i2c_write(st.hw->addr, st.reg->accel_cfg2, 1, data))
return -1;
#endif
/* Set to invalid values to ensure no I2C writes are skipped. */
st.chip_cfg.sensors = 0xFF;
st.chip_cfg.gyro_fsr = 0xFF;
st.chip_cfg.accel_fsr = 0xFF;
st.chip_cfg.lpf = 0xFF;
st.chip_cfg.sample_rate = 0xFFFF;
st.chip_cfg.fifo_enable = 0xFF;
st.chip_cfg.bypass_mode = 0xFF;
#ifdef AK89xx_SECONDARY
st.chip_cfg.compass_sample_rate = 0xFFFF;
#endif
/* mpu_set_sensors always preserves this setting. */
st.chip_cfg.clk_src = INV_CLK_PLL;
/* Handled in next call to mpu_set_bypass. */
st.chip_cfg.active_low_int = 1;
st.chip_cfg.latched_int = 0;
st.chip_cfg.int_motion_only = 0;
st.chip_cfg.lp_accel_mode = 0;
memset(&st.chip_cfg.cache, 0, sizeof(st.chip_cfg.cache));
st.chip_cfg.dmp_on = 0;
st.chip_cfg.dmp_loaded = 0;
st.chip_cfg.dmp_sample_rate = 0;
if (mpu_set_gyro_fsr(2000))
return -1;
if (mpu_set_accel_fsr(2))
return -1;
if (mpu_set_lpf(42))
return -1;
if (mpu_set_sample_rate(50))
return -1;
if (mpu_configure_fifo(0))
return -1;
// if (int_param)
// reg_int_cb(int_param);
#ifdef AK89xx_SECONDARY
setup_compass();
if (mpu_set_compass_sample_rate(10))
return -1;
#else
/* Already disabled by setup_compass. */
if (mpu_set_bypass(0))
return -1;
#endif
mpu_set_sensors(0);
return 0;
}
/**
* @brief Enter low-power accel-only mode.
* In low-power accel mode, the chip goes to sleep and only wakes up to sample
* the accelerometer at one of the following frequencies:
* \n MPU6050: 1.25Hz, 5Hz, 20Hz, 40Hz
* \n MPU6500: 1.25Hz, 2.5Hz, 5Hz, 10Hz, 20Hz, 40Hz, 80Hz, 160Hz, 320Hz, 640Hz
* \n If the requested rate is not one listed above, the device will be set to
* the next highest rate. Requesting a rate above the maximum supported
* frequency will result in an error.
* \n To select a fractional wake-up frequency, round down the value passed to
* @e rate.
* @param[in] rate Minimum sampling rate, or zero to disable LP
* accel mode.
* @return 0 if successful.
*/
int mpu_lp_accel_mode(unsigned char rate)
{
unsigned char tmp[2];
if (rate > 40)
return -1;
if (!rate)
{
mpu_set_int_latched(0);
tmp[0] = 0;
tmp[1] = BIT_STBY_XYZG;
if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 2, tmp))
return -1;
st.chip_cfg.lp_accel_mode = 0;
return 0;
}
/* For LP accel, we automatically configure the hardware to produce latched
* interrupts. In LP accel mode, the hardware cycles into sleep mode before
* it gets a chance to deassert the interrupt pin; therefore, we shift this
* responsibility over to the MCU.
*
* Any register read will clear the interrupt.
*/
mpu_set_int_latched(1);
#if defined MPU6050
tmp[0] = BIT_LPA_CYCLE;
if (rate == 1)
{
tmp[1] = INV_LPA_1_25HZ;
mpu_set_lpf(5);
}
else if (rate <= 5)
{
tmp[1] = INV_LPA_5HZ;
mpu_set_lpf(5);
}
else if (rate <= 20)
{
tmp[1] = INV_LPA_20HZ;
mpu_set_lpf(10);
}
else
{
tmp[1] = INV_LPA_40HZ;
mpu_set_lpf(20);
}
tmp[1] = (tmp[1] << 6) | BIT_STBY_XYZG;
if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 2, tmp))
return -1;
#elif defined MPU6500
/* Set wake frequency. */
if (rate == 1)
tmp[0] = INV_LPA_1_25HZ;
else if (rate == 2)
tmp[0] = INV_LPA_2_5HZ;
else if (rate <= 5)
tmp[0] = INV_LPA_5HZ;
else if (rate <= 10)
tmp[0] = INV_LPA_10HZ;
else if (rate <= 20)
tmp[0] = INV_LPA_20HZ;
else if (rate <= 40)
tmp[0] = INV_LPA_40HZ;
else if (rate <= 80)
tmp[0] = INV_LPA_80HZ;
else if (rate <= 160)
tmp[0] = INV_LPA_160HZ;
else if (rate <= 320)
tmp[0] = INV_LPA_320HZ;
else
tmp[0] = INV_LPA_640HZ;
if (i2c_write(st.hw->addr, st.reg->lp_accel_odr, 1, tmp))
return -1;
tmp[0] = BIT_LPA_CYCLE;
if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 1, tmp))
return -1;
#endif
st.chip_cfg.sensors = INV_XYZ_ACCEL;
st.chip_cfg.clk_src = 0;
st.chip_cfg.lp_accel_mode = 1;
mpu_configure_fifo(0);
return 0;
}
/**
* @brief Read raw gyro data directly from the registers.
* @param[out] data Raw data in hardware units.
* @param[out] timestamp Timestamp in milliseconds. Null if not needed.
* @return 0 if successful.
*/
int mpu_get_gyro_reg(short *data, unsigned long *timestamp)
{
unsigned char tmp[6];
if (!(st.chip_cfg.sensors & INV_XYZ_GYRO))
return -1;
if (i2c_read(st.hw->addr, st.reg->raw_gyro, 6, tmp))
return -1;
data[0] = (tmp[0] << 8) | tmp[1];
data[1] = (tmp[2] << 8) | tmp[3];
data[2] = (tmp[4] << 8) | tmp[5];
if (timestamp)
get_ms(timestamp);
return 0;
}
/**
* @brief Read raw accel data directly from the registers.
* @param[out] data Raw data in hardware units.
* @param[out] timestamp Timestamp in milliseconds. Null if not needed.
* @return 0 if successful.
*/
int mpu_get_accel_reg(short *data, unsigned long *timestamp)
{
unsigned char tmp[6];
if (!(st.chip_cfg.sensors & INV_XYZ_ACCEL))
return -1;
if (i2c_read(st.hw->addr, st.reg->raw_accel, 6, tmp))
return -1;
data[0] = (tmp[0] << 8) | tmp[1];
data[1] = (tmp[2] << 8) | tmp[3];
data[2] = (tmp[4] << 8) | tmp[5];
if (timestamp)
get_ms(timestamp);
return 0;
}
/**
* @brief Read temperature data directly from the registers.
* @param[out] data Data in q16 format.
* @param[out] timestamp Timestamp in milliseconds. Null if not needed.
* @return 0 if successful.
*/
int mpu_get_temperature(long *data, unsigned long *timestamp)
{
unsigned char tmp[2];
short raw;
if (!(st.chip_cfg.sensors))
return -1;
if (i2c_read(st.hw->addr, st.reg->temp, 2, tmp))
return -1;
raw = (tmp[0] << 8) | tmp[1];
if (timestamp)
get_ms(timestamp);
data[0] = (long)((35 + ((raw - (float)st.hw->temp_offset) / st.hw->temp_sens)) * 65536L);
return 0;
}
/**
* @brief Push biases to the accel bias registers.
* This function expects biases relative to the current sensor output, and
* these biases will be added to the factory-supplied values.
* @param[in] accel_bias New biases.
* @return 0 if successful.
*/
int mpu_set_accel_bias(const long *accel_bias)
{
unsigned char data[6];
short accel_hw[3];
short got_accel[3];
short fg[3];
if (!accel_bias)
return -1;
if (!accel_bias[0] && !accel_bias[1] && !accel_bias[2])
return 0;
if (i2c_read(st.hw->addr, 3, 3, data))
return -1;
fg[0] = ((data[0] >> 4) + 8) & 0xf;
fg[1] = ((data[1] >> 4) + 8) & 0xf;
fg[2] = ((data[2] >> 4) + 8) & 0xf;
accel_hw[0] = (short)(accel_bias[0] * 2 / (64 + fg[0]));
accel_hw[1] = (short)(accel_bias[1] * 2 / (64 + fg[1]));
accel_hw[2] = (short)(accel_bias[2] * 2 / (64 + fg[2]));
if (i2c_read(st.hw->addr, 0x06, 6, data))
return -1;
got_accel[0] = ((short)data[0] << 8) | data[1];
got_accel[1] = ((short)data[2] << 8) | data[3];
got_accel[2] = ((short)data[4] << 8) | data[5];
accel_hw[0] += got_accel[0];
accel_hw[1] += got_accel[1];
accel_hw[2] += got_accel[2];
data[0] = (accel_hw[0] >> 8) & 0xff;
data[1] = (accel_hw[0]) & 0xff;
data[2] = (accel_hw[1] >> 8) & 0xff;
data[3] = (accel_hw[1]) & 0xff;
data[4] = (accel_hw[2] >> 8) & 0xff;
data[5] = (accel_hw[2]) & 0xff;
if (i2c_write(st.hw->addr, 0x06, 6, data))
return -1;
return 0;
}
/**
* @brief Reset FIFO read/write pointers.
* @return 0 if successful.
*/
int mpu_reset_fifo(void)
{
unsigned char data;
if (!(st.chip_cfg.sensors))
return -1;
data = 0;
if (i2c_write(st.hw->addr, st.reg->int_enable, 1, &data))
return -1;
if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, &data))
return -1;
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &data))
return -1;
if (st.chip_cfg.dmp_on)
{
data = BIT_FIFO_RST | BIT_DMP_RST;
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &data))
return -1;
HAL_Delay(50);
data = BIT_DMP_EN | BIT_FIFO_EN;
if (st.chip_cfg.sensors & INV_XYZ_COMPASS)
data |= BIT_AUX_IF_EN;
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &data))
return -1;
if (st.chip_cfg.int_enable)
data = BIT_DMP_INT_EN;
else
data = 0;
if (i2c_write(st.hw->addr, st.reg->int_enable, 1, &data))
return -1;
data = 0;
if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, &data))
return -1;
}
else
{
data = BIT_FIFO_RST;
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &data))
return -1;
if (st.chip_cfg.bypass_mode || !(st.chip_cfg.sensors & INV_XYZ_COMPASS))
data = BIT_FIFO_EN;
else
data = BIT_FIFO_EN | BIT_AUX_IF_EN;
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &data))
return -1;
HAL_Delay(50);
if (st.chip_cfg.int_enable)
data = BIT_DATA_RDY_EN;
else
data = 0;
if (i2c_write(st.hw->addr, st.reg->int_enable, 1, &data))
return -1;
if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, &st.chip_cfg.fifo_enable))
return -1;
}
return 0;
}
/**
* @brief Get the gyro full-scale range.
* @param[out] fsr Current full-scale range.
* @return 0 if successful.
*/
int mpu_get_gyro_fsr(unsigned short *fsr)
{
switch (st.chip_cfg.gyro_fsr)
{
case INV_FSR_250DPS:
fsr[0] = 250;
break;
case INV_FSR_500DPS:
fsr[0] = 500;
break;
case INV_FSR_1000DPS:
fsr[0] = 1000;
break;
case INV_FSR_2000DPS:
fsr[0] = 2000;
break;
default:
fsr[0] = 0;
break;
}
return 0;
}
/**
* @brief Set the gyro full-scale range.
* @param[in] fsr Desired full-scale range.
* @return 0 if successful.
*/
int mpu_set_gyro_fsr(unsigned short fsr)
{
unsigned char data;
if (!(st.chip_cfg.sensors))
return -1;
switch (fsr)
{
case 250:
data = INV_FSR_250DPS << 3;
break;
case 500:
data = INV_FSR_500DPS << 3;
break;
case 1000:
data = INV_FSR_1000DPS << 3;
break;
case 2000:
data = INV_FSR_2000DPS << 3;
break;
default:
return -1;
}
if (st.chip_cfg.gyro_fsr == (data >> 3))
return 0;
if (i2c_write(st.hw->addr, st.reg->gyro_cfg, 1, &data))
return -1;
st.chip_cfg.gyro_fsr = data >> 3;
return 0;
}
/**
* @brief Get the accel full-scale range.
* @param[out] fsr Current full-scale range.
* @return 0 if successful.
*/
int mpu_get_accel_fsr(unsigned char *fsr)
{
switch (st.chip_cfg.accel_fsr)
{
case INV_FSR_2G:
fsr[0] = 2;
break;
case INV_FSR_4G:
fsr[0] = 4;
break;
case INV_FSR_8G:
fsr[0] = 8;
break;
case INV_FSR_16G:
fsr[0] = 16;
break;
default:
return -1;
}
if (st.chip_cfg.accel_half)
fsr[0] <<= 1;
return 0;
}
/**
* @brief Set the accel full-scale range.
* @param[in] fsr Desired full-scale range.
* @return 0 if successful.
*/
int mpu_set_accel_fsr(unsigned char fsr)
{
unsigned char data;
if (!(st.chip_cfg.sensors))
return -1;
switch (fsr)
{
case 2:
data = INV_FSR_2G << 3;
break;
case 4:
data = INV_FSR_4G << 3;
break;
case 8:
data = INV_FSR_8G << 3;
break;
case 16:
data = INV_FSR_16G << 3;
break;
default:
return -1;
}
if (st.chip_cfg.accel_fsr == (data >> 3))
return 0;
if (i2c_write(st.hw->addr, st.reg->accel_cfg, 1, &data))
return -1;
st.chip_cfg.accel_fsr = data >> 3;
return 0;
}
/**
* @brief Get the current DLPF setting.
* @param[out] lpf Current LPF setting.
* 0 if successful.
*/
int mpu_get_lpf(unsigned short *lpf)
{
switch (st.chip_cfg.lpf)
{
case INV_FILTER_188HZ:
lpf[0] = 188;
break;
case INV_FILTER_98HZ:
lpf[0] = 98;
break;
case INV_FILTER_42HZ:
lpf[0] = 42;
break;
case INV_FILTER_20HZ:
lpf[0] = 20;
break;
case INV_FILTER_10HZ:
lpf[0] = 10;
break;
case INV_FILTER_5HZ:
lpf[0] = 5;
break;
case INV_FILTER_256HZ_NOLPF2:
case INV_FILTER_2100HZ_NOLPF:
default:
lpf[0] = 0;
break;
}
return 0;
}
/**
* @brief Set digital low pass filter.
* The following LPF settings are supported: 188, 98, 42, 20, 10, 5.
* @param[in] lpf Desired LPF setting.
* @return 0 if successful.
*/
int mpu_set_lpf(unsigned short lpf)
{
unsigned char data;
if (!(st.chip_cfg.sensors))
return -1;
if (lpf >= 188)
data = INV_FILTER_188HZ;
else if (lpf >= 98)
data = INV_FILTER_98HZ;
else if (lpf >= 42)
data = INV_FILTER_42HZ;
else if (lpf >= 20)
data = INV_FILTER_20HZ;
else if (lpf >= 10)
data = INV_FILTER_10HZ;
else
data = INV_FILTER_5HZ;
if (st.chip_cfg.lpf == data)
return 0;
if (i2c_write(st.hw->addr, st.reg->lpf, 1, &data))
return -1;
st.chip_cfg.lpf = data;
return 0;
}
/**
* @brief Get sampling rate.
* @param[out] rate Current sampling rate (Hz).
* @return 0 if successful.
*/
int mpu_get_sample_rate(unsigned short *rate)
{
if (st.chip_cfg.dmp_on)
return -1;
else
rate[0] = st.chip_cfg.sample_rate;
return 0;
}
/**
* @brief Set sampling rate.
* Sampling rate must be between 4Hz and 1kHz.
* @param[in] rate Desired sampling rate (Hz).
* @return 0 if successful.
*/
int mpu_set_sample_rate(unsigned short rate)
{
unsigned char data;
if (!(st.chip_cfg.sensors))
return -1;
if (st.chip_cfg.dmp_on)
return -1;
else
{
if (st.chip_cfg.lp_accel_mode)
{
if (rate && (rate <= 40))
{
/* Just stay in low-power accel mode. */
mpu_lp_accel_mode(rate);
return 0;
}
/* Requested rate exceeds the allowed frequencies in LP accel mode,
* switch back to full-power mode.
*/
mpu_lp_accel_mode(0);
}
if (rate < 4)
rate = 4;
else if (rate > 1000)
rate = 1000;
data = 1000 / rate - 1;
if (i2c_write(st.hw->addr, st.reg->rate_div, 1, &data))
return -1;
st.chip_cfg.sample_rate = 1000 / (1 + data);
#ifdef AK89xx_SECONDARY
mpu_set_compass_sample_rate(min(st.chip_cfg.compass_sample_rate, MAX_COMPASS_SAMPLE_RATE));
#endif
/* Automatically set LPF to 1/2 sampling rate. */
mpu_set_lpf(st.chip_cfg.sample_rate >> 1);
return 0;
}
}
/**
* @brief Get compass sampling rate.
* @param[out] rate Current compass sampling rate (Hz).
* @return 0 if successful.
*/
int mpu_get_compass_sample_rate(unsigned short *rate)
{
#ifdef AK89xx_SECONDARY
rate[0] = st.chip_cfg.compass_sample_rate;
return 0;
#else
rate[0] = 0;
return -1;
#endif
}
/**
* @brief Set compass sampling rate.
* The compass on the auxiliary I2C bus is read by the MPU hardware at a
* maximum of 100Hz. The actual rate can be set to a fraction of the gyro
* sampling rate.
*
* \n WARNING: The new rate may be different than what was requested. Call
* mpu_get_compass_sample_rate to check the actual setting.
* @param[in] rate Desired compass sampling rate (Hz).
* @return 0 if successful.
*/
int mpu_set_compass_sample_rate(unsigned short rate)
{
#ifdef AK89xx_SECONDARY
unsigned char div;
if (!rate || rate > st.chip_cfg.sample_rate || rate > MAX_COMPASS_SAMPLE_RATE)
return -1;
div = st.chip_cfg.sample_rate / rate - 1;
if (i2c_write(st.hw->addr, st.reg->s4_ctrl, 1, &div))
return -1;
st.chip_cfg.compass_sample_rate = st.chip_cfg.sample_rate / (div + 1);
return 0;
#else
return -1;
#endif
}
/**
* @brief Get gyro sensitivity scale factor.
* @param[out] sens Conversion from hardware units to dps.
* @return 0 if successful.
*/
int mpu_get_gyro_sens(float *sens)
{
switch (st.chip_cfg.gyro_fsr)
{
case INV_FSR_250DPS:
sens[0] = 131.f;
break;
case INV_FSR_500DPS:
sens[0] = 65.5f;
break;
case INV_FSR_1000DPS:
sens[0] = 32.8f;
break;
case INV_FSR_2000DPS:
sens[0] = 16.4f;
break;
default:
return -1;
}
return 0;
}
/**
* @brief Get accel sensitivity scale factor.
* @param[out] sens Conversion from hardware units to g's.
* @return 0 if successful.
*/
int mpu_get_accel_sens(unsigned short *sens)
{
switch (st.chip_cfg.accel_fsr)
{
case INV_FSR_2G:
sens[0] = 16384;
break;
case INV_FSR_4G:
sens[0] = 8092;
break;
case INV_FSR_8G:
sens[0] = 4096;
break;
case INV_FSR_16G:
sens[0] = 2048;
break;
default:
return -1;
}
if (st.chip_cfg.accel_half)
sens[0] >>= 1;
return 0;
}
/**
* @brief Get current FIFO configuration.
* @e sensors can contain a combination of the following flags:
* \n INV_X_GYRO, INV_Y_GYRO, INV_Z_GYRO
* \n INV_XYZ_GYRO
* \n INV_XYZ_ACCEL
* @param[out] sensors Mask of sensors in FIFO.
* @return 0 if successful.
*/
int mpu_get_fifo_config(unsigned char *sensors)
{
sensors[0] = st.chip_cfg.fifo_enable;
return 0;
}
/**
* @brief Select which sensors are pushed to FIFO.
* @e sensors can contain a combination of the following flags:
* \n INV_X_GYRO, INV_Y_GYRO, INV_Z_GYRO
* \n INV_XYZ_GYRO
* \n INV_XYZ_ACCEL
* @param[in] sensors Mask of sensors to push to FIFO.
* @return 0 if successful.
*/
int mpu_configure_fifo(unsigned char sensors)
{
unsigned char prev;
int result = 0;
/* Compass data isn't going into the FIFO. Stop trying. */
sensors &= ~INV_XYZ_COMPASS;
if (st.chip_cfg.dmp_on)
return 0;
else
{
if (!(st.chip_cfg.sensors))
return -1;
prev = st.chip_cfg.fifo_enable;
st.chip_cfg.fifo_enable = sensors & st.chip_cfg.sensors;
if (st.chip_cfg.fifo_enable != sensors)
/* You're not getting what you asked for. Some sensors are
* asleep.
*/
result = -1;
else
result = 0;
if (sensors || st.chip_cfg.lp_accel_mode)
set_int_enable(1);
else
set_int_enable(0);
if (sensors)
{
if (mpu_reset_fifo())
{
st.chip_cfg.fifo_enable = prev;
return -1;
}
}
}
return result;
}
/**
* @brief Get current power state.
* @param[in] power_on 1 if turned on, 0 if suspended.
* @return 0 if successful.
*/
int mpu_get_power_state(unsigned char *power_on)
{
if (st.chip_cfg.sensors)
power_on[0] = 1;
else
power_on[0] = 0;
return 0;
}
/**
* @brief Turn specific sensors on/off.
* @e sensors can contain a combination of the following flags:
* \n INV_X_GYRO, INV_Y_GYRO, INV_Z_GYRO
* \n INV_XYZ_GYRO
* \n INV_XYZ_ACCEL
* \n INV_XYZ_COMPASS
* @param[in] sensors Mask of sensors to wake.
* @return 0 if successful.
*/
int mpu_set_sensors(unsigned char sensors)
{
unsigned char data;
#ifdef AK89xx_SECONDARY
unsigned char user_ctrl;
#endif
if (sensors & INV_XYZ_GYRO)
data = INV_CLK_PLL;
else if (sensors)
data = 0;
else
data = BIT_SLEEP;
if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 1, &data))
{
st.chip_cfg.sensors = 0;
return -1;
}
st.chip_cfg.clk_src = data & ~BIT_SLEEP;
data = 0;
if (!(sensors & INV_X_GYRO))
data |= BIT_STBY_XG;
if (!(sensors & INV_Y_GYRO))
data |= BIT_STBY_YG;
if (!(sensors & INV_Z_GYRO))
data |= BIT_STBY_ZG;
if (!(sensors & INV_XYZ_ACCEL))
data |= BIT_STBY_XYZA;
if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_2, 1, &data))
{
st.chip_cfg.sensors = 0;
return -1;
}
if (sensors && (sensors != INV_XYZ_ACCEL))
/* Latched interrupts only used in LP accel mode. */
mpu_set_int_latched(0);
#ifdef AK89xx_SECONDARY
#ifdef AK89xx_BYPASS
if (sensors & INV_XYZ_COMPASS)
mpu_set_bypass(1);
else
mpu_set_bypass(0);
#else
if (i2c_read(st.hw->addr, st.reg->user_ctrl, 1, &user_ctrl))
return -1;
/* Handle AKM power management. */
if (sensors & INV_XYZ_COMPASS)
{
data = AKM_SINGLE_MEASUREMENT;
user_ctrl |= BIT_AUX_IF_EN;
}
else
{
data = AKM_POWER_DOWN;
user_ctrl &= ~BIT_AUX_IF_EN;
}
if (st.chip_cfg.dmp_on)
user_ctrl |= BIT_DMP_EN;
else
user_ctrl &= ~BIT_DMP_EN;
if (i2c_write(st.hw->addr, st.reg->s1_do, 1, &data))
return -1;
/* Enable/disable I2C master mode. */
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &user_ctrl))
return -1;
#endif
#endif
st.chip_cfg.sensors = sensors;
st.chip_cfg.lp_accel_mode = 0;
HAL_Delay(50);
return 0;
}
/**
* @brief Read the MPU interrupt status registers.
* @param[out] status Mask of interrupt bits.
* @return 0 if successful.
*/
int mpu_get_int_status(short *status)
{
unsigned char tmp[2];
if (!st.chip_cfg.sensors)
return -1;
if (i2c_read(st.hw->addr, st.reg->dmp_int_status, 2, tmp))
return -1;
status[0] = (tmp[0] << 8) | tmp[1];
return 0;
}
/**
* @brief Get one packet from the FIFO.
* If @e sensors does not contain a particular sensor, disregard the data
* returned to that pointer.
* \n @e sensors can contain a combination of the following flags:
* \n INV_X_GYRO, INV_Y_GYRO, INV_Z_GYRO
* \n INV_XYZ_GYRO
* \n INV_XYZ_ACCEL
* \n If the FIFO has no new data, @e sensors will be zero.
* \n If the FIFO is disabled, @e sensors will be zero and this function will
* return a non-zero error code.
* @param[out] gyro Gyro data in hardware units.
* @param[out] accel Accel data in hardware units.
* @param[out] timestamp Timestamp in milliseconds.
* @param[out] sensors Mask of sensors read from FIFO.
* @param[out] more Number of remaining packets.
* @return 0 if successful.
*/
int mpu_read_fifo(short *gyro, short *accel, unsigned long *timestamp,
unsigned char *sensors, unsigned char *more)
{
/* Assumes maximum packet size is gyro (6) + accel (6). */
unsigned char data[MAX_PACKET_LENGTH];
unsigned char packet_size = 0;
unsigned short fifo_count, index = 0;
if (st.chip_cfg.dmp_on)
return -1;
sensors[0] = 0;
if (!st.chip_cfg.sensors)
return -1;
if (!st.chip_cfg.fifo_enable)
return -1;
if (st.chip_cfg.fifo_enable & INV_X_GYRO)
packet_size += 2;
if (st.chip_cfg.fifo_enable & INV_Y_GYRO)
packet_size += 2;
if (st.chip_cfg.fifo_enable & INV_Z_GYRO)
packet_size += 2;
if (st.chip_cfg.fifo_enable & INV_XYZ_ACCEL)
packet_size += 6;
if (i2c_read(st.hw->addr, st.reg->fifo_count_h, 2, data))
return -1;
fifo_count = (data[0] << 8) | data[1];
if (fifo_count < packet_size)
return 0;
// log_i("FIFO count: %hd\n", fifo_count);
if (fifo_count > (st.hw->max_fifo >> 1))
{
/* FIFO is 50% full, better check overflow bit. */
if (i2c_read(st.hw->addr, st.reg->int_status, 1, data))
return -1;
if (data[0] & BIT_FIFO_OVERFLOW)
{
mpu_reset_fifo();
return -2;
}
}
get_ms((unsigned long *)timestamp);
if (i2c_read(st.hw->addr, st.reg->fifo_r_w, packet_size, data))
return -1;
more[0] = fifo_count / packet_size - 1;
sensors[0] = 0;
if ((index != packet_size) && st.chip_cfg.fifo_enable & INV_XYZ_ACCEL)
{
accel[0] = (data[index + 0] << 8) | data[index + 1];
accel[1] = (data[index + 2] << 8) | data[index + 3];
accel[2] = (data[index + 4] << 8) | data[index + 5];
sensors[0] |= INV_XYZ_ACCEL;
index += 6;
}
if ((index != packet_size) && st.chip_cfg.fifo_enable & INV_X_GYRO)
{
gyro[0] = (data[index + 0] << 8) | data[index + 1];
sensors[0] |= INV_X_GYRO;
index += 2;
}
if ((index != packet_size) && st.chip_cfg.fifo_enable & INV_Y_GYRO)
{
gyro[1] = (data[index + 0] << 8) | data[index + 1];
sensors[0] |= INV_Y_GYRO;
index += 2;
}
if ((index != packet_size) && st.chip_cfg.fifo_enable & INV_Z_GYRO)
{
gyro[2] = (data[index + 0] << 8) | data[index + 1];
sensors[0] |= INV_Z_GYRO;
index += 2;
}
return 0;
}
/**
* @brief Get one unparsed packet from the FIFO.
* This function should be used if the packet is to be parsed elsewhere.
* @param[in] length Length of one FIFO packet.
* @param[in] data FIFO packet.
* @param[in] more Number of remaining packets.
*/
int mpu_read_fifo_stream(unsigned short length, unsigned char *data,
unsigned char *more)
{
unsigned char tmp[2];
unsigned short fifo_count;
if (!st.chip_cfg.dmp_on)
return -1;
if (!st.chip_cfg.sensors)
return -1;
if (i2c_read(st.hw->addr, st.reg->fifo_count_h, 2, tmp))
return -1;
fifo_count = (tmp[0] << 8) | tmp[1];
if (fifo_count < length)
{
more[0] = 0;
return -1;
}
if (fifo_count > (st.hw->max_fifo >> 1))
{
/* FIFO is 50% full, better check overflow bit. */
if (i2c_read(st.hw->addr, st.reg->int_status, 1, tmp))
return -1;
if (tmp[0] & BIT_FIFO_OVERFLOW)
{
mpu_reset_fifo();
return -2;
}
}
if (i2c_read(st.hw->addr, st.reg->fifo_r_w, length, data))
return -1;
more[0] = fifo_count / length - 1;
return 0;
}
/**
* @brief Set device to bypass mode.
* @param[in] bypass_on 1 to enable bypass mode.
* @return 0 if successful.
*/
int mpu_set_bypass(unsigned char bypass_on)
{
unsigned char tmp;
if (st.chip_cfg.bypass_mode == bypass_on)
return 0;
if (bypass_on)
{
if (i2c_read(st.hw->addr, st.reg->user_ctrl, 1, &tmp))
return -1;
tmp &= ~BIT_AUX_IF_EN;
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &tmp))
return -1;
HAL_Delay(3);
tmp = BIT_BYPASS_EN;
if (st.chip_cfg.active_low_int)
tmp |= BIT_ACTL;
if (st.chip_cfg.latched_int)
tmp |= BIT_LATCH_EN | BIT_ANY_RD_CLR;
if (i2c_write(st.hw->addr, st.reg->int_pin_cfg, 1, &tmp))
return -1;
}
else
{
/* Enable I2C master mode if compass is being used. */
if (i2c_read(st.hw->addr, st.reg->user_ctrl, 1, &tmp))
return -1;
if (st.chip_cfg.sensors & INV_XYZ_COMPASS)
tmp |= BIT_AUX_IF_EN;
else
tmp &= ~BIT_AUX_IF_EN;
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, &tmp))
return -1;
HAL_Delay(3);
if (st.chip_cfg.active_low_int)
tmp = BIT_ACTL;
else
tmp = 0;
if (st.chip_cfg.latched_int)
tmp |= BIT_LATCH_EN | BIT_ANY_RD_CLR;
if (i2c_write(st.hw->addr, st.reg->int_pin_cfg, 1, &tmp))
return -1;
}
st.chip_cfg.bypass_mode = bypass_on;
return 0;
}
/**
* @brief Set interrupt level.
* @param[in] active_low 1 for active low, 0 for active high.
* @return 0 if successful.
*/
int mpu_set_int_level(unsigned char active_low)
{
st.chip_cfg.active_low_int = active_low;
return 0;
}
/**
* @brief Enable latched interrupts.
* Any MPU register will clear the interrupt.
* @param[in] enable 1 to enable, 0 to disable.
* @return 0 if successful.
*/
int mpu_set_int_latched(unsigned char enable)
{
unsigned char tmp;
if (st.chip_cfg.latched_int == enable)
return 0;
if (enable)
tmp = BIT_LATCH_EN | BIT_ANY_RD_CLR;
else
tmp = 0;
if (st.chip_cfg.bypass_mode)
tmp |= BIT_BYPASS_EN;
if (st.chip_cfg.active_low_int)
tmp |= BIT_ACTL;
if (i2c_write(st.hw->addr, st.reg->int_pin_cfg, 1, &tmp))
return -1;
st.chip_cfg.latched_int = enable;
return 0;
}
#ifdef MPU6050
static int get_accel_prod_shift(float *st_shift)
{
unsigned char tmp[4], shift_code[3], ii;
if (i2c_read(st.hw->addr, 0x0D, 4, tmp))
return 0x07;
shift_code[0] = ((tmp[0] & 0xE0) >> 3) | ((tmp[3] & 0x30) >> 4);
shift_code[1] = ((tmp[1] & 0xE0) >> 3) | ((tmp[3] & 0x0C) >> 2);
shift_code[2] = ((tmp[2] & 0xE0) >> 3) | (tmp[3] & 0x03);
for (ii = 0; ii < 3; ii++)
{
if (!shift_code[ii])
{
st_shift[ii] = 0.f;
continue;
}
/* Equivalent to..
* st_shift[ii] = 0.34f * powf(0.92f/0.34f, (shift_code[ii]-1) / 30.f)
*/
st_shift[ii] = 0.34f;
while (--shift_code[ii])
st_shift[ii] *= 1.034f;
}
return 0;
}
static int accel_self_test(long *bias_regular, long *bias_st)
{
int jj, result = 0;
float st_shift[3], st_shift_cust, st_shift_var;
get_accel_prod_shift(st_shift);
for (jj = 0; jj < 3; jj++)
{
st_shift_cust = labs(bias_regular[jj] - bias_st[jj]) / 65536.f;
if (st_shift[jj])
{
st_shift_var = st_shift_cust / st_shift[jj] - 1.f;
if (fabs(st_shift_var) > test.max_accel_var)
result |= 1 << jj;
}
else if ((st_shift_cust < test.min_g) ||
(st_shift_cust > test.max_g))
result |= 1 << jj;
}
return result;
}
static int gyro_self_test(long *bias_regular, long *bias_st)
{
int jj, result = 0;
unsigned char tmp[3];
float st_shift, st_shift_cust, st_shift_var;
if (i2c_read(st.hw->addr, 0x0D, 3, tmp))
return 0x07;
tmp[0] &= 0x1F;
tmp[1] &= 0x1F;
tmp[2] &= 0x1F;
for (jj = 0; jj < 3; jj++)
{
st_shift_cust = labs(bias_regular[jj] - bias_st[jj]) / 65536.f;
if (tmp[jj])
{
st_shift = 3275.f / test.gyro_sens;
while (--tmp[jj])
st_shift *= 1.046f;
st_shift_var = st_shift_cust / st_shift - 1.f;
if (fabs(st_shift_var) > test.max_gyro_var)
result |= 1 << jj;
}
else if ((st_shift_cust < test.min_dps) ||
(st_shift_cust > test.max_dps))
result |= 1 << jj;
}
return result;
}
#ifdef AK89xx_SECONDARY
static int compass_self_test(void)
{
unsigned char tmp[6];
unsigned char tries = 10;
int result = 0x07;
short data;
mpu_set_bypass(1);
tmp[0] = AKM_POWER_DOWN;
if (i2c_write(st.chip_cfg.compass_addr, AKM_REG_CNTL, 1, tmp))
return 0x07;
tmp[0] = AKM_BIT_SELF_TEST;
if (i2c_write(st.chip_cfg.compass_addr, AKM_REG_ASTC, 1, tmp))
goto AKM_restore;
tmp[0] = AKM_MODE_SELF_TEST;
if (i2c_write(st.chip_cfg.compass_addr, AKM_REG_CNTL, 1, tmp))
goto AKM_restore;
do
{
delay_ms(10);
if (i2c_read(st.chip_cfg.compass_addr, AKM_REG_ST1, 1, tmp))
goto AKM_restore;
if (tmp[0] & AKM_DATA_READY)
break;
} while (tries--);
if (!(tmp[0] & AKM_DATA_READY))
goto AKM_restore;
if (i2c_read(st.chip_cfg.compass_addr, AKM_REG_HXL, 6, tmp))
goto AKM_restore;
result = 0;
data = (short)(tmp[1] << 8) | tmp[0];
if ((data > 100) || (data < -100))
result |= 0x01;
data = (short)(tmp[3] << 8) | tmp[2];
if ((data > 100) || (data < -100))
result |= 0x02;
data = (short)(tmp[5] << 8) | tmp[4];
if ((data > -300) || (data < -1000))
result |= 0x04;
AKM_restore:
tmp[0] = 0 | SUPPORTS_AK89xx_HIGH_SENS;
i2c_write(st.chip_cfg.compass_addr, AKM_REG_ASTC, 1, tmp);
tmp[0] = SUPPORTS_AK89xx_HIGH_SENS;
i2c_write(st.chip_cfg.compass_addr, AKM_REG_CNTL, 1, tmp);
mpu_set_bypass(0);
return result;
}
#endif
#endif
static int get_st_biases(long *gyro, long *accel, unsigned char hw_test)
{
unsigned char data[MAX_PACKET_LENGTH];
unsigned char packet_count, ii;
unsigned short fifo_count;
data[0] = 0x01;
data[1] = 0;
if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 2, data))
return -1;
HAL_Delay(200);
data[0] = 0;
if (i2c_write(st.hw->addr, st.reg->int_enable, 1, data))
return -1;
if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, data))
return -1;
if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 1, data))
return -1;
if (i2c_write(st.hw->addr, st.reg->i2c_mst, 1, data))
return -1;
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, data))
return -1;
data[0] = BIT_FIFO_RST | BIT_DMP_RST;
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, data))
return -1;
HAL_Delay(15);
data[0] = st.test->reg_lpf;
if (i2c_write(st.hw->addr, st.reg->lpf, 1, data))
return -1;
data[0] = st.test->reg_rate_div;
if (i2c_write(st.hw->addr, st.reg->rate_div, 1, data))
return -1;
if (hw_test)
data[0] = st.test->reg_gyro_fsr | 0xE0;
else
data[0] = st.test->reg_gyro_fsr;
if (i2c_write(st.hw->addr, st.reg->gyro_cfg, 1, data))
return -1;
if (hw_test)
data[0] = st.test->reg_accel_fsr | 0xE0;
else
data[0] = test.reg_accel_fsr;
if (i2c_write(st.hw->addr, st.reg->accel_cfg, 1, data))
return -1;
if (hw_test)
HAL_Delay(200);
/* Fill FIFO for test.wait_ms milliseconds. */
data[0] = BIT_FIFO_EN;
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, data))
return -1;
data[0] = INV_XYZ_GYRO | INV_XYZ_ACCEL;
if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, data))
return -1;
HAL_Delay(test.wait_ms);
data[0] = 0;
if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, data))
return -1;
if (i2c_read(st.hw->addr, st.reg->fifo_count_h, 2, data))
return -1;
fifo_count = (data[0] << 8) | data[1];
packet_count = fifo_count / MAX_PACKET_LENGTH;
gyro[0] = gyro[1] = gyro[2] = 0;
accel[0] = accel[1] = accel[2] = 0;
for (ii = 0; ii < packet_count; ii++)
{
short accel_cur[3], gyro_cur[3];
if (i2c_read(st.hw->addr, st.reg->fifo_r_w, MAX_PACKET_LENGTH, data))
return -1;
accel_cur[0] = ((short)data[0] << 8) | data[1];
accel_cur[1] = ((short)data[2] << 8) | data[3];
accel_cur[2] = ((short)data[4] << 8) | data[5];
accel[0] += (long)accel_cur[0];
accel[1] += (long)accel_cur[1];
accel[2] += (long)accel_cur[2];
gyro_cur[0] = (((short)data[6] << 8) | data[7]);
gyro_cur[1] = (((short)data[8] << 8) | data[9]);
gyro_cur[2] = (((short)data[10] << 8) | data[11]);
gyro[0] += (long)gyro_cur[0];
gyro[1] += (long)gyro_cur[1];
gyro[2] += (long)gyro_cur[2];
}
#ifdef EMPL_NO_64BIT
gyro[0] = (long)(((float)gyro[0] * 65536.f) / test.gyro_sens / packet_count);
gyro[1] = (long)(((float)gyro[1] * 65536.f) / test.gyro_sens / packet_count);
gyro[2] = (long)(((float)gyro[2] * 65536.f) / test.gyro_sens / packet_count);
if (has_accel)
{
accel[0] = (long)(((float)accel[0] * 65536.f) / test.accel_sens /
packet_count);
accel[1] = (long)(((float)accel[1] * 65536.f) / test.accel_sens /
packet_count);
accel[2] = (long)(((float)accel[2] * 65536.f) / test.accel_sens /
packet_count);
/* Don't remove gravity! */
accel[2] -= 65536L;
}
#else
gyro[0] = (long)(((long long)gyro[0] << 16) / test.gyro_sens / packet_count);
gyro[1] = (long)(((long long)gyro[1] << 16) / test.gyro_sens / packet_count);
gyro[2] = (long)(((long long)gyro[2] << 16) / test.gyro_sens / packet_count);
accel[0] = (long)(((long long)accel[0] << 16) / test.accel_sens /
packet_count);
accel[1] = (long)(((long long)accel[1] << 16) / test.accel_sens /
packet_count);
accel[2] = (long)(((long long)accel[2] << 16) / test.accel_sens /
packet_count);
/* Don't remove gravity! */
if (accel[2] > 0L)
accel[2] -= 65536L;
else
accel[2] += 65536L;
#endif
return 0;
}
/**
* @brief Trigger gyro/accel/compass self-test.
* On success/error, the self-test returns a mask representing the sensor(s)
* that failed. For each bit, a one (1) represents a "pass" case; conversely,
* a zero (0) indicates a failure.
*
* \n The mask is defined as follows:
* \n Bit 0: Gyro.
* \n Bit 1: Accel.
* \n Bit 2: Compass.
*
* \n Currently, the hardware self-test is unsupported for MPU6500. However,
* this function can still be used to obtain the accel and gyro biases.
*
* \n This function must be called with the device either face-up or face-down
* (z-axis is parallel to gravity).
* @param[out] gyro Gyro biases in q16 format.
* @param[out] accel Accel biases (if applicable) in q16 format.
* @return Result mask (see above).
*/
int mpu_run_self_test(long *gyro, long *accel)
{
#ifdef MPU6050
const unsigned char tries = 2;
long gyro_st[3], accel_st[3];
unsigned char accel_result, gyro_result;
#ifdef AK89xx_SECONDARY
unsigned char compass_result;
#endif
int ii;
#endif
int result;
unsigned char accel_fsr, fifo_sensors, sensors_on;
unsigned short gyro_fsr, sample_rate, lpf;
unsigned char dmp_was_on;
if (st.chip_cfg.dmp_on)
{
mpu_set_dmp_state(0);
dmp_was_on = 1;
}
else
dmp_was_on = 0;
/* Get initial settings. */
mpu_get_gyro_fsr(&gyro_fsr);
mpu_get_accel_fsr(&accel_fsr);
mpu_get_lpf(&lpf);
mpu_get_sample_rate(&sample_rate);
sensors_on = st.chip_cfg.sensors;
mpu_get_fifo_config(&fifo_sensors);
/* For older chips, the self-test will be different. */
#if defined MPU6050
for (ii = 0; ii < tries; ii++)
if (!get_st_biases(gyro, accel, 0))
break;
if (ii == tries)
{
/* If we reach this point, we most likely encountered an I2C error.
* We'll just report an error for all three sensors.
*/
result = 0;
goto restore;
}
for (ii = 0; ii < tries; ii++)
if (!get_st_biases(gyro_st, accel_st, 1))
break;
if (ii == tries)
{
/* Again, probably an I2C error. */
result = 0;
goto restore;
}
accel_result = accel_self_test(accel, accel_st);
gyro_result = gyro_self_test(gyro, gyro_st);
result = 0;
if (!gyro_result)
result |= 0x01;
if (!accel_result)
result |= 0x02;
#ifdef AK89xx_SECONDARY
compass_result = compass_self_test();
if (!compass_result)
result |= 0x04;
#endif
restore:
#elif defined MPU6500
/* For now, this function will return a "pass" result for all three sensors
* for compatibility with current test applications.
*/
get_st_biases(gyro, accel, 0);
result = 0x7;
#endif
/* Set to invalid values to ensure no I2C writes are skipped. */
st.chip_cfg.gyro_fsr = 0xFF;
st.chip_cfg.accel_fsr = 0xFF;
st.chip_cfg.lpf = 0xFF;
st.chip_cfg.sample_rate = 0xFFFF;
st.chip_cfg.sensors = 0xFF;
st.chip_cfg.fifo_enable = 0xFF;
st.chip_cfg.clk_src = INV_CLK_PLL;
mpu_set_gyro_fsr(gyro_fsr);
mpu_set_accel_fsr(accel_fsr);
mpu_set_lpf(lpf);
mpu_set_sample_rate(sample_rate);
mpu_set_sensors(sensors_on);
mpu_configure_fifo(fifo_sensors);
if (dmp_was_on)
mpu_set_dmp_state(1);
return result;
}
/**
* @brief Write to the DMP memory.
* This function prevents I2C writes past the bank boundaries. The DMP memory
* is only accessible when the chip is awake.
* @param[in] mem_addr Memory location (bank << 8 | start address)
* @param[in] length Number of bytes to write.
* @param[in] data Bytes to write to memory.
* @return 0 if successful.
*/
int mpu_write_mem(unsigned short mem_addr, unsigned short length,
unsigned char *data)
{
unsigned char tmp[2];
if (!data)
return -1;
if (!st.chip_cfg.sensors)
return -1;
tmp[0] = (unsigned char)(mem_addr >> 8);
tmp[1] = (unsigned char)(mem_addr & 0xFF);
/* Check bank boundaries. */
if (tmp[1] + length > st.hw->bank_size)
return -1;
if (i2c_write(st.hw->addr, st.reg->bank_sel, 2, tmp))
return -1;
if (i2c_write(st.hw->addr, st.reg->mem_r_w, length, data))
return -1;
return 0;
}
/**
* @brief Read from the DMP memory.
* This function prevents I2C reads past the bank boundaries. The DMP memory
* is only accessible when the chip is awake.
* @param[in] mem_addr Memory location (bank << 8 | start address)
* @param[in] length Number of bytes to read.
* @param[out] data Bytes read from memory.
* @return 0 if successful.
*/
int mpu_read_mem(unsigned short mem_addr, unsigned short length,
unsigned char *data)
{
unsigned char tmp[2];
if (!data)
return -1;
if (!st.chip_cfg.sensors)
return -1;
tmp[0] = (unsigned char)(mem_addr >> 8);
tmp[1] = (unsigned char)(mem_addr & 0xFF);
/* Check bank boundaries. */
if (tmp[1] + length > st.hw->bank_size)
return -1;
if (i2c_write(st.hw->addr, st.reg->bank_sel, 2, tmp))
return -1;
if (i2c_read(st.hw->addr, st.reg->mem_r_w, length, data))
return -1;
return 0;
}
/**
* @brief Load and verify DMP image.
* @param[in] length Length of DMP image.
* @param[in] firmware DMP code.
* @param[in] start_addr Starting address of DMP code memory.
* @param[in] sample_rate Fixed sampling rate used when DMP is enabled.
* @return 0 if successful.
*/
int mpu_load_firmware(unsigned short length, const unsigned char *firmware,
unsigned short start_addr, unsigned short sample_rate)
{
unsigned short ii;
unsigned short this_write;
/* Must divide evenly into st.hw->bank_size to avoid bank crossings. */
#define LOAD_CHUNK (16)
unsigned char cur[LOAD_CHUNK], tmp[2];
if (st.chip_cfg.dmp_loaded)
/* DMP should only be loaded once. */
return -1;
if (!firmware)
return -1;
for (ii = 0; ii < length; ii += this_write)
{
this_write = min(LOAD_CHUNK, length - ii);
if (mpu_write_mem(ii, this_write, (unsigned char *)&firmware[ii]))
return -1;
if (mpu_read_mem(ii, this_write, cur))
return -1;
if (memcmp(firmware + ii, cur, this_write))
return -2;
}
/* Set program start address. */
tmp[0] = start_addr >> 8;
tmp[1] = start_addr & 0xFF;
if (i2c_write(st.hw->addr, st.reg->prgm_start_h, 2, tmp))
return -1;
st.chip_cfg.dmp_loaded = 1;
st.chip_cfg.dmp_sample_rate = sample_rate;
return 0;
}
/**
* @brief Enable/disable DMP support.
* @param[in] enable 1 to turn on the DMP.
* @return 0 if successful.
*/
int mpu_set_dmp_state(unsigned char enable)
{
unsigned char tmp;
if (st.chip_cfg.dmp_on == enable)
return 0;
if (enable)
{
if (!st.chip_cfg.dmp_loaded)
return -1;
/* Disable data ready interrupt. */
set_int_enable(0);
/* Disable bypass mode. */
mpu_set_bypass(0);
/* Keep constant sample rate, FIFO rate controlled by DMP. */
mpu_set_sample_rate(st.chip_cfg.dmp_sample_rate);
/* Remove FIFO elements. */
tmp = 0;
i2c_write(st.hw->addr, 0x23, 1, &tmp);
st.chip_cfg.dmp_on = 1;
/* Enable DMP interrupt. */
set_int_enable(1);
mpu_reset_fifo();
}
else
{
/* Disable DMP interrupt. */
set_int_enable(0);
/* Restore FIFO settings. */
tmp = st.chip_cfg.fifo_enable;
i2c_write(st.hw->addr, 0x23, 1, &tmp);
st.chip_cfg.dmp_on = 0;
mpu_reset_fifo();
}
return 0;
}
/**
* @brief Get DMP state.
* @param[out] enabled 1 if enabled.
* @return 0 if successful.
*/
int mpu_get_dmp_state(unsigned char *enabled)
{
enabled[0] = st.chip_cfg.dmp_on;
return 0;
}
/* This initialization is similar to the one in ak8975.c. */
int setup_compass(void)
{
#ifdef AK89xx_SECONDARY
unsigned char data[4], akm_addr;
mpu_set_bypass(1);
/* Find compass. Possible addresses range from 0x0C to 0x0F. */
for (akm_addr = 0x0C; akm_addr <= 0x0F; akm_addr++)
{
int result;
result = i2c_read(akm_addr, AKM_REG_WHOAMI, 1, data);
if (!result && (data[0] == AKM_WHOAMI))
break;
}
if (akm_addr > 0x0F)
{
/* TODO: Handle this case in all compass-related functions. */
log_e("Compass not found.\n");
return -1;
}
st.chip_cfg.compass_addr = akm_addr;
data[0] = AKM_POWER_DOWN;
if (i2c_write(st.chip_cfg.compass_addr, AKM_REG_CNTL, 1, data))
return -1;
delay_ms(1);
data[0] = AKM_FUSE_ROM_ACCESS;
if (i2c_write(st.chip_cfg.compass_addr, AKM_REG_CNTL, 1, data))
return -1;
delay_ms(1);
/* Get sensitivity adjustment data from fuse ROM. */
if (i2c_read(st.chip_cfg.compass_addr, AKM_REG_ASAX, 3, data))
return -1;
st.chip_cfg.mag_sens_adj[0] = (long)data[0] + 128;
st.chip_cfg.mag_sens_adj[1] = (long)data[1] + 128;
st.chip_cfg.mag_sens_adj[2] = (long)data[2] + 128;
data[0] = AKM_POWER_DOWN;
if (i2c_write(st.chip_cfg.compass_addr, AKM_REG_CNTL, 1, data))
return -1;
delay_ms(1);
mpu_set_bypass(0);
/* Set up master mode, master clock, and ES bit. */
data[0] = 0x40;
if (i2c_write(st.hw->addr, st.reg->i2c_mst, 1, data))
return -1;
/* Slave 0 reads from AKM data registers. */
data[0] = BIT_I2C_READ | st.chip_cfg.compass_addr;
if (i2c_write(st.hw->addr, st.reg->s0_addr, 1, data))
return -1;
/* Compass reads start at this register. */
data[0] = AKM_REG_ST1;
if (i2c_write(st.hw->addr, st.reg->s0_reg, 1, data))
return -1;
/* Enable slave 0, 8-byte reads. */
data[0] = BIT_SLAVE_EN | 8;
if (i2c_write(st.hw->addr, st.reg->s0_ctrl, 1, data))
return -1;
/* Slave 1 changes AKM measurement mode. */
data[0] = st.chip_cfg.compass_addr;
if (i2c_write(st.hw->addr, st.reg->s1_addr, 1, data))
return -1;
/* AKM measurement mode register. */
data[0] = AKM_REG_CNTL;
if (i2c_write(st.hw->addr, st.reg->s1_reg, 1, data))
return -1;
/* Enable slave 1, 1-byte writes. */
data[0] = BIT_SLAVE_EN | 1;
if (i2c_write(st.hw->addr, st.reg->s1_ctrl, 1, data))
return -1;
/* Set slave 1 data. */
data[0] = AKM_SINGLE_MEASUREMENT;
if (i2c_write(st.hw->addr, st.reg->s1_do, 1, data))
return -1;
/* Trigger slave 0 and slave 1 actions at each sample. */
data[0] = 0x03;
if (i2c_write(st.hw->addr, st.reg->i2c_delay_ctrl, 1, data))
return -1;
#ifdef MPU9150
/* For the MPU9150, the auxiliary I2C bus needs to be set to VDD. */
data[0] = BIT_I2C_MST_VDDIO;
if (i2c_write(st.hw->addr, st.reg->yg_offs_tc, 1, data))
return -1;
#endif
return 0;
#else
return -1;
#endif
}
/**
* @brief Read raw compass data.
* @param[out] data Raw data in hardware units.
* @param[out] timestamp Timestamp in milliseconds. Null if not needed.
* @return 0 if successful.
*/
int mpu_get_compass_reg(short *data, unsigned long *timestamp)
{
#ifdef AK89xx_SECONDARY
unsigned char tmp[9];
if (!(st.chip_cfg.sensors & INV_XYZ_COMPASS))
return -1;
#ifdef AK89xx_BYPASS
if (i2c_read(st.chip_cfg.compass_addr, AKM_REG_ST1, 8, tmp))
return -1;
tmp[8] = AKM_SINGLE_MEASUREMENT;
if (i2c_write(st.chip_cfg.compass_addr, AKM_REG_CNTL, 1, tmp + 8))
return -1;
#else
if (i2c_read(st.hw->addr, st.reg->raw_compass, 8, tmp))
return -1;
#endif
#if defined AK8975_SECONDARY
/* AK8975 doesn't have the overrun error bit. */
if (!(tmp[0] & AKM_DATA_READY))
return -2;
if ((tmp[7] & AKM_OVERFLOW) || (tmp[7] & AKM_DATA_ERROR))
return -3;
#elif defined AK8963_SECONDARY
/* AK8963 doesn't have the data read error bit. */
if (!(tmp[0] & AKM_DATA_READY) || (tmp[0] & AKM_DATA_OVERRUN))
return -2;
if (tmp[7] & AKM_OVERFLOW)
return -3;
#endif
data[0] = (tmp[2] << 8) | tmp[1];
data[1] = (tmp[4] << 8) | tmp[3];
data[2] = (tmp[6] << 8) | tmp[5];
data[0] = ((long)data[0] * st.chip_cfg.mag_sens_adj[0]) >> 8;
data[1] = ((long)data[1] * st.chip_cfg.mag_sens_adj[1]) >> 8;
data[2] = ((long)data[2] * st.chip_cfg.mag_sens_adj[2]) >> 8;
if (timestamp)
get_ms(timestamp);
return 0;
#else
return -1;
#endif
}
/**
* @brief Get the compass full-scale range.
* @param[out] fsr Current full-scale range.
* @return 0 if successful.
*/
int mpu_get_compass_fsr(unsigned short *fsr)
{
#ifdef AK89xx_SECONDARY
fsr[0] = st.hw->compass_fsr;
return 0;
#else
return -1;
#endif
}
/**
* @brief Enters LP accel motion interrupt mode.
* The behavior of this feature is very different between the MPU6050 and the
* MPU6500. Each chip's version of this feature is explained below.
*
* \n MPU6050:
* \n When this mode is first enabled, the hardware captures a single accel
* sample, and subsequent samples are compared with this one to determine if
* the device is in motion. Therefore, whenever this "locked" sample needs to
* be changed, this function must be called again.
*
* \n The hardware motion threshold can be between 32mg and 8160mg in 32mg
* increments.
*
* \n Low-power accel mode supports the following frequencies:
* \n 1.25Hz, 5Hz, 20Hz, 40Hz
*
* \n MPU6500:
* \n Unlike the MPU6050 version, the hardware does not "lock in" a reference
* sample. The hardware monitors the accel data and detects any large change
* over a short period of time.
*
* \n The hardware motion threshold can be between 4mg and 1020mg in 4mg
* increments.
*
* \n MPU6500 Low-power accel mode supports the following frequencies:
* \n 1.25Hz, 2.5Hz, 5Hz, 10Hz, 20Hz, 40Hz, 80Hz, 160Hz, 320Hz, 640Hz
*
* \n\n NOTES:
* \n The driver will round down @e thresh to the nearest supported value if
* an unsupported threshold is selected.
* \n To select a fractional wake-up frequency, round down the value passed to
* @e lpa_freq.
* \n The MPU6500 does not support a delay parameter. If this function is used
* for the MPU6500, the value passed to @e time will be ignored.
* \n To disable this mode, set @e lpa_freq to zero. The driver will restore
* the previous configuration.
*
* @param[in] thresh Motion threshold in mg.
* @param[in] time Duration in milliseconds that the accel data must
* exceed @e thresh before motion is reported.
* @param[in] lpa_freq Minimum sampling rate, or zero to disable.
* @return 0 if successful.
*/
int mpu_lp_motion_interrupt(unsigned short thresh, unsigned char time,
unsigned char lpa_freq)
{
unsigned char data[3];
if (lpa_freq)
{
unsigned char thresh_hw;
#if defined MPU6050
/* TODO: Make these const/#defines. */
/* 1LSb = 32mg. */
if (thresh > 8160)
thresh_hw = 255;
else if (thresh < 32)
thresh_hw = 1;
else
thresh_hw = thresh >> 5;
#elif defined MPU6500
/* 1LSb = 4mg. */
if (thresh > 1020)
thresh_hw = 255;
else if (thresh < 4)
thresh_hw = 1;
else
thresh_hw = thresh >> 2;
#endif
if (!time)
/* Minimum duration must be 1ms. */
time = 1;
#if defined MPU6050
if (lpa_freq > 40)
#elif defined MPU6500
if (lpa_freq > 640)
#endif
/* At this point, the chip has not been re-configured, so the
* function can safely exit.
*/
return -1;
if (!st.chip_cfg.int_motion_only)
{
/* Store current settings for later. */
if (st.chip_cfg.dmp_on)
{
mpu_set_dmp_state(0);
st.chip_cfg.cache.dmp_on = 1;
}
else
st.chip_cfg.cache.dmp_on = 0;
mpu_get_gyro_fsr(&st.chip_cfg.cache.gyro_fsr);
mpu_get_accel_fsr(&st.chip_cfg.cache.accel_fsr);
mpu_get_lpf(&st.chip_cfg.cache.lpf);
mpu_get_sample_rate(&st.chip_cfg.cache.sample_rate);
st.chip_cfg.cache.sensors_on = st.chip_cfg.sensors;
mpu_get_fifo_config(&st.chip_cfg.cache.fifo_sensors);
}
#ifdef MPU6050
/* Disable hardware interrupts for now. */
set_int_enable(0);
/* Enter full-power accel-only mode. */
mpu_lp_accel_mode(0);
/* Override current LPF (and HPF) settings to obtain a valid accel
* reading.
*/
data[0] = INV_FILTER_256HZ_NOLPF2;
if (i2c_write(st.hw->addr, st.reg->lpf, 1, data))
return -1;
/* NOTE: Digital high pass filter should be configured here. Since this
* driver doesn't modify those bits anywhere, they should already be
* cleared by default.
*/
/* Configure the device to send motion interrupts. */
/* Enable motion interrupt. */
data[0] = BIT_MOT_INT_EN;
if (i2c_write(st.hw->addr, st.reg->int_enable, 1, data))
goto lp_int_restore;
/* Set motion interrupt parameters. */
data[0] = thresh_hw;
data[1] = time;
if (i2c_write(st.hw->addr, st.reg->motion_thr, 2, data))
goto lp_int_restore;
/* Force hardware to "lock" current accel sample. */
delay_ms(5);
data[0] = (st.chip_cfg.accel_fsr << 3) | BITS_HPF;
if (i2c_write(st.hw->addr, st.reg->accel_cfg, 1, data))
goto lp_int_restore;
/* Set up LP accel mode. */
data[0] = BIT_LPA_CYCLE;
if (lpa_freq == 1)
data[1] = INV_LPA_1_25HZ;
else if (lpa_freq <= 5)
data[1] = INV_LPA_5HZ;
else if (lpa_freq <= 20)
data[1] = INV_LPA_20HZ;
else
data[1] = INV_LPA_40HZ;
data[1] = (data[1] << 6) | BIT_STBY_XYZG;
if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 2, data))
goto lp_int_restore;
st.chip_cfg.int_motion_only = 1;
return 0;
#elif defined MPU6500
/* Disable hardware interrupts. */
set_int_enable(0);
/* Enter full-power accel-only mode, no FIFO/DMP. */
data[0] = 0;
data[1] = 0;
data[2] = BIT_STBY_XYZG;
if (i2c_write(st.hw->addr, st.reg->user_ctrl, 3, data))
goto lp_int_restore;
/* Set motion threshold. */
data[0] = thresh_hw;
if (i2c_write(st.hw->addr, st.reg->motion_thr, 1, data))
goto lp_int_restore;
/* Set wake frequency. */
if (lpa_freq == 1)
data[0] = INV_LPA_1_25HZ;
else if (lpa_freq == 2)
data[0] = INV_LPA_2_5HZ;
else if (lpa_freq <= 5)
data[0] = INV_LPA_5HZ;
else if (lpa_freq <= 10)
data[0] = INV_LPA_10HZ;
else if (lpa_freq <= 20)
data[0] = INV_LPA_20HZ;
else if (lpa_freq <= 40)
data[0] = INV_LPA_40HZ;
else if (lpa_freq <= 80)
data[0] = INV_LPA_80HZ;
else if (lpa_freq <= 160)
data[0] = INV_LPA_160HZ;
else if (lpa_freq <= 320)
data[0] = INV_LPA_320HZ;
else
data[0] = INV_LPA_640HZ;
if (i2c_write(st.hw->addr, st.reg->lp_accel_odr, 1, data))
goto lp_int_restore;
/* Enable motion interrupt (MPU6500 version). */
data[0] = BITS_WOM_EN;
if (i2c_write(st.hw->addr, st.reg->accel_intel, 1, data))
goto lp_int_restore;
/* Enable cycle mode. */
data[0] = BIT_LPA_CYCLE;
if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 1, data))
goto lp_int_restore;
/* Enable interrupt. */
data[0] = BIT_MOT_INT_EN;
if (i2c_write(st.hw->addr, st.reg->int_enable, 1, data))
goto lp_int_restore;
st.chip_cfg.int_motion_only = 1;
return 0;
#endif
}
else
{
/* Don't "restore" the previous state if no state has been saved. */
int ii;
char *cache_ptr = (char *)&st.chip_cfg.cache;
for (ii = 0; ii < sizeof(st.chip_cfg.cache); ii++)
{
if (cache_ptr[ii] != 0)
goto lp_int_restore;
}
/* If we reach this point, motion interrupt mode hasn't been used yet. */
return -1;
}
lp_int_restore:
/* Set to invalid values to ensure no I2C writes are skipped. */
st.chip_cfg.gyro_fsr = 0xFF;
st.chip_cfg.accel_fsr = 0xFF;
st.chip_cfg.lpf = 0xFF;
st.chip_cfg.sample_rate = 0xFFFF;
st.chip_cfg.sensors = 0xFF;
st.chip_cfg.fifo_enable = 0xFF;
st.chip_cfg.clk_src = INV_CLK_PLL;
mpu_set_sensors(st.chip_cfg.cache.sensors_on);
mpu_set_gyro_fsr(st.chip_cfg.cache.gyro_fsr);
mpu_set_accel_fsr(st.chip_cfg.cache.accel_fsr);
mpu_set_lpf(st.chip_cfg.cache.lpf);
mpu_set_sample_rate(st.chip_cfg.cache.sample_rate);
mpu_configure_fifo(st.chip_cfg.cache.fifo_sensors);
if (st.chip_cfg.cache.dmp_on)
mpu_set_dmp_state(1);
#ifdef MPU6500
/* Disable motion interrupt (MPU6500 version). */
data[0] = 0;
if (i2c_write(st.hw->addr, st.reg->accel_intel, 1, data))
goto lp_int_restore;
#endif
st.chip_cfg.int_motion_only = 0;
return 0;
}
//////////////////////////////////////////////////////////////////////////////////
// 添加的代码部分
//////////////////////////////////////////////////////////////////////////////////
// 本程序只供学习使用,未经作者许可,不得用于其它任何用途
// ALIENTEK精英STM32开发板V3
// MPU6050 DMP 驱动代码
// 正点原子@ALIENTEK
// 技术论坛:www.openedv.com
// 创建日期:2015/1/17
// 版本:V1.0
// 版权所有,盗版必究。
// Copyright(C) 广州市星翼电子科技有限公司 2009-2019
// All rights reserved
//////////////////////////////////////////////////////////////////////////////////
// q30格式,long转float时的除数.
#define q30 1073741824.0f
// 陀螺仪方向设置
static signed char gyro_orientation[9] = {1, 0, 0,
0, 1, 0,
0, 0, 1};
// MPU6050自测试
// 返回值:0,正常
// 其他,失败
uint8_t run_self_test(void)
{
int result;
// char test_packet[4] = {0};
long gyro[3], accel[3];
result = mpu_run_self_test(gyro, accel);
if (result == 0x3)
{
/* Test passed. We can trust the gyro data here, so let's push it down
* to the DMP.
*/
float sens;
unsigned short accel_sens;
mpu_get_gyro_sens(&sens);
gyro[0] = (long)(gyro[0] * sens);
gyro[1] = (long)(gyro[1] * sens);
gyro[2] = (long)(gyro[2] * sens);
dmp_set_gyro_bias(gyro);
mpu_get_accel_sens(&accel_sens);
accel[0] *= accel_sens;
accel[1] *= accel_sens;
accel[2] *= accel_sens;
dmp_set_accel_bias(accel);
return 0;
}
else
return 1;
}
// 陀螺仪方向控制
unsigned short inv_orientation_matrix_to_scalar(
const signed char *mtx)
{
unsigned short scalar;
/*
XYZ 010_001_000 Identity Matrix
XZY 001_010_000
YXZ 010_000_001
YZX 000_010_001
ZXY 001_000_010
ZYX 000_001_010
*/
scalar = inv_row_2_scale(mtx);
scalar |= inv_row_2_scale(mtx + 3) << 3;
scalar |= inv_row_2_scale(mtx + 6) << 6;
return scalar;
}
// 方向转换
unsigned short inv_row_2_scale(const signed char *row)
{
unsigned short b;
if (row[0] > 0)
b = 0;
else if (row[0] < 0)
b = 4;
else if (row[1] > 0)
b = 1;
else if (row[1] < 0)
b = 5;
else if (row[2] > 0)
b = 2;
else if (row[2] < 0)
b = 6;
else
b = 7; // error
return b;
}
// 空函数,未用到.
void mget_ms(unsigned long *time)
{
}
// mpu6050,dmp初始化
// 返回值:0,正常
// 其他,失败
uint8_t mpu_dmp_init(void)
{
uint8_t res = 0;
MPU_IIC_Init(); // 初始化IIC总线
if (mpu_init() == 0) // 初始化MPU6050
{
res = mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL); // 设置所需要的传感器
if (res)
return 1;
res = mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL); // 设置FIFO
if (res)
return 2;
res = mpu_set_sample_rate(DEFAULT_MPU_HZ); // 设置采样率
if (res)
return 3;
res = dmp_load_motion_driver_firmware(); // 加载dmp固件
if (res)
return 4;
res = dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation)); // 设置陀螺仪方向
if (res)
return 5;
res = dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT | DMP_FEATURE_TAP | // 设置dmp功能
DMP_FEATURE_ANDROID_ORIENT | DMP_FEATURE_SEND_RAW_ACCEL | DMP_FEATURE_SEND_CAL_GYRO |
DMP_FEATURE_GYRO_CAL);
if (res)
return 6;
res = dmp_set_fifo_rate(DEFAULT_MPU_HZ); // 设置DMP输出速率(最大不超过200Hz)
if (res)
return 7;
res = run_self_test(); // 自检
if (res)
return 8;
res = mpu_set_dmp_state(1); // 使能DMP
if (res)
return 9;
}
else
return 10;
return 0;
}
// 得到dmp处理后的数据(注意,本函数需要比较多堆栈,局部变量有点多)
// pitch:俯仰角 精度:0.1° 范围:-90.0° <---> +90.0°
// roll:横滚角 精度:0.1° 范围:-180.0°<---> +180.0°
// yaw:航向角 精度:0.1° 范围:-180.0°<---> +180.0°
// 返回值:0,正常
// 其他,失败
uint8_t mpu_dmp_get_data(float *pitch, float *roll, float *yaw)
{
float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f;
unsigned long sensor_timestamp;
short gyro[3], accel[3], sensors;
unsigned char more;
long quat[4];
if (dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors, &more))
return 1;
/* Gyro and accel data are written to the FIFO by the DMP in chip frame and hardware units.
* This behavior is convenient because it keeps the gyro and accel outputs of dmp_read_fifo and mpu_read_fifo consistent.
**/
/*if (sensors & INV_XYZ_GYRO )
send_packet(PACKET_TYPE_GYRO, gyro);
if (sensors & INV_XYZ_ACCEL)
send_packet(PACKET_TYPE_ACCEL, accel); */
/* Unlike gyro and accel, quaternions are written to the FIFO in the body frame, q30.
* The orientation is set by the scalar passed to dmp_set_orientation during initialization.
**/
if (sensors & INV_WXYZ_QUAT)
{
q0 = quat[0] / q30; // q30格式转换为浮点数
q1 = quat[1] / q30;
q2 = quat[2] / q30;
q3 = quat[3] / q30;
// 计算得到俯仰角/横滚角/航向角
*pitch = asin(-2 * q1 * q3 + 2 * q0 * q2) * 57.3; // pitch
*roll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2 * q2 + 1) * 57.3; // roll
*yaw = atan2(2 * (q1 * q2 + q0 * q3), q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3) * 57.3; // yaw
}
else
return 2;
return 0;
}
/*
* inv_mpu_dmp_motion_driver.c
*
* Created on: Jul 11, 2023
* Author: zzy
*/
/*
$License:
Copyright (C) 2011-2012 InvenSense Corporation, All Rights Reserved.
See included License.txt for License information.
$
*/
/**
* @addtogroup DRIVERS Sensor Driver Layer
* @brief Hardware drivers to communicate with sensors via I2C.
*
* @{
* @file inv_mpu_dmp_motion_driver.c
* @brief DMP image and interface functions.
* @details All functions are preceded by the dmp_ prefix to
* differentiate among MPL and general driver function calls.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
#include "dmpKey.h"
#include "dmpmap.h"
#include "usart.h"
// 定义目标板采用MSP430
#define MOTION_DRIVER_TARGET_MSP430
/* The following functions must be defined for this platform:
* i2c_write(unsigned char slave_addr, unsigned char reg_addr,
* unsigned char length, unsigned char const *data)
* i2c_read(unsigned char slave_addr, unsigned char reg_addr,
* unsigned char length, unsigned char *data)
* delay_ms(unsigned long num_ms)
* get_ms(unsigned long *count)
*/
#if defined MOTION_DRIVER_TARGET_MSP430
// #include "msp430.h"
// #include "msp430_clock.h"
#define delay_ms delay_ms
#define get_ms mget_ms
#define log_i printf
#define log_e printf
#elif defined EMPL_TARGET_MSP430
#include "msp430.h"
#include "msp430_clock.h"
#include "log.h"
#define delay_ms msp430_delay_ms
#define get_ms msp430_get_clock_ms
#define log_i MPL_LOGI
#define log_e MPL_LOGE
#elif defined EMPL_TARGET_UC3L0
/* Instead of using the standard TWI driver from the ASF library, we're using
* a TWI driver that follows the slave address + register address convention.
*/
#include "delay.h"
#include "sysclk.h"
#include "log.h"
#include "uc3l0_clock.h"
/* delay_ms is a function already defined in ASF. */
#define get_ms uc3l0_get_clock_ms
#define log_i MPL_LOGI
#define log_e MPL_LOGE
#else
#error Gyro driver is missing the system layer implementations.
#endif
/* These defines are copied from dmpDefaultMPU6050.c in the general MPL
* releases. These defines may change for each DMP image, so be sure to modify
* these values when switching to a new image.
*/
#define CFG_LP_QUAT (2712)
#define END_ORIENT_TEMP (1866)
#define CFG_27 (2742)
#define CFG_20 (2224)
#define CFG_23 (2745)
#define CFG_FIFO_ON_EVENT (2690)
#define END_PREDICTION_UPDATE (1761)
#define CGNOTICE_INTR (2620)
#define X_GRT_Y_TMP (1358)
#define CFG_DR_INT (1029)
#define CFG_AUTH (1035)
#define UPDATE_PROP_ROT (1835)
#define END_COMPARE_Y_X_TMP2 (1455)
#define SKIP_X_GRT_Y_TMP (1359)
#define SKIP_END_COMPARE (1435)
#define FCFG_3 (1088)
#define FCFG_2 (1066)
#define FCFG_1 (1062)
#define END_COMPARE_Y_X_TMP3 (1434)
#define FCFG_7 (1073)
#define FCFG_6 (1106)
#define FLAT_STATE_END (1713)
#define SWING_END_4 (1616)
#define SWING_END_2 (1565)
#define SWING_END_3 (1587)
#define SWING_END_1 (1550)
#define CFG_8 (2718)
#define CFG_15 (2727)
#define CFG_16 (2746)
#define CFG_EXT_GYRO_BIAS (1189)
#define END_COMPARE_Y_X_TMP (1407)
#define DO_NOT_UPDATE_PROP_ROT (1839)
#define CFG_7 (1205)
#define FLAT_STATE_END_TEMP (1683)
#define END_COMPARE_Y_X (1484)
#define SKIP_SWING_END_1 (1551)
#define SKIP_SWING_END_3 (1588)
#define SKIP_SWING_END_2 (1566)
#define TILTG75_START (1672)
#define CFG_6 (2753)
#define TILTL75_END (1669)
#define END_ORIENT (1884)
#define CFG_FLICK_IN (2573)
#define TILTL75_START (1643)
#define CFG_MOTION_BIAS (1208)
#define X_GRT_Y (1408)
#define TEMPLABEL (2324)
#define CFG_ANDROID_ORIENT_INT (1853)
#define CFG_GYRO_RAW_DATA (2722)
#define X_GRT_Y_TMP2 (1379)
#define D_0_22 (22 + 512)
#define D_0_24 (24 + 512)
#define D_0_36 (36)
#define D_0_52 (52)
#define D_0_96 (96)
#define D_0_104 (104)
#define D_0_108 (108)
#define D_0_163 (163)
#define D_0_188 (188)
#define D_0_192 (192)
#define D_0_224 (224)
#define D_0_228 (228)
#define D_0_232 (232)
#define D_0_236 (236)
#define D_1_2 (256 + 2)
#define D_1_4 (256 + 4)
#define D_1_8 (256 + 8)
#define D_1_10 (256 + 10)
#define D_1_24 (256 + 24)
#define D_1_28 (256 + 28)
#define D_1_36 (256 + 36)
#define D_1_40 (256 + 40)
#define D_1_44 (256 + 44)
#define D_1_72 (256 + 72)
#define D_1_74 (256 + 74)
#define D_1_79 (256 + 79)
#define D_1_88 (256 + 88)
#define D_1_90 (256 + 90)
#define D_1_92 (256 + 92)
#define D_1_96 (256 + 96)
#define D_1_98 (256 + 98)
#define D_1_106 (256 + 106)
#define D_1_108 (256 + 108)
#define D_1_112 (256 + 112)
#define D_1_128 (256 + 144)
#define D_1_152 (256 + 12)
#define D_1_160 (256 + 160)
#define D_1_176 (256 + 176)
#define D_1_178 (256 + 178)
#define D_1_218 (256 + 218)
#define D_1_232 (256 + 232)
#define D_1_236 (256 + 236)
#define D_1_240 (256 + 240)
#define D_1_244 (256 + 244)
#define D_1_250 (256 + 250)
#define D_1_252 (256 + 252)
#define D_2_12 (512 + 12)
#define D_2_96 (512 + 96)
#define D_2_108 (512 + 108)
#define D_2_208 (512 + 208)
#define D_2_224 (512 + 224)
#define D_2_236 (512 + 236)
#define D_2_244 (512 + 244)
#define D_2_248 (512 + 248)
#define D_2_252 (512 + 252)
#define CPASS_BIAS_X (35 * 16 + 4)
#define CPASS_BIAS_Y (35 * 16 + 8)
#define CPASS_BIAS_Z (35 * 16 + 12)
#define CPASS_MTX_00 (36 * 16)
#define CPASS_MTX_01 (36 * 16 + 4)
#define CPASS_MTX_02 (36 * 16 + 8)
#define CPASS_MTX_10 (36 * 16 + 12)
#define CPASS_MTX_11 (37 * 16)
#define CPASS_MTX_12 (37 * 16 + 4)
#define CPASS_MTX_20 (37 * 16 + 8)
#define CPASS_MTX_21 (37 * 16 + 12)
#define CPASS_MTX_22 (43 * 16 + 12)
#define D_EXT_GYRO_BIAS_X (61 * 16)
#define D_EXT_GYRO_BIAS_Y (61 * 16) + 4
#define D_EXT_GYRO_BIAS_Z (61 * 16) + 8
#define D_ACT0 (40 * 16)
#define D_ACSX (40 * 16 + 4)
#define D_ACSY (40 * 16 + 8)
#define D_ACSZ (40 * 16 + 12)
#define FLICK_MSG (45 * 16 + 4)
#define FLICK_COUNTER (45 * 16 + 8)
#define FLICK_LOWER (45 * 16 + 12)
#define FLICK_UPPER (46 * 16 + 12)
#define D_AUTH_OUT (992)
#define D_AUTH_IN (996)
#define D_AUTH_A (1000)
#define D_AUTH_B (1004)
#define D_PEDSTD_BP_B (768 + 0x1C)
#define D_PEDSTD_HP_A (768 + 0x78)
#define D_PEDSTD_HP_B (768 + 0x7C)
#define D_PEDSTD_BP_A4 (768 + 0x40)
#define D_PEDSTD_BP_A3 (768 + 0x44)
#define D_PEDSTD_BP_A2 (768 + 0x48)
#define D_PEDSTD_BP_A1 (768 + 0x4C)
#define D_PEDSTD_INT_THRSH (768 + 0x68)
#define D_PEDSTD_CLIP (768 + 0x6C)
#define D_PEDSTD_SB (768 + 0x28)
#define D_PEDSTD_SB_TIME (768 + 0x2C)
#define D_PEDSTD_PEAKTHRSH (768 + 0x98)
#define D_PEDSTD_TIML (768 + 0x2A)
#define D_PEDSTD_TIMH (768 + 0x2E)
#define D_PEDSTD_PEAK (768 + 0X94)
#define D_PEDSTD_STEPCTR (768 + 0x60)
#define D_PEDSTD_TIMECTR (964)
#define D_PEDSTD_DECI (768 + 0xA0)
#define D_HOST_NO_MOT (976)
#define D_ACCEL_BIAS (660)
#define D_ORIENT_GAP (76)
#define D_TILT0_H (48)
#define D_TILT0_L (50)
#define D_TILT1_H (52)
#define D_TILT1_L (54)
#define D_TILT2_H (56)
#define D_TILT2_L (58)
#define D_TILT3_H (60)
#define D_TILT3_L (62)
#define DMP_CODE_SIZE (3062)
static const unsigned char dmp_memory[DMP_CODE_SIZE] = {
/* bank # 0 */
0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00,
0x00, 0x65, 0x00, 0x54, 0xff, 0xef, 0x00, 0x00, 0xfa, 0x80, 0x00, 0x0b, 0x12, 0x82, 0x00, 0x01,
0x03, 0x0c, 0x30, 0xc3, 0x0e, 0x8c, 0x8c, 0xe9, 0x14, 0xd5, 0x40, 0x02, 0x13, 0x71, 0x0f, 0x8e,
0x38, 0x83, 0xf8, 0x83, 0x30, 0x00, 0xf8, 0x83, 0x25, 0x8e, 0xf8, 0x83, 0x30, 0x00, 0xf8, 0x83,
0xff, 0xff, 0xff, 0xff, 0x0f, 0xfe, 0xa9, 0xd6, 0x24, 0x00, 0x04, 0x00, 0x1a, 0x82, 0x79, 0xa1,
0x00, 0x00, 0x00, 0x3c, 0xff, 0xff, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x38, 0x83, 0x6f, 0xa2,
0x00, 0x3e, 0x03, 0x30, 0x40, 0x00, 0x00, 0x00, 0x02, 0xca, 0xe3, 0x09, 0x3e, 0x80, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x6e, 0x00, 0x00, 0x06, 0x92, 0x0a, 0x16, 0xc0, 0xdf,
0xff, 0xff, 0x02, 0x56, 0xfd, 0x8c, 0xd3, 0x77, 0xff, 0xe1, 0xc4, 0x96, 0xe0, 0xc5, 0xbe, 0xaa,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0b, 0x2b, 0x00, 0x00, 0x16, 0x57, 0x00, 0x00, 0x03, 0x59,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0xfa, 0x00, 0x02, 0x6c, 0x1d, 0x00, 0x00, 0x00, 0x00,
0x3f, 0xff, 0xdf, 0xeb, 0x00, 0x3e, 0xb3, 0xb6, 0x00, 0x0d, 0x22, 0x78, 0x00, 0x00, 0x2f, 0x3c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x42, 0xb5, 0x00, 0x00, 0x39, 0xa2, 0x00, 0x00, 0xb3, 0x65,
0xd9, 0x0e, 0x9f, 0xc9, 0x1d, 0xcf, 0x4c, 0x34, 0x30, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
0x3b, 0xb6, 0x7a, 0xe8, 0x00, 0x64, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* bank # 1 */
0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0xfa, 0x92, 0x10, 0x00, 0x22, 0x5e, 0x00, 0x0d, 0x22, 0x9f,
0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0xff, 0x46, 0x00, 0x00, 0x63, 0xd4, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x04, 0xd6, 0x00, 0x00, 0x04, 0xcc, 0x00, 0x00, 0x04, 0xcc, 0x00, 0x00,
0x00, 0x00, 0x10, 0x72, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x06, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x64, 0x00, 0x20, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x03, 0x00,
0x00, 0x00, 0x00, 0x32, 0xf8, 0x98, 0x00, 0x00, 0xff, 0x65, 0x00, 0x00, 0x83, 0x0f, 0x00, 0x00,
0xff, 0x9b, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xb2, 0x6a, 0x00, 0x02, 0x00, 0x00,
0x00, 0x01, 0xfb, 0x83, 0x00, 0x68, 0x00, 0x00, 0x00, 0xd9, 0xfc, 0x00, 0x7c, 0xf1, 0xff, 0x83,
0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x64, 0x03, 0xe8, 0x00, 0x64, 0x00, 0x28,
0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x16, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x10, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x10, 0x00,
/* bank # 2 */
0x00, 0x28, 0x00, 0x00, 0xff, 0xff, 0x45, 0x81, 0xff, 0xff, 0xfa, 0x72, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x05, 0x00, 0x05, 0xba, 0xc6, 0x00, 0x47, 0x78, 0xa2,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x14,
0x00, 0x00, 0x25, 0x4d, 0x00, 0x2f, 0x70, 0x6d, 0x00, 0x00, 0x05, 0xae, 0x00, 0x0c, 0x02, 0xd0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x64, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x0e,
0x00, 0x00, 0x0a, 0xc7, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0xff, 0xff, 0xff, 0x9c,
0x00, 0x00, 0x0b, 0x2b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64,
0xff, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* bank # 3 */
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x24, 0x26, 0xd3,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x10, 0x00, 0x96, 0x00, 0x3c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0c, 0x0a, 0x4e, 0x68, 0xcd, 0xcf, 0x77, 0x09, 0x50, 0x16, 0x67, 0x59, 0xc6, 0x19, 0xce, 0x82,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xd7, 0x84, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x93, 0x8f, 0x9d, 0x1e, 0x1b, 0x1c, 0x19,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x18, 0x85, 0x00, 0x00, 0x40, 0x00,
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x67, 0x7d, 0xdf, 0x7e, 0x72, 0x90, 0x2e, 0x55, 0x4c, 0xf6, 0xe6, 0x88,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* bank # 4 */
0xd8, 0xdc, 0xb4, 0xb8, 0xb0, 0xd8, 0xb9, 0xab, 0xf3, 0xf8, 0xfa, 0xb3, 0xb7, 0xbb, 0x8e, 0x9e,
0xae, 0xf1, 0x32, 0xf5, 0x1b, 0xf1, 0xb4, 0xb8, 0xb0, 0x80, 0x97, 0xf1, 0xa9, 0xdf, 0xdf, 0xdf,
0xaa, 0xdf, 0xdf, 0xdf, 0xf2, 0xaa, 0xc5, 0xcd, 0xc7, 0xa9, 0x0c, 0xc9, 0x2c, 0x97, 0xf1, 0xa9,
0x89, 0x26, 0x46, 0x66, 0xb2, 0x89, 0x99, 0xa9, 0x2d, 0x55, 0x7d, 0xb0, 0xb0, 0x8a, 0xa8, 0x96,
0x36, 0x56, 0x76, 0xf1, 0xba, 0xa3, 0xb4, 0xb2, 0x80, 0xc0, 0xb8, 0xa8, 0x97, 0x11, 0xb2, 0x83,
0x98, 0xba, 0xa3, 0xf0, 0x24, 0x08, 0x44, 0x10, 0x64, 0x18, 0xb2, 0xb9, 0xb4, 0x98, 0x83, 0xf1,
0xa3, 0x29, 0x55, 0x7d, 0xba, 0xb5, 0xb1, 0xa3, 0x83, 0x93, 0xf0, 0x00, 0x28, 0x50, 0xf5, 0xb2,
0xb6, 0xaa, 0x83, 0x93, 0x28, 0x54, 0x7c, 0xf1, 0xb9, 0xa3, 0x82, 0x93, 0x61, 0xba, 0xa2, 0xda,
0xde, 0xdf, 0xdb, 0x81, 0x9a, 0xb9, 0xae, 0xf5, 0x60, 0x68, 0x70, 0xf1, 0xda, 0xba, 0xa2, 0xdf,
0xd9, 0xba, 0xa2, 0xfa, 0xb9, 0xa3, 0x82, 0x92, 0xdb, 0x31, 0xba, 0xa2, 0xd9, 0xba, 0xa2, 0xf8,
0xdf, 0x85, 0xa4, 0xd0, 0xc1, 0xbb, 0xad, 0x83, 0xc2, 0xc5, 0xc7, 0xb8, 0xa2, 0xdf, 0xdf, 0xdf,
0xba, 0xa0, 0xdf, 0xdf, 0xdf, 0xd8, 0xd8, 0xf1, 0xb8, 0xaa, 0xb3, 0x8d, 0xb4, 0x98, 0x0d, 0x35,
0x5d, 0xb2, 0xb6, 0xba, 0xaf, 0x8c, 0x96, 0x19, 0x8f, 0x9f, 0xa7, 0x0e, 0x16, 0x1e, 0xb4, 0x9a,
0xb8, 0xaa, 0x87, 0x2c, 0x54, 0x7c, 0xba, 0xa4, 0xb0, 0x8a, 0xb6, 0x91, 0x32, 0x56, 0x76, 0xb2,
0x84, 0x94, 0xa4, 0xc8, 0x08, 0xcd, 0xd8, 0xb8, 0xb4, 0xb0, 0xf1, 0x99, 0x82, 0xa8, 0x2d, 0x55,
0x7d, 0x98, 0xa8, 0x0e, 0x16, 0x1e, 0xa2, 0x2c, 0x54, 0x7c, 0x92, 0xa4, 0xf0, 0x2c, 0x50, 0x78,
/* bank # 5 */
0xf1, 0x84, 0xa8, 0x98, 0xc4, 0xcd, 0xfc, 0xd8, 0x0d, 0xdb, 0xa8, 0xfc, 0x2d, 0xf3, 0xd9, 0xba,
0xa6, 0xf8, 0xda, 0xba, 0xa6, 0xde, 0xd8, 0xba, 0xb2, 0xb6, 0x86, 0x96, 0xa6, 0xd0, 0xf3, 0xc8,
0x41, 0xda, 0xa6, 0xc8, 0xf8, 0xd8, 0xb0, 0xb4, 0xb8, 0x82, 0xa8, 0x92, 0xf5, 0x2c, 0x54, 0x88,
0x98, 0xf1, 0x35, 0xd9, 0xf4, 0x18, 0xd8, 0xf1, 0xa2, 0xd0, 0xf8, 0xf9, 0xa8, 0x84, 0xd9, 0xc7,
0xdf, 0xf8, 0xf8, 0x83, 0xc5, 0xda, 0xdf, 0x69, 0xdf, 0x83, 0xc1, 0xd8, 0xf4, 0x01, 0x14, 0xf1,
0xa8, 0x82, 0x4e, 0xa8, 0x84, 0xf3, 0x11, 0xd1, 0x82, 0xf5, 0xd9, 0x92, 0x28, 0x97, 0x88, 0xf1,
0x09, 0xf4, 0x1c, 0x1c, 0xd8, 0x84, 0xa8, 0xf3, 0xc0, 0xf9, 0xd1, 0xd9, 0x97, 0x82, 0xf1, 0x29,
0xf4, 0x0d, 0xd8, 0xf3, 0xf9, 0xf9, 0xd1, 0xd9, 0x82, 0xf4, 0xc2, 0x03, 0xd8, 0xde, 0xdf, 0x1a,
0xd8, 0xf1, 0xa2, 0xfa, 0xf9, 0xa8, 0x84, 0x98, 0xd9, 0xc7, 0xdf, 0xf8, 0xf8, 0xf8, 0x83, 0xc7,
0xda, 0xdf, 0x69, 0xdf, 0xf8, 0x83, 0xc3, 0xd8, 0xf4, 0x01, 0x14, 0xf1, 0x98, 0xa8, 0x82, 0x2e,
0xa8, 0x84, 0xf3, 0x11, 0xd1, 0x82, 0xf5, 0xd9, 0x92, 0x50, 0x97, 0x88, 0xf1, 0x09, 0xf4, 0x1c,
0xd8, 0x84, 0xa8, 0xf3, 0xc0, 0xf8, 0xf9, 0xd1, 0xd9, 0x97, 0x82, 0xf1, 0x49, 0xf4, 0x0d, 0xd8,
0xf3, 0xf9, 0xf9, 0xd1, 0xd9, 0x82, 0xf4, 0xc4, 0x03, 0xd8, 0xde, 0xdf, 0xd8, 0xf1, 0xad, 0x88,
0x98, 0xcc, 0xa8, 0x09, 0xf9, 0xd9, 0x82, 0x92, 0xa8, 0xf5, 0x7c, 0xf1, 0x88, 0x3a, 0xcf, 0x94,
0x4a, 0x6e, 0x98, 0xdb, 0x69, 0x31, 0xda, 0xad, 0xf2, 0xde, 0xf9, 0xd8, 0x87, 0x95, 0xa8, 0xf2,
0x21, 0xd1, 0xda, 0xa5, 0xf9, 0xf4, 0x17, 0xd9, 0xf1, 0xae, 0x8e, 0xd0, 0xc0, 0xc3, 0xae, 0x82,
/* bank # 6 */
0xc6, 0x84, 0xc3, 0xa8, 0x85, 0x95, 0xc8, 0xa5, 0x88, 0xf2, 0xc0, 0xf1, 0xf4, 0x01, 0x0e, 0xf1,
0x8e, 0x9e, 0xa8, 0xc6, 0x3e, 0x56, 0xf5, 0x54, 0xf1, 0x88, 0x72, 0xf4, 0x01, 0x15, 0xf1, 0x98,
0x45, 0x85, 0x6e, 0xf5, 0x8e, 0x9e, 0x04, 0x88, 0xf1, 0x42, 0x98, 0x5a, 0x8e, 0x9e, 0x06, 0x88,
0x69, 0xf4, 0x01, 0x1c, 0xf1, 0x98, 0x1e, 0x11, 0x08, 0xd0, 0xf5, 0x04, 0xf1, 0x1e, 0x97, 0x02,
0x02, 0x98, 0x36, 0x25, 0xdb, 0xf9, 0xd9, 0x85, 0xa5, 0xf3, 0xc1, 0xda, 0x85, 0xa5, 0xf3, 0xdf,
0xd8, 0x85, 0x95, 0xa8, 0xf3, 0x09, 0xda, 0xa5, 0xfa, 0xd8, 0x82, 0x92, 0xa8, 0xf5, 0x78, 0xf1,
0x88, 0x1a, 0x84, 0x9f, 0x26, 0x88, 0x98, 0x21, 0xda, 0xf4, 0x1d, 0xf3, 0xd8, 0x87, 0x9f, 0x39,
0xd1, 0xaf, 0xd9, 0xdf, 0xdf, 0xfb, 0xf9, 0xf4, 0x0c, 0xf3, 0xd8, 0xfa, 0xd0, 0xf8, 0xda, 0xf9,
0xf9, 0xd0, 0xdf, 0xd9, 0xf9, 0xd8, 0xf4, 0x0b, 0xd8, 0xf3, 0x87, 0x9f, 0x39, 0xd1, 0xaf, 0xd9,
0xdf, 0xdf, 0xf4, 0x1d, 0xf3, 0xd8, 0xfa, 0xfc, 0xa8, 0x69, 0xf9, 0xf9, 0xaf, 0xd0, 0xda, 0xde,
0xfa, 0xd9, 0xf8, 0x8f, 0x9f, 0xa8, 0xf1, 0xcc, 0xf3, 0x98, 0xdb, 0x45, 0xd9, 0xaf, 0xdf, 0xd0,
0xf8, 0xd8, 0xf1, 0x8f, 0x9f, 0xa8, 0xca, 0xf3, 0x88, 0x09, 0xda, 0xaf, 0x8f, 0xcb, 0xf8, 0xd8,
0xf2, 0xad, 0x97, 0x8d, 0x0c, 0xd9, 0xa5, 0xdf, 0xf9, 0xba, 0xa6, 0xf3, 0xfa, 0xf4, 0x12, 0xf2,
0xd8, 0x95, 0x0d, 0xd1, 0xd9, 0xba, 0xa6, 0xf3, 0xfa, 0xda, 0xa5, 0xf2, 0xc1, 0xba, 0xa6, 0xf3,
0xdf, 0xd8, 0xf1, 0xba, 0xb2, 0xb6, 0x86, 0x96, 0xa6, 0xd0, 0xca, 0xf3, 0x49, 0xda, 0xa6, 0xcb,
0xf8, 0xd8, 0xb0, 0xb4, 0xb8, 0xd8, 0xad, 0x84, 0xf2, 0xc0, 0xdf, 0xf1, 0x8f, 0xcb, 0xc3, 0xa8,
/* bank # 7 */
0xb2, 0xb6, 0x86, 0x96, 0xc8, 0xc1, 0xcb, 0xc3, 0xf3, 0xb0, 0xb4, 0x88, 0x98, 0xa8, 0x21, 0xdb,
0x71, 0x8d, 0x9d, 0x71, 0x85, 0x95, 0x21, 0xd9, 0xad, 0xf2, 0xfa, 0xd8, 0x85, 0x97, 0xa8, 0x28,
0xd9, 0xf4, 0x08, 0xd8, 0xf2, 0x8d, 0x29, 0xda, 0xf4, 0x05, 0xd9, 0xf2, 0x85, 0xa4, 0xc2, 0xf2,
0xd8, 0xa8, 0x8d, 0x94, 0x01, 0xd1, 0xd9, 0xf4, 0x11, 0xf2, 0xd8, 0x87, 0x21, 0xd8, 0xf4, 0x0a,
0xd8, 0xf2, 0x84, 0x98, 0xa8, 0xc8, 0x01, 0xd1, 0xd9, 0xf4, 0x11, 0xd8, 0xf3, 0xa4, 0xc8, 0xbb,
0xaf, 0xd0, 0xf2, 0xde, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xd8, 0xf1, 0xb8, 0xf6,
0xb5, 0xb9, 0xb0, 0x8a, 0x95, 0xa3, 0xde, 0x3c, 0xa3, 0xd9, 0xf8, 0xd8, 0x5c, 0xa3, 0xd9, 0xf8,
0xd8, 0x7c, 0xa3, 0xd9, 0xf8, 0xd8, 0xf8, 0xf9, 0xd1, 0xa5, 0xd9, 0xdf, 0xda, 0xfa, 0xd8, 0xb1,
0x85, 0x30, 0xf7, 0xd9, 0xde, 0xd8, 0xf8, 0x30, 0xad, 0xda, 0xde, 0xd8, 0xf2, 0xb4, 0x8c, 0x99,
0xa3, 0x2d, 0x55, 0x7d, 0xa0, 0x83, 0xdf, 0xdf, 0xdf, 0xb5, 0x91, 0xa0, 0xf6, 0x29, 0xd9, 0xfb,
0xd8, 0xa0, 0xfc, 0x29, 0xd9, 0xfa, 0xd8, 0xa0, 0xd0, 0x51, 0xd9, 0xf8, 0xd8, 0xfc, 0x51, 0xd9,
0xf9, 0xd8, 0x79, 0xd9, 0xfb, 0xd8, 0xa0, 0xd0, 0xfc, 0x79, 0xd9, 0xfa, 0xd8, 0xa1, 0xf9, 0xf9,
0xf9, 0xf9, 0xf9, 0xa0, 0xda, 0xdf, 0xdf, 0xdf, 0xd8, 0xa1, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xac,
0xde, 0xf8, 0xad, 0xde, 0x83, 0x93, 0xac, 0x2c, 0x54, 0x7c, 0xf1, 0xa8, 0xdf, 0xdf, 0xdf, 0xf6,
0x9d, 0x2c, 0xda, 0xa0, 0xdf, 0xd9, 0xfa, 0xdb, 0x2d, 0xf8, 0xd8, 0xa8, 0x50, 0xda, 0xa0, 0xd0,
0xde, 0xd9, 0xd0, 0xf8, 0xf8, 0xf8, 0xdb, 0x55, 0xf8, 0xd8, 0xa8, 0x78, 0xda, 0xa0, 0xd0, 0xdf,
/* bank # 8 */
0xd9, 0xd0, 0xfa, 0xf8, 0xf8, 0xf8, 0xf8, 0xdb, 0x7d, 0xf8, 0xd8, 0x9c, 0xa8, 0x8c, 0xf5, 0x30,
0xdb, 0x38, 0xd9, 0xd0, 0xde, 0xdf, 0xa0, 0xd0, 0xde, 0xdf, 0xd8, 0xa8, 0x48, 0xdb, 0x58, 0xd9,
0xdf, 0xd0, 0xde, 0xa0, 0xdf, 0xd0, 0xde, 0xd8, 0xa8, 0x68, 0xdb, 0x70, 0xd9, 0xdf, 0xdf, 0xa0,
0xdf, 0xdf, 0xd8, 0xf1, 0xa8, 0x88, 0x90, 0x2c, 0x54, 0x7c, 0x98, 0xa8, 0xd0, 0x5c, 0x38, 0xd1,
0xda, 0xf2, 0xae, 0x8c, 0xdf, 0xf9, 0xd8, 0xb0, 0x87, 0xa8, 0xc1, 0xc1, 0xb1, 0x88, 0xa8, 0xc6,
0xf9, 0xf9, 0xda, 0x36, 0xd8, 0xa8, 0xf9, 0xda, 0x36, 0xd8, 0xa8, 0xf9, 0xda, 0x36, 0xd8, 0xa8,
0xf9, 0xda, 0x36, 0xd8, 0xa8, 0xf9, 0xda, 0x36, 0xd8, 0xf7, 0x8d, 0x9d, 0xad, 0xf8, 0x18, 0xda,
0xf2, 0xae, 0xdf, 0xd8, 0xf7, 0xad, 0xfa, 0x30, 0xd9, 0xa4, 0xde, 0xf9, 0xd8, 0xf2, 0xae, 0xde,
0xfa, 0xf9, 0x83, 0xa7, 0xd9, 0xc3, 0xc5, 0xc7, 0xf1, 0x88, 0x9b, 0xa7, 0x7a, 0xad, 0xf7, 0xde,
0xdf, 0xa4, 0xf8, 0x84, 0x94, 0x08, 0xa7, 0x97, 0xf3, 0x00, 0xae, 0xf2, 0x98, 0x19, 0xa4, 0x88,
0xc6, 0xa3, 0x94, 0x88, 0xf6, 0x32, 0xdf, 0xf2, 0x83, 0x93, 0xdb, 0x09, 0xd9, 0xf2, 0xaa, 0xdf,
0xd8, 0xd8, 0xae, 0xf8, 0xf9, 0xd1, 0xda, 0xf3, 0xa4, 0xde, 0xa7, 0xf1, 0x88, 0x9b, 0x7a, 0xd8,
0xf3, 0x84, 0x94, 0xae, 0x19, 0xf9, 0xda, 0xaa, 0xf1, 0xdf, 0xd8, 0xa8, 0x81, 0xc0, 0xc3, 0xc5,
0xc7, 0xa3, 0x92, 0x83, 0xf6, 0x28, 0xad, 0xde, 0xd9, 0xf8, 0xd8, 0xa3, 0x50, 0xad, 0xd9, 0xf8,
0xd8, 0xa3, 0x78, 0xad, 0xd9, 0xf8, 0xd8, 0xf8, 0xf9, 0xd1, 0xa1, 0xda, 0xde, 0xc3, 0xc5, 0xc7,
0xd8, 0xa1, 0x81, 0x94, 0xf8, 0x18, 0xf2, 0xb0, 0x89, 0xac, 0xc3, 0xc5, 0xc7, 0xf1, 0xd8, 0xb8,
/* bank # 9 */
0xb4, 0xb0, 0x97, 0x86, 0xa8, 0x31, 0x9b, 0x06, 0x99, 0x07, 0xab, 0x97, 0x28, 0x88, 0x9b, 0xf0,
0x0c, 0x20, 0x14, 0x40, 0xb0, 0xb4, 0xb8, 0xf0, 0xa8, 0x8a, 0x9a, 0x28, 0x50, 0x78, 0xb7, 0x9b,
0xa8, 0x29, 0x51, 0x79, 0x24, 0x70, 0x59, 0x44, 0x69, 0x38, 0x64, 0x48, 0x31, 0xf1, 0xbb, 0xab,
0x88, 0x00, 0x2c, 0x54, 0x7c, 0xf0, 0xb3, 0x8b, 0xb8, 0xa8, 0x04, 0x28, 0x50, 0x78, 0xf1, 0xb0,
0x88, 0xb4, 0x97, 0x26, 0xa8, 0x59, 0x98, 0xbb, 0xab, 0xb3, 0x8b, 0x02, 0x26, 0x46, 0x66, 0xb0,
0xb8, 0xf0, 0x8a, 0x9c, 0xa8, 0x29, 0x51, 0x79, 0x8b, 0x29, 0x51, 0x79, 0x8a, 0x24, 0x70, 0x59,
0x8b, 0x20, 0x58, 0x71, 0x8a, 0x44, 0x69, 0x38, 0x8b, 0x39, 0x40, 0x68, 0x8a, 0x64, 0x48, 0x31,
0x8b, 0x30, 0x49, 0x60, 0x88, 0xf1, 0xac, 0x00, 0x2c, 0x54, 0x7c, 0xf0, 0x8c, 0xa8, 0x04, 0x28,
0x50, 0x78, 0xf1, 0x88, 0x97, 0x26, 0xa8, 0x59, 0x98, 0xac, 0x8c, 0x02, 0x26, 0x46, 0x66, 0xf0,
0x89, 0x9c, 0xa8, 0x29, 0x51, 0x79, 0x24, 0x70, 0x59, 0x44, 0x69, 0x38, 0x64, 0x48, 0x31, 0xa9,
0x88, 0x09, 0x20, 0x59, 0x70, 0xab, 0x11, 0x38, 0x40, 0x69, 0xa8, 0x19, 0x31, 0x48, 0x60, 0x8c,
0xa8, 0x3c, 0x41, 0x5c, 0x20, 0x7c, 0x00, 0xf1, 0x87, 0x98, 0x19, 0x86, 0xa8, 0x6e, 0x76, 0x7e,
0xa9, 0x99, 0x88, 0x2d, 0x55, 0x7d, 0xd8, 0xb1, 0xb5, 0xb9, 0xa3, 0xdf, 0xdf, 0xdf, 0xae, 0xd0,
0xdf, 0xaa, 0xd0, 0xde, 0xf2, 0xab, 0xf8, 0xf9, 0xd9, 0xb0, 0x87, 0xc4, 0xaa, 0xf1, 0xdf, 0xdf,
0xbb, 0xaf, 0xdf, 0xdf, 0xb9, 0xd8, 0xb1, 0xf1, 0xa3, 0x97, 0x8e, 0x60, 0xdf, 0xb0, 0x84, 0xf2,
0xc8, 0xf8, 0xf9, 0xd9, 0xde, 0xd8, 0x93, 0x85, 0xf1, 0x4a, 0xb1, 0x83, 0xa3, 0x08, 0xb5, 0x83,
/* bank # 10 */
0x9a, 0x08, 0x10, 0xb7, 0x9f, 0x10, 0xd8, 0xf1, 0xb0, 0xba, 0xae, 0xb0, 0x8a, 0xc2, 0xb2, 0xb6,
0x8e, 0x9e, 0xf1, 0xfb, 0xd9, 0xf4, 0x1d, 0xd8, 0xf9, 0xd9, 0x0c, 0xf1, 0xd8, 0xf8, 0xf8, 0xad,
0x61, 0xd9, 0xae, 0xfb, 0xd8, 0xf4, 0x0c, 0xf1, 0xd8, 0xf8, 0xf8, 0xad, 0x19, 0xd9, 0xae, 0xfb,
0xdf, 0xd8, 0xf4, 0x16, 0xf1, 0xd8, 0xf8, 0xad, 0x8d, 0x61, 0xd9, 0xf4, 0xf4, 0xac, 0xf5, 0x9c,
0x9c, 0x8d, 0xdf, 0x2b, 0xba, 0xb6, 0xae, 0xfa, 0xf8, 0xf4, 0x0b, 0xd8, 0xf1, 0xae, 0xd0, 0xf8,
0xad, 0x51, 0xda, 0xae, 0xfa, 0xf8, 0xf1, 0xd8, 0xb9, 0xb1, 0xb6, 0xa3, 0x83, 0x9c, 0x08, 0xb9,
0xb1, 0x83, 0x9a, 0xb5, 0xaa, 0xc0, 0xfd, 0x30, 0x83, 0xb7, 0x9f, 0x10, 0xb5, 0x8b, 0x93, 0xf2,
0x02, 0x02, 0xd1, 0xab, 0xda, 0xde, 0xd8, 0xf1, 0xb0, 0x80, 0xba, 0xab, 0xc0, 0xc3, 0xb2, 0x84,
0xc1, 0xc3, 0xd8, 0xb1, 0xb9, 0xf3, 0x8b, 0xa3, 0x91, 0xb6, 0x09, 0xb4, 0xd9, 0xab, 0xde, 0xb0,
0x87, 0x9c, 0xb9, 0xa3, 0xdd, 0xf1, 0xb3, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0xb0, 0x87, 0xa3, 0xa3,
0xa3, 0xa3, 0xb2, 0x8b, 0xb6, 0x9b, 0xf2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xf1, 0xb0, 0x87, 0xb5, 0x9a, 0xa3, 0xf3, 0x9b, 0xa3, 0xa3, 0xdc, 0xba, 0xac, 0xdf, 0xb9,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xd8, 0xd8, 0xd8, 0xbb, 0xb3, 0xb7, 0xf1, 0xaa, 0xf9, 0xda, 0xff, 0xd9, 0x80, 0x9a, 0xaa, 0x28,
0xb4, 0x80, 0x98, 0xa7, 0x20, 0xb7, 0x97, 0x87, 0xa8, 0x66, 0x88, 0xf0, 0x79, 0x51, 0xf1, 0x90,
0x2c, 0x87, 0x0c, 0xa7, 0x81, 0x97, 0x62, 0x93, 0xf0, 0x71, 0x71, 0x60, 0x85, 0x94, 0x01, 0x29,
/* bank # 11 */
0x51, 0x79, 0x90, 0xa5, 0xf1, 0x28, 0x4c, 0x6c, 0x87, 0x0c, 0x95, 0x18, 0x85, 0x78, 0xa3, 0x83,
0x90, 0x28, 0x4c, 0x6c, 0x88, 0x6c, 0xd8, 0xf3, 0xa2, 0x82, 0x00, 0xf2, 0x10, 0xa8, 0x92, 0x19,
0x80, 0xa2, 0xf2, 0xd9, 0x26, 0xd8, 0xf1, 0x88, 0xa8, 0x4d, 0xd9, 0x48, 0xd8, 0x96, 0xa8, 0x39,
0x80, 0xd9, 0x3c, 0xd8, 0x95, 0x80, 0xa8, 0x39, 0xa6, 0x86, 0x98, 0xd9, 0x2c, 0xda, 0x87, 0xa7,
0x2c, 0xd8, 0xa8, 0x89, 0x95, 0x19, 0xa9, 0x80, 0xd9, 0x38, 0xd8, 0xa8, 0x89, 0x39, 0xa9, 0x80,
0xda, 0x3c, 0xd8, 0xa8, 0x2e, 0xa8, 0x39, 0x90, 0xd9, 0x0c, 0xd8, 0xa8, 0x95, 0x31, 0x98, 0xd9,
0x0c, 0xd8, 0xa8, 0x09, 0xd9, 0xff, 0xd8, 0x01, 0xda, 0xff, 0xd8, 0x95, 0x39, 0xa9, 0xda, 0x26,
0xff, 0xd8, 0x90, 0xa8, 0x0d, 0x89, 0x99, 0xa8, 0x10, 0x80, 0x98, 0x21, 0xda, 0x2e, 0xd8, 0x89,
0x99, 0xa8, 0x31, 0x80, 0xda, 0x2e, 0xd8, 0xa8, 0x86, 0x96, 0x31, 0x80, 0xda, 0x2e, 0xd8, 0xa8,
0x87, 0x31, 0x80, 0xda, 0x2e, 0xd8, 0xa8, 0x82, 0x92, 0xf3, 0x41, 0x80, 0xf1, 0xd9, 0x2e, 0xd8,
0xa8, 0x82, 0xf3, 0x19, 0x80, 0xf1, 0xd9, 0x2e, 0xd8, 0x82, 0xac, 0xf3, 0xc0, 0xa2, 0x80, 0x22,
0xf1, 0xa6, 0x2e, 0xa7, 0x2e, 0xa9, 0x22, 0x98, 0xa8, 0x29, 0xda, 0xac, 0xde, 0xff, 0xd8, 0xa2,
0xf2, 0x2a, 0xf1, 0xa9, 0x2e, 0x82, 0x92, 0xa8, 0xf2, 0x31, 0x80, 0xa6, 0x96, 0xf1, 0xd9, 0x00,
0xac, 0x8c, 0x9c, 0x0c, 0x30, 0xac, 0xde, 0xd0, 0xde, 0xff, 0xd8, 0x8c, 0x9c, 0xac, 0xd0, 0x10,
0xac, 0xde, 0x80, 0x92, 0xa2, 0xf2, 0x4c, 0x82, 0xa8, 0xf1, 0xca, 0xf2, 0x35, 0xf1, 0x96, 0x88,
0xa6, 0xd9, 0x00, 0xd8, 0xf1, 0xff};
static const unsigned short sStartAddress = 0x0400;
/* END OF SECTION COPIED FROM dmpDefaultMPU6050.c */
#define INT_SRC_TAP (0x01)
#define INT_SRC_ANDROID_ORIENT (0x08)
#define DMP_FEATURE_SEND_ANY_GYRO (DMP_FEATURE_SEND_RAW_GYRO | \
DMP_FEATURE_SEND_CAL_GYRO)
#define MAX_PACKET_LENGTH (32)
#define DMP_SAMPLE_RATE (200)
#define GYRO_SF (46850825LL * 200 / DMP_SAMPLE_RATE)
#define FIFO_CORRUPTION_CHECK
#ifdef FIFO_CORRUPTION_CHECK
#define QUAT_ERROR_THRESH (1L << 24)
#define QUAT_MAG_SQ_NORMALIZED (1L << 28)
#define QUAT_MAG_SQ_MIN (QUAT_MAG_SQ_NORMALIZED - QUAT_ERROR_THRESH)
#define QUAT_MAG_SQ_MAX (QUAT_MAG_SQ_NORMALIZED + QUAT_ERROR_THRESH)
#endif
struct dmp_s
{
void (*tap_cb)(unsigned char count, unsigned char direction);
void (*android_orient_cb)(unsigned char orientation);
unsigned short orient;
unsigned short feature_mask;
unsigned short fifo_rate;
unsigned char packet_length;
};
// static struct dmp_s dmp = {
// .tap_cb = NULL,
// .android_orient_cb = NULL,
// .orient = 0,
// .feature_mask = 0,
// .fifo_rate = 0,
// .packet_length = 0
// };
static struct dmp_s dmp = {
NULL,
NULL,
0,
0,
0,
0};
/**
* @brief Load the DMP with this image.
* @return 0 if successful.
*/
int dmp_load_motion_driver_firmware(void)
{
return mpu_load_firmware(DMP_CODE_SIZE, dmp_memory, sStartAddress,
DMP_SAMPLE_RATE);
}
/**
* @brief Push gyro and accel orientation to the DMP.
* The orientation is represented here as the output of
* @e inv_orientation_matrix_to_scalar.
* @param[in] orient Gyro and accel orientation in body frame.
* @return 0 if successful.
*/
int dmp_set_orientation(unsigned short orient)
{
unsigned char gyro_regs[3], accel_regs[3];
const unsigned char gyro_axes[3] = {DINA4C, DINACD, DINA6C};
const unsigned char accel_axes[3] = {DINA0C, DINAC9, DINA2C};
const unsigned char gyro_sign[3] = {DINA36, DINA56, DINA76};
const unsigned char accel_sign[3] = {DINA26, DINA46, DINA66};
gyro_regs[0] = gyro_axes[orient & 3];
gyro_regs[1] = gyro_axes[(orient >> 3) & 3];
gyro_regs[2] = gyro_axes[(orient >> 6) & 3];
accel_regs[0] = accel_axes[orient & 3];
accel_regs[1] = accel_axes[(orient >> 3) & 3];
accel_regs[2] = accel_axes[(orient >> 6) & 3];
/* Chip-to-body, axes only. */
if (mpu_write_mem(FCFG_1, 3, gyro_regs))
return -1;
if (mpu_write_mem(FCFG_2, 3, accel_regs))
return -1;
memcpy(gyro_regs, gyro_sign, 3);
memcpy(accel_regs, accel_sign, 3);
if (orient & 4)
{
gyro_regs[0] |= 1;
accel_regs[0] |= 1;
}
if (orient & 0x20)
{
gyro_regs[1] |= 1;
accel_regs[1] |= 1;
}
if (orient & 0x100)
{
gyro_regs[2] |= 1;
accel_regs[2] |= 1;
}
/* Chip-to-body, sign only. */
if (mpu_write_mem(FCFG_3, 3, gyro_regs))
return -1;
if (mpu_write_mem(FCFG_7, 3, accel_regs))
return -1;
dmp.orient = orient;
return 0;
}
/**
* @brief Push gyro biases to the DMP.
* Because the gyro integration is handled in the DMP, any gyro biases
* calculated by the MPL should be pushed down to DMP memory to remove
* 3-axis quaternion drift.
* \n NOTE: If the DMP-based gyro calibration is enabled, the DMP will
* overwrite the biases written to this location once a new one is computed.
* @param[in] bias Gyro biases in q16.
* @return 0 if successful.
*/
int dmp_set_gyro_bias(long *bias)
{
long gyro_bias_body[3];
unsigned char regs[4];
gyro_bias_body[0] = bias[dmp.orient & 3];
if (dmp.orient & 4)
gyro_bias_body[0] *= -1;
gyro_bias_body[1] = bias[(dmp.orient >> 3) & 3];
if (dmp.orient & 0x20)
gyro_bias_body[1] *= -1;
gyro_bias_body[2] = bias[(dmp.orient >> 6) & 3];
if (dmp.orient & 0x100)
gyro_bias_body[2] *= -1;
#ifdef EMPL_NO_64BIT
gyro_bias_body[0] = (long)(((float)gyro_bias_body[0] * GYRO_SF) / 1073741824.f);
gyro_bias_body[1] = (long)(((float)gyro_bias_body[1] * GYRO_SF) / 1073741824.f);
gyro_bias_body[2] = (long)(((float)gyro_bias_body[2] * GYRO_SF) / 1073741824.f);
#else
gyro_bias_body[0] = (long)(((long long)gyro_bias_body[0] * GYRO_SF) >> 30);
gyro_bias_body[1] = (long)(((long long)gyro_bias_body[1] * GYRO_SF) >> 30);
gyro_bias_body[2] = (long)(((long long)gyro_bias_body[2] * GYRO_SF) >> 30);
#endif
regs[0] = (unsigned char)((gyro_bias_body[0] >> 24) & 0xFF);
regs[1] = (unsigned char)((gyro_bias_body[0] >> 16) & 0xFF);
regs[2] = (unsigned char)((gyro_bias_body[0] >> 8) & 0xFF);
regs[3] = (unsigned char)(gyro_bias_body[0] & 0xFF);
if (mpu_write_mem(D_EXT_GYRO_BIAS_X, 4, regs))
return -1;
regs[0] = (unsigned char)((gyro_bias_body[1] >> 24) & 0xFF);
regs[1] = (unsigned char)((gyro_bias_body[1] >> 16) & 0xFF);
regs[2] = (unsigned char)((gyro_bias_body[1] >> 8) & 0xFF);
regs[3] = (unsigned char)(gyro_bias_body[1] & 0xFF);
if (mpu_write_mem(D_EXT_GYRO_BIAS_Y, 4, regs))
return -1;
regs[0] = (unsigned char)((gyro_bias_body[2] >> 24) & 0xFF);
regs[1] = (unsigned char)((gyro_bias_body[2] >> 16) & 0xFF);
regs[2] = (unsigned char)((gyro_bias_body[2] >> 8) & 0xFF);
regs[3] = (unsigned char)(gyro_bias_body[2] & 0xFF);
return mpu_write_mem(D_EXT_GYRO_BIAS_Z, 4, regs);
}
/**
* @brief Push accel biases to the DMP.
* These biases will be removed from the DMP 6-axis quaternion.
* @param[in] bias Accel biases in q16.
* @return 0 if successful.
*/
int dmp_set_accel_bias(long *bias)
{
long accel_bias_body[3];
unsigned char regs[12];
long long accel_sf;
unsigned short accel_sens;
mpu_get_accel_sens(&accel_sens);
accel_sf = (long long)accel_sens << 15;
//__no_operation();
accel_bias_body[0] = bias[dmp.orient & 3];
if (dmp.orient & 4)
accel_bias_body[0] *= -1;
accel_bias_body[1] = bias[(dmp.orient >> 3) & 3];
if (dmp.orient & 0x20)
accel_bias_body[1] *= -1;
accel_bias_body[2] = bias[(dmp.orient >> 6) & 3];
if (dmp.orient & 0x100)
accel_bias_body[2] *= -1;
#ifdef EMPL_NO_64BIT
accel_bias_body[0] = (long)(((float)accel_bias_body[0] * accel_sf) / 1073741824.f);
accel_bias_body[1] = (long)(((float)accel_bias_body[1] * accel_sf) / 1073741824.f);
accel_bias_body[2] = (long)(((float)accel_bias_body[2] * accel_sf) / 1073741824.f);
#else
accel_bias_body[0] = (long)(((long long)accel_bias_body[0] * accel_sf) >> 30);
accel_bias_body[1] = (long)(((long long)accel_bias_body[1] * accel_sf) >> 30);
accel_bias_body[2] = (long)(((long long)accel_bias_body[2] * accel_sf) >> 30);
#endif
regs[0] = (unsigned char)((accel_bias_body[0] >> 24) & 0xFF);
regs[1] = (unsigned char)((accel_bias_body[0] >> 16) & 0xFF);
regs[2] = (unsigned char)((accel_bias_body[0] >> 8) & 0xFF);
regs[3] = (unsigned char)(accel_bias_body[0] & 0xFF);
regs[4] = (unsigned char)((accel_bias_body[1] >> 24) & 0xFF);
regs[5] = (unsigned char)((accel_bias_body[1] >> 16) & 0xFF);
regs[6] = (unsigned char)((accel_bias_body[1] >> 8) & 0xFF);
regs[7] = (unsigned char)(accel_bias_body[1] & 0xFF);
regs[8] = (unsigned char)((accel_bias_body[2] >> 24) & 0xFF);
regs[9] = (unsigned char)((accel_bias_body[2] >> 16) & 0xFF);
regs[10] = (unsigned char)((accel_bias_body[2] >> 8) & 0xFF);
regs[11] = (unsigned char)(accel_bias_body[2] & 0xFF);
return mpu_write_mem(D_ACCEL_BIAS, 12, regs);
}
/**
* @brief Set DMP output rate.
* Only used when DMP is on.
* @param[in] rate Desired fifo rate (Hz).
* @return 0 if successful.
*/
int dmp_set_fifo_rate(unsigned short rate)
{
const unsigned char regs_end[12] = {DINAFE, DINAF2, DINAAB,
0xc4, DINAAA, DINAF1, DINADF, DINADF, 0xBB, 0xAF, DINADF, DINADF};
unsigned short div;
unsigned char tmp[8];
if (rate > DMP_SAMPLE_RATE)
return -1;
div = DMP_SAMPLE_RATE / rate - 1;
tmp[0] = (unsigned char)((div >> 8) & 0xFF);
tmp[1] = (unsigned char)(div & 0xFF);
if (mpu_write_mem(D_0_22, 2, tmp))
return -1;
if (mpu_write_mem(CFG_6, 12, (unsigned char *)regs_end))
return -1;
dmp.fifo_rate = rate;
return 0;
}
/**
* @brief Get DMP output rate.
* @param[out] rate Current fifo rate (Hz).
* @return 0 if successful.
*/
int dmp_get_fifo_rate(unsigned short *rate)
{
rate[0] = dmp.fifo_rate;
return 0;
}
/**
* @brief Set tap threshold for a specific axis.
* @param[in] axis 1, 2, and 4 for XYZ accel, respectively.
* @param[in] thresh Tap threshold, in mg/ms.
* @return 0 if successful.
*/
int dmp_set_tap_thresh(unsigned char axis, unsigned short thresh)
{
unsigned char tmp[4], accel_fsr;
float scaled_thresh;
unsigned short dmp_thresh, dmp_thresh_2;
if (!(axis & TAP_XYZ) || thresh > 1600)
return -1;
scaled_thresh = (float)thresh / DMP_SAMPLE_RATE;
mpu_get_accel_fsr(&accel_fsr);
switch (accel_fsr)
{
case 2:
dmp_thresh = (unsigned short)(scaled_thresh * 16384);
/* dmp_thresh * 0.75 */
dmp_thresh_2 = (unsigned short)(scaled_thresh * 12288);
break;
case 4:
dmp_thresh = (unsigned short)(scaled_thresh * 8192);
/* dmp_thresh * 0.75 */
dmp_thresh_2 = (unsigned short)(scaled_thresh * 6144);
break;
case 8:
dmp_thresh = (unsigned short)(scaled_thresh * 4096);
/* dmp_thresh * 0.75 */
dmp_thresh_2 = (unsigned short)(scaled_thresh * 3072);
break;
case 16:
dmp_thresh = (unsigned short)(scaled_thresh * 2048);
/* dmp_thresh * 0.75 */
dmp_thresh_2 = (unsigned short)(scaled_thresh * 1536);
break;
default:
return -1;
}
tmp[0] = (unsigned char)(dmp_thresh >> 8);
tmp[1] = (unsigned char)(dmp_thresh & 0xFF);
tmp[2] = (unsigned char)(dmp_thresh_2 >> 8);
tmp[3] = (unsigned char)(dmp_thresh_2 & 0xFF);
if (axis & TAP_X)
{
if (mpu_write_mem(DMP_TAP_THX, 2, tmp))
return -1;
if (mpu_write_mem(D_1_36, 2, tmp + 2))
return -1;
}
if (axis & TAP_Y)
{
if (mpu_write_mem(DMP_TAP_THY, 2, tmp))
return -1;
if (mpu_write_mem(D_1_40, 2, tmp + 2))
return -1;
}
if (axis & TAP_Z)
{
if (mpu_write_mem(DMP_TAP_THZ, 2, tmp))
return -1;
if (mpu_write_mem(D_1_44, 2, tmp + 2))
return -1;
}
return 0;
}
/**
* @brief Set which axes will register a tap.
* @param[in] axis 1, 2, and 4 for XYZ, respectively.
* @return 0 if successful.
*/
int dmp_set_tap_axes(unsigned char axis)
{
unsigned char tmp = 0;
if (axis & TAP_X)
tmp |= 0x30;
if (axis & TAP_Y)
tmp |= 0x0C;
if (axis & TAP_Z)
tmp |= 0x03;
return mpu_write_mem(D_1_72, 1, &tmp);
}
/**
* @brief Set minimum number of taps needed for an interrupt.
* @param[in] min_taps Minimum consecutive taps (1-4).
* @return 0 if successful.
*/
int dmp_set_tap_count(unsigned char min_taps)
{
unsigned char tmp;
if (min_taps < 1)
min_taps = 1;
else if (min_taps > 4)
min_taps = 4;
tmp = min_taps - 1;
return mpu_write_mem(D_1_79, 1, &tmp);
}
/**
* @brief Set length between valid taps.
* @param[in] time Milliseconds between taps.
* @return 0 if successful.
*/
int dmp_set_tap_time(unsigned short time)
{
unsigned short dmp_time;
unsigned char tmp[2];
dmp_time = time / (1000 / DMP_SAMPLE_RATE);
tmp[0] = (unsigned char)(dmp_time >> 8);
tmp[1] = (unsigned char)(dmp_time & 0xFF);
return mpu_write_mem(DMP_TAPW_MIN, 2, tmp);
}
/**
* @brief Set max time between taps to register as a multi-tap.
* @param[in] time Max milliseconds between taps.
* @return 0 if successful.
*/
int dmp_set_tap_time_multi(unsigned short time)
{
unsigned short dmp_time;
unsigned char tmp[2];
dmp_time = time / (1000 / DMP_SAMPLE_RATE);
tmp[0] = (unsigned char)(dmp_time >> 8);
tmp[1] = (unsigned char)(dmp_time & 0xFF);
return mpu_write_mem(D_1_218, 2, tmp);
}
/**
* @brief Set shake rejection threshold.
* If the DMP detects a gyro sample larger than @e thresh, taps are rejected.
* @param[in] sf Gyro scale factor.
* @param[in] thresh Gyro threshold in dps.
* @return 0 if successful.
*/
int dmp_set_shake_reject_thresh(long sf, unsigned short thresh)
{
unsigned char tmp[4];
long thresh_scaled = sf / 1000 * thresh;
tmp[0] = (unsigned char)(((long)thresh_scaled >> 24) & 0xFF);
tmp[1] = (unsigned char)(((long)thresh_scaled >> 16) & 0xFF);
tmp[2] = (unsigned char)(((long)thresh_scaled >> 8) & 0xFF);
tmp[3] = (unsigned char)((long)thresh_scaled & 0xFF);
return mpu_write_mem(D_1_92, 4, tmp);
}
/**
* @brief Set shake rejection time.
* Sets the length of time that the gyro must be outside of the threshold set
* by @e gyro_set_shake_reject_thresh before taps are rejected. A mandatory
* 60 ms is added to this parameter.
* @param[in] time Time in milliseconds.
* @return 0 if successful.
*/
int dmp_set_shake_reject_time(unsigned short time)
{
unsigned char tmp[2];
time /= (1000 / DMP_SAMPLE_RATE);
tmp[0] = time >> 8;
tmp[1] = time & 0xFF;
return mpu_write_mem(D_1_90, 2, tmp);
}
/**
* @brief Set shake rejection timeout.
* Sets the length of time after a shake rejection that the gyro must stay
* inside of the threshold before taps can be detected again. A mandatory
* 60 ms is added to this parameter.
* @param[in] time Time in milliseconds.
* @return 0 if successful.
*/
int dmp_set_shake_reject_timeout(unsigned short time)
{
unsigned char tmp[2];
time /= (1000 / DMP_SAMPLE_RATE);
tmp[0] = time >> 8;
tmp[1] = time & 0xFF;
return mpu_write_mem(D_1_88, 2, tmp);
}
/**
* @brief Get current step count.
* @param[out] count Number of steps detected.
* @return 0 if successful.
*/
int dmp_get_pedometer_step_count(unsigned long *count)
{
unsigned char tmp[4];
if (!count)
return -1;
if (mpu_read_mem(D_PEDSTD_STEPCTR, 4, tmp))
return -1;
count[0] = ((unsigned long)tmp[0] << 24) | ((unsigned long)tmp[1] << 16) |
((unsigned long)tmp[2] << 8) | tmp[3];
return 0;
}
/**
* @brief Overwrite current step count.
* WARNING: This function writes to DMP memory and could potentially encounter
* a race condition if called while the pedometer is enabled.
* @param[in] count New step count.
* @return 0 if successful.
*/
int dmp_set_pedometer_step_count(unsigned long count)
{
unsigned char tmp[4];
tmp[0] = (unsigned char)((count >> 24) & 0xFF);
tmp[1] = (unsigned char)((count >> 16) & 0xFF);
tmp[2] = (unsigned char)((count >> 8) & 0xFF);
tmp[3] = (unsigned char)(count & 0xFF);
return mpu_write_mem(D_PEDSTD_STEPCTR, 4, tmp);
}
/**
* @brief Get duration of walking time.
* @param[in] time Walk time in milliseconds.
* @return 0 if successful.
*/
int dmp_get_pedometer_walk_time(unsigned long *time)
{
unsigned char tmp[4];
if (!time)
return -1;
if (mpu_read_mem(D_PEDSTD_TIMECTR, 4, tmp))
return -1;
time[0] = (((unsigned long)tmp[0] << 24) | ((unsigned long)tmp[1] << 16) |
((unsigned long)tmp[2] << 8) | tmp[3]) *
20;
return 0;
}
/**
* @brief Overwrite current walk time.
* WARNING: This function writes to DMP memory and could potentially encounter
* a race condition if called while the pedometer is enabled.
* @param[in] time New walk time in milliseconds.
*/
int dmp_set_pedometer_walk_time(unsigned long time)
{
unsigned char tmp[4];
time /= 20;
tmp[0] = (unsigned char)((time >> 24) & 0xFF);
tmp[1] = (unsigned char)((time >> 16) & 0xFF);
tmp[2] = (unsigned char)((time >> 8) & 0xFF);
tmp[3] = (unsigned char)(time & 0xFF);
return mpu_write_mem(D_PEDSTD_TIMECTR, 4, tmp);
}
/**
* @brief Enable DMP features.
* The following \#define's are used in the input mask:
* \n DMP_FEATURE_TAP
* \n DMP_FEATURE_ANDROID_ORIENT
* \n DMP_FEATURE_LP_QUAT
* \n DMP_FEATURE_6X_LP_QUAT
* \n DMP_FEATURE_GYRO_CAL
* \n DMP_FEATURE_SEND_RAW_ACCEL
* \n DMP_FEATURE_SEND_RAW_GYRO
* \n NOTE: DMP_FEATURE_LP_QUAT and DMP_FEATURE_6X_LP_QUAT are mutually
* exclusive.
* \n NOTE: DMP_FEATURE_SEND_RAW_GYRO and DMP_FEATURE_SEND_CAL_GYRO are also
* mutually exclusive.
* @param[in] mask Mask of features to enable.
* @return 0 if successful.
*/
int dmp_enable_feature(unsigned short mask)
{
unsigned char tmp[10];
/* TODO: All of these settings can probably be integrated into the default
* DMP image.
*/
/* Set integration scale factor. */
tmp[0] = (unsigned char)((GYRO_SF >> 24) & 0xFF);
tmp[1] = (unsigned char)((GYRO_SF >> 16) & 0xFF);
tmp[2] = (unsigned char)((GYRO_SF >> 8) & 0xFF);
tmp[3] = (unsigned char)(GYRO_SF & 0xFF);
mpu_write_mem(D_0_104, 4, tmp);
/* Send sensor data to the FIFO. */
tmp[0] = 0xA3;
if (mask & DMP_FEATURE_SEND_RAW_ACCEL)
{
tmp[1] = 0xC0;
tmp[2] = 0xC8;
tmp[3] = 0xC2;
}
else
{
tmp[1] = 0xA3;
tmp[2] = 0xA3;
tmp[3] = 0xA3;
}
if (mask & DMP_FEATURE_SEND_ANY_GYRO)
{
tmp[4] = 0xC4;
tmp[5] = 0xCC;
tmp[6] = 0xC6;
}
else
{
tmp[4] = 0xA3;
tmp[5] = 0xA3;
tmp[6] = 0xA3;
}
tmp[7] = 0xA3;
tmp[8] = 0xA3;
tmp[9] = 0xA3;
mpu_write_mem(CFG_15, 10, tmp);
/* Send gesture data to the FIFO. */
if (mask & (DMP_FEATURE_TAP | DMP_FEATURE_ANDROID_ORIENT))
tmp[0] = DINA20;
else
tmp[0] = 0xD8;
mpu_write_mem(CFG_27, 1, tmp);
if (mask & DMP_FEATURE_GYRO_CAL)
dmp_enable_gyro_cal(1);
else
dmp_enable_gyro_cal(0);
if (mask & DMP_FEATURE_SEND_ANY_GYRO)
{
if (mask & DMP_FEATURE_SEND_CAL_GYRO)
{
tmp[0] = 0xB2;
tmp[1] = 0x8B;
tmp[2] = 0xB6;
tmp[3] = 0x9B;
}
else
{
tmp[0] = DINAC0;
tmp[1] = DINA80;
tmp[2] = DINAC2;
tmp[3] = DINA90;
}
mpu_write_mem(CFG_GYRO_RAW_DATA, 4, tmp);
}
if (mask & DMP_FEATURE_TAP)
{
/* Enable tap. */
tmp[0] = 0xF8;
mpu_write_mem(CFG_20, 1, tmp);
dmp_set_tap_thresh(TAP_XYZ, 250);
dmp_set_tap_axes(TAP_XYZ);
dmp_set_tap_count(1);
dmp_set_tap_time(100);
dmp_set_tap_time_multi(500);
dmp_set_shake_reject_thresh(GYRO_SF, 200);
dmp_set_shake_reject_time(40);
dmp_set_shake_reject_timeout(10);
}
else
{
tmp[0] = 0xD8;
mpu_write_mem(CFG_20, 1, tmp);
}
if (mask & DMP_FEATURE_ANDROID_ORIENT)
{
tmp[0] = 0xD9;
}
else
tmp[0] = 0xD8;
mpu_write_mem(CFG_ANDROID_ORIENT_INT, 1, tmp);
if (mask & DMP_FEATURE_LP_QUAT)
dmp_enable_lp_quat(1);
else
dmp_enable_lp_quat(0);
if (mask & DMP_FEATURE_6X_LP_QUAT)
dmp_enable_6x_lp_quat(1);
else
dmp_enable_6x_lp_quat(0);
/* Pedometer is always enabled. */
dmp.feature_mask = mask | DMP_FEATURE_PEDOMETER;
mpu_reset_fifo();
dmp.packet_length = 0;
if (mask & DMP_FEATURE_SEND_RAW_ACCEL)
dmp.packet_length += 6;
if (mask & DMP_FEATURE_SEND_ANY_GYRO)
dmp.packet_length += 6;
if (mask & (DMP_FEATURE_LP_QUAT | DMP_FEATURE_6X_LP_QUAT))
dmp.packet_length += 16;
if (mask & (DMP_FEATURE_TAP | DMP_FEATURE_ANDROID_ORIENT))
dmp.packet_length += 4;
return 0;
}
/**
* @brief Get list of currently enabled DMP features.
* @param[out] Mask of enabled features.
* @return 0 if successful.
*/
int dmp_get_enabled_features(unsigned short *mask)
{
mask[0] = dmp.feature_mask;
return 0;
}
/**
* @brief Calibrate the gyro data in the DMP.
* After eight seconds of no motion, the DMP will compute gyro biases and
* subtract them from the quaternion output. If @e dmp_enable_feature is
* called with @e DMP_FEATURE_SEND_CAL_GYRO, the biases will also be
* subtracted from the gyro output.
* @param[in] enable 1 to enable gyro calibration.
* @return 0 if successful.
*/
int dmp_enable_gyro_cal(unsigned char enable)
{
if (enable)
{
unsigned char regs[9] = {0xb8, 0xaa, 0xb3, 0x8d, 0xb4, 0x98, 0x0d, 0x35, 0x5d};
return mpu_write_mem(CFG_MOTION_BIAS, 9, regs);
}
else
{
unsigned char regs[9] = {0xb8, 0xaa, 0xaa, 0xaa, 0xb0, 0x88, 0xc3, 0xc5, 0xc7};
return mpu_write_mem(CFG_MOTION_BIAS, 9, regs);
}
}
/**
* @brief Generate 3-axis quaternions from the DMP.
* In this driver, the 3-axis and 6-axis DMP quaternion features are mutually
* exclusive.
* @param[in] enable 1 to enable 3-axis quaternion.
* @return 0 if successful.
*/
int dmp_enable_lp_quat(unsigned char enable)
{
unsigned char regs[4];
if (enable)
{
regs[0] = DINBC0;
regs[1] = DINBC2;
regs[2] = DINBC4;
regs[3] = DINBC6;
}
else
memset(regs, 0x8B, 4);
mpu_write_mem(CFG_LP_QUAT, 4, regs);
return mpu_reset_fifo();
}
/**
* @brief Generate 6-axis quaternions from the DMP.
* In this driver, the 3-axis and 6-axis DMP quaternion features are mutually
* exclusive.
* @param[in] enable 1 to enable 6-axis quaternion.
* @return 0 if successful.
*/
int dmp_enable_6x_lp_quat(unsigned char enable)
{
unsigned char regs[4];
if (enable)
{
regs[0] = DINA20;
regs[1] = DINA28;
regs[2] = DINA30;
regs[3] = DINA38;
}
else
memset(regs, 0xA3, 4);
mpu_write_mem(CFG_8, 4, regs);
return mpu_reset_fifo();
}
/**
* @brief Decode the four-byte gesture data and execute any callbacks.
* @param[in] gesture Gesture data from DMP packet.
* @return 0 if successful.
*/
static int decode_gesture(unsigned char *gesture)
{
unsigned char tap, android_orient;
android_orient = gesture[3] & 0xC0;
tap = 0x3F & gesture[3];
if (gesture[1] & INT_SRC_TAP)
{
unsigned char direction, count;
direction = tap >> 3;
count = (tap % 8) + 1;
if (dmp.tap_cb)
dmp.tap_cb(direction, count);
}
if (gesture[1] & INT_SRC_ANDROID_ORIENT)
{
if (dmp.android_orient_cb)
dmp.android_orient_cb(android_orient >> 6);
}
return 0;
}
/**
* @brief Specify when a DMP interrupt should occur.
* A DMP interrupt can be configured to trigger on either of the two
* conditions below:
* \n a. One FIFO period has elapsed (set by @e mpu_set_sample_rate).
* \n b. A tap event has been detected.
* @param[in] mode DMP_INT_GESTURE or DMP_INT_CONTINUOUS.
* @return 0 if successful.
*/
int dmp_set_interrupt_mode(unsigned char mode)
{
const unsigned char regs_continuous[11] =
{0xd8, 0xb1, 0xb9, 0xf3, 0x8b, 0xa3, 0x91, 0xb6, 0x09, 0xb4, 0xd9};
const unsigned char regs_gesture[11] =
{0xda, 0xb1, 0xb9, 0xf3, 0x8b, 0xa3, 0x91, 0xb6, 0xda, 0xb4, 0xda};
switch (mode)
{
case DMP_INT_CONTINUOUS:
return mpu_write_mem(CFG_FIFO_ON_EVENT, 11,
(unsigned char *)regs_continuous);
case DMP_INT_GESTURE:
return mpu_write_mem(CFG_FIFO_ON_EVENT, 11,
(unsigned char *)regs_gesture);
default:
return -1;
}
}
/**
* @brief Get one packet from the FIFO.
* If @e sensors does not contain a particular sensor, disregard the data
* returned to that pointer.
* \n @e sensors can contain a combination of the following flags:
* \n INV_X_GYRO, INV_Y_GYRO, INV_Z_GYRO
* \n INV_XYZ_GYRO
* \n INV_XYZ_ACCEL
* \n INV_WXYZ_QUAT
* \n If the FIFO has no new data, @e sensors will be zero.
* \n If the FIFO is disabled, @e sensors will be zero and this function will
* return a non-zero error code.
* @param[out] gyro Gyro data in hardware units.
* @param[out] accel Accel data in hardware units.
* @param[out] quat 3-axis quaternion data in hardware units.
* @param[out] timestamp Timestamp in milliseconds.
* @param[out] sensors Mask of sensors read from FIFO.
* @param[out] more Number of remaining packets.
* @return 0 if successful.
*/
int dmp_read_fifo(short *gyro, short *accel, long *quat,
unsigned long *timestamp, short *sensors, unsigned char *more)
{
unsigned char fifo_data[MAX_PACKET_LENGTH];
unsigned char ii = 0;
/* TODO: sensors[0] only changes when dmp_enable_feature is called. We can
* cache this value and save some cycles.
*/
sensors[0] = 0;
/* Get a packet. */
if (mpu_read_fifo_stream(dmp.packet_length, fifo_data, more))
return -1;
/* Parse DMP packet. */
if (dmp.feature_mask & (DMP_FEATURE_LP_QUAT | DMP_FEATURE_6X_LP_QUAT))
{
#ifdef FIFO_CORRUPTION_CHECK
long quat_q14[4], quat_mag_sq;
#endif
quat[0] = ((long)fifo_data[0] << 24) | ((long)fifo_data[1] << 16) |
((long)fifo_data[2] << 8) | fifo_data[3];
quat[1] = ((long)fifo_data[4] << 24) | ((long)fifo_data[5] << 16) |
((long)fifo_data[6] << 8) | fifo_data[7];
quat[2] = ((long)fifo_data[8] << 24) | ((long)fifo_data[9] << 16) |
((long)fifo_data[10] << 8) | fifo_data[11];
quat[3] = ((long)fifo_data[12] << 24) | ((long)fifo_data[13] << 16) |
((long)fifo_data[14] << 8) | fifo_data[15];
ii += 16;
#ifdef FIFO_CORRUPTION_CHECK
/* We can detect a corrupted FIFO by monitoring the quaternion data and
* ensuring that the magnitude is always normalized to one. This
* shouldn't happen in normal operation, but if an I2C error occurs,
* the FIFO reads might become misaligned.
*
* Let's start by scaling down the quaternion data to avoid long long
* math.
*/
quat_q14[0] = quat[0] >> 16;
quat_q14[1] = quat[1] >> 16;
quat_q14[2] = quat[2] >> 16;
quat_q14[3] = quat[3] >> 16;
quat_mag_sq = quat_q14[0] * quat_q14[0] + quat_q14[1] * quat_q14[1] +
quat_q14[2] * quat_q14[2] + quat_q14[3] * quat_q14[3];
if ((quat_mag_sq < QUAT_MAG_SQ_MIN) ||
(quat_mag_sq > QUAT_MAG_SQ_MAX))
{
/* Quaternion is outside of the acceptable threshold. */
mpu_reset_fifo();
sensors[0] = 0;
return -1;
}
sensors[0] |= INV_WXYZ_QUAT;
#endif
}
if (dmp.feature_mask & DMP_FEATURE_SEND_RAW_ACCEL)
{
accel[0] = ((short)fifo_data[ii + 0] << 8) | fifo_data[ii + 1];
accel[1] = ((short)fifo_data[ii + 2] << 8) | fifo_data[ii + 3];
accel[2] = ((short)fifo_data[ii + 4] << 8) | fifo_data[ii + 5];
ii += 6;
sensors[0] |= INV_XYZ_ACCEL;
}
if (dmp.feature_mask & DMP_FEATURE_SEND_ANY_GYRO)
{
gyro[0] = ((short)fifo_data[ii + 0] << 8) | fifo_data[ii + 1];
gyro[1] = ((short)fifo_data[ii + 2] << 8) | fifo_data[ii + 3];
gyro[2] = ((short)fifo_data[ii + 4] << 8) | fifo_data[ii + 5];
ii += 6;
sensors[0] |= INV_XYZ_GYRO;
}
/* Gesture data is at the end of the DMP packet. Parse it and call
* the gesture callbacks (if registered).
*/
if (dmp.feature_mask & (DMP_FEATURE_TAP | DMP_FEATURE_ANDROID_ORIENT))
decode_gesture(fifo_data + ii);
get_ms(timestamp);
return 0;
}
/**
* @brief Register a function to be executed on a tap event.
* The tap direction is represented by one of the following:
* \n TAP_X_UP
* \n TAP_X_DOWN
* \n TAP_Y_UP
* \n TAP_Y_DOWN
* \n TAP_Z_UP
* \n TAP_Z_DOWN
* @param[in] func Callback function.
* @return 0 if successful.
*/
int dmp_register_tap_cb(void (*func)(unsigned char, unsigned char))
{
dmp.tap_cb = func;
return 0;
}
/**
* @brief Register a function to be executed on a android orientation event.
* @param[in] func Callback function.
* @return 0 if successful.
*/
int dmp_register_android_orient_cb(void (*func)(unsigned char))
{
dmp.android_orient_cb = func;
return 0;
}
/**
* @}
*/
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "IIC.h"
#include "mpu6050.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint8_t t = 0;
float pitch, roll, yaw;
short aacx, aacy, aacz;
short gyrox, gyroy, gyroz;
short temp;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM1_Init();
MX_TIM4_Init();
/* USER CODE BEGIN 2 */
MPU_Init();
while (mpu_dmp_init())
{
printf("MPU6050 Error!!!\r\n");
HAL_Delay(500);
}
printf("MPU6050 OK\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if (mpu_dmp_get_data(&pitch, &roll, &yaw) == 0)
{
temp = MPU_Get_Temperature();
MPU_Get_Accelerometer(&aacx, &aacy, &aacz);
MPU_Get_Gyroscope(&gyrox, &gyroy, &gyroz);
if (1)
{
if ((t % 10) == 0)
{
// if (temp < 0)
// {
// temp = -temp; // 转为正数
// printf(" Temp: -%d.%dC\r\n", temp / 100, temp % 10);
// }
// else
// printf(" Temp: %d.%dC\r\n", temp / 100, temp % 10);
// pitch�??????
temp = yaw * 10;
if (temp < 0)
{
temp = -temp; // 转为正数
printf("-%d.%d\r\n", temp / 10, temp % 10);
}
else
printf("%d.%d\r\n", temp / 10, temp % 10);
// // roll�??????
// temp = roll * 10;
// if (temp < 0)
// {
// temp = -temp; // 转为正数
// printf(" Roll: -%d.%dC\r\n", temp / 10, temp % 10);
// }
// else
// printf(" Roll: %d.%dC\r\n", temp / 10, temp % 10);
//
// temp = yaw * 10;
// if (temp < 0)
// {
// temp = -temp; // 转为正数
// printf(" Yaw: -%d.%dC\r\n", temp / 10, temp % 10);
// }
// else
// printf(" Yaw: %d.%dC\r\n", temp / 10, temp % 10);
//
// printf("\r\n");
t = 0;
}
}
}
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/*
* mpu6050.c
*
* Created on: Jul 11, 2023
* Author: zzy
*/
#include "mpu6050.h"
// 初始化MPU6050
// 返回值:0,成功
// 其他,错误代码
uint8_t MPU_Init(void)
{
uint8_t res;
MPU_IIC_Init(); // 初始化IIC总线
MPU_Write_Byte(MPU_PWR_MGMT1_REG, 0X80); // 复位MPU6050
delay_ms(100);
MPU_Write_Byte(MPU_PWR_MGMT1_REG, 0X00); // 唤醒MPU6050
MPU_Set_Gyro_Fsr(3); // 陀螺仪传感器,±2000dps
MPU_Set_Accel_Fsr(0); // 加速度传感器,±2g
MPU_Set_Rate(50); // 设置采样率50Hz
MPU_Write_Byte(MPU_INT_EN_REG, 0X00); // 关闭所有中断
MPU_Write_Byte(MPU_USER_CTRL_REG, 0X00); // I2C主模式关闭
MPU_Write_Byte(MPU_FIFO_EN_REG, 0X00); // 关闭FIFO
MPU_Write_Byte(MPU_INTBP_CFG_REG, 0X80); // INT引脚低电平有效
res = MPU_Read_Byte(MPU_DEVICE_ID_REG);
if (res == MPU_ADDR) // 器件ID正确
{
MPU_Write_Byte(MPU_PWR_MGMT1_REG, 0X01); // 设置CLKSEL,PLL X轴为参考
MPU_Write_Byte(MPU_PWR_MGMT2_REG, 0X00); // 加速度与陀螺仪都工作
MPU_Set_Rate(50); // 设置采样率为50Hz
}
else
return 1;
return 0;
}
// 设置MPU6050陀螺仪传感器满量程范围
// fsr:0,±250dps;1,±500dps;2,±1000dps;3,±2000dps
// 返回值:0,设置成功
// 其他,设置失败
uint8_t MPU_Set_Gyro_Fsr(uint8_t fsr)
{
return MPU_Write_Byte(MPU_GYRO_CFG_REG, fsr << 3); // 设置陀螺仪满量程范围
}
// 设置MPU6050加速度传感器满量程范围
// fsr:0,±2g;1,±4g;2,±8g;3,±16g
// 返回值:0,设置成功
// 其他,设置失败
uint8_t MPU_Set_Accel_Fsr(uint8_t fsr)
{
return MPU_Write_Byte(MPU_ACCEL_CFG_REG, fsr << 3); // 设置加速度传感器满量程范围
}
// 设置MPU6050的数字低通滤波器
// lpf:数字低通滤波频率(Hz)
// 返回值:0,设置成功
// 其他,设置失败
uint8_t MPU_Set_LPF(uint16_t lpf)
{
uint8_t data = 0;
if (lpf >= 188)
data = 1;
else if (lpf >= 98)
data = 2;
else if (lpf >= 42)
data = 3;
else if (lpf >= 20)
data = 4;
else if (lpf >= 10)
data = 5;
else
data = 6;
return MPU_Write_Byte(MPU_CFG_REG, data); // 设置数字低通滤波器
}
// 设置MPU6050的采样率(假定Fs=1KHz)
// rate:4~1000(Hz)
// 返回值:0,设置成功
// 其他,设置失败
uint8_t MPU_Set_Rate(uint16_t rate)
{
uint8_t data;
if (rate > 1000)
rate = 1000;
if (rate < 4)
rate = 4;
data = 1000 / rate - 1;
data = MPU_Write_Byte(MPU_SAMPLE_RATE_REG, data); // 设置数字低通滤波器
return MPU_Set_LPF(rate / 2); // 自动设置LPF为采样率的一半
}
// 得到温度值
// 返回值:温度值(扩大了100倍)
short MPU_Get_Temperature(void)
{
uint8_t buf[2];
short raw;
float temp;
MPU_Read_Len(MPU_ADDR, MPU_TEMP_OUTH_REG, 2, buf);
raw = ((uint16_t)buf[0] << 8) | buf[1];
temp = 36.53 + ((double)raw) / 340;
return temp * 100;
;
}
// 得到陀螺仪值(原始值)
// gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)
// 返回值:0,成功
// 其他,错误代码
uint8_t MPU_Get_Gyroscope(short *gx, short *gy, short *gz)
{
uint8_t buf[6], res;
res = MPU_Read_Len(MPU_ADDR, MPU_GYRO_XOUTH_REG, 6, buf);
if (res == 0)
{
*gx = ((uint16_t)buf[0] << 8) | buf[1];
*gy = ((uint16_t)buf[2] << 8) | buf[3];
*gz = ((uint16_t)buf[4] << 8) | buf[5];
}
return res;
;
}
// 得到加速度值(原始值)
// gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)
// 返回值:0,成功
// 其他,错误代码
uint8_t MPU_Get_Accelerometer(short *ax, short *ay, short *az)
{
uint8_t buf[6], res;
res = MPU_Read_Len(MPU_ADDR, MPU_ACCEL_XOUTH_REG, 6, buf);
if (res == 0)
{
*ax = ((uint16_t)buf[0] << 8) | buf[1];
*ay = ((uint16_t)buf[2] << 8) | buf[3];
*az = ((uint16_t)buf[4] << 8) | buf[5];
}
return res;
;
}
// IIC连续写
// addr:器件地址
// reg:寄存器地址
// len:写入长度
// buf:数据区
// 返回值:0,正常
// 其他,错误代码
uint8_t MPU_Write_Len(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *buf)
{
uint8_t i;
MPU_IIC_Start();
MPU_IIC_Send_Byte((addr << 1) | 0); // 发送器件地址+写命令
if (MPU_IIC_Wait_Ack()) // 等待应答
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Send_Byte(reg); // 写寄存器地址
MPU_IIC_Wait_Ack(); // 等待应答
for (i = 0; i < len; i++)
{
MPU_IIC_Send_Byte(buf[i]); // 发送数据
if (MPU_IIC_Wait_Ack()) // 等待ACK
{
MPU_IIC_Stop();
return 1;
}
}
MPU_IIC_Stop();
return 0;
}
// IIC连续读
// addr:器件地址
// reg:要读取的寄存器地址
// len:要读取的长度
// buf:读取到的数据存储区
// 返回值:0,正常
// 其他,错误代码
uint8_t MPU_Read_Len(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *buf)
{
MPU_IIC_Start();
MPU_IIC_Send_Byte((addr << 1) | 0); // 发送器件地址+写命令
if (MPU_IIC_Wait_Ack()) // 等待应答
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Send_Byte(reg); // 写寄存器地址
MPU_IIC_Wait_Ack(); // 等待应答
MPU_IIC_Start();
MPU_IIC_Send_Byte((addr << 1) | 1); // 发送器件地址+读命令
MPU_IIC_Wait_Ack(); // 等待应答
while (len)
{
if (len == 1)
*buf = MPU_IIC_Read_Byte(0); // 读数据,发送nACK
else
*buf = MPU_IIC_Read_Byte(1); // 读数据,发送ACK
len--;
buf++;
}
MPU_IIC_Stop(); // 产生一个停止条件
return 0;
}
// IIC写一个字节
// reg:寄存器地址
// data:数据
// 返回值:0,正常
// 其他,错误代码
uint8_t MPU_Write_Byte(uint8_t reg, uint8_t data)
{
MPU_IIC_Start();
MPU_IIC_Send_Byte((MPU_ADDR << 1) | 0); // 发送器件地址+写命令
if (MPU_IIC_Wait_Ack()) // 等待应答
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Send_Byte(reg); // 写寄存器地址
MPU_IIC_Wait_Ack(); // 等待应答
MPU_IIC_Send_Byte(data); // 发送数据
if (MPU_IIC_Wait_Ack()) // 等待ACK
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Stop();
return 0;
}
// IIC读一个字节
// reg:寄存器地址
// 返回值:读到的数据
uint8_t MPU_Read_Byte(uint8_t reg)
{
uint8_t res;
MPU_IIC_Start();
MPU_IIC_Send_Byte((MPU_ADDR << 1) | 0); // 发送器件地址+写命令
MPU_IIC_Wait_Ack(); // 等待应答
MPU_IIC_Send_Byte(reg); // 写寄存器地址
MPU_IIC_Wait_Ack(); // 等待应答
MPU_IIC_Start();
MPU_IIC_Send_Byte((MPU_ADDR << 1) | 1); // 发送器件地址+读命令
MPU_IIC_Wait_Ack(); // 等待应答
res = MPU_IIC_Read_Byte(0); // 读取数据,发送nACK
MPU_IIC_Stop(); // 产生一个停止条件
return res;
}
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file stm32f1xx_hal_msp.c
* @brief This file provides code for the MSP Initialization
* and de-Initialization codes.
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */
/* USER CODE END TD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN Define */
/* USER CODE END Define */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN Macro */
/* USER CODE END Macro */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* External functions --------------------------------------------------------*/
/* USER CODE BEGIN ExternalFunctions */
/* USER CODE END ExternalFunctions */
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* Initializes the Global MSP.
*/
void HAL_MspInit(void)
{
/* USER CODE BEGIN MspInit 0 */
/* USER CODE END MspInit 0 */
__HAL_RCC_AFIO_CLK_ENABLE();
__HAL_RCC_PWR_CLK_ENABLE();
/* System interrupt init*/
/** NOJTAG: JTAG-DP Disabled and SW-DP Enabled
*/
__HAL_AFIO_REMAP_SWJ_NOJTAG();
/* USER CODE BEGIN MspInit 1 */
/* USER CODE END MspInit 1 */
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file stm32f1xx_it.c
* @brief Interrupt Service Routines.
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */
/* USER CODE END TD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/* External variables --------------------------------------------------------*/
/* USER CODE BEGIN EV */
/* USER CODE END EV */
/******************************************************************************/
/* Cortex-M3 Processor Interruption and Exception Handlers */
/******************************************************************************/
/**
* @brief This function handles Non maskable interrupt.
*/
void NMI_Handler(void)
{
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
/* USER CODE END NonMaskableInt_IRQn 0 */
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
while (1)
{
}
/* USER CODE END NonMaskableInt_IRQn 1 */
}
/**
* @brief This function handles Hard fault interrupt.
*/
void HardFault_Handler(void)
{
/* USER CODE BEGIN HardFault_IRQn 0 */
/* USER CODE END HardFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
/* USER CODE END W1_HardFault_IRQn 0 */
}
}
/**
* @brief This function handles Memory management fault.
*/
void MemManage_Handler(void)
{
/* USER CODE BEGIN MemoryManagement_IRQn 0 */
/* USER CODE END MemoryManagement_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
/* USER CODE END W1_MemoryManagement_IRQn 0 */
}
}
/**
* @brief This function handles Prefetch fault, memory access fault.
*/
void BusFault_Handler(void)
{
/* USER CODE BEGIN BusFault_IRQn 0 */
/* USER CODE END BusFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_BusFault_IRQn 0 */
/* USER CODE END W1_BusFault_IRQn 0 */
}
}
/**
* @brief This function handles Undefined instruction or illegal state.
*/
void UsageFault_Handler(void)
{
/* USER CODE BEGIN UsageFault_IRQn 0 */
/* USER CODE END UsageFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_UsageFault_IRQn 0 */
/* USER CODE END W1_UsageFault_IRQn 0 */
}
}
/**
* @brief This function handles System service call via SWI instruction.
*/
void SVC_Handler(void)
{
/* USER CODE BEGIN SVCall_IRQn 0 */
/* USER CODE END SVCall_IRQn 0 */
/* USER CODE BEGIN SVCall_IRQn 1 */
/* USER CODE END SVCall_IRQn 1 */
}
/**
* @brief This function handles Debug monitor.
*/
void DebugMon_Handler(void)
{
/* USER CODE BEGIN DebugMonitor_IRQn 0 */
/* USER CODE END DebugMonitor_IRQn 0 */
/* USER CODE BEGIN DebugMonitor_IRQn 1 */
/* USER CODE END DebugMonitor_IRQn 1 */
}
/**
* @brief This function handles Pendable request for system service.
*/
void PendSV_Handler(void)
{
/* USER CODE BEGIN PendSV_IRQn 0 */
/* USER CODE END PendSV_IRQn 0 */
/* USER CODE BEGIN PendSV_IRQn 1 */
/* USER CODE END PendSV_IRQn 1 */
}
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file (startup_stm32f1xx.s). */
/******************************************************************************/
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/**
******************************************************************************
* @file syscalls.c
* @author Auto-generated by STM32CubeIDE
* @brief STM32CubeIDE Minimal System calls file
*
* For more information about which c-functions
* need which of these lowlevel functions
* please consult the Newlib libc-manual
******************************************************************************
* @attention
*
* Copyright (c) 2022 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Includes */
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <sys/times.h>
/* Variables */
extern int __io_putchar(int ch) __attribute__((weak));
extern int __io_getchar(void) __attribute__((weak));
char *__env[1] = { 0 };
char **environ = __env;
/* Functions */
void initialise_monitor_handles()
{
}
int _getpid(void)
{
return 1;
}
int _kill(int pid, int sig)
{
errno = EINVAL;
return -1;
}
void _exit (int status)
{
_kill(status, -1);
while (1) {} /* Make sure we hang here */
}
__attribute__((weak)) int _read(int file, char *ptr, int len)
{
int DataIdx;
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
*ptr++ = __io_getchar();
}
return len;
}
__attribute__((weak)) int _write(int file, char *ptr, int len)
{
int DataIdx;
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
__io_putchar(*ptr++);
}
return len;
}
int _close(int file)
{
return -1;
}
int _fstat(int file, struct stat *st)
{
st->st_mode = S_IFCHR;
return 0;
}
int _isatty(int file)
{
return 1;
}
int _lseek(int file, int ptr, int dir)
{
return 0;
}
int _open(char *path, int flags, ...)
{
/* Pretend like we always fail */
return -1;
}
int _wait(int *status)
{
errno = ECHILD;
return -1;
}
int _unlink(char *name)
{
errno = ENOENT;
return -1;
}
int _times(struct tms *buf)
{
return -1;
}
int _stat(char *file, struct stat *st)
{
st->st_mode = S_IFCHR;
return 0;
}
int _link(char *old, char *new)
{
errno = EMLINK;
return -1;
}
int _fork(void)
{
errno = EAGAIN;
return -1;
}
int _execve(char *name, char **argv, char **env)
{
errno = ENOMEM;
return -1;
}
/**
******************************************************************************
* @file sysmem.c
* @author Generated by STM32CubeIDE
* @brief STM32CubeIDE System Memory calls file
*
* For more information about which C functions
* need which of these lowlevel functions
* please consult the newlib libc manual
******************************************************************************
* @attention
*
* Copyright (c) 2022 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Includes */
#include <errno.h>
#include <stdint.h>
/**
* Pointer to the current high watermark of the heap usage
*/
static uint8_t *__sbrk_heap_end = NULL;
/**
* @brief _sbrk() allocates memory to the newlib heap and is used by malloc
* and others from the C library
*
* @verbatim
* ############################################################################
* # .data # .bss # newlib heap # MSP stack #
* # # # # Reserved by _Min_Stack_Size #
* ############################################################################
* ^-- RAM start ^-- _end _estack, RAM end --^
* @endverbatim
*
* This implementation starts allocating at the '_end' linker symbol
* The '_Min_Stack_Size' linker symbol reserves a memory for the MSP stack
* The implementation considers '_estack' linker symbol to be RAM end
* NOTE: If the MSP stack, at any point during execution, grows larger than the
* reserved size, please increase the '_Min_Stack_Size'.
*
* @param incr Memory size
* @return Pointer to allocated memory
*/
void *_sbrk(ptrdiff_t incr)
{
extern uint8_t _end; /* Symbol defined in the linker script */
extern uint8_t _estack; /* Symbol defined in the linker script */
extern uint32_t _Min_Stack_Size; /* Symbol defined in the linker script */
const uint32_t stack_limit = (uint32_t)&_estack - (uint32_t)&_Min_Stack_Size;
const uint8_t *max_heap = (uint8_t *)stack_limit;
uint8_t *prev_heap_end;
/* Initialize heap end at first call */
if (NULL == __sbrk_heap_end)
{
__sbrk_heap_end = &_end;
}
/* Protect heap from growing into the reserved MSP stack */
if (__sbrk_heap_end + incr > max_heap)
{
errno = ENOMEM;
return (void *)-1;
}
prev_heap_end = __sbrk_heap_end;
__sbrk_heap_end += incr;
return (void *)prev_heap_end;
}
/**
******************************************************************************
* @file system_stm32f1xx.c
* @author MCD Application Team
* @brief CMSIS Cortex-M3 Device Peripheral Access Layer System Source File.
*
* 1. This file provides two functions and one global variable to be called from
* user application:
* - SystemInit(): Setups the system clock (System clock source, PLL Multiplier
* factors, AHB/APBx prescalers and Flash settings).
* This function is called at startup just after reset and
* before branch to main program. This call is made inside
* the "startup_stm32f1xx_xx.s" file.
*
* - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
* by the user application to setup the SysTick
* timer or configure other parameters.
*
* - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
* be called whenever the core clock is changed
* during program execution.
*
* 2. After each device reset the HSI (8 MHz) is used as system clock source.
* Then SystemInit() function is called, in "startup_stm32f1xx_xx.s" file, to
* configure the system clock before to branch to main program.
*
* 4. The default value of HSE crystal is set to 8 MHz (or 25 MHz, depending on
* the product used), refer to "HSE_VALUE".
* When HSE is used as system clock source, directly or through PLL, and you
* are using different crystal you have to adapt the HSE value to your own
* configuration.
*
******************************************************************************
* @attention
*
* Copyright (c) 2017-2021 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/** @addtogroup CMSIS
* @{
*/
/** @addtogroup stm32f1xx_system
* @{
*/
/** @addtogroup STM32F1xx_System_Private_Includes
* @{
*/
#include "stm32f1xx.h"
/**
* @}
*/
/** @addtogroup STM32F1xx_System_Private_TypesDefinitions
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F1xx_System_Private_Defines
* @{
*/
#if !defined (HSE_VALUE)
#define HSE_VALUE 8000000U /*!< Default value of the External oscillator in Hz.
This value can be provided and adapted by the user application. */
#endif /* HSE_VALUE */
#if !defined (HSI_VALUE)
#define HSI_VALUE 8000000U /*!< Default value of the Internal oscillator in Hz.
This value can be provided and adapted by the user application. */
#endif /* HSI_VALUE */
/*!< Uncomment the following line if you need to use external SRAM */
#if defined(STM32F100xE) || defined(STM32F101xE) || defined(STM32F101xG) || defined(STM32F103xE) || defined(STM32F103xG)
/* #define DATA_IN_ExtSRAM */
#endif /* STM32F100xE || STM32F101xE || STM32F101xG || STM32F103xE || STM32F103xG */
/* Note: Following vector table addresses must be defined in line with linker
configuration. */
/*!< Uncomment the following line if you need to relocate the vector table
anywhere in Flash or Sram, else the vector table is kept at the automatic
remap of boot address selected */
/* #define USER_VECT_TAB_ADDRESS */
#if defined(USER_VECT_TAB_ADDRESS)
/*!< Uncomment the following line if you need to relocate your vector Table
in Sram else user remap will be done in Flash. */
/* #define VECT_TAB_SRAM */
#if defined(VECT_TAB_SRAM)
#define VECT_TAB_BASE_ADDRESS SRAM_BASE /*!< Vector Table base address field.
This value must be a multiple of 0x200. */
#define VECT_TAB_OFFSET 0x00000000U /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
#else
#define VECT_TAB_BASE_ADDRESS FLASH_BASE /*!< Vector Table base address field.
This value must be a multiple of 0x200. */
#define VECT_TAB_OFFSET 0x00000000U /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
#endif /* VECT_TAB_SRAM */
#endif /* USER_VECT_TAB_ADDRESS */
/******************************************************************************/
/**
* @}
*/
/** @addtogroup STM32F1xx_System_Private_Macros
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F1xx_System_Private_Variables
* @{
*/
/* This variable is updated in three ways:
1) by calling CMSIS function SystemCoreClockUpdate()
2) by calling HAL API function HAL_RCC_GetHCLKFreq()
3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency
Note: If you use this function to configure the system clock; then there
is no need to call the 2 first functions listed above, since SystemCoreClock
variable is updated automatically.
*/
uint32_t SystemCoreClock = 16000000;
const uint8_t AHBPrescTable[16U] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
const uint8_t APBPrescTable[8U] = {0, 0, 0, 0, 1, 2, 3, 4};
/**
* @}
*/
/** @addtogroup STM32F1xx_System_Private_FunctionPrototypes
* @{
*/
#if defined(STM32F100xE) || defined(STM32F101xE) || defined(STM32F101xG) || defined(STM32F103xE) || defined(STM32F103xG)
#ifdef DATA_IN_ExtSRAM
static void SystemInit_ExtMemCtl(void);
#endif /* DATA_IN_ExtSRAM */
#endif /* STM32F100xE || STM32F101xE || STM32F101xG || STM32F103xE || STM32F103xG */
/**
* @}
*/
/** @addtogroup STM32F1xx_System_Private_Functions
* @{
*/
/**
* @brief Setup the microcontroller system
* Initialize the Embedded Flash Interface, the PLL and update the
* SystemCoreClock variable.
* @note This function should be used only after reset.
* @param None
* @retval None
*/
void SystemInit (void)
{
#if defined(STM32F100xE) || defined(STM32F101xE) || defined(STM32F101xG) || defined(STM32F103xE) || defined(STM32F103xG)
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM */
#endif
/* Configure the Vector Table location -------------------------------------*/
#if defined(USER_VECT_TAB_ADDRESS)
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#endif /* USER_VECT_TAB_ADDRESS */
}
/**
* @brief Update SystemCoreClock variable according to Clock Register Values.
* The SystemCoreClock variable contains the core clock (HCLK), it can
* be used by the user application to setup the SysTick timer or configure
* other parameters.
*
* @note Each time the core clock (HCLK) changes, this function must be called
* to update SystemCoreClock variable value. Otherwise, any configuration
* based on this variable will be incorrect.
*
* @note - The system frequency computed by this function is not the real
* frequency in the chip. It is calculated based on the predefined
* constant and the selected clock source:
*
* - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
*
* - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
*
* - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**)
* or HSI_VALUE(*) multiplied by the PLL factors.
*
* (*) HSI_VALUE is a constant defined in stm32f1xx.h file (default value
* 8 MHz) but the real value may vary depending on the variations
* in voltage and temperature.
*
* (**) HSE_VALUE is a constant defined in stm32f1xx.h file (default value
* 8 MHz or 25 MHz, depending on the product used), user has to ensure
* that HSE_VALUE is same as the real frequency of the crystal used.
* Otherwise, this function may have wrong result.
*
* - The result of this function could be not correct when using fractional
* value for HSE crystal.
* @param None
* @retval None
*/
void SystemCoreClockUpdate (void)
{
uint32_t tmp = 0U, pllmull = 0U, pllsource = 0U;
#if defined(STM32F105xC) || defined(STM32F107xC)
uint32_t prediv1source = 0U, prediv1factor = 0U, prediv2factor = 0U, pll2mull = 0U;
#endif /* STM32F105xC */
#if defined(STM32F100xB) || defined(STM32F100xE)
uint32_t prediv1factor = 0U;
#endif /* STM32F100xB or STM32F100xE */
/* Get SYSCLK source -------------------------------------------------------*/
tmp = RCC->CFGR & RCC_CFGR_SWS;
switch (tmp)
{
case 0x00U: /* HSI used as system clock */
SystemCoreClock = HSI_VALUE;
break;
case 0x04U: /* HSE used as system clock */
SystemCoreClock = HSE_VALUE;
break;
case 0x08U: /* PLL used as system clock */
/* Get PLL clock source and multiplication factor ----------------------*/
pllmull = RCC->CFGR & RCC_CFGR_PLLMULL;
pllsource = RCC->CFGR & RCC_CFGR_PLLSRC;
#if !defined(STM32F105xC) && !defined(STM32F107xC)
pllmull = ( pllmull >> 18U) + 2U;
if (pllsource == 0x00U)
{
/* HSI oscillator clock divided by 2 selected as PLL clock entry */
SystemCoreClock = (HSI_VALUE >> 1U) * pllmull;
}
else
{
#if defined(STM32F100xB) || defined(STM32F100xE)
prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1U;
/* HSE oscillator clock selected as PREDIV1 clock entry */
SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull;
#else
/* HSE selected as PLL clock entry */
if ((RCC->CFGR & RCC_CFGR_PLLXTPRE) != (uint32_t)RESET)
{/* HSE oscillator clock divided by 2 */
SystemCoreClock = (HSE_VALUE >> 1U) * pllmull;
}
else
{
SystemCoreClock = HSE_VALUE * pllmull;
}
#endif
}
#else
pllmull = pllmull >> 18U;
if (pllmull != 0x0DU)
{
pllmull += 2U;
}
else
{ /* PLL multiplication factor = PLL input clock * 6.5 */
pllmull = 13U / 2U;
}
if (pllsource == 0x00U)
{
/* HSI oscillator clock divided by 2 selected as PLL clock entry */
SystemCoreClock = (HSI_VALUE >> 1U) * pllmull;
}
else
{/* PREDIV1 selected as PLL clock entry */
/* Get PREDIV1 clock source and division factor */
prediv1source = RCC->CFGR2 & RCC_CFGR2_PREDIV1SRC;
prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1U;
if (prediv1source == 0U)
{
/* HSE oscillator clock selected as PREDIV1 clock entry */
SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull;
}
else
{/* PLL2 clock selected as PREDIV1 clock entry */
/* Get PREDIV2 division factor and PLL2 multiplication factor */
prediv2factor = ((RCC->CFGR2 & RCC_CFGR2_PREDIV2) >> 4U) + 1U;
pll2mull = ((RCC->CFGR2 & RCC_CFGR2_PLL2MUL) >> 8U) + 2U;
SystemCoreClock = (((HSE_VALUE / prediv2factor) * pll2mull) / prediv1factor) * pllmull;
}
}
#endif /* STM32F105xC */
break;
default:
SystemCoreClock = HSI_VALUE;
break;
}
/* Compute HCLK clock frequency ----------------*/
/* Get HCLK prescaler */
tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4U)];
/* HCLK clock frequency */
SystemCoreClock >>= tmp;
}
#if defined(STM32F100xE) || defined(STM32F101xE) || defined(STM32F101xG) || defined(STM32F103xE) || defined(STM32F103xG)
/**
* @brief Setup the external memory controller. Called in startup_stm32f1xx.s
* before jump to __main
* @param None
* @retval None
*/
#ifdef DATA_IN_ExtSRAM
/**
* @brief Setup the external memory controller.
* Called in startup_stm32f1xx_xx.s/.c before jump to main.
* This function configures the external SRAM mounted on STM3210E-EVAL
* board (STM32 High density devices). This SRAM will be used as program
* data memory (including heap and stack).
* @param None
* @retval None
*/
void SystemInit_ExtMemCtl(void)
{
__IO uint32_t tmpreg;
/*!< FSMC Bank1 NOR/SRAM3 is used for the STM3210E-EVAL, if another Bank is
required, then adjust the Register Addresses */
/* Enable FSMC clock */
RCC->AHBENR = 0x00000114U;
/* Delay after an RCC peripheral clock enabling */
tmpreg = READ_BIT(RCC->AHBENR, RCC_AHBENR_FSMCEN);
/* Enable GPIOD, GPIOE, GPIOF and GPIOG clocks */
RCC->APB2ENR = 0x000001E0U;
/* Delay after an RCC peripheral clock enabling */
tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPDEN);
(void)(tmpreg);
/* --------------- SRAM Data lines, NOE and NWE configuration ---------------*/
/*---------------- SRAM Address lines configuration -------------------------*/
/*---------------- NOE and NWE configuration --------------------------------*/
/*---------------- NE3 configuration ----------------------------------------*/
/*---------------- NBL0, NBL1 configuration ---------------------------------*/
GPIOD->CRL = 0x44BB44BBU;
GPIOD->CRH = 0xBBBBBBBBU;
GPIOE->CRL = 0xB44444BBU;
GPIOE->CRH = 0xBBBBBBBBU;
GPIOF->CRL = 0x44BBBBBBU;
GPIOF->CRH = 0xBBBB4444U;
GPIOG->CRL = 0x44BBBBBBU;
GPIOG->CRH = 0x444B4B44U;
/*---------------- FSMC Configuration ---------------------------------------*/
/*---------------- Enable FSMC Bank1_SRAM Bank ------------------------------*/
FSMC_Bank1->BTCR[4U] = 0x00001091U;
FSMC_Bank1->BTCR[5U] = 0x00110212U;
}
#endif /* DATA_IN_ExtSRAM */
#endif /* STM32F100xE || STM32F101xE || STM32F101xG || STM32F103xE || STM32F103xG */
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file tim.c
* @brief This file provides code for the configuration
* of the TIM instances.
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "tim.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim4;
/* TIM1 init function */
void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 65535;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_DISABLE;
sSlaveConfig.InputTrigger = TIM_TS_ITR0;
if (HAL_TIM_SlaveConfigSynchro(&htim1, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
/* USER CODE END TIM1_Init 2 */
}
/* TIM4 init function */
void MX_TIM4_Init(void)
{
/* USER CODE BEGIN TIM4_Init 0 */
/* USER CODE END TIM4_Init 0 */
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM4_Init 1 */
/* USER CODE END TIM4_Init 1 */
htim4.Instance = TIM4;
htim4.Init.Prescaler = 0;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 65535;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;
sSlaveConfig.InputTrigger = TIM_TS_ITR0;
if (HAL_TIM_SlaveConfigSynchro(&htim4, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM4_Init 2 */
/* USER CODE END TIM4_Init 2 */
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspInit 0 */
/* USER CODE END TIM1_MspInit 0 */
/* TIM1 clock enable */
__HAL_RCC_TIM1_CLK_ENABLE();
/* USER CODE BEGIN TIM1_MspInit 1 */
/* USER CODE END TIM1_MspInit 1 */
}
else if(tim_baseHandle->Instance==TIM4)
{
/* USER CODE BEGIN TIM4_MspInit 0 */
/* USER CODE END TIM4_MspInit 0 */
/* TIM4 clock enable */
__HAL_RCC_TIM4_CLK_ENABLE();
/* USER CODE BEGIN TIM4_MspInit 1 */
/* USER CODE END TIM4_MspInit 1 */
}
}
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspDeInit 0 */
/* USER CODE END TIM1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM1_CLK_DISABLE();
/* USER CODE BEGIN TIM1_MspDeInit 1 */
/* USER CODE END TIM1_MspDeInit 1 */
}
else if(tim_baseHandle->Instance==TIM4)
{
/* USER CODE BEGIN TIM4_MspDeInit 0 */
/* USER CODE END TIM4_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM4_CLK_DISABLE();
/* USER CODE BEGIN TIM4_MspDeInit 1 */
/* USER CODE END TIM4_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file usart.c
* @brief This file provides code for the configuration
* of the USART instances.
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "usart.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
UART_HandleTypeDef huart1;
/* USART1 init function */
void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(uartHandle->Instance==USART1)
{
/* USER CODE BEGIN USART1_MspInit 0 */
/* USER CODE END USART1_MspInit 0 */
/* USART1 clock enable */
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN USART1_MspInit 1 */
/* USER CODE END USART1_MspInit 1 */
}
}
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{
if(uartHandle->Instance==USART1)
{
/* USER CODE BEGIN USART1_MspDeInit 0 */
/* USER CODE END USART1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USART1_CLK_DISABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);
/* USER CODE BEGIN USART1_MspDeInit 1 */
/* USER CODE END USART1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/**
*************** (C) COPYRIGHT 2017 STMicroelectronics ************************
* @file startup_stm32f103xb.s
* @author MCD Application Team
* @brief STM32F103xB Devices vector table for Atollic toolchain.
* This module performs:
* - Set the initial SP
* - Set the initial PC == Reset_Handler,
* - Set the vector table entries with the exceptions ISR address
* - Configure the clock system
* - Branches to main in the C library (which eventually
* calls main()).
* After Reset the Cortex-M3 processor is in Thread mode,
* priority is Privileged, and the Stack is set to Main.
******************************************************************************
* @attention
*
* Copyright (c) 2017-2021 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
.syntax unified
.cpu cortex-m3
.fpu softvfp
.thumb
.global g_pfnVectors
.global Default_Handler
/* start address for the initialization values of the .data section.
defined in linker script */
.word _sidata
/* start address for the .data section. defined in linker script */
.word _sdata
/* end address for the .data section. defined in linker script */
.word _edata
/* start address for the .bss section. defined in linker script */
.word _sbss
/* end address for the .bss section. defined in linker script */
.word _ebss
.equ BootRAM, 0xF108F85F
/**
* @brief This is the code that gets called when the processor first
* starts execution following a reset event. Only the absolutely
* necessary set is performed, after which the application
* supplied main() routine is called.
* @param None
* @retval : None
*/
.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
/* Call the clock system initialization function.*/
bl SystemInit
/* Copy the data segment initializers from flash to SRAM */
ldr r0, =_sdata
ldr r1, =_edata
ldr r2, =_sidata
movs r3, #0
b LoopCopyDataInit
CopyDataInit:
ldr r4, [r2, r3]
str r4, [r0, r3]
adds r3, r3, #4
LoopCopyDataInit:
adds r4, r0, r3
cmp r4, r1
bcc CopyDataInit
/* Zero fill the bss segment. */
ldr r2, =_sbss
ldr r4, =_ebss
movs r3, #0
b LoopFillZerobss
FillZerobss:
str r3, [r2]
adds r2, r2, #4
LoopFillZerobss:
cmp r2, r4
bcc FillZerobss
/* Call static constructors */
bl __libc_init_array
/* Call the application's entry point.*/
bl main
bx lr
.size Reset_Handler, .-Reset_Handler
/**
* @brief This is the code that gets called when the processor receives an
* unexpected interrupt. This simply enters an infinite loop, preserving
* the system state for examination by a debugger.
*
* @param None
* @retval : None
*/
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler
/******************************************************************************
*
* The minimal vector table for a Cortex M3. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
*
******************************************************************************/
.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
.size g_pfnVectors, .-g_pfnVectors
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
.word WWDG_IRQHandler
.word PVD_IRQHandler
.word TAMPER_IRQHandler
.word RTC_IRQHandler
.word FLASH_IRQHandler
.word RCC_IRQHandler
.word EXTI0_IRQHandler
.word EXTI1_IRQHandler
.word EXTI2_IRQHandler
.word EXTI3_IRQHandler
.word EXTI4_IRQHandler
.word DMA1_Channel1_IRQHandler
.word DMA1_Channel2_IRQHandler
.word DMA1_Channel3_IRQHandler
.word DMA1_Channel4_IRQHandler
.word DMA1_Channel5_IRQHandler
.word DMA1_Channel6_IRQHandler
.word DMA1_Channel7_IRQHandler
.word ADC1_2_IRQHandler
.word USB_HP_CAN1_TX_IRQHandler
.word USB_LP_CAN1_RX0_IRQHandler
.word CAN1_RX1_IRQHandler
.word CAN1_SCE_IRQHandler
.word EXTI9_5_IRQHandler
.word TIM1_BRK_IRQHandler
.word TIM1_UP_IRQHandler
.word TIM1_TRG_COM_IRQHandler
.word TIM1_CC_IRQHandler
.word TIM2_IRQHandler
.word TIM3_IRQHandler
.word TIM4_IRQHandler
.word I2C1_EV_IRQHandler
.word I2C1_ER_IRQHandler
.word I2C2_EV_IRQHandler
.word I2C2_ER_IRQHandler
.word SPI1_IRQHandler
.word SPI2_IRQHandler
.word USART1_IRQHandler
.word USART2_IRQHandler
.word USART3_IRQHandler
.word EXTI15_10_IRQHandler
.word RTC_Alarm_IRQHandler
.word USBWakeUp_IRQHandler
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word BootRAM /* @0x108. This is for boot in RAM mode for
STM32F10x Medium Density devices. */
/*******************************************************************************
*
* Provide weak aliases for each Exception handler to the Default_Handler.
* As they are weak aliases, any function with the same name will override
* this definition.
*
*******************************************************************************/
.weak NMI_Handler
.thumb_set NMI_Handler,Default_Handler
.weak HardFault_Handler
.thumb_set HardFault_Handler,Default_Handler
.weak MemManage_Handler
.thumb_set MemManage_Handler,Default_Handler
.weak BusFault_Handler
.thumb_set BusFault_Handler,Default_Handler
.weak UsageFault_Handler
.thumb_set UsageFault_Handler,Default_Handler
.weak SVC_Handler
.thumb_set SVC_Handler,Default_Handler
.weak DebugMon_Handler
.thumb_set DebugMon_Handler,Default_Handler
.weak PendSV_Handler
.thumb_set PendSV_Handler,Default_Handler
.weak SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler
.weak WWDG_IRQHandler
.thumb_set WWDG_IRQHandler,Default_Handler
.weak PVD_IRQHandler
.thumb_set PVD_IRQHandler,Default_Handler
.weak TAMPER_IRQHandler
.thumb_set TAMPER_IRQHandler,Default_Handler
.weak RTC_IRQHandler
.thumb_set RTC_IRQHandler,Default_Handler
.weak FLASH_IRQHandler
.thumb_set FLASH_IRQHandler,Default_Handler
.weak RCC_IRQHandler
.thumb_set RCC_IRQHandler,Default_Handler
.weak EXTI0_IRQHandler
.thumb_set EXTI0_IRQHandler,Default_Handler
.weak EXTI1_IRQHandler
.thumb_set EXTI1_IRQHandler,Default_Handler
.weak EXTI2_IRQHandler
.thumb_set EXTI2_IRQHandler,Default_Handler
.weak EXTI3_IRQHandler
.thumb_set EXTI3_IRQHandler,Default_Handler
.weak EXTI4_IRQHandler
.thumb_set EXTI4_IRQHandler,Default_Handler
.weak DMA1_Channel1_IRQHandler
.thumb_set DMA1_Channel1_IRQHandler,Default_Handler
.weak DMA1_Channel2_IRQHandler
.thumb_set DMA1_Channel2_IRQHandler,Default_Handler
.weak DMA1_Channel3_IRQHandler
.thumb_set DMA1_Channel3_IRQHandler,Default_Handler
.weak DMA1_Channel4_IRQHandler
.thumb_set DMA1_Channel4_IRQHandler,Default_Handler
.weak DMA1_Channel5_IRQHandler
.thumb_set DMA1_Channel5_IRQHandler,Default_Handler
.weak DMA1_Channel6_IRQHandler
.thumb_set DMA1_Channel6_IRQHandler,Default_Handler
.weak DMA1_Channel7_IRQHandler
.thumb_set DMA1_Channel7_IRQHandler,Default_Handler
.weak ADC1_2_IRQHandler
.thumb_set ADC1_2_IRQHandler,Default_Handler
.weak USB_HP_CAN1_TX_IRQHandler
.thumb_set USB_HP_CAN1_TX_IRQHandler,Default_Handler
.weak USB_LP_CAN1_RX0_IRQHandler
.thumb_set USB_LP_CAN1_RX0_IRQHandler,Default_Handler
.weak CAN1_RX1_IRQHandler
.thumb_set CAN1_RX1_IRQHandler,Default_Handler
.weak CAN1_SCE_IRQHandler
.thumb_set CAN1_SCE_IRQHandler,Default_Handler
.weak EXTI9_5_IRQHandler
.thumb_set EXTI9_5_IRQHandler,Default_Handler
.weak TIM1_BRK_IRQHandler
.thumb_set TIM1_BRK_IRQHandler,Default_Handler
.weak TIM1_UP_IRQHandler
.thumb_set TIM1_UP_IRQHandler,Default_Handler
.weak TIM1_TRG_COM_IRQHandler
.thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler
.weak TIM1_CC_IRQHandler
.thumb_set TIM1_CC_IRQHandler,Default_Handler
.weak TIM2_IRQHandler
.thumb_set TIM2_IRQHandler,Default_Handler
.weak TIM3_IRQHandler
.thumb_set TIM3_IRQHandler,Default_Handler
.weak TIM4_IRQHandler
.thumb_set TIM4_IRQHandler,Default_Handler
.weak I2C1_EV_IRQHandler
.thumb_set I2C1_EV_IRQHandler,Default_Handler
.weak I2C1_ER_IRQHandler
.thumb_set I2C1_ER_IRQHandler,Default_Handler
.weak I2C2_EV_IRQHandler
.thumb_set I2C2_EV_IRQHandler,Default_Handler
.weak I2C2_ER_IRQHandler
.thumb_set I2C2_ER_IRQHandler,Default_Handler
.weak SPI1_IRQHandler
.thumb_set SPI1_IRQHandler,Default_Handler
.weak SPI2_IRQHandler
.thumb_set SPI2_IRQHandler,Default_Handler
.weak USART1_IRQHandler
.thumb_set USART1_IRQHandler,Default_Handler
.weak USART2_IRQHandler
.thumb_set USART2_IRQHandler,Default_Handler
.weak USART3_IRQHandler
.thumb_set USART3_IRQHandler,Default_Handler
.weak EXTI15_10_IRQHandler
.thumb_set EXTI15_10_IRQHandler,Default_Handler
.weak RTC_Alarm_IRQHandler
.thumb_set RTC_Alarm_IRQHandler,Default_Handler
.weak USBWakeUp_IRQHandler
.thumb_set USBWakeUp_IRQHandler,Default_Handler
Core/Src/IIC.o: ../Core/Src/IIC.c ../Core/Inc/IIC.h ../Core/Inc/main.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h \
../Core/Inc/stm32f1xx_hal_conf.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_def.h \
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f1xx.h \
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f103xb.h \
../Drivers/CMSIS/Include/core_cm3.h \
../Drivers/CMSIS/Include/cmsis_version.h \
../Drivers/CMSIS/Include/cmsis_compiler.h \
../Drivers/CMSIS/Include/cmsis_gcc.h \
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc_ex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio_ex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_exti.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma_ex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_cortex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_tim.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_tim_ex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_uart.h
../Core/Inc/IIC.h:
../Core/Inc/main.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h:
../Core/Inc/stm32f1xx_hal_conf.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_def.h:
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f1xx.h:
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f103xb.h:
../Drivers/CMSIS/Include/core_cm3.h:
../Drivers/CMSIS/Include/cmsis_version.h:
../Drivers/CMSIS/Include/cmsis_compiler.h:
../Drivers/CMSIS/Include/cmsis_gcc.h:
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc_ex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio_ex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_exti.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma_ex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_cortex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_tim.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_tim_ex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_uart.h:
../Core/Src/IIC.c:46:13:IIC_Delay 16 static
../Core/Src/IIC.c:71:6:IIC_Start 8 static
../Core/Src/IIC.c:91:6:IIC_Stop 8 static
../Core/Src/IIC.c:108:6:IIC_Send_Byte 24 static
../Core/Src/IIC.c:144:9:IIC_Read_Byte 24 static
../Core/Src/IIC.c:178:9:IIC_Wait_Ack 16 static
../Core/Src/IIC.c:207:6:IIC_Ack 8 static
../Core/Src/IIC.c:226:6:IIC_NAck 8 static
../Core/Src/IIC.c:244:6:IIC_GPIO_Init 32 static
../Core/Src/IIC.c:276:9:IIC_CheckDevice 24 static
Core/Src/gpio.o: ../Core/Src/gpio.c ../Core/Inc/gpio.h ../Core/Inc/main.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h \
../Core/Inc/stm32f1xx_hal_conf.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_def.h \
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f1xx.h \
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f103xb.h \
../Drivers/CMSIS/Include/core_cm3.h \
../Drivers/CMSIS/Include/cmsis_version.h \
../Drivers/CMSIS/Include/cmsis_compiler.h \
../Drivers/CMSIS/Include/cmsis_gcc.h \
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc_ex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio_ex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_exti.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma_ex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_cortex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_tim.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_tim_ex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_uart.h
../Core/Inc/gpio.h:
../Core/Inc/main.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h:
../Core/Inc/stm32f1xx_hal_conf.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_def.h:
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f1xx.h:
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f103xb.h:
../Drivers/CMSIS/Include/core_cm3.h:
../Drivers/CMSIS/Include/cmsis_version.h:
../Drivers/CMSIS/Include/cmsis_compiler.h:
../Drivers/CMSIS/Include/cmsis_gcc.h:
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc_ex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio_ex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_exti.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma_ex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_cortex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_tim.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_tim_ex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_uart.h:
../Core/Src/gpio.c:42:6:MX_GPIO_Init 40 static
Core/Src/inv_mpu.o: ../Core/Src/inv_mpu.c ../Core/Inc/inv_mpu.h \
../Core/Inc/main.h ../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h \
../Core/Inc/stm32f1xx_hal_conf.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_def.h \
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f1xx.h \
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f103xb.h \
../Drivers/CMSIS/Include/core_cm3.h \
../Drivers/CMSIS/Include/cmsis_version.h \
../Drivers/CMSIS/Include/cmsis_compiler.h \
../Drivers/CMSIS/Include/cmsis_gcc.h \
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc_ex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio_ex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_exti.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma_ex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_cortex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_tim.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_tim_ex.h \
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_uart.h \
../Core/Inc/inv_mpu_dmp_motion_driver.h ../Core/Inc/MPU6050.h \
../Core/Inc/IIC.h ../Core/Inc/usart.h
../Core/Inc/inv_mpu.h:
../Core/Inc/main.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal.h:
../Core/Inc/stm32f1xx_hal_conf.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_def.h:
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f1xx.h:
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/stm32f103xb.h:
../Drivers/CMSIS/Include/core_cm3.h:
../Drivers/CMSIS/Include/cmsis_version.h:
../Drivers/CMSIS/Include/cmsis_compiler.h:
../Drivers/CMSIS/Include/cmsis_gcc.h:
../Drivers/CMSIS/Device/ST/STM32F1xx/Include/system_stm32f1xx.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/stm32_hal_legacy.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_rcc_ex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_gpio_ex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_exti.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_dma_ex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_cortex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_flash_ex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_pwr.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_tim.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_tim_ex.h:
../Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_uart.h:
../Core/Inc/inv_mpu_dmp_motion_driver.h:
../Core/Inc/MPU6050.h:
../Core/Inc/IIC.h:
../Core/Inc/usart.h:
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment