第1章 传感器基础:加速度计与陀螺仪的工作原理、I2C/SPI通信协议简介、MPU6050数据手册解读
1.1 加速度计:你感受到的“力”
加速度计这东西,说白了就是测量“加速度”的。但你别被名字骗了——它测的其实不是速度变化,而是力。
我刚开始做体感游戏机时,有个问题困扰了我很久:为什么静止放在桌上的加速度计,读出来不是0,而是1g?后来才明白,它测的是“感受到的力”。重力也是一种力,所以静止时它感受到的就是地球引力。
加速度计内部有个微小的质量块,用弹簧悬着。当芯片加速时,质量块会滞后,弹簧被拉伸或压缩。通过测量这个位移,就能算出加速度。MEMS(微机电系统)版本更小,用的是电容变化来检测位移。
关键点:加速度计测的是“比力”(specific force),即物体受到的合力减去重力。所以自由落体时读数为0。
在游戏手柄里,加速度计用来检测倾斜、晃动、拍打等动作。比如你挥动Switch手柄打网球,加速度计就能捕捉到挥动的方向和力度。
1.2 陀螺仪:角速度的“旋转感知器”
陀螺仪测的是角速度,也就是旋转的快慢。单位是°/s(度每秒)。
你想想看,加速度计能测倾斜,但如果你快速旋转手柄,加速度计就分不清是重力方向变了还是旋转产生的向心力。这时候就需要陀螺仪来帮忙。
MEMS陀螺仪的原理有点意思——它利用科里奥利力。一个质量块在高速振动,当芯片旋转时,振动方向会偏转,产生一个垂直于振动方向的力。测这个力的大小,就能算出角速度。
我记得第一次用陀螺仪做体感控制时,发现数据漂移得厉害。静止时输出明明是0,过几分钟就变成几十°/s了。这就是陀螺仪的“零偏漂移”,需要定期校准。
我的经验:陀螺仪适合短时间高动态的测量,加速度计适合长时间低动态的姿态估计。两者互补,所以MPU6050把两个传感器集成在一起。
1.3 I2C与SPI:传感器怎么跟MCU说话?
传感器测到了数据,怎么传给主控芯片?两种主流方式:I2C和SPI。
I2C协议(两线制)
I2C只用两根线:SCL(时钟)和SDA(数据)。所有设备挂在同一条总线上,每个设备有唯一地址。
我习惯用I2C,因为接线少。但要注意:速度一般只有400kHz(快速模式),而且总线电容有限制,线太长容易出问题。
I2C的通信流程大致是:
- 主机发送起始条件
- 发送设备地址+读写位
- 从机应答
- 传输数据(每字节后跟应答位)
- 主机发送停止条件
// I2C读取MPU6050加速度数据的伪代码
i2c_start();
i2c_write(0xD0); // MPU6050地址+写
i2c_write(0x3B); // 加速度数据起始寄存器
i2c_stop();
i2c_start();
i2c_write(0xD1); // 地址+读
accel_x_h = i2c_read(ACK);
accel_x_l = i2c_read(ACK);
// ... 读取剩余5个字节
i2c_stop();
SPI协议(四线制)
SPI用四根线:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。速度可以跑到几十MHz,比I2C快得多。
什么时候用SPI?当你的游戏机需要高刷新率时。比如VR头显,传感器数据要1000Hz以上,I2C就扛不住了。
注意:SPI没有设备地址,靠片选线选择设备。所以每增加一个传感器,就要多一根CS线。我曾经因为CS线接反了,折腾了一下午才发现。
| 特性 | I2C | SPI |
|---|---|---|
| 线数 | 2根 | 4根(最少) |
| 速度 | 100kHz-3.4MHz | 最高几十MHz |
| 多设备 | 地址区分,总线共享 | 片选区分,每设备一根CS |
| 距离 | 短(<1m) | 短(<1m) |
| 典型场景 | 传感器、EEPROM | SD卡、显示屏、高速传感器 |
1.4 MPU6050数据手册解读
MPU6050是InvenSense(现属TDK)的6轴运动跟踪芯片,集成了3轴加速度计和3轴陀螺仪。我敢说,市面上80%的体感游戏机原型都用过它。
拿到数据手册,先看这几页:
- 第1页:特性概述——加速度计±2/±4/±8/±16g可调,陀螺仪±250/±500/±1000/±2000°/s可调。我一般用±2g和±500°/s,精度和范围平衡。
- 第8页:寄存器映射——这是核心。加速度数据在0x3B-0x40,陀螺仪数据在0x43-0x48,温度在0x41-0x42。
- 第13页:电源管理寄存器——0x6B,写0x00唤醒芯片,写0x40复位。
- 第28页:数字低通滤波器——0x1A寄存器,设置滤波带宽。我建议游戏机应用设成44Hz,能滤掉高频噪声又不至于太滞后。
避坑指南:我曾经在项目里直接读0x3B-0x40的原始值,发现数据跳动很大。后来仔细看手册,才发现需要先读0x3B触发数据更新,再连续读取6个字节。否则读到的是新旧混搭的数据。
MPU6050的I2C地址是0x68(AD0接GND)或0x69(AD0接VCC)。如果你同时用两个MPU6050,一个接高一个接低,就能用同一条I2C总线。
// MPU6050初始化代码片段(I2C模式)
void MPU6050_Init() {
// 唤醒芯片
I2C_WriteByte(0x68, 0x6B, 0x00);
// 设置加速度计量程±2g
I2C_WriteByte(0x68, 0x1C, 0x00);
// 设置陀螺仪量程±500°/s
I2C_WriteByte(0x68, 0x1B, 0x08);
// 设置数字低通滤波器44Hz
I2C_WriteByte(0x68, 0x1A, 0x03);
// 配置采样率50Hz
I2C_WriteByte(0x68, 0x19, 0x13);
}
读数据时要注意:加速度计和陀螺仪的数据都是16位有符号整数,高字节在前。转换公式是:
// 加速度:原始值 / 16384 = g值(±2g量程时)
accel_g = (int16_t)(accel_h << 8 | accel_l) / 16384.0f;
// 陀螺仪:原始值 / 65.5 = °/s(±500°/s量程时)
gyro_dps = (int16_t)(gyro_h << 8 | gyro_l) / 65.5f;
小技巧:16384这个系数怎么来的?±2g量程对应16位ADC,满量程是-32768到32767,对应-2g到+2g。所以1g = 32768/2 = 16384。其他量程类推。
嗯,到这里你应该对加速度计、陀螺仪、I2C/SPI以及MPU6050有了基本认识。下一章我们开始实战——把MPU6050焊到开发板上,用逻辑分析仪抓波形,看看数据到底怎么传的。