ESP-Drone 传感器驱动全解析:MPU6050 / VL53LXX / PMW3901 从寄存器配置到飞控应用

原创2026-09-17 16:19:431,855 阅读
文章标签:无人机嵌入式物联网智能硬件

ESP-Drone 传感器驱动全解析:MPU6050 / VL53LXX / PMW3901 从寄存器配置到飞控应用

本指南基于 ESP-Drone(ESP32/ESP32-S 系列 SoC 的迷你四轴飞控固件)的官方中文文档 drivers.rst,系统讲解飞控固件中三大传感器驱动的完整技术细节:I2C 总线上的 MPU6050 六轴惯性测量单元(IMU)与 VL53LXX 激光测距(ToF)传感器,以及 SPI 总线上的 PMW3901 光流传感器。读完本文,你将掌握这三类传感器的测量原理、关键寄存器配置、初始化与测距/测速流程,并能对照仓库内 mpu6050.c、vl53l1x.c、pmw3901.c 等源码,理解它们在姿态解算、定高与水平定点控制中的实际作用。

I2C 设备驱动总览

ESP-Drone 的 I2C 设备驱动(i2c_devices)主要涵盖两类传感器驱动:

  • MPU6050 传感器驱动:提供三轴陀螺仪与三轴加速度计的原始测量,是飞控姿态估计的核心输入;
  • VL53LXX 传感器驱动(VL53L0X / VL53L1X):提供 ToF 激光测距,用于定高控制。

在仓库中,I2C 设备驱动位于 components/drivers/i2c_devices 目录,每个设备一个子目录,内部按 src 与 include 组织源码与头文件,并通过各自的 CMakeLists.txt 声明编译单元;I2C 底层通信则复用 i2cdev_esp32.c 提供的 i2cdevRead* / i2cdevWrite* 系列封装。下文将从传感器主要特性、关键寄存器和编程注意事项三个层面展开。

MPU6050 传感器

概述与工作原理

MPU6050 是一款整合型 6 轴运动处理组件,内部集成 3 轴陀螺仪、3 轴加速度计和数字运动处理器(DMP,Digital Motion Processor)。

  • 陀螺仪:当陀螺仪围绕任何感应轴旋转时,由于科里奥利效应会产生振动,这种振动可被电容式传感器检测到;传感器所得信号经过放大、解调和滤波后,产生与角速度成比例的电压。
  • 电子加速度计:加速度沿特定轴使相应的检测质量发生位移,电容式传感器即可检测到电容的变化。

测量范围

MPU6050 的测量范围可通过寄存器灵活配置:

  • 可配置陀螺仪测量范围:±250 °/sec、±500 °/sec、±1000 °/sec、±2000 °/sec;
  • 可配置加速度计测量范围:±2 g、±4 g、±8 g、±16 g。

AUX I2C 接口

MPU6050 具有一个辅助 I2C 总线,用于与片外 3 轴数字磁力计或其它传感器通信。AUX I2C 接口支持两种工作模式:

  • I2C Master 模式:由 MPU6050 作为总线主机直接管理外部从机;
  • Pass-Through 模式:将主 I2C 总线直通到外部传感器,由外部主机直接访问。

在 ESP-Drone 的传感器组合中,MPU6050 常与 HMC5883L 磁力计配合使用(参见 sensors_mpu6050_hm5883L_ms5611.h),AUX I2C 通道正是为此类扩展设计的。

MPU6050 FIFO

MPU6050 包含一个可通过串行接口访问的 1024 字节 FIFO 寄存器。FIFO 配置寄存器(FIFO_EN,地址 0x23)决定哪些数据写入 FIFO,包括:陀螺仪数据、加速度计数据、温度读数、辅助传感器读数和 FSYNC 输入。在 mpu6050.h 中可以看到每个数据源的使能位定义,例如 MPU6050_XG_FIFO_EN_BIT(bit6)、MPU6050_ACCEL_FIFO_EN_BIT(bit3)等。

数字低通滤波器(DLPF)

MPU6050 自带数字低通滤波器,可通过寄存器 26(CONFIG) 的 DLPF_CFG 字段控制滤波频段,以降低高频干扰,但代价是降低传感器输入速率:开启 DLPF 时加速度计输出 1 kHz,关闭 DLPF 时可以输出 8 kHz。

mpu6050.c 的注释中给出了完整的 DLPF 配置表:DLPF_CFG = 0 时加速度计带宽 260 Hz、陀螺仪带宽 256 Hz、采样率 8 kHz;DLPF_CFG = 1~6 时带宽依次降低(184/94/44/21/10/5 Hz),采样率降为 1 kHz;DLPF_CFG = 7 为保留值。对应头文件中的常量如 MPU6050_DLPF_BW_256(0x00)到 MPU6050_DLPF_BW_5(0x06),可通过 mpu6050SetDLPFMode() 写入。

FSYNC 帧同步采样管脚

寄存器 26 中的 EXT_SYNC_SET 字段用于配置外部帧同步(FSYNC)管脚的采样方式。FSYNC 外部信号可以被锁存并在采样率下被采样,采样结果会替代指定传感器数据寄存器的 LSB。EXT_SYNC_SET 取值为 0 时输入禁用,取值为 1~7 时分别将 FSYNC 状态嵌入温度、陀螺仪或加速度计输出寄存器的 bit0(见 mpu6050.h)。

数字运动处理器(DMP)

  • MPU6050 内部存在一个数字运动处理单元(DMP),可以计算四元数等姿态数据,减轻主 CPU 的运算压力;
  • DMP 可通过管脚触发中断通知主机读取结果。

MPU6050 DMP

在 mpu6050.h 中,DMP 的固件代码(约 1929 字节的 dmpMemory 数组)以 MPU6050_INCLUDE_DMP_MOTIONAPPS20 宏保护,按需编译以节省约 2 KB 程序存储空间。

MPU6050 方向定义

传感器定义了固定的 X、Y、Z 方向,飞控代码在进行姿态解算时必须与机身坐标系对齐,否则会导致姿态输出错乱:

MPU6050 X、Y、Z 方向

MPU6050 初始化步骤

官方推荐的初始化流程如下:

  1. 恢复寄存器默认值:设置 PWR_MGMT_1 bit7(DEVICE_RESET)为 1,恢复后该位自动回到 0,bit6(SLEEP)自动置 1,传感器进入 sleep 模式;
  2. 设置 PWR_MGMT_1 bit6 为 0,唤醒传感器;
  3. 设置时钟源(CLKSEL 字段,见 mpu6050.h 中的 MPU6050_CLOCK_INTERNAL、MPU6050_CLOCK_PLL_XGYRO 等选项);
  4. 设置量程:分别配置陀螺仪量程(GYRO_CONFIG)和加速度计量程(ACCEL_CONFIG);
  5. 设置采样率(SMPLRT_DIV);
  6. 设置数字低通滤波器(可选)。

在仓库驱动中,初始化入口为 mpu6050Init(I2C_Dev *i2cPort)(mpu6050.c),默认使用 MPU6050_ADDRESS_AD0_LOW(0x68)作为设备地址,即 AD0 管脚接地;若 AD0 接高电平,则地址为 0x69(见 mpu6050.h)。mpu6050TestConnection() 通过读取 WHO_AM_I 并比对 0b110100 来验证 I2C 连接是否正常(mpu6050.c)。

MPU6050 关键寄存器

寄存器典型值

下表为 ESP-Drone 传感器初始化时常用的寄存器典型配置值:

寄存器 典型值 功能
PWR_MGMT_1 0x00 正常启用
SMPLRT_DIV 0x07 陀螺仪采样率 125 Hz
CONFIG 0x06 低通滤波器频率为 5 Hz
GYRO_CONFIG 0x18 陀螺仪不自检,输出满量程范围为 ±2000 °/s
ACCEL_CONFIG 0x01 加速度计不自检,输出的满量程范围为 ±2 g

寄存器 117 WHO_AM_I —— 设备地址

WHO_AM_I

[6:1] 位保存设备地址,默认为 0x68,不反映 AD0 管脚的实际电平值。因此读取 WHO_AM_I 只能验证芯片型号与 I2C 通信链路,不能用来判断 AD0 接线。

寄存器 107 PWR_MGMT_1 —— 电源管理 1

PWR_MGMT_1

  • DEVICE_RESET(bit7):置位后所有寄存器恢复默认值;
  • SLEEP(bit6):置位后 MPU6050 进入睡眠模式;
  • CYCLE(bit5):在 SLEEP 禁用的情况下置位,MPU6050 进入循环模式(CYCLE)。循环模式下,芯片睡眠一段时间后,按 LP_WAKE_CTRL(寄存器 108)设定的唤醒频率醒来,完成一次对活动传感器的采样后再次入睡,如此循环以降低功耗。

寄存器 26 CONFIG —— 配置数字低通滤波器

CONFIG

DLPF_CFG 的取值与滤波频段的关系如下:

DLPF_CFG

寄存器 27 GYRO_CONFIG —— 陀螺仪量程配置

GYRO_CONFIG

  • XG_ST / YG_ST / ZG_ST:对应轴陀螺仪自检使能位;
  • FS_SEL:配置陀螺仪满量程范围,取值 0~3 分别对应 ±250、±500、±1000、±2000 °/s:

FS_SEL

在源码中,mpu6050GetFullScaleGyroDPL()(mpu6050.c)会根据当前 FS_SEL 返回对应的每 LSB 角速度,即灵敏度系数:MPU6050_DEG_PER_LSB_250 = 2×250/65536,依此类推(mpu6050.h)。将原始 16 位整型读数乘以该系数,即可换算为实际的角速度(°/s)。

寄存器 28 ACCEL_CONFIG —— 配置加速度计量程

ACCEL_CONFIG

AFS_SEL

AFS_SEL 取值 0~3 分别对应 ±2 g、±4 g、±8 g、±16 g。对应地,mpu6050GetFullScaleAccelGPL()(mpu6050.c)返回每 LSB 的加速度(g),如 MPU6050_G_PER_LSB_2 = 2×2/65536。此外,寄存器 28 还包含加速度计自检位(XA_ST/YA_ST/ZA_ST)与高通滤波器配置字段(ACCEL_HPF)。

寄存器 25 SMPRT_DIV —— 采样速率分频器

SMPRT_DIV

该寄存器指定陀螺仪输出速率的分频器,用于生成 MPU6050 的采样率。传感器寄存器的输出、FIFO 输出和 DMP 采样都基于该采样率,计算公式为:

Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)

其中:

  • 当 DLPF 禁用时(DLPF_CFG = 0 或 7),陀螺仪输出速率为 8 kHz;
  • 当 DLPF 使能时(见寄存器 26),陀螺仪输出速率为 1 kHz。

注意,在不开启 DLPF 的情况下,将 SMPLRT_DIV 设置为 7,可以使芯片产生 1 kHz 的中断信号:

SMPLRT_DIV=7 中断波形

寄存器 59 ~ 64 —— 加速度计测量值

REG_3B_40

  • 大端序存放:地址低位存放数据高位,地址高位存放数据低位;
  • 补码存放:测量值为有符号整数,采用补码方式存放。

驱动中的 mpu6050GetAcceleration() / mpu6050GetMotion6() 即按此格式一次读取 6 或 14 字节(加速度 + 温度 + 陀螺仪),并拼装成有符号 16 位整型(见 mpu6050.c 中的 mpu6050GetMotion6 实现)。

寄存器 65 ~ 66 —— 温度测量

REG_41_42

寄存器 67 ~ 72 —— 陀螺仪测量值

REG_43_48

陀螺仪测量值同样按大端序、补码存放,mpu6050GetRotation() 负责读取这三个轴的原始角速度。

仓库中的 MPU6050 驱动实现

ESP-Drone 的 MPU6050 驱动源自 I2Cdev 库并移植到 ESP-IDF 环境,头文件 mpu6050.h 中完整定义了全部寄存器地址(如 MPU6050_RA_SMPLRT_DIV 0x19、MPU6050_RA_CONFIG 0x1A、MPU6050_RA_GYRO_CONFIG 0x1B、MPU6050_RA_ACCEL_CONFIG 0x1C、MPU6050_RA_PWR_MGMT_1 0x6B、MPU6050_RA_WHO_AM_I 0x75 等),以及与文档章节一一对应的 API 函数声明。

特别值得一提的是 mpu6050SelfTest()(mpu6050.c)的实现流程:它先读取自检前的静态测量值,然后同时使能三轴陀螺仪与加速度计的自检位,等待 1 秒后再次采样,最后对比两组数据是否落在 MPU6050_ST_GYRO_LOW=10.0 至 MPU6050_ST_GYRO_HIGH=105.0(°/s)以及 MPU6050_ST_ACCEL_LOW=0.300 至 MPU6050_ST_ACCEL_HIGH=0.950(g)的允许范围内,从而判定传感器是否工作正常。这一自检逻辑是文档中"寄存器 27/28 自检位"的实际应用,可以在上电自检阶段调用以快速发现硬件故障。

VL53LXX 传感器

概述与工作原理

VL53L1X 是 ST 公司提供的 ToF(Time-of-Flight)测距与姿态检测传感器。VL53L0X/VL53L1X 芯片内部集成了激光发射器和 SPAD(单光子雪崩二极管)红外接收器,通过探测光子发送和接收的时间差计算飞行距离,最远测量距离可达两米,适合中短距离测量应用。

VL53LXX

测量区域 —— ROI

VL53L0X/VL53L1X 的测量值为测量区域中的最短距离。测量区域(ROI,Region-of-Interest)可以根据使用场景放大或缩小,但较大的探测范围可能引起测量值波动。ROI 的最小尺寸为 4×4,最大为 16×16。

ROI

关于测量区域的详细配置,参见 VL53LXX 数据手册中 "Ranging Description" 与 "Sensing Array Optical Center" 两节。

测量距离

  • VL53L0X 传感器存在 3 ~ 4 cm 的盲区,有效测量范围为 3 ~ 200 cm,精度 ±3%;
  • VL53L1X 是 VL53L0X 的升级版本,探测距离可达 400 cm。

VL53L1X 提供了多种测量模式,测量时间、测量范围与应用场景各有取舍:

模式 测量时间 (ms) 测量范围 (m) 应用场景
默认模式 30 1.2 标准场景
高精度模式 200 1.2(±3%) 精密测距
长距离模式 33 2 远距离,仅适用于黑暗环境
高速模式 20 1.2(±5%) 追求速度、精度要求低

VL53L1X 测量距离

VL53LXX 的测量距离与光线环境强相关:黑暗环境下探测距离更远;在室外强光下,激光传感器可能受到很大干扰,导致测量精度降低,因此室外场景需要结合气压计进行定高(气压计 MS5611 驱动见 ms5611.c)。

测量频率

  • VL53L0X 响应频率最快可达 50 Hz,测量误差 ±5%;
  • VL53L1X 的 I2C 最高时钟频率可达 400 kHz,上拉电阻需要根据电压和总线电容值选择。

VL53L1X 典型电路

VL53L1X 的关键外部管脚:

  • XSHUT:输入管脚,用于模式选择(休眠),需要上拉电阻防止漏电流;
  • GPIO1:中断输出管脚,用于输出测量数据就绪(dataready)中断。

工作模式

通过设置 XSHUT 管脚电平,可以切换传感器进入 HW Standby 或 SW Standby 模式,实现有条件启动、降低待机功耗。如果主机放弃管理传感器模式,可将 XSHUT 管脚默认设置为上拉。

  • HW Standby:XSHUT 拉低,传感器电源关闭;
  • SW Standby:XSHUT 拉高,进入 Boot 和 SW Standby 模式。

HW Standby

SW Standby

VL53LXX 初始化步骤

官方推荐的初始化步骤(除第 7 步 VL53L1_SetUserROI 外,其余步骤不可省略):

  1. 等待硬件初始化完成;
  2. 数据初始化;
  3. 静态初始化,装载数据;
  4. 设置测量距离模式;
  5. 设置单次测量最长等待时间;
  6. 设置测量频率(时间间隔);
  7. 设置测量区域 ROI(可选);
  8. 启动测量。
/*init  vl53l1 module*/
void vl53l1_init()
{

    Roi0.TopLeftX = 0;    //测量目标区(可选)最小 4*4,最大 16*16。
    Roi0.TopLeftY = 15;
    Roi0.BotRightX = 7;
    Roi0.BotRightY = 0;
    Roi1.TopLeftX = 8;
    Roi1.TopLeftY = 15;
    Roi1.BotRightX = 15;
    Roi1.BotRightY = 0;

    int status = VL53L1_WaitDeviceBooted(Dev); //等待硬件初始化完成。
    status = VL53L1_DataInit(Dev); //数据初始化,上电后立刻执行。
    status = VL53L1_StaticInit(Dev); //静态初始化,装载参数。
    status = VL53L1_SetDistanceMode(Dev, VL53L1_DISTANCEMODE_LONG);//设置测量模式。
    status = VL53L1_SetMeasurementTimingBudgetMicroSeconds(Dev, 50000); //根据测量模式确定最长等待时间。
    status = VL53L1_SetInterMeasurementPeriodMilliSeconds(Dev, 100); //设置测量间隔。

    status = VL53L1_SetUserROI(Dev, &Roi0); //设置测量区域 ROI
    status = VL53L1_StartMeasurement(Dev); //启动测量
    if(status) {
        printf("VL53L1_StartMeasurement failed \n");
        while(1);
    }

}

上述示例中定义了左右两个 ROI(Roi0 与 Roi1),可实现一个传感器轮流测量两个区域,常用于左右测距应用。

VL53LXX 测距步骤

轮询测量模式

轮询测量的完整流程如下图所示:

VL53LXX 测量流程

轮询测量有两种方式:一种是阻塞方式(Drivers polling mode,驱动在读取数据前等待);一种是非阻塞方式(Host polling mode,主机周期性查询状态)。无论哪种方式,完成一次测量和读取后,都需要使用 VL53L1_ClearInterruptAndStartMeasurement 清除中断标志并重新开始。以下代码为阻塞测量方式:

/* Autonomous ranging loop*/
static void
AutonomousLowPowerRangingTest(void)
{
    printf("Autonomous Ranging Test\n");

    static VL53L1_RangingMeasurementData_t RangingData;
    VL53L1_UserRoi_t Roi1;
    int roi = 0;
    float left = 0, right = 0;
    if (0/*isInterrupt*/) {
    } else {
        do // polling mode
            {
                int status = VL53L1_WaitMeasurementDataReady(Dev); //等待测量结果
                if(!status) {
                    status = VL53L1_GetRangingMeasurementData(Dev, &RangingData); //获取单次测量数据
                    if(status==0) {
                        if (roi & 1) {
                            left = RangingData.RangeMilliMeter;
                            printf("L %3.1f R %3.1f\n", right/10.0, left/10.0);
                        } else
                            right = RangingData.RangeMilliMeter;
                    }
                    if (++roi & 1) {
                        status = VL53L1_SetUserROI(Dev, &Roi1);
                    } else {
                        status = VL53L1_SetUserROI(Dev, &Roi0);
                    }
                    status = VL53L1_ClearInterruptAndStartMeasurement(Dev); //释放中断
                }
            }
        while (1);
    }
    //  return status;
}

该例程每轮测量交替使用两个 ROI,从而用单颗 VL53L1X 同时输出左右两个方向的测距结果(L 与 R,单位 cm)。

中断测量模式

中断测量模式需要使用中断管脚 GPIO1:数据就绪时,GPIO1 管脚电平被拉低,通知主机读取数据。

VL53LXX 自主测量流程

VL53LXX 传感器校准

如果传感器接收器上方安装了光罩,或传感器安装在透明盖板背后,透光率会发生变化,此时必须对传感器进行校准。有两种校准途径:根据校准流程调用 API 编写校准程序,或使用官方提供的 GUI 上位机直接测量出校准值。

使用官方 API 编写校准程序

校准流程中 API 的调用顺序必须完全一致:

VL53LXX 校准流程

/*VL53L1 模块校准*/
static VL53L1_CalibrationData_t vl53l1_calibration(VL53L1_Dev_t *dev)
{
    int status;
    int32_t targetDistanceMilliMeter = 703;
    VL53L1_CalibrationData_t calibrationData;
    status = VL53L1_WaitDeviceBooted(dev);
    status = VL53L1_DataInit(dev);                                       //设备初始化
    status = VL53L1_StaticInit(dev);                                     // 为给定用例,加载设备设置。
    status = VL53L1_SetPresetMode(dev,VL53L1_PRESETMODE_AUTONOMOUS);
    status = VL53L1_PerformRefSpadManagement(dev);
    status = VL53L1_PerformOffsetCalibration(dev,targetDistanceMilliMeter);
    status = VL53L1_PerformSingleTargetXTalkCalibration(dev,targetDistanceMilliMeter);
    status = VL53L1_GetCalibrationData(dev,&calibrationData);

    if (status)
    {
        ESP_LOGE(TAG, "vl53l1_calibration failed \n");
        calibrationData.struct_version = 0;
        return calibrationData;

    }else
    {
        ESP_LOGI(TAG, "vl53l1_calibration done ! version = %u \n",calibrationData.struct_version);
        return calibrationData;
    }

}

校准过程通常需要一个已知距离的目标物(示例中为 703 mm),依次完成 SPAD 管理、偏移量校准与串扰校准后,将 VL53L1_CalibrationData_t 结果保存,在正式初始化时写入。

使用官方 GUI 上位机校准传感器

ST 官方提供了用于配置和校准传感器的 GUI 上位机(STSW-IMG008),配合 ST 官方 STM32F401RE nucleo 开发板连接传感器,使用软件校准得到基准值后,在初始化时填入即可:

STSW-IMG008 校准 GUI

VL53L1X 例程

  • 例程功能:通过 VL53L1X 检测到高度变化(持续 1 秒),红灯亮起;高度恢复正常值(持续 1 秒),绿灯亮起;
  • 可配置参数:通过 make menuconfig 设置 I2C 号码、端口号、LED 端口号;
  • 例程解析:见代码注释和用户手册。

注意事项:

  1. 该例程只适用于 VL53L1X,VL53L0X 为老版本硬件,不适用本例程;
  2. 官方标称的 400 cm 测量距离为黑暗环境下测得;室内正常灯光环境下,可保证 10 ~ 260 cm 范围的有效测量;
  3. 初始化函数 vl53l1_init(VL53L1_Dev_t *) 中的部分参数需要根据实际使用环境确定,还有优化空间;
  4. 传感器安装位置应确保在检测位置正上方;
  5. 模块上电时自动矫正基准高度,如果基准高度有变化,需要重新上电重置参数。

仓库中的 VL53L1X 驱动实现

ESP-Drone 仓库完整集成了 ST 官方 VL53L1 驱动库,位于 components/drivers/i2c_devices/vl53l1,其中:

  • vl53l1x.c 是适配 ESP-IDF 平台的移植层:VL53L1_WriteMulti / VL53L1_ReadMulti / VL53L1_WrByte 等底层通信函数均基于 i2cdevWrite16 / i2cdevRead16 实现(VL53L1X 使用 16 位寄存器地址),时间等待函数 VL53L1_WaitUs / VL53L1_WaitMs 则基于 FreeRTOS 的 vTaskDelay 实现;
  • vl53l1xInit() 会读取芯片的 Model_ID 与 Module_Type(寄存器 0x010F / 0x0110)用于调试确认芯片型号,随后依次调用 VL53L1_WaitDeviceBooted → VL53L1_DataInit → VL53L1_StaticInit,并通过 SET_VL53LX_ROI 宏控制是否配置测量 ROI;
  • 底层 ST 官方 API 源码位于 components/drivers/i2c_devices/vl53l1/core/src,包括 vl53l1_api.c、vl53l1_api_calibration.c 等,是文档中初始化、测距、校准各 API 的具体实现;
  • 上层应用方面,zranger2.c(zranger2.c)封装了测距数据的周期读取逻辑。

SPI 设备驱动:PMW3901

PMW3901 是 PixArt 公司的高精度、低功耗光学追踪模组,可直接获取 X-Y 方向运动速度信息,对地高度 8 cm 以上即可进行有效测量,是 ESP-Drone 实现水平定点控制(光流定高 + 定点)的核心传感器。

主要参数

参数 参数值
供电电压 (V) VDD:1.8 ~ 2.1 V;VDDIO:1.8 ~ 3.6 V
工作范围 (mm) 80 ~ +∞
接口 4 线 SPI @ 2 MHz
封装 28 管脚 COB 封装,尺寸:6 x 6 x 2.28 mm

PMW3901 封装

PMW3901 管脚映射

注意:传感器工作电压较低(VDD 最低 1.8 V),与 3.3 V 的 ESP32 通信时,需要为 VDD 和 VDDIO 提供不同的电压(VDDIO 可直接接 3.3 V 逻辑电平)。

在仓库中,PMW3901 驱动位于 components/drivers/spi_devices/pmw3901,SPI 底层通过 deck_spi 接口(deck_spi.c)以 2 MHz 波特率通信。registerWrite() 将寄存器地址的 MSB 置 1 表示写操作,registerRead() 清除 MSB 表示读操作,并严格控制 NCS 拉低/拉高前后的延时(见 pmw3901.c),这正是文档所述 4 线 SPI 时序的源码实现。

上电启动流程

PMW3901MB 虽然可执行内部上电自复位,但仍建议每次上电时对 Power_Up_Reset 寄存器执行写操作,具体顺序如下:

  1. 首先对 VDDIO 供电,然后对 VDD 供电,中间延迟不应超过 100 ms,并确保供电稳定;
  2. 等待至少 40 ms;
  3. 先拉高、再拉低 NCS,以复位 SPI 口;
  4. 写 0x5A 到 Power_Up_Reset 寄存器(或切换至 NRESET 管脚);
  5. 等待至少 1 ms;
  6. 无论运动管脚状态如何,一次性读取寄存器 0x02、0x03、0x04、0x05 和 0x06;
  7. 参考 PWM3901MB 数据手册章节 8.2 "性能优化寄存器",配置所需寄存器以实现芯片最佳性能。

上述流程在 pmw3901.c 的 pmw3901Init() 中有完整实现:先通过 NCS 高低电平翻转复位 SPI 口,再读取 chipId(0x00)与 invChipId(0x5F)验证芯片(期望 0x49 与 0xB6),随后写 0x5A 到寄存器 0x3A 完成 Power_Up_Reset,按序读取 0x02~0x06 运动寄存器,最后调用 InitRegisters() 写入一长串性能优化寄存器配置(对应数据手册章节 8.2)。

掉电流程

通过写 Shutdown 寄存器,可将 PMW3901MB 设置为 Shutdown 模式。Shutdown 模式下,PMW3901MB 只对上电启动指令(写 0x5A 到寄存器 0x3A)响应,不响应其它访问操作。同一 SPI 总线下的其它设备不受 PMW3901MB Shutdown 模式的影响,在 NCS 管脚不冲突的情况下可以正常访问。

从 Shutdown 模式复位:

  1. 拉高、再拉低 NCS,复位 SPI 口;
  2. 写 0x5A 到 Power_Up_Reset 寄存器(或切换至 NRESET 管脚);
  3. 等待至少 1 ms;
  4. 无论运动管脚状态如何,一次性读取寄存器 0x02、0x03、0x04、0x05 和 0x06;
  5. 参考 PWM3901MB 数据手册章节 8.2 配置性能优化寄存器。

部分代码解读

关键结构体

光流数据处理的核心结构体 opFlow_t 定义了从原始像素到位置/速度信息转换所需的全部中间量:

typedef struct opFlow_s
{
    float pixSum[2]; /*累积像素*/
    float pixComp[2]; /*像素补偿*/
    float pixValid[2]; /*有效像素*/
    float pixValidLast[2]; /*上一次有效像素*/
    float deltaPos[2]; /*2 帧之间的位移 单位:cm*/
    float deltaVel[2]; /*速度 单位 cm/s*/
    float posSum[2]; /*累积位移 单位 cm*/
    float velLpf[2]; /*速度低通 单位 cm/s*/
    bool isOpFlowOk; /*光流状态*/
    bool isDataValid; /*数据有效*/
} opFlow_t;

各字段含义:

  • 累积像素:飞行器起飞后的累积像素;
  • 像素补偿:补偿由于飞行器倾斜导致的像素误差;
  • 有效像素:经过补偿后的实际像素;
  • 2 帧之间的位移:由像素转换出的实际位移,单位 cm;
  • 速度:瞬时速度,由位移变化量微分得到,单位 cm/s;
  • 累积位移:实际位移,单位 cm;
  • 速度低通:对速度进行低通滤波,增加数据平滑性;
  • 光流状态:检查光流是否正常工作;
  • 数据有效:在一定高度范围内数据有效。

SPI 单次突发读取返回的运动数据结构体 motionBurst_t 与 PMW3901 寄存器映射一一对应:

typedef struct motionBurst_s {
  union {
    uint8_t motion;
    struct {
      uint8_t frameFrom0    : 1;
      uint8_t runMode       : 2;
      uint8_t reserved1     : 1;
      uint8_t rawFrom0      : 1;
      uint8_t reserved2     : 2;
      uint8_t motionOccured : 1;
    };
  };

  uint8_t observation;
  int16_t deltaX;
  int16_t deltaY;

  uint8_t squal;

  uint8_t rawDataSum;
  uint8_t maxRawData;
  uint8_t minRawData;

  uint16_t shutter;
} __attribute__((packed)) motionBurst_t;

各字段含义:

  • motion:运动信息,按位判断帧判别(frameFrom0)、运行模式(runMode)和运动检测(motionOccured)等;
  • observation:用于检测 IC 是否出现 EFT/B 或 ESD 问题,传感器正常工作时读取值应为 0xBF;
  • deltaX / deltaY:光流检测到的图像 X、Y 方向运动信息;
  • squal:运动信息质量,即运动信息的可信度;
  • rawDataSum:原始数据求和,可用于对一帧数据求平均值;
  • maxRawData / minRawData:最大和最小原始数据;
  • shutter:实时自动调整的值,确保平均运动数据保持在正常可操作范围内;shutter 可搭配 squal 判断运动信息是否可用。

在驱动中,pmw3901ReadMotion()(pmw3901.c)从地址 0x16 突发读取整个 motionBurst_t 结构体,并对 shutter 字段做字节序交换后返回给上层。

编程注意事项

文档结合 ESP-Drone 定点控制的实际经验,给出了以下重要注意事项:

  • 故障检测:如果连续 1 s 内光流数据都为 0,说明出现故障,需要做挂起光流任务等处理;
  • 安装方向:传感器镜头必须朝下安装。由于机身位置固定,根据相对运动原理,传感器采集的位移数据与飞机实际运动方向相反,软件中需要做符号翻转;
  • 模式依赖:只有在定高模式测试稳定时,才能进入定点模式。精确的高度信息用于确定图像像素和实际距离的对应关系;
  • 倾角补偿:手动测试倾角补偿,实现飞行器有一定倾角时传感器输出基本不变;
  • 位移与速度换算:有了倾角补偿和运动累积像素即可得到实际累积像素,进一步计算:
    • 2 帧之间变化像素 = 实际累积像素 - 上次实际像素;
    • 2 帧之间的位移变化 = 2 帧之间变化像素 × 系数。对系数的限制:高度小于 5 cm 时光流无法工作,系数设置为 0;
    • 对位移积分得到四轴到起飞点的位移;对位移微分得到瞬时速度。注意对速度进行低通以增加数据平滑性,并进行限幅处理以增加数据安全性。

通过光流得到四轴的位置和速度信息后:

  • 将位置和速度信息与加速度计数据融合(参考 state_estimator.c 的估计器实现),即可得到估测位置和速度;
  • 估测位置和速度参与 PID 运算,用于水平方向位置控制(参考 position_controller_pid.c 中位置环和速度环 PID 的处理过程)。

通过上述完整链路,即可实现水平定点控制。

总结

本文从 ESP-Drone 官方文档出发,结合仓库源码完整梳理了三大传感器驱动的技术细节:

  • MPU6050(I2C):六轴 IMU,重点掌握 PWR_MGMT_1、CONFIG、GYRO_CONFIG、ACCEL_CONFIG、SMPRT_DIV 等关键寄存器的配置方法、DLPF 与采样率的换算关系,以及 WHO_AM_I 验证、自检流程等驱动实现;
  • VL53LXX(I2C):ToF 测距传感器,重点掌握初始化八步流程、轮询/中断两种测距方式、ROI 配置、传感器校准方法及其在定高控制中的适用边界(室内 10~260 cm 有效);
  • PMW3901(SPI):光流传感器,重点掌握上电/掉电流程、opFlow_t 与 motionBurst_t 数据结构,以及从像素 → 位移 → 速度 → 定点控制的数据链路。

相关驱动源码均可在 components/drivers/i2c_devices 与 components/drivers/spi_devices 目录下找到,配合本文所述寄存器与流程,即可在此基础上进行传感器调试、参数优化与二次开发。

登录后查看全文
esp-drone