2、传感器数据采集基础:ADC采样原理、采样率与分辨率、I2C/SPI通信协议简介、传感器寄存器配置
好,咱们正式开始聊传感器数据采集。说实话,这部分是嵌入式开发里最基础、也最容易翻车的地方。我见过不少项目,算法模型建得漂漂亮亮,结果一上硬件,数据全是噪声——问题就出在采集这一环没打好。
今天这一章,我会把ADC采样、通信协议、寄存器配置这几个核心点串起来讲。你想想看,传感器就像人的眼睛,数据采集就是视觉神经,如果神经传回来的信号都是花的,大脑再聪明也没用。
2.1 ADC采样原理:从模拟到数字的那一步
ADC,全称模数转换器。说白了,就是把连续的电压值,变成一串离散的数字。为什么需要这步?因为单片机不认识模拟信号,它只认0和1。
ADC的核心原理其实不复杂:内部有一个比较器,加上一个逐次逼近寄存器(SAR)。它会从最高位开始猜,猜对了就保留,猜错了就改。这个过程叫逐次逼近。我当年第一次用STM32的ADC时,以为直接读寄存器就行,结果发现采样值跳得厉害——后来才意识到,是参考电压没接稳。
关键参数:
- 分辨率:比如12位ADC,输出范围是0~4095。分辨率越高,能区分的电压台阶就越细。
- 采样率:每秒能采多少次。单位是SPS(Samples Per Second)。
- 量化误差:因为数字是离散的,所以必然有误差。分辨率越高,误差越小。
举个例子,一个3.3V参考电压、12位分辨率的ADC,每个LSB(最低有效位)对应的电压是:
LSB = 3.3V / 4096 ≈ 0.805 mV
也就是说,你读到的数值每变化1,实际电压变化了不到1毫伏。嗯,这个精度对于大多数传感器来说已经够用了。
我的经验:选ADC时别一味追求高分辨率。12位够用就别上16位,因为位数越高,采样速度越慢,而且对电源噪声越敏感。我曾经在一个项目里用了16位ADC,结果电源纹波直接吃掉低4位,白费功夫。
2.2 采样率与分辨率:一对冤家
采样率和分辨率,就像鱼和熊掌。你想想看,ADC内部完成一次转换需要时间,分辨率越高,转换时间越长,采样率自然就上不去。
我习惯用一个简单的公式来估算:
最大采样率 ≈ 1 / (转换时间 + 采样时间)
比如STM32F4的ADC,12位分辨率下,最快能跑到2.4MSPS(兆采样每秒)。但如果你把它设成16位(通过过采样),速度就会掉到几百KSPS。
这里有个坑:采样率不是越高越好。根据奈奎斯特定理,采样率至少要是信号最高频率的两倍。但实际工程中,我一般取5~10倍。为什么?因为抗混叠滤波器不是理想滤波器,留点余量更安全。
注意:采样率太低会导致混叠,高频信号会伪装成低频信号混进来。我曾经在采集振动信号时吃过这个亏,频谱分析出来全是假峰,排查了两天才发现是采样率设低了。
还有一个概念叫过采样。简单说,就是用高采样率采多次,然后取平均,等效提高分辨率。比如你采16次12位的数据,平均后能得到一个13位的结果。但代价是速度降为原来的1/16。
2.3 I2C/SPI通信协议简介:传感器怎么跟MCU说话
传感器采集完数据,怎么传给MCU?最常用的就是I2C和SPI。这两个协议,我几乎每天都在用。
2.3.1 I2C:两根线走天下
I2C只需要两根线:SCL(时钟)和SDA(数据)。它是个多主从总线,每个设备都有唯一的地址。通信流程大概是:
- 主机发送起始信号
- 主机发送设备地址 + 读写位
- 从机应答
- 传输数据(每字节后跟一个应答位)
- 主机发送停止信号
I2C的优点:接线少,支持多设备。缺点:速度慢(标准模式100kHz,快速模式400kHz),而且没有片选信号,容易受干扰。
避坑指南:我曾经在一个项目里,I2C总线挂了三个传感器,结果通信时断时续。排查半天,发现是总线电容太大,上拉电阻没选对。标准做法是:根据总线电容和速度,计算上拉电阻值,一般在2kΩ~10kΩ之间。
2.3.2 SPI:四根线,快就一个字
SPI用四根线:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。它是全双工通信,速度可以跑到几十MHz。
SPI的通信模式由时钟极性(CPOL)和时钟相位(CPHA)决定,共有四种模式。我刚开始学的时候,经常搞混这四种模式,后来总结了一个口诀:
- CPOL=0:空闲时时钟为低
- CPOL=1:空闲时时钟为高
- CPHA=0:第一个边沿采样
- CPHA=1:第二个边沿采样
嗯,其实不用死记。你只要看传感器的数据手册,它会在时序图里明确标出采样边沿。照着配就行。
选型建议:
- 如果传感器数量多、速度要求不高,用I2C
- 如果传感器速度快、数据量大,用SPI
- 如果只有一颗传感器,且距离近,SPI更可靠
2.4 传感器寄存器配置:让传感器听你的话
传感器内部通常有一堆寄存器。你得通过I2C或SPI往这些寄存器里写值,才能让传感器按你的要求工作。
常见的寄存器类型包括:
- 控制寄存器:设置工作模式、采样率、量程等
- 状态寄存器:读取传感器状态,比如数据是否准备好
- 数据寄存器:存放采集到的原始数据
- 校准寄存器:存放校准参数
我以MPU6050(六轴陀螺仪+加速度计)为例,展示一下典型的寄存器配置流程:
// 1. 唤醒传感器(默认是睡眠模式)
i2c_write(MPU6050_ADDR, PWR_MGMT_1, 0x00);
// 2. 设置加速度计量程为±2g
i2c_write(MPU6050_ADDR, ACCEL_CONFIG, 0x00);
// 3. 设置陀螺仪量程为±250°/s
i2c_write(MPU6050_ADDR, GYRO_CONFIG, 0x00);
// 4. 配置采样率(分频系数)
i2c_write(MPU6050_ADDR, SMPLRT_DIV, 0x07); // 1kHz / (1+7) = 125Hz
// 5. 读取数据
uint8_t buf[14];
i2c_read(MPU6050_ADDR, ACCEL_XOUT_H, buf, 14);
重要提醒:配置寄存器时,一定要先读后写。很多寄存器是保留位的,直接写会覆盖掉默认值,导致传感器工作异常。我习惯的做法是:先读当前值,用位操作修改目标位,再写回去。
还有一个细节:传感器的数据寄存器通常是16位的,分高8位和低8位。读取时要注意字节序(大端还是小端)。MPU6050是大端模式,高字节在前。组合数据时:
int16_t accel_x = (buf[0] << 8) | buf[1];
嗯,这里要特别注意符号位。如果传感器输出的是有符号数,直接这么拼没问题。但如果是无符号的,就得用uint16_t。
我的习惯:每次配置完传感器,我都会读一遍配置寄存器,确认写进去的值是对的。别嫌麻烦,这一步能省掉后面大量的调试时间。我曾经因为一个寄存器地址写错,折腾了整整一个下午。
好了,这一章的内容就到这里。ADC采样、通信协议、寄存器配置,这三块是传感器数据采集的基石。下一章我们会聊传感器数据预处理,包括滤波、校准、单位转换这些实战内容。到时候我会拿几个真实项目里的案例来讲,保证干货满满。