4、传感器驱动开发(上):MPU6050/ICM20602六轴IMU驱动、I2C/SPI总线通信、原始数据读取与校验

各位同学,欢迎来到第四讲。

从这一章开始,我们正式进入「硬核」环节。飞控系统里,传感器就是眼睛和耳朵。没有它们,飞控就是瞎子。今天我们要聊的,是植保无人机最核心的传感器之一——六轴IMU。

我选了两颗最常用的芯片:MPU6050和ICM20602。前者是经典款,资料多、便宜;后者是升级款,性能更好、噪声更低。我个人习惯是,做原型验证用MPU6050,量产或高性能机型用ICM20602。你想想看,成本差不了几块钱,但数据质量差不少。

4.1 六轴IMU到底在测什么?

六轴,说白了就是三个加速度计 + 三个陀螺仪。

  • 加速度计:测量物体在X、Y、Z轴上的加速度(包括重力)。
  • 陀螺仪:测量物体绕X、Y、Z轴的角速度。

嗯,这里要注意:加速度计测的是「比力」,不是单纯的运动加速度。你静止放在桌上,它读出来的是1g(重力加速度)。陀螺仪测的是角速度,单位是°/s。积分之后才能得到角度。

我在项目中遇到过一个问题:有同学直接把加速度计数据当角度用,结果飞控一倾斜就炸机。为什么?因为加速度计对震动敏感,动态响应差。陀螺仪积分又会有漂移。所以实际飞控里,两者要融合——这就是后面要讲的姿态解算。

4.2 I2C vs SPI:选哪个?

MPU6050和ICM20602都支持I2C和SPI。我直接给结论:

总线 速度 引脚数 抗干扰 我的建议
I2C 400kHz(标准) 2根(SDA+SCL) 一般 调试用、引脚紧张时用
SPI 10MHz+ 4根(CS+SCLK+MOSI+MISO) 量产、高性能场景首选

我个人习惯:只要MCU引脚够,一律用SPI。为什么?速度快、数据更新率高、抗干扰强。植保无人机在田间地头飞,电磁环境复杂,SPI比I2C稳定得多。我曾经在测试中发现,I2C总线在电机大电流启动时偶尔会丢数据,换成SPI就再没出现过。

4.3 原始数据读取流程

不管用哪颗芯片,流程都差不多。我把它拆成四步:

  1. 初始化:配置电源、复位、设置量程和采样率。
  2. 校验:读取WHO_AM_I寄存器,确认芯片活着。
  3. 读取:从数据寄存器连续读出6轴原始值。
  4. 转换:把原始值(int16)换算成物理量(g或°/s)。

嗯,这里有个坑:很多同学直接读寄存器,不校验WHO_AM_I。结果I2C地址配错了,读了一堆0还以为是正常数据。我曾经在产线上遇到过一批芯片焊接不良,WHO_AM_I读出来是0xFF,直接报错,避免了飞控带病上天。

4.4 代码实战:MPU6050 SPI驱动(核心片段)

下面是我写的一个精简版驱动。注意,我只贴核心部分,完整代码在课程资料里。

// MPU6050 SPI 初始化
void mpu6050_spi_init(void) {
    // 配置SPI引脚、速率、模式(模式0或3)
    spi_config(SPI_MODE_0, SPI_SPEED_1MHZ);
    // 复位MPU6050
    mpu6050_write_reg(PWR_MGMT_1, 0x80);
    delay_ms(100);
    // 唤醒并选择时钟源
    mpu6050_write_reg(PWR_MGMT_1, 0x01);
    // 设置加速度计量程为±2g
    mpu6050_write_reg(ACCEL_CONFIG, 0x00);
    // 设置陀螺仪量程为±250°/s
    mpu6050_write_reg(GYRO_CONFIG, 0x00);
    // 配置数字低通滤波器
    mpu6050_write_reg(CONFIG, 0x03);
}

// 读取原始数据
void mpu6050_read_raw(int16_t *accel, int16_t *gyro) {
    uint8_t buf[14];
    // 连续读取14个字节(从ACCEL_XOUT_H开始)
    mpu6050_read_regs(ACCEL_XOUT_H, buf, 14);
    // 组合成有符号16位整数
    accel[0] = (buf[0] << 8) | buf[1];  // X轴加速度
    accel[1] = (buf[2] << 8) | buf[3];  // Y轴加速度
    accel[2] = (buf[4] << 8) | buf[5];  // Z轴加速度
    gyro[0]  = (buf[8] << 8) | buf[9];  // X轴角速度
    gyro[1]  = (buf[10] << 8) | buf[11]; // Y轴角速度
    gyro[2]  = (buf[12] << 8) | buf[13]; // Z轴角速度
}

关键点

  • SPI读写时,CS片选要拉低再拉高,不能漏。
  • 读取数据时,一定要连续读(burst read),不要一个寄存器一个寄存器读。否则数据可能不是同一时刻的。
  • 原始数据是int16,最高位是符号位。直接组合就行,不用额外处理。

4.5 数据校验:别信传感器,信你自己

数据读回来了,怎么知道对不对?我一般做三层校验:

  • 第一层:WHO_AM_I。MPU6050固定返回0x68,ICM20602返回0x12(或0xAC,看版本)。不对就报错。
  • 第二层:静止检测。把IMU平放,加速度计Z轴应该接近16384(±2g量程下),X和Y接近0。陀螺仪三个轴都应该接近0。如果偏差太大,说明芯片坏了或者焊接有问题。
  • 第三层:数据连续性。连续读100次,看有没有跳变。正常数据应该是平滑变化的。如果出现单个点突然跳变,可能是SPI干扰,需要加滤波。

我的小技巧:在飞控上电自检时,我会让电机不转,采集200组静止数据,计算均值和方差。如果方差超过阈值,就提示「传感器噪声过大,建议校准」。这个功能救过我好几次,有一次是IMU安装螺丝松动导致数据异常。

4.6 避坑指南:我曾经踩过的三个坑

  • 坑一:I2C地址搞错。MPU6050的AD0引脚接地时地址是0x68,接VCC时是0x69。我见过有人画板子把AD0悬空了,结果地址飘忽不定。解决办法:原理图上明确标注,软件里做地址扫描。
  • 坑二:SPI模式不匹配。MPU6050支持SPI模式0和模式3,但有些MCU默认是模式0。我曾经在STM32上忘了改CPOL和CPHA,读出来的数据全是0xFF。排查了一下午才发现。
  • 坑三:电源纹波影响。IMU对电源质量很敏感。如果供电纹波太大,加速度计数据会抖动。我建议在IMU的VCC引脚旁边加一个10μF电解电容+0.1μF陶瓷电容,离芯片越近越好。

警告:千万不要在飞控运行时热插拔IMU模块!我亲眼见过有人调试时手贱拔了一下,结果SPI接口烧了,MCU和IMU一起报废。断电操作,这是底线。

4.7 小结

这一章我们讲了六轴IMU的基本原理、I2C和SPI的选择、原始数据读取流程,以及校验方法。说白了,传感器驱动就是「配置-读取-校验」三个步骤,但每一步都有细节。

下一章,我们会继续深入,讲如何把原始数据转换成有意义的姿态角,以及如何做传感器校准。嗯,到时候你会看到,原始数据只是开始,真正的挑战在后面。

记住:飞控的可靠性,从传感器驱动开始。别偷懒,每一行代码都值得认真对待。