STM32H750XB_RT-THREAD/44-MPU6050(包含程序+上位机)/程序/4.STM32-MPU6050_DMP测试例程/User/main.c
2025-07-21 14:34:29 +08:00

1285 lines
42 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
******************************************************************************
* @file main.c
* @author fire
* @version V1.0
* @date 2016-xx-xx
* @brief MPU6050测试程序
* 本程序必须配合匿名上位机使用!!串口向上位机传输的是数据包,所以直接用串口调试助手看是乱码,这是正常的!!
******************************************************************************
* @attention
*
* 实验平台:秉火 STM32 H750 开发板
* 论坛 :http://www.firebbs.cn
* 淘宝 :http://firestm32.taobao.com
*
******************************************************************************
*/
#include "stm32h7xx.h"
#include "./led/bsp_led.h"
#include "./usart/bsp_debug_usart.h"
#include <stdlib.h>
#include "main.h"
#include "./i2c/i2c.h"
#include "./mpu6050/bsp_mpu_exti.h"
#include "stdio.h"
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
#include "invensense.h"
#include "invensense_adv.h"
#include "eMPL_outputs.h"
#include "mltypes.h"
#include "mpu.h"
#include "log.h"
#include "packet.h"
/* Private typedef -----------------------------------------------------------*/
/* Data read from MPL. */
#define PRINT_ACCEL (0x01)
#define PRINT_GYRO (0x02)
#define PRINT_QUAT (0x04)
#define PRINT_COMPASS (0x08)
#define PRINT_EULER (0x10)
#define PRINT_ROT_MAT (0x20)
#define PRINT_HEADING (0x40)
#define PRINT_PEDO (0x80)
#define PRINT_LINEAR_ACCEL (0x100)
#define PRINT_GRAVITY_VECTOR (0x200)
volatile uint32_t hal_timestamp = 0;
#define ACCEL_ON (0x01)
#define GYRO_ON (0x02)
#define COMPASS_ON (0x04)
#define MOTION (0)
#define NO_MOTION (1)
/* Starting sampling rate. */
#define DEFAULT_MPU_HZ (20)
//#define FLASH_SIZE (512)
#define FLASH_MEM_START ((void*)0x1800)
#define PEDO_READ_MS (1000)
#define TEMP_READ_MS (500)
#define COMPASS_READ_MS (100)
struct rx_s {
unsigned char header[3];
unsigned char cmd;
};
struct hal_s {
unsigned char lp_accel_mode;
unsigned char sensors;
unsigned char dmp_on;
unsigned char wait_for_tap;
volatile unsigned char new_gyro;
unsigned char motion_int_mode;
unsigned long no_dmp_hz;
unsigned long next_pedo_ms;
unsigned long next_temp_ms;
unsigned long next_compass_ms;
unsigned int report;
unsigned short dmp_features;
struct rx_s rx;
};
static struct hal_s hal = {0};
/* USB RX binary semaphore. Actually, it's just a flag. Not included in struct
* because it's declared extern elsewhere.
*/
volatile unsigned char rx_new;
unsigned char *mpl_key = (unsigned char*)"eMPL 5.1";
/* Platform-specific information. Kinda like a boardfile. */
struct platform_data_s {
signed char orientation[9];
};
/* The sensors can be mounted onto the board in any orientation. The mounting
* matrix seen below tells the MPL how to rotate the raw data from the
* driver(s).
* TODO: The following matrices refer to the configuration on internal test
* boards at Invensense. If needed, please modify the matrices to match the
* chip-to-body matrix for your particular set up.
*/
static struct platform_data_s gyro_pdata = {
.orientation = { 1, 0, 0,
0, 1, 0,
0, 0, 1}
};
#if defined MPU9150 || defined MPU9250
static struct platform_data_s compass_pdata = {
.orientation = { 0, 1, 0,
1, 0, 0,
0, 0, -1}
};
#define COMPASS_ENABLED 1
#elif defined AK8975_SECONDARY
static struct platform_data_s compass_pdata = {
.orientation = {-1, 0, 0,
0, 1, 0,
0, 0,-1}
};
#define COMPASS_ENABLED 1
#elif defined AK8963_SECONDARY
static struct platform_data_s compass_pdata = {
.orientation = {-1, 0, 0,
0,-1, 0,
0, 0, 1}
};
#define COMPASS_ENABLED 1
#endif
#define BYTE0(dwTemp) (*(char *)(&dwTemp))
#define BYTE1(dwTemp) (*((char *)(&dwTemp) + 1))
#define BYTE2(dwTemp) (*((char *)(&dwTemp) + 2))
#define BYTE3(dwTemp) (*((char *)(&dwTemp) + 3))
/**
* @brief 控制串口发送1个字符
* @param c:要发送的字符
* @retval none
*/
void usart_send_char(uint8_t c)
{
HAL_UART_Transmit( &UartHandle,(uint8_t *)&c ,1,1000);
}
/*函数功能根据匿名最新上位机协议写的显示姿态的程序上位机0512版本
*具体协议说明请查看上位机软件的帮助说明。
*/
void Data_Send_Status(float Pitch,float Roll,float Yaw)
{
unsigned char i=0;
unsigned char _cnt=0,sum = 0;
unsigned int _temp;
unsigned char data_to_send[50];
data_to_send[_cnt++]=0xAA;
data_to_send[_cnt++]=0xAA;
data_to_send[_cnt++]=0x01;
data_to_send[_cnt++]=0;
_temp = (int)(Roll*100);
data_to_send[_cnt++]=BYTE1(_temp);
data_to_send[_cnt++]=BYTE0(_temp);
_temp = 0-(int)(Pitch*100);
data_to_send[_cnt++]=BYTE1(_temp);
data_to_send[_cnt++]=BYTE0(_temp);
_temp = (int)(Yaw*100);
data_to_send[_cnt++]=BYTE1(_temp);
data_to_send[_cnt++]=BYTE0(_temp);
_temp = 0;
data_to_send[_cnt++]=BYTE3(_temp);
data_to_send[_cnt++]=BYTE2(_temp);
data_to_send[_cnt++]=BYTE1(_temp);
data_to_send[_cnt++]=BYTE0(_temp);
data_to_send[_cnt++]=0xA0;
data_to_send[3] = _cnt-4;
//和校验
for(i=0;i<_cnt;i++)
sum+= data_to_send[i];
data_to_send[_cnt++]=sum;
//串口发送数据
for(i=0;i<_cnt;i++)
usart_send_char(data_to_send[i]);
}
/*函数功能根据匿名最新上位机协议写的显示传感器数据上位机0512版本
*具体协议说明请查看上位机软件的帮助说明。
*/
void Send_Data(int16_t *Gyro,int16_t *Accel)
{
unsigned char i=0;
unsigned char _cnt=0,sum = 0;
// unsigned int _temp;
unsigned char data_to_send[50];
data_to_send[_cnt++]=0xAA;
data_to_send[_cnt++]=0xAA;
data_to_send[_cnt++]=0x02;
data_to_send[_cnt++]=0;
data_to_send[_cnt++]=BYTE1(Accel[0]);
data_to_send[_cnt++]=BYTE0(Accel[0]);
data_to_send[_cnt++]=BYTE1(Accel[1]);
data_to_send[_cnt++]=BYTE0(Accel[1]);
data_to_send[_cnt++]=BYTE1(Accel[2]);
data_to_send[_cnt++]=BYTE0(Accel[2]);
data_to_send[_cnt++]=BYTE1(Gyro[0]);
data_to_send[_cnt++]=BYTE0(Gyro[0]);
data_to_send[_cnt++]=BYTE1(Gyro[1]);
data_to_send[_cnt++]=BYTE0(Gyro[1]);
data_to_send[_cnt++]=BYTE1(Gyro[2]);
data_to_send[_cnt++]=BYTE0(Gyro[2]);
data_to_send[_cnt++]=0;
data_to_send[_cnt++]=0;
data_to_send[_cnt++]=0;
data_to_send[_cnt++]=0;
data_to_send[_cnt++]=0;
data_to_send[_cnt++]=0;
data_to_send[3] = _cnt-4;
//和校验
for(i=0;i<_cnt;i++)
sum+= data_to_send[i];
data_to_send[_cnt++]=sum;
//串口发送数据
for(i=0;i<_cnt;i++)
usart_send_char(data_to_send[i]);
}
extern struct inv_sensor_cal_t sensors;
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* ---------------------------------------------------------------------------*/
/* Get data from MPL.
* TODO: Add return values to the inv_get_sensor_type_xxx APIs to differentiate
* between new and stale data.
*/
static void read_from_mpl(void)
{
long msg, data[9];
int8_t accuracy;
unsigned long timestamp;
float float_data[3] = {0};
if (inv_get_sensor_type_quat(data, &accuracy, (inv_time_t*)&timestamp)) {
/* Sends a quaternion packet to the PC. Since this is used by the Python
* test app to visually represent a 3D quaternion, it's sent each time
* the MPL has new data.
*/
eMPL_send_quat(data);
/* Specific data packets can be sent or suppressed using USB commands. */
if (hal.report & PRINT_QUAT)
eMPL_send_data(PACKET_DATA_QUAT, data);
}
if (hal.report & PRINT_ACCEL) {
if (inv_get_sensor_type_accel(data, &accuracy,
(inv_time_t*)&timestamp))
eMPL_send_data(PACKET_DATA_ACCEL, data);
}
if (hal.report & PRINT_GYRO) {
if (inv_get_sensor_type_gyro(data, &accuracy,
(inv_time_t*)&timestamp))
eMPL_send_data(PACKET_DATA_GYRO, data);
}
#ifdef COMPASS_ENABLED
if (hal.report & PRINT_COMPASS) {
if (inv_get_sensor_type_compass(data, &accuracy,
(inv_time_t*)&timestamp))
eMPL_send_data(PACKET_DATA_COMPASS, data);
}
#endif
if (hal.report & PRINT_EULER) {
if (inv_get_sensor_type_euler(data, &accuracy,
(inv_time_t*)&timestamp))
eMPL_send_data(PACKET_DATA_EULER, data);
}
/********************使用液晶屏显示数据**************************/
if(1)
{
char cStr [ 70 ];
unsigned long timestamp,step_count,walk_time;
/*获取欧拉角*/
if (inv_get_sensor_type_euler(data, &accuracy,(inv_time_t*)&timestamp))
{
float Pitch,Roll,Yaw;
//inv_get_sensor_type_euler读出的数据是Q16格式所以左移16位.
Pitch =data[0]*1.0/(1<<16) ;
Roll = data[1]*1.0/(1<<16);
Yaw = data[2]*1.0/(1<<16);
/*向匿名上位机发送姿态*/
Data_Send_Status(Pitch,Roll,Yaw);
/*向匿名上位机发送原始数据*/
Send_Data((int16_t *)&sensors.gyro.raw,(int16_t *)&sensors.accel.raw);
}
/*获取步数*/
get_tick_count(&timestamp);
if (timestamp > hal.next_pedo_ms) {
hal.next_pedo_ms = timestamp + PEDO_READ_MS;
dmp_get_pedometer_step_count(&step_count);
dmp_get_pedometer_walk_time(&walk_time);
}
}
if (hal.report & PRINT_ROT_MAT) {
if (inv_get_sensor_type_rot_mat(data, &accuracy,
(inv_time_t*)&timestamp))
eMPL_send_data(PACKET_DATA_ROT, data);
}
if (hal.report & PRINT_HEADING) {
if (inv_get_sensor_type_heading(data, &accuracy,
(inv_time_t*)&timestamp))
eMPL_send_data(PACKET_DATA_HEADING, data);
}
if (hal.report & PRINT_LINEAR_ACCEL) {
if (inv_get_sensor_type_linear_acceleration(float_data, &accuracy, (inv_time_t*)&timestamp)) {
MPL_LOGI("Linear Accel: %7.5f %7.5f %7.5f\r\n",
float_data[0], float_data[1], float_data[2]);
}
}
if (hal.report & PRINT_GRAVITY_VECTOR) {
if (inv_get_sensor_type_gravity(float_data, &accuracy,
(inv_time_t*)&timestamp))
MPL_LOGI("Gravity Vector: %7.5f %7.5f %7.5f\r\n",
float_data[0], float_data[1], float_data[2]);
}
if (hal.report & PRINT_PEDO) {
unsigned long timestamp;
get_tick_count(&timestamp);
if (timestamp > hal.next_pedo_ms) {
hal.next_pedo_ms = timestamp + PEDO_READ_MS;
unsigned long step_count, walk_time;
dmp_get_pedometer_step_count(&step_count);
dmp_get_pedometer_walk_time(&walk_time);
MPL_LOGI("Walked %ld steps over %ld milliseconds..\n", step_count,
walk_time);
}
}
/* Whenever the MPL detects a change in motion state, the application can
* be notified. For this example, we use an LED to represent the current
* motion state.
*/
msg = inv_get_message_level_0(INV_MSG_MOTION_EVENT |
INV_MSG_NO_MOTION_EVENT);
if (msg) {
if (msg & INV_MSG_MOTION_EVENT) {
MPL_LOGI("Motion!\n");
} else if (msg & INV_MSG_NO_MOTION_EVENT) {
MPL_LOGI("No motion!\n");
}
}
}
#ifdef COMPASS_ENABLED
void send_status_compass() {
long data[3] = { 0 };
int8_t accuracy = { 0 };
unsigned long timestamp;
inv_get_compass_set(data, &accuracy, (inv_time_t*) &timestamp);
MPL_LOGI("Compass: %7.4f %7.4f %7.4f ",
data[0]/65536.f, data[1]/65536.f, data[2]/65536.f);
MPL_LOGI("Accuracy= %d\r\n", accuracy);
}
#endif
/* Handle sensor on/off combinations. */
static void setup_gyro(void)
{
unsigned char mask = 0, lp_accel_was_on = 0;
if (hal.sensors & ACCEL_ON)
mask |= INV_XYZ_ACCEL;
if (hal.sensors & GYRO_ON) {
mask |= INV_XYZ_GYRO;
lp_accel_was_on |= hal.lp_accel_mode;
}
#ifdef COMPASS_ENABLED
if (hal.sensors & COMPASS_ON) {
mask |= INV_XYZ_COMPASS;
lp_accel_was_on |= hal.lp_accel_mode;
}
#endif
/* If you need a power transition, this function should be called with a
* mask of the sensors still enabled. The driver turns off any sensors
* excluded from this mask.
*/
mpu_set_sensors(mask);
mpu_configure_fifo(mask);
if (lp_accel_was_on) {
unsigned short rate;
hal.lp_accel_mode = 0;
/* Switching out of LP accel, notify MPL of new accel sampling rate. */
mpu_get_sample_rate(&rate);
inv_set_accel_sample_rate(1000000L / rate);
}
}
static void tap_cb(unsigned char direction, unsigned char count)
{
switch (direction) {
case TAP_X_UP:
MPL_LOGI("Tap X+ ");
break;
case TAP_X_DOWN:
MPL_LOGI("Tap X- ");
break;
case TAP_Y_UP:
MPL_LOGI("Tap Y+ ");
break;
case TAP_Y_DOWN:
MPL_LOGI("Tap Y- ");
break;
case TAP_Z_UP:
MPL_LOGI("Tap Z+ ");
break;
case TAP_Z_DOWN:
MPL_LOGI("Tap Z- ");
break;
default:
return;
}
MPL_LOGI("x%d\n", count);
return;
}
static void android_orient_cb(unsigned char orientation)
{
switch (orientation) {
case ANDROID_ORIENT_PORTRAIT:
MPL_LOGI("Portrait\n");
break;
case ANDROID_ORIENT_LANDSCAPE:
MPL_LOGI("Landscape\n");
break;
case ANDROID_ORIENT_REVERSE_PORTRAIT:
MPL_LOGI("Reverse Portrait\n");
break;
case ANDROID_ORIENT_REVERSE_LANDSCAPE:
MPL_LOGI("Reverse Landscape\n");
break;
default:
return;
}
}
static inline void run_self_test(void)
{
int result;
long gyro[3], accel[3];
#if defined (MPU6500) || defined (MPU9250)
result = mpu_run_6500_self_test(gyro, accel, 0);
#elif defined (MPU6050) || defined (MPU9150)
result = mpu_run_self_test(gyro, accel);
#endif
if (result == 0x7) {
MPL_LOGI("Passed!\n");
MPL_LOGI("accel: %7.4f %7.4f %7.4f\n",
accel[0]/65536.f,
accel[1]/65536.f,
accel[2]/65536.f);
MPL_LOGI("gyro: %7.4f %7.4f %7.4f\n",
gyro[0]/65536.f,
gyro[1]/65536.f,
gyro[2]/65536.f);
/* Test passed. We can trust the gyro data here, so now we need to update calibrated data*/
#ifdef USE_CAL_HW_REGISTERS
/*
* This portion of the code uses the HW offset registers that are in the MPUxxxx devices
* instead of pushing the cal data to the MPL software library
*/
unsigned char i = 0;
for(i = 0; i<3; i++) {
gyro[i] = (long)(gyro[i] * 32.8f); //convert to +-1000dps
accel[i] *= 2048.f; //convert to +-16G
accel[i] = accel[i] >> 16;
gyro[i] = (long)(gyro[i] >> 16);
}
mpu_set_gyro_bias_reg(gyro);
#if defined (MPU6500) || defined (MPU9250)
mpu_set_accel_bias_6500_reg(accel);
#elif defined (MPU6050) || defined (MPU9150)
mpu_set_accel_bias_6050_reg(accel);
#endif
#else
/* Push the calibrated data to the MPL library.
*
* MPL expects biases in hardware units << 16, but self test returns
* biases in g's << 16.
*/
unsigned short accel_sens;
float gyro_sens;
mpu_get_accel_sens(&accel_sens);
accel[0] *= accel_sens;
accel[1] *= accel_sens;
accel[2] *= accel_sens;
inv_set_accel_bias(accel, 3);
mpu_get_gyro_sens(&gyro_sens);
gyro[0] = (long) (gyro[0] * gyro_sens);
gyro[1] = (long) (gyro[1] * gyro_sens);
gyro[2] = (long) (gyro[2] * gyro_sens);
inv_set_gyro_bias(gyro, 3);
#endif
}
else {
if (!(result & 0x1))
MPL_LOGE("Gyro failed.\n");
if (!(result & 0x2))
MPL_LOGE("Accel failed.\n");
if (!(result & 0x4))
MPL_LOGE("Compass failed.\n");
}
}
static void handle_input(void)
{
char c;
HAL_UART_Receive(&UartHandle, (uint8_t *)&c, 1, 1000); //USART_ReceiveData(DEBUG_USART);
switch (c) {
/* These commands turn off individual sensors. */
case '8':
hal.sensors ^= ACCEL_ON;
setup_gyro();
if (!(hal.sensors & ACCEL_ON))
inv_accel_was_turned_off();
break;
case '9':
hal.sensors ^= GYRO_ON;
setup_gyro();
if (!(hal.sensors & GYRO_ON))
inv_gyro_was_turned_off();
break;
#ifdef COMPASS_ENABLED
case '0':
hal.sensors ^= COMPASS_ON;
setup_gyro();
if (!(hal.sensors & COMPASS_ON))
inv_compass_was_turned_off();
break;
#endif
/* The commands send individual sensor data or fused data to the PC. */
case 'a':
hal.report ^= PRINT_ACCEL;
break;
case 'g':
hal.report ^= PRINT_GYRO;
break;
#ifdef COMPASS_ENABLED
case 'c':
hal.report ^= PRINT_COMPASS;
break;
#endif
case 'e':
hal.report ^= PRINT_EULER;
break;
case 'r':
hal.report ^= PRINT_ROT_MAT;
break;
case 'q':
hal.report ^= PRINT_QUAT;
break;
case 'h':
hal.report ^= PRINT_HEADING;
break;
case 'i':
hal.report ^= PRINT_LINEAR_ACCEL;
break;
case 'o':
hal.report ^= PRINT_GRAVITY_VECTOR;
break;
#ifdef COMPASS_ENABLED
case 'w':
send_status_compass();
break;
#endif
/* This command prints out the value of each gyro register for debugging.
* If logging is disabled, this function has no effect.
*/
case 'd':
mpu_reg_dump();
break;
/* Test out low-power accel mode. */
case 'p':
if (hal.dmp_on)
/* LP accel is not compatible with the DMP. */
break;
mpu_lp_accel_mode(20);
/* When LP accel mode is enabled, the driver automatically configures
* the hardware for latched interrupts. However, the MCU sometimes
* misses the rising/falling edge, and the hal.new_gyro flag is never
* set. To avoid getting locked in this state, we're overriding the
* driver's configuration and sticking to unlatched interrupt mode.
*
* TODO: The MCU supports level-triggered interrupts.
*/
mpu_set_int_latched(0);
hal.sensors &= ~(GYRO_ON|COMPASS_ON);
hal.sensors |= ACCEL_ON;
hal.lp_accel_mode = 1;
inv_gyro_was_turned_off();
inv_compass_was_turned_off();
break;
/* The hardware self test can be run without any interaction with the
* MPL since it's completely localized in the gyro driver. Logging is
* assumed to be enabled; otherwise, a couple LEDs could probably be used
* here to display the test results.
*/
case 't':
run_self_test();
/* Let MPL know that contiguity was broken. */
inv_accel_was_turned_off();
inv_gyro_was_turned_off();
inv_compass_was_turned_off();
break;
/* Depending on your application, sensor data may be needed at a faster or
* slower rate. These commands can speed up or slow down the rate at which
* the sensor data is pushed to the MPL.
*
* In this example, the compass rate is never changed.
*/
case '1':
if (hal.dmp_on) {
dmp_set_fifo_rate(10);
inv_set_quat_sample_rate(100000L);
} else
mpu_set_sample_rate(10);
inv_set_gyro_sample_rate(100000L);
inv_set_accel_sample_rate(100000L);
break;
case '2':
if (hal.dmp_on) {
dmp_set_fifo_rate(20);
inv_set_quat_sample_rate(50000L);
} else
mpu_set_sample_rate(20);
inv_set_gyro_sample_rate(50000L);
inv_set_accel_sample_rate(50000L);
break;
case '3':
if (hal.dmp_on) {
dmp_set_fifo_rate(40);
inv_set_quat_sample_rate(25000L);
} else
mpu_set_sample_rate(40);
inv_set_gyro_sample_rate(25000L);
inv_set_accel_sample_rate(25000L);
break;
case '4':
if (hal.dmp_on) {
dmp_set_fifo_rate(50);
inv_set_quat_sample_rate(20000L);
} else
mpu_set_sample_rate(50);
inv_set_gyro_sample_rate(20000L);
inv_set_accel_sample_rate(20000L);
break;
case '5':
if (hal.dmp_on) {
dmp_set_fifo_rate(100);
inv_set_quat_sample_rate(10000L);
} else
mpu_set_sample_rate(100);
inv_set_gyro_sample_rate(10000L);
inv_set_accel_sample_rate(10000L);
break;
case ',':
/* Set hardware to interrupt on gesture event only. This feature is
* useful for keeping the MCU asleep until the DMP detects as a tap or
* orientation event.
*/
dmp_set_interrupt_mode(DMP_INT_GESTURE);
break;
case '.':
/* Set hardware to interrupt periodically. */
dmp_set_interrupt_mode(DMP_INT_CONTINUOUS);
break;
case '6':
/* Toggle pedometer display. */
hal.report ^= PRINT_PEDO;
break;
case '7':
/* Reset pedometer. */
dmp_set_pedometer_step_count(0);
dmp_set_pedometer_walk_time(0);
break;
case 'f':
if (hal.lp_accel_mode)
/* LP accel is not compatible with the DMP. */
return;
/* Toggle DMP. */
if (hal.dmp_on) {
unsigned short dmp_rate;
unsigned char mask = 0;
hal.dmp_on = 0;
mpu_set_dmp_state(0);
/* Restore FIFO settings. */
if (hal.sensors & ACCEL_ON)
mask |= INV_XYZ_ACCEL;
if (hal.sensors & GYRO_ON)
mask |= INV_XYZ_GYRO;
if (hal.sensors & COMPASS_ON)
mask |= INV_XYZ_COMPASS;
mpu_configure_fifo(mask);
/* When the DMP is used, the hardware sampling rate is fixed at
* 200Hz, and the DMP is configured to downsample the FIFO output
* using the function dmp_set_fifo_rate. However, when the DMP is
* turned off, the sampling rate remains at 200Hz. This could be
* handled in inv_mpu.c, but it would need to know that
* inv_mpu_dmp_motion_driver.c exists. To avoid this, we'll just
* put the extra logic in the application layer.
*/
dmp_get_fifo_rate(&dmp_rate);
mpu_set_sample_rate(dmp_rate);
inv_quaternion_sensor_was_turned_off();
MPL_LOGI("DMP disabled.\n");
} else {
unsigned short sample_rate;
hal.dmp_on = 1;
/* Preserve current FIFO rate. */
mpu_get_sample_rate(&sample_rate);
dmp_set_fifo_rate(sample_rate);
inv_set_quat_sample_rate(1000000L / sample_rate);
mpu_set_dmp_state(1);
MPL_LOGI("DMP enabled.\n");
}
break;
case 'm':
/* Test the motion interrupt hardware feature. */
#ifndef MPU6050 // not enabled for 6050 product
hal.motion_int_mode = 1;
#endif
break;
case 'v':
/* Toggle LP quaternion.
* The DMP features can be enabled/disabled at runtime. Use this same
* approach for other features.
*/
hal.dmp_features ^= DMP_FEATURE_6X_LP_QUAT;
dmp_enable_feature(hal.dmp_features);
if (!(hal.dmp_features & DMP_FEATURE_6X_LP_QUAT)) {
inv_quaternion_sensor_was_turned_off();
MPL_LOGI("LP quaternion disabled.\n");
} else
MPL_LOGI("LP quaternion enabled.\n");
break;
default:
break;
}
hal.rx.cmd = 0;
}
/* Every time new gyro data is available, this function is called in an
* ISR context. In this example, it sets a flag protecting the FIFO read
* function.
*/
void gyro_data_ready_cb(void)
{
hal.new_gyro = 1;
}
/*******************************************************************************/
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
inv_error_t result;
unsigned char accel_fsr, new_temp = 0;
unsigned short gyro_rate, gyro_fsr;
unsigned long timestamp;
struct int_param_s int_param;
#ifdef COMPASS_ENABLED
unsigned char new_compass = 0;
unsigned short compass_fsr;
#endif
/* 系统时钟初始化成480 MHz */
SystemClock_Config();
/* 默认不配置 MPU若需要更高性能当配置 MPU 后,使用
DMA 时需注意 Cache 与 内存内容一致性的问题,
具体注意事项请参考配套教程的 MPU 配置相关章节 */
// Board_MPU_Config(0, MPU_Normal_WT, 0xD0000000, MPU_32MB);
// Board_MPU_Config(1, MPU_Normal_WT, 0x24000000, MPU_512KB);
SCB_EnableICache(); // 使能指令 Cache
SCB_EnableDCache(); // 使能数据 Cache
/* LED 端口初始化 */
LED_GPIO_Config();
/*初始化USART1*/
DEBUG_USART_Config();
EXTI_MPU_Config();
//初始化 I2C
I2cMaster_Init();
result = mpu_init(&int_param);
if (result) {
MPL_LOGE("Could not initialize gyro.\n");
LED1_ON;
}
else
{
LED2_ON;
}
/* If you're not using an MPU9150 AND you're not using DMP features, this
* function will place all slaves on the primary bus.
* mpu_set_bypass(1);
*/
result = inv_init_mpl();
if (result) {
MPL_LOGE("Could not initialize MPL.\n");
}
/* Compute 6-axis and 9-axis quaternions. */
inv_enable_quaternion();
inv_enable_9x_sensor_fusion();
/* The MPL expects compass data at a constant rate (matching the rate
* passed to inv_set_compass_sample_rate). If this is an issue for your
* application, call this function, and the MPL will depend on the
* timestamps passed to inv_build_compass instead.
*
* inv_9x_fusion_use_timestamps(1);
*/
/* This function has been deprecated.
* inv_enable_no_gyro_fusion();
*/
/* Update gyro biases when not in motion.
* WARNING: These algorithms are mutually exclusive.
*/
inv_enable_fast_nomot();
/* inv_enable_motion_no_motion(); */
/* inv_set_no_motion_time(1000); */
/* Update gyro biases when temperature changes. */
inv_enable_gyro_tc();
/* This algorithm updates the accel biases when in motion. A more accurate
* bias measurement can be made when running the self-test (see case 't' in
* handle_input), but this algorithm can be enabled if the self-test can't
* be executed in your application.
*
* inv_enable_in_use_auto_calibration();
*/
#ifdef COMPASS_ENABLED
/* Compass calibration algorithms. */
inv_enable_vector_compass_cal();
inv_enable_magnetic_disturbance();
#endif
/* If you need to estimate your heading before the compass is calibrated,
* enable this algorithm. It becomes useless after a good figure-eight is
* detected, so we'll just leave it out to save memory.
* inv_enable_heading_from_gyro();
*/
/* Allows use of the MPL APIs in read_from_mpl. */
inv_enable_eMPL_outputs();
result = inv_start_mpl();
if (result == INV_ERROR_NOT_AUTHORIZED) {
while (1) {
MPL_LOGE("Not authorized.\n");
}
}
if (result) {
MPL_LOGE("Could not start the MPL.\n");
}
/* Get/set hardware configuration. Start gyro. */
/* Wake up all sensors. */
#ifdef COMPASS_ENABLED
mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL | INV_XYZ_COMPASS);
#else
mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL);
#endif
/* Push both gyro and accel data into the FIFO. */
mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL);
mpu_set_sample_rate(DEFAULT_MPU_HZ);
#ifdef COMPASS_ENABLED
/* The compass sampling rate can be less than the gyro/accel sampling rate.
* Use this function for proper power management.
*/
mpu_set_compass_sample_rate(1000 / COMPASS_READ_MS);
#endif
/* Read back configuration in case it was set improperly. */
mpu_get_sample_rate(&gyro_rate);
mpu_get_gyro_fsr(&gyro_fsr);
mpu_get_accel_fsr(&accel_fsr);
#ifdef COMPASS_ENABLED
mpu_get_compass_fsr(&compass_fsr);
#endif
/* Sync driver configuration with MPL. */
/* Sample rate expected in microseconds. */
inv_set_gyro_sample_rate(1000000L / gyro_rate);
inv_set_accel_sample_rate(1000000L / gyro_rate);
#ifdef COMPASS_ENABLED
/* The compass rate is independent of the gyro and accel rates. As long as
* inv_set_compass_sample_rate is called with the correct value, the 9-axis
* fusion algorithm's compass correction gain will work properly.
*/
inv_set_compass_sample_rate(COMPASS_READ_MS * 1000L);
#endif
/* Set chip-to-body orientation matrix.
* Set hardware units to dps/g's/degrees scaling factor.
*/
inv_set_gyro_orientation_and_scale(
inv_orientation_matrix_to_scalar(gyro_pdata.orientation),
(long)gyro_fsr<<15);
inv_set_accel_orientation_and_scale(
inv_orientation_matrix_to_scalar(gyro_pdata.orientation),
(long)accel_fsr<<15);
#ifdef COMPASS_ENABLED
inv_set_compass_orientation_and_scale(
inv_orientation_matrix_to_scalar(compass_pdata.orientation),
(long)compass_fsr<<15);
#endif
/* Initialize HAL state variables. */
#ifdef COMPASS_ENABLED
hal.sensors = ACCEL_ON | GYRO_ON | COMPASS_ON;
#else
hal.sensors = ACCEL_ON | GYRO_ON;
#endif
hal.dmp_on = 0;
hal.report = 0;
hal.rx.cmd = 0;
hal.next_pedo_ms = 0;
hal.next_compass_ms = 0;
hal.next_temp_ms = 0;
/* Compass reads are handled by scheduler. */
get_tick_count(&timestamp);
/* To initialize the DMP:
* 1. Call dmp_load_motion_driver_firmware(). This pushes the DMP image in
* inv_mpu_dmp_motion_driver.h into the MPU memory.
* 2. Push the gyro and accel orientation matrix to the DMP.
* 3. Register gesture callbacks. Don't worry, these callbacks won't be
* executed unless the corresponding feature is enabled.
* 4. Call dmp_enable_feature(mask) to enable different features.
* 5. Call dmp_set_fifo_rate(freq) to select a DMP output rate.
* 6. Call any feature-specific control functions.
*
* To enable the DMP, just call mpu_set_dmp_state(1). This function can
* be called repeatedly to enable and disable the DMP at runtime.
*
* The following is a short summary of the features supported in the DMP
* image provided in inv_mpu_dmp_motion_driver.c:
* DMP_FEATURE_LP_QUAT: Generate a gyro-only quaternion on the DMP at
* 200Hz. Integrating the gyro data at higher rates reduces numerical
* errors (compared to integration on the MCU at a lower sampling rate).
* DMP_FEATURE_6X_LP_QUAT: Generate a gyro/accel quaternion on the DMP at
* 200Hz. Cannot be used in combination with DMP_FEATURE_LP_QUAT.
* DMP_FEATURE_TAP: Detect taps along the X, Y, and Z axes.
* DMP_FEATURE_ANDROID_ORIENT: Google's screen rotation algorithm. Triggers
* an event at the four orientations where the screen should rotate.
* DMP_FEATURE_GYRO_CAL: Calibrates the gyro data after eight seconds of
* no motion.
* DMP_FEATURE_SEND_RAW_ACCEL: Add raw accelerometer data to the FIFO.
* DMP_FEATURE_SEND_RAW_GYRO: Add raw gyro data to the FIFO.
* DMP_FEATURE_SEND_CAL_GYRO: Add calibrated gyro data to the FIFO. Cannot
* be used in combination with DMP_FEATURE_SEND_RAW_GYRO.
*/
dmp_load_motion_driver_firmware();
dmp_set_orientation(
inv_orientation_matrix_to_scalar(gyro_pdata.orientation));
dmp_register_tap_cb(tap_cb);
dmp_register_android_orient_cb(android_orient_cb);
/*
* Known Bug -
* DMP when enabled will sample sensor data at 200Hz and output to FIFO at the rate
* specified in the dmp_set_fifo_rate API. The DMP will then sent an interrupt once
* a sample has been put into the FIFO. Therefore if the dmp_set_fifo_rate is at 25Hz
* there will be a 25Hz interrupt from the MPU device.
*
* There is a known issue in which if you do not enable DMP_FEATURE_TAP
* then the interrupts will be at 200Hz even if fifo rate
* is set at a different rate. To avoid this issue include the DMP_FEATURE_TAP
*
* DMP sensor fusion works only with gyro at +-2000dps and accel +-2G
*/
hal.dmp_features = DMP_FEATURE_6X_LP_QUAT | DMP_FEATURE_TAP |
DMP_FEATURE_ANDROID_ORIENT | DMP_FEATURE_SEND_RAW_ACCEL | DMP_FEATURE_SEND_CAL_GYRO |
DMP_FEATURE_GYRO_CAL;
dmp_enable_feature(hal.dmp_features);
dmp_set_fifo_rate(DEFAULT_MPU_HZ);
mpu_set_dmp_state(1);
hal.dmp_on = 1;
while(1){
unsigned long sensor_timestamp;
int new_data = 0;
if (__HAL_UART_GET_FLAG(&UartHandle, UART_FLAG_RXNE)) {
/* A byte has been received via USART. See handle_input for a list of
* valid commands.
*/
//USART_ClearFlag(&UartHandle, USART_FLAG_RXNE);
handle_input();
}
get_tick_count(&timestamp);
#ifdef COMPASS_ENABLED
/* We're not using a data ready interrupt for the compass, so we'll
* make our compass reads timer-based instead.
*/
if ((timestamp > hal.next_compass_ms) && !hal.lp_accel_mode &&
hal.new_gyro && (hal.sensors & COMPASS_ON)) {
hal.next_compass_ms = timestamp + COMPASS_READ_MS;
new_compass = 1;
}
#endif
/* Temperature data doesn't need to be read with every gyro sample.
* Let's make them timer-based like the compass reads.
*/
if (timestamp > hal.next_temp_ms) {
hal.next_temp_ms = timestamp + TEMP_READ_MS;
new_temp = 1;
}
if (hal.motion_int_mode) {
/* Enable motion interrupt. */
mpu_lp_motion_interrupt(500, 1, 5);
/* Notify the MPL that contiguity was broken. */
inv_accel_was_turned_off();
inv_gyro_was_turned_off();
inv_compass_was_turned_off();
inv_quaternion_sensor_was_turned_off();
/* Wait for the MPU interrupt. */
while (!hal.new_gyro) {}
/* Restore the previous sensor configuration. */
mpu_lp_motion_interrupt(0, 0, 0);
hal.motion_int_mode = 0;
}
if (!hal.sensors || !hal.new_gyro) {
continue;
}
if (hal.new_gyro && hal.lp_accel_mode) {
short accel_short[3];
long accel[3];
mpu_get_accel_reg(accel_short, &sensor_timestamp);
accel[0] = (long)accel_short[0];
accel[1] = (long)accel_short[1];
accel[2] = (long)accel_short[2];
inv_build_accel(accel, 0, sensor_timestamp);
new_data = 1;
hal.new_gyro = 0;
} else if (hal.new_gyro && hal.dmp_on) {
short gyro[3], accel_short[3], sensors;
unsigned char more;
long accel[3], quat[4], temperature;
/* This function gets new data from the FIFO when the DMP is in
* use. The FIFO can contain any combination of gyro, accel,
* quaternion, and gesture data. The sensors parameter tells the
* caller which data fields were actually populated with new data.
* For example, if sensors == (INV_XYZ_GYRO | INV_WXYZ_QUAT), then
* the FIFO isn't being filled with accel data.
* The driver parses the gesture data to determine if a gesture
* event has occurred; on an event, the application will be notified
* via a callback (assuming that a callback function was properly
* registered). The more parameter is non-zero if there are
* leftover packets in the FIFO.
*/
dmp_read_fifo(gyro, accel_short, quat, &sensor_timestamp, &sensors, &more);
if (!more)
hal.new_gyro = 0;
if (sensors & INV_XYZ_GYRO) {
/* Push the new data to the MPL. */
inv_build_gyro(gyro, sensor_timestamp);
new_data = 1;
if (new_temp) {
new_temp = 0;
/* Temperature only used for gyro temp comp. */
mpu_get_temperature(&temperature, &sensor_timestamp);
inv_build_temp(temperature, sensor_timestamp);
}
}
if (sensors & INV_XYZ_ACCEL) {
accel[0] = (long)accel_short[0];
accel[1] = (long)accel_short[1];
accel[2] = (long)accel_short[2];
inv_build_accel(accel, 0, sensor_timestamp);
new_data = 1;
}
if (sensors & INV_WXYZ_QUAT) {
inv_build_quat(quat, 0, sensor_timestamp);
new_data = 1;
}
} else if (hal.new_gyro) {
short gyro[3], accel_short[3];
unsigned char sensors, more;
long accel[3], temperature;
/* This function gets new data from the FIFO. The FIFO can contain
* gyro, accel, both, or neither. The sensors parameter tells the
* caller which data fields were actually populated with new data.
* For example, if sensors == INV_XYZ_GYRO, then the FIFO isn't
* being filled with accel data. The more parameter is non-zero if
* there are leftover packets in the FIFO. The HAL can use this
* information to increase the frequency at which this function is
* called.
*/
hal.new_gyro = 0;
mpu_read_fifo(gyro, accel_short, &sensor_timestamp,
&sensors, &more);
if (more)
hal.new_gyro = 1;
if (sensors & INV_XYZ_GYRO) {
/* Push the new data to the MPL. */
inv_build_gyro(gyro, sensor_timestamp);
new_data = 1;
if (new_temp) {
new_temp = 0;
/* Temperature only used for gyro temp comp. */
mpu_get_temperature(&temperature, &sensor_timestamp);
inv_build_temp(temperature, sensor_timestamp);
}
}
if (sensors & INV_XYZ_ACCEL) {
accel[0] = (long)accel_short[0];
accel[1] = (long)accel_short[1];
accel[2] = (long)accel_short[2];
inv_build_accel(accel, 0, sensor_timestamp);
new_data = 1;
}
}
#ifdef COMPASS_ENABLED
if (new_compass) {
short compass_short[3];
long compass[3];
new_compass = 0;
/* For any MPU device with an AKM on the auxiliary I2C bus, the raw
* magnetometer registers are copied to special gyro registers.
*/
if (!mpu_get_compass_reg(compass_short, &sensor_timestamp)) {
compass[0] = (long)compass_short[0];
compass[1] = (long)compass_short[1];
compass[2] = (long)compass_short[2];
/* NOTE: If using a third-party compass calibration library,
* pass in the compass data in uT * 2^16 and set the second
* parameter to INV_CALIBRATED | acc, where acc is the
* accuracy from 0 to 3.
*/
inv_build_compass(compass, 0, sensor_timestamp);
}
new_data = 1;
}
#endif
if (new_data) {
inv_execute_on_data();
/* This function reads bias-compensated sensor data and sensor
* fusion outputs from the MPL. The outputs are formatted as seen
* in eMPL_outputs.c. This function only needs to be called at the
* rate requested by the host.
*/
read_from_mpl();
}
}
}
/**
* @brief System Clock 配置
* system Clock 配置如下:
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 480000000 (CPU Clock)
* HCLK(Hz) = 240000000 (AXI and AHBs Clock)
* AHB Prescaler = 2
* D1 APB3 Prescaler = 2 (APB3 Clock 120MHz)
* D2 APB1 Prescaler = 2 (APB1 Clock 120MHz)
* D2 APB2 Prescaler = 2 (APB2 Clock 120MHz)
* D3 APB4 Prescaler = 2 (APB4 Clock 120MHz)
* HSE Frequency(Hz) = 25000000
* PLL_M = 5
* PLL_N = 192
* PLL_P = 2
* PLL_Q = 2
* PLL_R = 2
* VDD(V) = 3.3
* Flash Latency(WS) = 4
* @param None
* @retval None
*/
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Supply configuration update enable
*/
HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);
/** Configure the main internal regulator output voltage
*/
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);
while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 5;
RCC_OscInitStruct.PLL.PLLN = 192;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
|RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
}
}
/*********************************************END OF FILE**********************/