地磁8面校准完成

This commit is contained in:
lmx
2025-11-20 19:30:34 +08:00
parent 2bfdc81991
commit 9ccf1acda8
17 changed files with 1475 additions and 603 deletions

View File

@ -0,0 +1,133 @@
#include "AK8963.h"
#include "math.h"
#include "os/os_api.h"
#include "../xtell.h"
#include "printf.h"
// 用于存放从Fuse ROM读取的磁力计灵敏度校准值
static float mag_asa_x = 1.0f;
static float mag_asa_y = 1.0f;
static float mag_asa_z = 1.0f;
// 磁力计在16-bit分辨率下的转换因子 (单位: uT/LSB)
#define MAG_RAW_TO_UT_FACTOR (4912.0f / 32760.0f)
/**
* @brief 初始化MPU9250的磁力计AK8963
* @return 0: 成功, 1: MPU9250连接失败, 2: AK8963连接失败
*/
u8 MPU9250_Mag_Init(void) {
u8 temp_data[3];
// --- 检查 MPU9250 连接并复位 ---
_gravity_sensor_get_ndata(MPU9250_ADDR_R, MPU9250_WHO_AM_I, temp_data, 1);
if (temp_data[0] != 0x71 && temp_data[0] != 0x73) {
printf("MPU9250 comm failed, read ID: 0x%X\n", temp_data[0]);
return 1;
}
printf("MPU9250 get id:0x%X\n", temp_data[0]);
gravity_sensor_command(MPU9250_ADDR_W, MPU9250_PWR_MGMT_1, 0x80); // 软复位
os_time_dly(10); // 等待复位完成
gravity_sensor_command(MPU9250_ADDR_W, MPU9250_PWR_MGMT_1, 0x01); // 退出睡眠,选择时钟源
os_time_dly(2);
// --- 强制复位 I2C Master 模块并开启旁路 ---
gravity_sensor_command(MPU9250_ADDR_W, MPU9250_USER_CTRL, 0x20);
os_time_dly(1);
gravity_sensor_command(MPU9250_ADDR_W, MPU9250_USER_CTRL, 0x00);
os_time_dly(1);
gravity_sensor_command(MPU9250_ADDR_W, MPU9250_INT_PIN_CFG, 0x02);
os_time_dly(2);
// --- 再次验证 AK8963 连接 ---
_gravity_sensor_get_ndata(AK8963_ADDR_R, AK8963_WIA, temp_data, 1);
if (temp_data[0] != 0x48) {
printf("AK8963 comm failed after final attempt, read ID: 0x%X\n", temp_data[0]);
return 2;
}
printf("AK8963 get id: 0x%X\n", temp_data[0]);
// ------------------ 配置 AK8963 ------------------
// Power-down模式
gravity_sensor_command(AK8963_ADDR_W, AK8963_CNTL1, 0x00);
os_time_dly(1);
// Fuse ROM access模式
gravity_sensor_command(AK8963_ADDR_W, AK8963_CNTL1, 0x0F);
os_time_dly(1);
_gravity_sensor_get_ndata(AK8963_ADDR_R, AK8963_ASAX, temp_data, 3);
// 计算校准系数
mag_asa_x = (float)(temp_data[0] - 128) / 256.0f + 1.0f;
mag_asa_y = (float)(temp_data[1] - 128) / 256.0f + 1.0f;
mag_asa_z = (float)(temp_data[2] - 128) / 256.0f + 1.0f;
// 再次进入Power-down模式
gravity_sensor_command(AK8963_ADDR_W, AK8963_CNTL1, 0x00);
os_time_dly(1);
// 设置工作模式16-bit分辨率100Hz连续测量模式 (0x16)
gravity_sensor_command(AK8963_ADDR_W, AK8963_CNTL1, 0x16);
os_time_dly(1);
printf("AK8963 configured successfully.\n");
return 0; // 初始化成功
}
/**
* @brief 读取磁力计的三轴原始数据
* @param mx, my, mz - 用于存放X, Y, Z轴数据的指针 (int16_t类型)
* @return 0: 成功, 1: 数据未就绪, 2: 数据溢出
*/
u8 MPU9250_Read_Mag_Raw(int16_t *mx, int16_t *my, int16_t *mz) {
u8 read_buf[7];
// 检查数据是否准备好 (使用8位读地址)
_gravity_sensor_get_ndata(AK8963_ADDR_R, AK8963_ST1, read_buf, 1);
if (!(read_buf[0] & 0x01)) {
return 1; // 数据未就绪
}
// 连续读取7个字节 (使用8位读地址)
_gravity_sensor_get_ndata(AK8963_ADDR_R, AK8963_HXL, read_buf, 7);
// 检查数据是否溢出
if (read_buf[6] & 0x08) {
return 2; // 数据溢出
}
// 组合数据
*mx = (int16_t)((read_buf[1] << 8) | read_buf[0]);
*my = (int16_t)((read_buf[3] << 8) | read_buf[2]);
*mz = (int16_t)((read_buf[5] << 8) | read_buf[4]);
return 0; // 读取成功
}
/**
* @brief 读取磁力计的三轴数据并转换为uT(微特斯拉) (此函数内部逻辑不变)
* @param mx, my, mz - 用于存放X, Y, Z轴数据的指针 (float类型)
* @return 0: 成功, 1: 数据未就绪, 2: 数据溢出
*/
u8 MPU9250_Read_Mag_uT(float *mx, float *my, float *mz) {
int16_t raw_mx, raw_my, raw_mz;
u8 status = MPU9250_Read_Mag_Raw(&raw_mx, &raw_my, &raw_mz);
if (status != 0) {
return status;
}
// 应用灵敏度校准并转换为uT单位
*mx = (float)raw_mx * mag_asa_x * MAG_RAW_TO_UT_FACTOR;
*my = (float)raw_my * mag_asa_y * MAG_RAW_TO_UT_FACTOR;
*mz = (float)raw_mz * mag_asa_z * MAG_RAW_TO_UT_FACTOR;
return 0;
}

View File

@ -0,0 +1,46 @@
// mpu9250_mag.h
#ifndef __MPU9250_MAG_H
#define __MPU9250_MAG_H
#include "stdint.h" // 假设你有标准整数类型u8 对应 uint8_t
#include "gSensor/gSensor_manage.h"
//==================================================================================
// MPU9250 和 AK8963 的 I2C 地址 (已转换为8位格式)
//==================================================================================
// MPU9250的7位地址是 0x68(接地)
#define MPU9250_ADDR_7BIT 0x69
#define MPU9250_ADDR_W (MPU9250_ADDR_7BIT << 1 | 0) // 8位写地址: 0xD0
#define MPU9250_ADDR_R (MPU9250_ADDR_7BIT << 1 | 1) // 8位读地址: 0xD1
// AK8963磁力计的7位地址是 0x0C
#define AK8963_ADDR_7BIT 0x0C
#define AK8963_ADDR_W (AK8963_ADDR_7BIT << 1 | 0) // 8位写地址: 0x18
#define AK8963_ADDR_R (AK8963_ADDR_7BIT << 1 | 1) // 8位读地址: 0x19
//==================================================================================
// MPU9250 相关寄存器 (用于开启旁路模式)
//==================================================================================
#define MPU9250_WHO_AM_I 0x75
#define MPU9250_INT_PIN_CFG 0x37
#define MPU9250_USER_CTRL 0x6A
#define MPU9250_PWR_MGMT_1 0x6B
//==================================================================================
// AK8963 磁力计相关寄存器
//==================================================================================
#define AK8963_WIA 0x00
#define AK8963_ST1 0x02
#define AK8963_HXL 0x03
#define AK8963_ST2 0x09
#define AK8963_CNTL1 0x0A
#define AK8963_ASAX 0x10
u8 MPU9250_Mag_Init(void);
u8 MPU9250_Read_Mag_Raw(int16_t *mx, int16_t *my, int16_t *mz);
u8 MPU9250_Read_Mag_uT(float *mx, float *my, float *mz);
#endif // __MPU9250_MAG_H

View File

@ -17,9 +17,7 @@
#define BMP_IIC_READ_ADDRESS (BMP_IIC_WRITE_ADDRESS | 0x01)
#endif
// BMP280 I2C 地址 (SDO/ADO 引脚接地)
#define BMP280_I2C_ADDR_LOW (0x76*2)
//7位地址:76, 8位地址:EC (接地)
// BMP280 寄存器地址
#define BMP280_REG_CALIB_START 0x88

View File

@ -1,6 +1,3 @@
/*
三轴磁力计 - MMC5603NJ
*/
#include "MMC56.h"
#include "math.h"
@ -9,45 +6,18 @@
#include "gSensor/gSensor_manage.h"
#include "printf.h"
#define ENABLE_XLOG 1
#ifdef xlog
#undef xlog
#endif
#if ENABLE_XLOG
#define xlog(format, ...) printf("[XT:%s] " format, __func__, ##__VA_ARGS__)
#else
#define xlog(format, ...) ((void)0)
#endif
/*==================================================================================*/
/* MMC5603NJ 内部定义 */
/*==================================================================================*/
// 用于跟踪当前是否处于连续测量模式
static u8 g_continuous_mode_enabled = 0;
static uint8_t g_continuous_mode_enabled = 0;
mmc5603nj_cal_data_t cal_data; //校准数据
/*==================================================================================*/
/* 封装的底层I2C读写函数 */
/*==================================================================================*/
/**
* @brief 写入单个字节到MMC5603NJ寄存器
*/
static void mmc5603nj_write_reg(uint8_t reg, uint8_t data) {
gravity_sensor_command(MMC_IIC_WRITE_ADDRESS, reg, data);
}
/**
* @brief 从MMC5603NJ读取多个字节
*/
static uint32_t mmc5603nj_read_regs(uint8_t reg, uint8_t *buf, uint8_t len) {
return _gravity_sensor_get_ndata(MMC_IIC_READ_ADDRESS, reg, buf, len);
}
/*==================================================================================*/
/* 外部接口函数实现 */
/*==================================================================================*/
// 外部接口函数实现
uint8_t mmc5603nj_get_pid(void) {
uint8_t pid = 0;
@ -56,69 +26,145 @@ uint8_t mmc5603nj_get_pid(void) {
}
int mmc5603nj_init(void) {
// 检查产品ID是否正确
if (mmc5603nj_get_pid() != 0x10) {
xlog("init error: check id error %d\n", mmc5603nj_get_pid());
return -1; // 设备ID不匹配
// ID
if (mmc5603nj_get_pid() != 0x80) {
printf("MMC5603NJ init failed: wrong Product ID (read: 0x%X)\n", mmc5603nj_get_pid());
return -1;
}
// 对传感器执行软件复位 (将 INCTRL0 寄存器的 Do_reset 位置1)
mmc5603nj_write_reg(MMC_INCTRL0, 0x08);
// 软件复位
mmc5603nj_write_reg(MMC_INCTRL1, 0x80); // SW_RESET bit
os_time_dly(20); // 等待复位完成
// 设置20位分辨率 (BW[1:0] = 11)
// 同时确保所有轴都使能 (X/Y/Z_inhibit = 0)
mmc5603nj_write_reg(MMC_INCTRL1, 0x03);
os_time_dly(1);
// 设置内部控制寄存器2
// CMM_EN = 1 (使能连续模式功能)
// HPOWER = 1 (高功耗模式,更稳定)
mmc5603nj_write_reg(MMC_INCTRL2, 0x90); // 0b10010000
// 设置自动SET/RESET功能
// AUTO_SR_EN = 1
mmc5603nj_write_reg(MMC_INCTRL0, 0x20); // 0b00100000
g_continuous_mode_enabled = 0;
return 0; // 初始化成功
printf("MMC5603NJ initialized successfully.\n");
mmc5603nj_enable_continuous_mode(0x04);
printf("\n--- Magnetometer Calibration Start ---\n");
printf("Slowly rotate the device in all directions (like drawing a 3D '8')...\n");
printf("Calibration will last for 20 seconds.\n\n");
printf("will start after 5 seconds\n\n");
os_time_dly(500);
// 定义校准时长和采样间隔
const uint32_t calibration_duration_ms = 20000; // 20秒
const uint32_t sample_interval_ms = 100; // 每100ms采样一次
// 初始化最大最小值
// 使用一个临时变量来读取数据避免干扰read函数的正常逻辑
mmc5603nj_mag_data_t temp_mag_data;
// 首次读取以获取初始值
mmc5603nj_read_mag_data(&temp_mag_data); // 首次读取不应用校准
float max_x = temp_mag_data.x;
float min_x = temp_mag_data.x;
float max_y = temp_mag_data.y;
float min_y = temp_mag_data.y;
float max_z = temp_mag_data.z;
float min_z = temp_mag_data.z;
uint32_t start_time = os_time_get(); // 假设os_time_get()返回毫秒级时间戳
int samples = 0;
int over = calibration_duration_ms/sample_interval_ms;
while (samples <= over) {
// 读取原始磁力计数据
mmc5603nj_read_mag_data(&temp_mag_data);
// 更新最大最小值
if (temp_mag_data.x > max_x) max_x = temp_mag_data.x;
if (temp_mag_data.x < min_x) min_x = temp_mag_data.x;
if (temp_mag_data.y > max_y) max_y = temp_mag_data.y;
if (temp_mag_data.y < min_y) min_y = temp_mag_data.y;
if (temp_mag_data.z > max_z) max_z = temp_mag_data.z;
if (temp_mag_data.z < min_z) min_z = temp_mag_data.z;
samples++;
os_time_dly(sample_interval_ms / 10); // os_time_dly的参数通常是ticks (1 tick = 10ms)
}
// 检查数据范围是否合理,防止传感器未动或故障
if ((max_x - min_x < 0.1f) || (max_y - min_y < 0.1f) || (max_z - min_z < 0.1f)) {
printf("\n--- Calibration Failed ---\n");
printf("Device might not have been rotated enough.\n");
printf("X range: %.2f, Y range: %.2f, Z range: %.2f\n", max_x - min_x, max_y - min_y, max_z - min_z);
return -1;
}
// 计算硬磁偏移 (椭球中心)
cal_data.offset_x = (max_x + min_x) / 2.0f;
cal_data.offset_y = (max_y + min_y) / 2.0f;
cal_data.offset_z = (max_z + min_z) / 2.0f;
printf("\n--- Calibration Complete ---\n");
printf("Collected %d samples.\n", samples);
printf("Offsets (Gauss):\n");
printf(" X: %.4f\n", cal_data.offset_x);
printf(" Y: %.4f\n", cal_data.offset_y);
printf(" Z: %.4f\n", cal_data.offset_z);
printf("Please save these values and apply them in your code.\n\n");
return 0;
}
void mmc5603nj_set_data_rate(uint8_t rate) {
mmc5603nj_write_reg(MMC_ODR, rate);
}
void mmc5603nj_enable_continuous_mode(void) {
uint8_t reg_val;
// 启用连续模式需要设置 INCTRL0 和 INCTRL2 寄存器
// 1. 设置 INCTRL0 的 Cmm_en 位 (bit 7)
mmc5603nj_read_regs(MMC_INCTRL0, &reg_val, 1);
reg_val |= 0x80;
mmc5603nj_write_reg(MMC_INCTRL0, reg_val);
// 2. 设置 INCTRL2 的 Cmm_freq_en 位 (bit 4)
mmc5603nj_read_regs(MMC_INCTRL2, &reg_val, 1);
reg_val |= 0x10;
mmc5603nj_write_reg(MMC_INCTRL2, reg_val);
void mmc5603nj_enable_continuous_mode(uint8_t rate) {
// 在连续模式下ODR寄存器必须被设置
mmc5603nj_write_reg(MMC_ODR, rate); //要设置频率
// mmc5603nj_set_data_rate(0x04);
// 启用连续模式 (INCTRL2的CMM_EN位已在init中设置)
// 只需要设置 INCTRL0 的 CMM_FREQ_EN 位
mmc5603nj_write_reg(MMC_INCTRL0, 0xA0); // 0b10100000 (CMM_FREQ_EN=1, AUTO_SR_EN=1)
g_continuous_mode_enabled = 1;
}
void mmc5603nj_disable_continuous_mode(void) {
uint8_t reg_val;
// 禁用连续模式只需要清除 INCTRL2 的 Cmm_freq_en 位
mmc5603nj_read_regs(MMC_INCTRL2, &reg_val, 1);
reg_val &= ~0x10; // 清除 bit 4
mmc5603nj_write_reg(MMC_INCTRL2, reg_val);
// 禁用连续模式
mmc5603nj_write_reg(MMC_INCTRL0, 0x20); // 恢复到仅使能 AUTO_SR_EN 的状态
g_continuous_mode_enabled = 0;
}
float mmc5603nj_get_temperature(void) {
uint8_t status = 0;
uint8_t temp_raw = 0;
uint8_t timeout = 20;
// 1. 触发一次温度测量 (写入 0x02 到 INCTRL0 寄存器)
mmc5603nj_write_reg(MMC_INCTRL0, 0x02);
// 触发一次温度测量
mmc5603nj_write_reg(MMC_INCTRL0, 0x02); // TAKE_MEAS_T
// 2. 等待测量完成 (轮询 STATUS1 寄存器的 Meas_T_done 位)
// 等待测量完成
do {
os_time_dly(10); // 等待一下避免过于频繁的I2C读取
os_time_dly(10);
mmc5603nj_read_regs(MMC_STATUS1, &status, 1);
} while ((status & 0x80) == 0);
timeout--;
} while ((status & 0x80) == 0 && timeout > 0);
if (timeout == 0) {
printf("Error: Temperature measurement timeout!\n");
return -273.15f; // 返回一个绝对零度的错误值
}
// 3. 读取温度原始值
mmc5603nj_read_regs(MMC_TOUT, &temp_raw, 1);
// 4. 根据公式计算实际温度: Temp(°C) = -75 + 0.8 * TOUT
return ((float)temp_raw * 0.8f) - 75.0f;
}
@ -126,32 +172,50 @@ void mmc5603nj_read_mag_data(mmc5603nj_mag_data_t *mag_data) {
uint8_t buffer[9];
if (g_continuous_mode_enabled) {
// 连续模式下,直接读取数据即可
mmc5603nj_read_regs(MMC_XOUT0, buffer, 9);
// 连续模式下,只需检查数据是否就绪
uint8_t status = 0;
mmc5603nj_read_regs(MMC_STATUS1, &status, 1);
if ((status & 0x40) == 0) { // Meas_M_done bit
// 数据未就绪,可以选择返回或等待,这里我们直接返回旧数据
return;
}
} else {
// 单次测量模式
uint8_t status = 0;
// 1. 触发一次磁场测量 (写入 0x01 到 INCTRL0 寄存器)
mmc5603nj_write_reg(MMC_INCTRL0, 0x01);
uint8_t timeout = 20;
// 2. 等待测量完成 (轮询 STATUS1 寄存器的 Meas_M_done 位)
// 触发一次带自动SET/RESET的磁场测量
mmc5603nj_write_reg(MMC_INCTRL0, 0x21); // 0b00100001 (TAKE_MEAS_M=1, AUTO_SR_EN=1)
// 等待测量完成
do {
os_time_dly(10); // 等待一下
os_time_dly(10);
mmc5603nj_read_regs(MMC_STATUS1, &status, 1);
} while ((status & 0x40) == 0);
// 3. 读取9个字节的原始数据
mmc5603nj_read_regs(MMC_XOUT0, buffer, 9);
timeout--;
} while ((status & 0x40) == 0 && timeout > 0);
if (timeout == 0) {
printf("Error: Magnetic measurement timeout!\n");
mag_data->x = mag_data->y = mag_data->z = 0.0f;
return;
}
}
// 读取9个字节的原始数据
mmc5603nj_read_regs(MMC_XOUT0, buffer, 9);
// 解析数据 (20位分辨率)
// 零点偏置: 2^19 = 524288, 灵敏度: 2^14 = 16384 LSB/Gauss
int32_t raw_x = (buffer[0] << 12) | (buffer[1] << 4) | (buffer[6] >> 4);
int32_t raw_y = (buffer[2] << 12) | (buffer[3] << 4) | (buffer[7] >> 4);
int32_t raw_z = (buffer[4] << 12) | (buffer[5] << 4) | (buffer[8] >> 4);
int32_t raw_x = ((uint32_t)buffer[0] << 12) | ((uint32_t)buffer[1] << 4) | ((uint32_t)buffer[6] & 0x0F);
int32_t raw_y = ((uint32_t)buffer[2] << 12) | ((uint32_t)buffer[3] << 4) | ((uint32_t)buffer[6] >> 4);
int32_t raw_z = ((uint32_t)buffer[4] << 12) | ((uint32_t)buffer[5] << 4) | ((uint32_t)buffer[8] & 0x0F);
// 应用偏置和灵敏度进行转换
mag_data->x = ((float)raw_x - 524288.0f) / 16384.0f;
mag_data->y = ((float)raw_y - 524288.0f) / 16384.0f;
mag_data->z = ((float)raw_z - 524288.0f) / 16384.0f;
//减去偏移
mag_data->x -= cal_data.offset_x;
mag_data->y -= cal_data.offset_y;
mag_data->z -= cal_data.offset_z;
}

View File

@ -43,6 +43,12 @@ typedef struct {
float z;
} mmc5603nj_mag_data_t;
// 定义一个结构体来存放磁力计的硬磁偏移校准数据
typedef struct {
float offset_x;
float offset_y;
float offset_z;
} mmc5603nj_cal_data_t;
/**
* @brief 初始化MMC5603NJ传感器
@ -60,7 +66,7 @@ void mmc5603nj_set_data_rate(uint8_t rate);
/**
* @brief 启用连续测量模式
*/
void mmc5603nj_enable_continuous_mode(void);
void mmc5603nj_enable_continuous_mode(uint8_t rate);
/**
* @brief 禁用连续测量模式

View File

@ -1182,15 +1182,31 @@ unsigned char get_calibration_state(void){
// Kp: 比例增益,决定了加速度计数据校正陀螺仪的权重。值越大,对加速度计的响应越快,但对运动加速度更敏感。
// Ki: 积分增益,决定了用于校正陀螺仪静态漂移的权重。
// Q_dt: 采样时间间隔单位这里是10ms (0.01s)对应100Hz的采样率。
#define HAVE_MAG 1
#if HAVE_MAG == 0
// -- 无地磁 --
const float Kp = 2.0f;
const float Ki = 0.005f;
const float Q_dt = 0.01f;
#else
// -- 有地磁 --
const float Kp = 0.3f;
const float Ki = 0.001f;
const float Q_dt = 0.01f;
#endif
// --- 状态变量 ---
// 四元数 (Quaternion),表示当前的姿态。初始化为 (1, 0, 0, 0),代表初始姿态为水平。
static float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f;
// 陀螺仪积分误差项,用于补偿静态漂移
static float exInt = 0.0f, eyInt = 0.0f, ezInt = 0.0f;
// 磁力计校准相关的变量
float Error_Mag_f[3] = {0.0f, 0.0f, 0.0f};
double Sum_Avg_Mag_f[3] = {0.0, 0.0, 0.0}; // 使用double避免累加过程中的精度损失
// 临时存储校准后数据的数组
signed short Temp_AccGyro[6] = {0};
float Temp_Mag[3] = {0.0f, 0.0f, 0.0f};
// =================================================================================================
@ -1207,15 +1223,237 @@ static float exInt = 0.0f, eyInt = 0.0f, ezInt = 0.0f;
* @param calibration_en 传入外部校准使能标志。如果为0则强制认为已经校准完成。
* @param acc_gyro_input 传入和传出包含6轴原始数据的数组指针顺序为 [ACC_X, ACC_Y, ACC_Z, GYR_X, GYR_Y, GYR_Z]。该函数会对其进行原地修改,填充为校准后的数据。
* @param Angle_output 传出:滤波后的结果,顺序为 [Pitch, Roll, Yaw],单位为度。
* @param mag_data_input 传入:指向包含三轴磁力计数据的结构体指针。数据单位应为高斯(Gauss)。已经8面校准过的数据
* @param yaw_rst 传入Yaw轴重置标志。如果为1则将整个姿态滤波器状态重置。
*
* @param quaternion_output 传出, 四元数,用于后续重力分量的去除计算
* @return
* - 0: 正在进行静态校准。
* - 1: 姿态角计算成功。
* - 2: 校准未完成,无法进行计算。
*/
unsigned char Q_SL_SC7U22_Angle_Output(unsigned char calibration_en, signed short *acc_gyro_input, float *Angle_output, unsigned char yaw_rst, float *quaternion_output)
unsigned char Q_SL_SC7U22_Angle_Output(unsigned char calibration_en, signed short *acc_gyro_input, float *Angle_output, const mmc5603nj_mag_data_t* _mag_data_input, unsigned char yaw_rst, float *quaternion_output)
{
#if 1 //有地磁置1
unsigned char sl_i = 0;
// 如果外部强制禁用校准则将标志位置1
if (calibration_en == 0) {
SL_SC7U22_Error_Flag = 1;
}
// ====================== 坐标对齐 ======================
mmc5603nj_mag_data_t mag_data_input;
mag_data_input.x = - _mag_data_input->x;
mag_data_input.y = - _mag_data_input->y;
mag_data_input.z = _mag_data_input->z;
// ================================================================
// =================================================================================
// 静态校准
// ---------------------------------------------------------------------------------
if (SL_SC7U22_Error_Flag == 0) {
unsigned short acc_gyro_delta[2];
acc_gyro_delta[0] = 0;
acc_gyro_delta[1] = 0;
for (sl_i = 0; sl_i < 3; sl_i++) {
acc_gyro_delta[0] += SL_GetAbsShort(acc_gyro_input[sl_i] - Temp_Accgyro[sl_i]);
acc_gyro_delta[1] += SL_GetAbsShort(acc_gyro_input[3 + sl_i] - Temp_Accgyro[3 + sl_i]);
}
for (sl_i = 0; sl_i < 6; sl_i++) {
Temp_Accgyro[sl_i] = acc_gyro_input[sl_i];
}
#if (ACC_RANGE == 2)
if ((acc_gyro_delta[0] / 8 < 160) && (acc_gyro_delta[1] < 40) && (SL_GetAbsShort(acc_gyro_input[0]) < 3000) && (SL_GetAbsShort(acc_gyro_input[1]) < 3000) && (SL_GetAbsShort(acc_gyro_input[2] - 16384) < 3000)) {
#elif (ACC_RANGE == 4)
if ((acc_gyro_delta[0] / 8 < 160) && (acc_gyro_delta[1] < 40) && (SL_GetAbsShort(acc_gyro_input[0]) < 3000) && (SL_GetAbsShort(acc_gyro_input[1]) < 3000) && (SL_GetAbsShort(acc_gyro_input[2] - 8192) < 3000)) {
#elif (ACC_RANGE == 8)
if ((acc_gyro_delta[0] / 8 < 160) && (acc_gyro_delta[1] < 40) && (SL_GetAbsShort(acc_gyro_input[0]) < 3000) && (SL_GetAbsShort(acc_gyro_input[1]) < 3000) && (SL_GetAbsShort(acc_gyro_input[2] - 4096) < 3000)) {
#elif (ACC_RANGE == 16)
if ((acc_gyro_delta[0] / 8 < 160) && (acc_gyro_delta[1] < 40) && (SL_GetAbsShort(acc_gyro_input[0]) < 3000) && (SL_GetAbsShort(acc_gyro_input[1]) < 3000) && (SL_GetAbsShort(acc_gyro_input[2] - 2048) < 3000)) {
#endif
if (SL_SC7U22_Error_cnt < 200) SL_SC7U22_Error_cnt++;
} else {
SL_SC7U22_Error_cnt = 0;
}
if (SL_SC7U22_Error_cnt > 190) {
//累加6轴数据
for (sl_i = 0; sl_i < 6; sl_i++) Sum_Avg_Accgyro[sl_i] += acc_gyro_input[sl_i];
Sum_Avg_Mag_f[0] += mag_data_input.x;
Sum_Avg_Mag_f[1] += mag_data_input.y;
Sum_Avg_Mag_f[2] += mag_data_input.z;
SL_SC7U22_Error_cnt2++;
if (SL_SC7U22_Error_cnt2 > 49) {
SL_SC7U22_Error_Flag = 1;
SL_SC7U22_Error_cnt2 = 0;
SL_SC7U22_Error_cnt = 0;
//6轴偏置
for (sl_i = 0; sl_i < 6; sl_i++) Sum_Avg_Accgyro[sl_i] = Sum_Avg_Accgyro[sl_i] / 50;
Error_Accgyro[0] = 0 - Sum_Avg_Accgyro[0];
Error_Accgyro[1] = 0 - Sum_Avg_Accgyro[1];
#if ACC_RANGE==2
Error_Accgyro[2] = 16384 - Sum_Avg_Accgyro[2];
#elif ACC_RANGE==4
Error_Accgyro[2] = 8192 - Sum_Avg_Accgyro[2];
#elif ACC_RANGE==8
Error_Accgyro[2] = 4096 - Sum_Avg_Accgyro[2];
#elif ACC_RANGE==16
Error_Accgyro[2] = 2048 - Sum_Avg_Accgyro[2];
#endif
Error_Accgyro[3] = 0 - Sum_Avg_Accgyro[3];
Error_Accgyro[4] = 0 - Sum_Avg_Accgyro[4];
Error_Accgyro[5] = 0 - Sum_Avg_Accgyro[5];
// //磁力计偏置 -- 不在这弄,在磁力计初始化的时候开始
// Sum_Avg_Mag_f[0] /= 50.0;
// Sum_Avg_Mag_f[1] /= 50.0;
// Sum_Avg_Mag_f[2] /= 50.0;
// Error_Mag_f[0] = 0.0f - (float)Sum_Avg_Mag_f[0];
// Error_Mag_f[1] = 0.0f - (float)Sum_Avg_Mag_f[1];
// Error_Mag_f[2] = 0.0f - (float)Sum_Avg_Mag_f[2];
// xlog("AVG_Recode AX:%d,AY:%d,AZ:%d,GX:%d,GY:%d,GZ:%d\r\n", Sum_Avg_Accgyro[0], Sum_Avg_Accgyro[1], Sum_Avg_Accgyro[2], Sum_Avg_Accgyro[3], Sum_Avg_Accgyro[4], Sum_Avg_Accgyro[5]);
// xlog("Error_Recode AX:%d,AY:%d,AZ:%d,GX:%d,GY:%d,GZ:%d\r\n", Error_Accgyro[0], Error_Accgyro[1], Error_Accgyro[2], Error_Accgyro[3], Error_Accgyro[4], Error_Accgyro[5]);
}
} else {
SL_SC7U22_Error_cnt2 = 0;
for (sl_i = 0; sl_i < 6; sl_i++) Sum_Avg_Accgyro[sl_i] = 0;
// Sum_Avg_Mag_f[0] = 0.0;
// Sum_Avg_Mag_f[1] = 0.0;
// Sum_Avg_Mag_f[2] = 0.0;
}
return 0; // 返回0表示正在校准
}
// =================================================================================
// 姿态解算 (Mahony AHRS)
// ---------------------------------------------------------------------------------
if (SL_SC7U22_Error_Flag == 1) { // 确认已经校准完成
// --- Yaw轴/姿态重置 ---
// 注意重置yaw会重置整个姿态滤波器使设备回到初始水平姿态
if (yaw_rst == 1) {
q0 = 1.0f; q1 = 0.0f; q2 = 0.0f; q3 = 0.0f;
exInt = 0.0f; eyInt = 0.0f; ezInt = 0.0f;
}
// --- 数据预处理 ---
// 应用零点偏移补偿
for (sl_i = 0; sl_i < 6; sl_i++) {
Temp_Accgyro[sl_i] = acc_gyro_input[sl_i] + Error_Accgyro[sl_i];
}
// Temp_Mag[0] = mag_data_input.x + Error_Mag_f[0];
// Temp_Mag[1] = mag_data_input.y + Error_Mag_f[1];
// Temp_Mag[2] = mag_data_input.z + Error_Mag_f[2];
Temp_Mag[0] = mag_data_input.x;
Temp_Mag[1] = mag_data_input.y;
Temp_Mag[2] = mag_data_input.z;
// 将校准后的数据写回输入数组
#if 1
for (sl_i = 0; sl_i < 6; sl_i++) {
acc_gyro_input[sl_i] = Temp_Accgyro[sl_i];
}
#endif
// 获取校准后的数据
float ax = (float)Temp_Accgyro[0];
float ay = (float)Temp_Accgyro[1];
float az = (float)Temp_Accgyro[2];
// 将陀螺仪数据从 LSB 转换为弧度/秒 (rad/s)
// 转换系数 0.061 ≈ 2000dps / 32768 LSB; PI/180 ≈ 0.01745
float gx = (float)Temp_Accgyro[3] * 0.061f * 0.0174533f; // Roll rate
float gy = (float)Temp_Accgyro[4] * 0.061f * 0.0174533f; // Pitch rate
float gz = (float)Temp_Accgyro[5] * 0.061f * 0.0174533f; // Yaw rate
float mx = Temp_Mag[0];
float my = Temp_Mag[1];
float mz = Temp_Mag[2];
// --- Mahony算法核心 ---
float norm;
float q0q0 = q0 * q0;
float q0q1 = q0 * q1;
float q0q2 = q0 * q2;
float q0q3 = q0 * q3;
float q1q1 = q1 * q1;
float q1q2 = q1 * q2;
float q1q3 = q1 * q3;
float q2q2 = q2 * q2;
float q2q3 = q2 * q3;
float q3q3 = q3 * q3;
float hx, hy, bx, bz;
float vx, vy, vz, wx, wy, wz;
float ex, ey, ez;
// 归一化加速度计测量值,得到单位重力向量
norm = sqrtf(ax * ax + ay * ay + az * az);
if (norm > 0.0f) { ax /= norm; ay /= norm; az /= norm; }
else { return 1; }
norm = sqrtf(mx * mx + my * my + mz * mz);
if (norm > 0.0f) { mx /= norm; my /= norm; mz /= norm; }
// 根据当前姿态(四元数)估计重力向量的方向
vx = 2.0f * (q1q3 - q0q2);
vy = 2.0f * (q0q1 + q2q3);
vz = q0q0 - q1q1 - q2q2 + q3q3;
// 计算磁场误差 (倾斜补偿)
hx = 2.0f * mx * (0.5f - q2q2 - q3q3) + 2.0f * my * (q1q2 - q0q3) + 2.0f * mz * (q1q3 + q0q2);
hy = 2.0f * mx * (q1q2 + q0q3) + 2.0f * my * (0.5f - q1q1 - q3q3) + 2.0f * mz * (q2q3 - q0q1);
bx = sqrtf(hx * hx + hy * hy);
bz = 2.0f * mx * (q1q3 - q0q2) + 2.0f * my * (q2q3 + q0q1) + 2.0f * mz * (0.5f - q1q1 - q2q2);
wx = 2.0f * bx * (0.5f - q2q2 - q3q3) + 2.0f * bz * (q1q3 - q0q2);
wy = 2.0f * bx * (q1q2 - q0q3) + 2.0f * bz * (q0q1 + q2q3);
wz = 2.0f * bx * (q1q3 + q0q2) + 2.0f * bz * (0.5f - q1q1 - q2q2);
// 合并重力和磁场误差
ex = (ay * vz - az * vy) + (my * wz - mz * wy);
ey = (az * vx - ax * vz) + (mz * wx - mx * wz);
ez = (ax * vy - ay * vx) + (mx * wy - my * wx);
// PI控制器
if (Ki > 0.0f) {
exInt += ex * Ki * Q_dt;
eyInt += ey * Ki * Q_dt;
ezInt += ez * Ki * Q_dt;
}
gx += Kp * ex + exInt;
gy += Kp * ey + eyInt;
gz += Kp * ez + ezInt;
// 使用校正后的角速度更新四元数 (一阶毕卡法)
q0 += (-q1 * gx - q2 * gy - q3 * gz) * 0.5f * Q_dt;
q1 += ( q0 * gx + q2 * gz - q3 * gy) * 0.5f * Q_dt;
q2 += ( q0 * gy - q1 * gz + q3 * gx) * 0.5f * Q_dt;
q3 += ( q0 * gz + q1 * gy - q2 * gx) * 0.5f * Q_dt;
// 归一化四元数,保持其单位长度
norm = sqrtf(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
q0 /= norm; q1 /= norm; q2 /= norm; q3 /= norm;
// --- 将四元数转换为欧拉角 (Pitch, Roll, Yaw) ---
// Pitch (绕Y轴旋转)
Angle_output[0] = asinf(-2.0f * (q1 * q3 - q0 * q2)) * 57.29578f;
// Roll (绕X轴旋转)
Angle_output[1] = atan2f(2.0f * (q0 * q1 + q2 * q3), q0q0 - q1q1 - q2q2 + q3q3) * 57.29578f;
// Yaw (绕Z轴旋转)
Angle_output[2] = atan2f(2.0f * (q1 * q2 + q0 * q3), q0q0 + q1q1 - q2q2 - q3q3) * 57.29578f;
//将四元数传出去
if (quaternion_output != NULL) {
quaternion_output[0] = q0; // w
quaternion_output[1] = q1; // x
quaternion_output[2] = q2; // y
quaternion_output[3] = q3; // z
}
return 1; // 返回1表示计算成功
}
return 2; // 校准未完成,返回错误状态
#else
unsigned char sl_i = 0;
// 如果外部强制禁用校准则将标志位置1
@ -1248,6 +1486,7 @@ unsigned char Q_SL_SC7U22_Angle_Output(unsigned char calibration_en, signed shor
#endif
if (SL_SC7U22_Error_cnt < 200) SL_SC7U22_Error_cnt++;
} else {
// printf("error: The calibration process has undergone a shift.\n");
SL_SC7U22_Error_cnt = 0;
}
if (SL_SC7U22_Error_cnt > 190) {
@ -1388,6 +1627,7 @@ unsigned char Q_SL_SC7U22_Angle_Output(unsigned char calibration_en, signed shor
}
return 2; // 校准未完成,返回错误状态
#endif
}
#endif

View File

@ -9,6 +9,7 @@ Copyright (c) 2022 Silan MEMS. All Rights Reserved.
#include "gSensor/gSensor_manage.h"
#include "printf.h"
#include "MMC56.h"
//是否使能串口打印调试
#define SL_Sensor_Algo_Release_Enable 0x00
@ -132,7 +133,7 @@ unsigned char SL_SC7U22_Angle_Output(unsigned char calibration_en,signed short *
unsigned char Original_SL_SC7U22_Angle_Output(unsigned char calibration_en, signed short *acc_gyro_input, float *Angle_output, unsigned char yaw_rst);
unsigned char SIX_SL_SC7U22_Angle_Output(unsigned char auto_calib_start, signed short *acc_gyro_input, float *Angle_output, unsigned char yaw_rst);
unsigned char Q_SL_SC7U22_Angle_Output(unsigned char calibration_en, signed short *acc_gyro_input, float *Angle_output, unsigned char yaw_rst, float *quaternion_output);
unsigned char Q_SL_SC7U22_Angle_Output(unsigned char calibration_en, signed short *acc_gyro_input, float *Angle_output, const mmc5603nj_mag_data_t *mag_data_input, unsigned char yaw_rst, float *quaternion_output);
unsigned char get_calibration_state(void);
/**寄存器宏定义*******************************/
#define SC7U22_WHO_AM_I 0x01