2. 信号类型与接口基础:模拟信号 vs 数字信号、常见接口协议(I2C、SPI、UART、ADC)简介
做嵌入式这些年,我见过不少新手在传感器选型上栽跟头。说白了,很多人连传感器输出的是模拟信号还是数字信号都没搞清楚,就急着画原理图了。结果板子打样回来,发现单片机引脚不够用,或者信号根本读不到。嗯,今天我们就来把这块地基打牢。
2.1 模拟信号 vs 数字信号:本质区别
传感器输出的信号,归根结底就两种:模拟量和数字量。
模拟信号,就是连续变化的电压或电流。比如一个电位器,你拧它,输出电压从0V到3.3V平滑变化。温度传感器LM35,温度每升高1℃,输出电压增加10mV。这就是模拟信号——连续、无限、实时。
数字信号,只有两个状态:高电平和低电平,也就是1和0。比如一个按键按下,引脚被拉低到0V;松开,被上拉电阻拉到3.3V。或者一个数字温度传感器,它内部已经把温度值算好了,通过I2C或SPI直接给你一个二进制数。这就是数字信号——离散、有限、抗干扰强。
核心区别一句话:模拟信号是连续的物理量,数字信号是离散的逻辑值。
我在项目中遇到过一个问题:用模拟输出的光照传感器,信号线走了1米长,结果单片机读到的电压值一直在跳。后来发现是线缆引入了工频干扰。换成数字输出的传感器,同样的走线,数据稳如老狗。这就是数字信号抗干扰的优势。
2.2 ADC:连接模拟世界与数字世界的桥梁
单片机是数字器件,它只能处理0和1。那模拟信号怎么读?靠ADC(模数转换器)。
ADC的核心参数有两个:分辨率和采样率。
- 分辨率:比如10位ADC,可以把0~3.3V分成2^10=1024份。每份是3.3V/1024≈3.22mV。分辨率越高,能分辨的电压变化就越小。
- 采样率:每秒能采样多少次。比如1000次/秒,就是1kHz。采样率不够,高频信号就测不准。
举个例子,你用一个10位ADC去读一个温度传感器,温度变化0.5℃对应电压变化5mV。而你的ADC每份是3.22mV,理论上能分辨出这个变化。但如果换成8位ADC,每份是12.9mV,那就可能读不出来了。
我的习惯:选ADC时,分辨率至少要比你需要的精度高一个数量级。比如你需要0.1℃的精度,那ADC的分辨率最好能分辨0.01℃的变化。留点余量,心里踏实。
2.3 常见数字接口协议:I2C、SPI、UART
数字传感器通常通过这三种接口与单片机通信。你想想看,它们就像三种不同的“语言”,各有各的脾气。
2.3.1 I2C:两线制,多设备共享
I2C只有两根线:SCL(时钟线)和SDA(数据线)。所有设备都挂在这两根线上,每个设备有一个唯一的地址。主机(单片机)通过地址来寻址。
- 优点:引脚少,可以挂多个设备(理论上127个)。
- 缺点:速度相对慢(标准模式100kHz,快速模式400kHz,高速模式3.4MHz)。
- 典型应用:温湿度传感器(如SHT30)、加速度计(如MPU6050)、EEPROM存储器。
我曾经在一个项目里挂了5个I2C传感器,结果发现其中一个地址冲突了。查了半天手册,才发现两个传感器用了同一个地址。最后只能换了一个地址可配置的型号。所以,选I2C传感器时,一定要确认地址是否可调。
2.3.2 SPI:四线制,高速传输
SPI需要四根线:SCLK(时钟)、MOSI(主机输出从机输入)、MISO(主机输入从机输出)、CS(片选,每个从机一根)。
- 优点:速度快(几十MHz很常见),全双工(可以同时收发)。
- 缺点:引脚多(每多一个从机就多一根CS线)。
- 典型应用:SD卡、显示屏、高速ADC、陀螺仪。
SPI的时序比I2C简单,但要注意时钟极性和相位。搞错了,数据就读不对。我建议你写驱动前,先拿逻辑分析仪抓一下波形,确认时序对不对。
2.3.3 UART:异步串行,点对点
UART只需要两根线:TX(发送)和RX(接收)。它没有时钟线,靠双方约定好的波特率来同步。
- 优点:简单,只需要两根线,很多单片机都有硬件UART。
- 缺点:只能点对点通信(一个发一个收),速度一般(通常115200bps)。
- 典型应用:GPS模块、蓝牙模块、串口屏。
注意:UART的TX和RX要交叉连接。单片机的TX接模块的RX,单片机的RX接模块的TX。这个我见过有人接反了,折腾了半天没反应。
2.4 如何选择:模拟还是数字?
这个问题没有标准答案,但我可以给你一些参考。
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 信号传输距离远(>1米) | 数字接口(I2C/SPI/UART) | 模拟信号长距离易受干扰 |
| 需要高精度、高分辨率 | 数字传感器(内部集成ADC) | 数字传感器内部ADC通常比单片机的好 |
| 成本敏感、引脚紧张 | 模拟传感器 + 单片机ADC | 模拟传感器通常更便宜,引脚少 |
| 需要快速响应(如电机控制) | 模拟信号(直接反馈) | 数字通信有延迟,模拟信号实时性更好 |
| 多传感器共享总线 | I2C或SPI | 一根总线可以挂多个设备 |
我个人习惯是:能用数字就用数字。调试方便,抗干扰强,代码也容易写。但有些场合,比如需要极低功耗的电池供电设备,模拟传感器加一个外部ADC可能更省电。具体问题具体分析。
2.5 代码示例:读取一个I2C数字温度传感器
这里以SHT30温湿度传感器为例,展示如何通过I2C读取数据。代码基于STM32 HAL库。
#include "stm32f1xx_hal.h"
#include "sht30.h"
// SHT30 I2C地址(0x44,ADDR引脚接GND)
#define SHT30_ADDR 0x44 << 1
// 发送测量命令
HAL_StatusTypeDef SHT30_StartMeasurement(I2C_HandleTypeDef *hi2c) {
uint8_t cmd[2] = {0x2C, 0x06}; // 高精度测量模式
return HAL_I2C_Master_Transmit(hi2c, SHT30_ADDR, cmd, 2, 100);
}
// 读取温湿度数据
HAL_StatusTypeDef SHT30_ReadData(I2C_HandleTypeDef *hi2c, float *temp, float *humi) {
uint8_t buf[6];
if (HAL_I2C_Master_Receive(hi2c, SHT30_ADDR, buf, 6, 100) != HAL_OK) {
return HAL_ERROR;
}
// 计算温度
uint16_t raw_temp = (buf[0] << 8) | buf[1];
*temp = -45.0f + 175.0f * raw_temp / 65535.0f;
// 计算湿度
uint16_t raw_humi = (buf[3] << 8) | buf[4];
*humi = 100.0f * raw_humi / 65535.0f;
return HAL_OK;
}
这段代码很简单:先发命令让传感器开始测量,等一会(通常几毫秒),再读回6个字节的数据。前两个字节是温度,中间两个是湿度,最后两个是CRC校验(这里省略了)。
避坑指南:我曾经在读取SHT30时,发现温度值一直跳。后来用示波器一看,I2C时钟线上有毛刺。加了个10kΩ上拉电阻,问题解决了。I2C总线一定要加上拉电阻,一般4.7kΩ到10kΩ。
2.6 小结
这一章我们聊了模拟信号和数字信号的区别,ADC的作用,以及I2C、SPI、UART三种接口的特点。你想想看,选传感器其实就是选接口。接口选对了,后面画原理图、写驱动都顺风顺水。选错了,那就是给自己挖坑。
下一章,我们会深入讲传感器数据手册怎么看。那些密密麻麻的参数,哪些是忽悠人的,哪些是真正关键的。到时候我会拿几份真实的数据手册,带你一条一条过。
公众号:蓝海资料掘金营,微信deep3321