2、硬件接口基础:I2C与UART通信协议详解
好,咱们进入正题。这一章我打算聊聊溶解氧传感器最常用的两种通信接口——I2C和UART。说实话,搞嵌入式这么多年,我见过太多工程师在接口通信上栽跟头。不是时序没调对,就是波特率配错了,折腾半天数据就是读不出来。其实你只要把底层逻辑搞明白,这些都不是事儿。
2.1 I2C通信协议详解
I2C,全称是Inter-Integrated Circuit。说白了,就是飞利浦(现在叫NXP)搞出来的一种两线制总线。一根时钟线SCL,一根数据线SDA。我最早接触I2C是在做一款光学溶解氧传感器的时候,当时就觉得这协议真省引脚——一个MCU挂七八个传感器都没问题。
2.1.1 时序基础
I2C的时序其实不复杂。你想想看,它本质上就是一个主从通信模型。MCU是主机,传感器是从机。主机负责产生时钟,从机跟着时钟走。
几个关键时序点:
- 起始条件:SCL高电平时,SDA从高变低。我习惯叫它"START信号"。
- 停止条件:SCL高电平时,SDA从低变高。这就是"STOP信号"。
- 数据采样:数据在SCL低电平时变化,高电平时被采样。记住这个,调试时很有用。
- 应答机制:每发送8位数据后,接收方要拉低SDA表示ACK。如果没拉低,那就是NACK,说明出问题了。
重要提示:I2C总线上的上拉电阻很关键。我一般选4.7kΩ,但具体要看总线电容和通信速率。100kHz标准模式用4.7kΩ没问题,400kHz快速模式可能得降到2.2kΩ。
2.1.2 设备地址
每个I2C设备都有一个7位或10位的地址。溶解氧传感器大多用7位地址。比如我常用的某款传感器,地址是0x77(二进制1110111)。
这里有个坑——地址的最后一位是读写位。0表示写,1表示读。所以:
- 写操作地址:0x77 << 1 | 0 = 0xEE
- 读操作地址:0x77 << 1 | 1 = 0xEF
我曾经遇到过一个问题:手册上写设备地址是0x77,我直接拿0x77去读,结果死活没响应。后来才发现,手册给的地址是7位的,需要左移一位再加上读写位。嗯,这里要注意。
2.1.3 读写操作
I2C的读写操作,我总结成三步走:
- 写寄存器:START → 设备地址+写位 → 寄存器地址 → 要写的数据 → STOP
- 读寄存器:START → 设备地址+写位 → 寄存器地址 → 重复起始条件 → 设备地址+读位 → 读取数据 → STOP
- 连续读取:读完一个字节后主机发ACK,从机继续发下一个字节。直到主机发NACK,然后发STOP。
给你看个实际代码片段,这是我调溶解氧传感器时用的:
// I2C读取传感器数据示例
uint8_t i2c_read_register(uint8_t dev_addr, uint8_t reg_addr) {
uint8_t data;
// 第一步:发送寄存器地址
i2c_start();
i2c_write_byte(dev_addr << 1 | 0); // 写操作
i2c_write_byte(reg_addr);
// 第二步:重复起始,切换为读模式
i2c_repeated_start();
i2c_write_byte(dev_addr << 1 | 1); // 读操作
// 第三步:读取数据,发NACK结束
data = i2c_read_byte();
i2c_send_nack();
i2c_stop();
return data;
}
个人经验:调试I2C时,我建议先用逻辑分析仪抓波形。别光靠眼睛看代码,波形图能告诉你一切——起始条件对不对、地址对不对、ACK有没有。我当年调一个传感器调了两天,最后发现是上拉电阻焊错了,哭笑不得。
2.2 UART通信协议详解
UART,通用异步收发器。跟I2C不一样,UART不需要时钟线,它靠的是双方约定好的波特率。说白了,就是两个人约好说话的速度,一个说一个听。
2.2.1 波特率
波特率就是每秒传输的符号数。常见的波特率有9600、19200、38400、115200等。溶解氧传感器大多用9600或115200。
波特率怎么算?拿STM32举例:
// 假设系统时钟72MHz,要配115200波特率
// USART_BRR = 系统时钟 / (16 * 目标波特率)
// USART_BRR = 72000000 / (16 * 115200) ≈ 39.06
// 取整后实际波特率 = 72000000 / (16 * 39) ≈ 115384
// 误差 = (115384 - 115200) / 115200 ≈ 0.16%
误差在2%以内一般没问题。我曾经遇到过用8MHz晶振配115200波特率,误差到了3.5%,结果数据全是乱码。后来换了11.0592MHz晶振才搞定。所以选晶振时,最好选能整除常用波特率的。
2.2.2 数据格式
UART的数据帧一般长这样:
- 起始位:1位,固定为低电平
- 数据位:5~9位,常用8位
- 校验位:可选,奇校验或偶校验
- 停止位:1位或2位,固定为高电平
溶解氧传感器最常用的配置是:9600波特率、8位数据、无校验、1位停止位,简写为9600-8-N-1。
避坑指南:我曾经遇到过一款传感器,手册上写的是9600-8-N-1,但实际通信时发现数据位是7位。后来仔细看手册才发现,它有个配置寄存器可以改数据位长度。所以拿到新传感器,第一件事就是确认出厂默认配置。
2.3 传感器与MCU的典型连接电路
好了,理论讲完了,咱们看看实际电路怎么接。我以一款典型的溶解氧传感器为例,它同时支持I2C和UART两种接口。
2.3.1 I2C连接电路
I2C连接很简单,就四根线:
- VCC → 3.3V(注意传感器供电电压)
- GND → 地
- SCL → MCU的SCL引脚
- SDA → MCU的SDA引脚
别忘了加上拉电阻。我习惯在SCL和SDA上各接一个4.7kΩ电阻到3.3V。如果总线上挂多个设备,上拉电阻要适当减小。
2.3.2 UART连接电路
UART连接需要三根线:
- VCC → 3.3V
- GND → 地
- TX → MCU的RX(交叉连接)
- RX → MCU的TX(交叉连接)
这里有个容易犯的错——TX和RX要交叉。传感器的TX接MCU的RX,传感器的RX接MCU的TX。我见过有人直接TX接TX,RX接RX,结果数据发出去收不到。
2.3.3 电平匹配问题
很多溶解氧传感器是3.3V供电,但有些老款是5V的。如果你的MCU是3.3V,接5V传感器时要注意电平转换。我常用的方案:
- 用MOS管做双向电平转换,适合I2C
- 用电阻分压,适合UART的单向信号
- 直接用电平转换芯片,比如TXB0104
我的建议:如果条件允许,尽量选3.3V的传感器。5V传感器在低功耗场景下很吃亏,而且电平转换电路多多少少会引入噪声。我做过一个项目,就是因为电平转换没做好,溶解氧数据跳来跳去,最后排查了三天才发现是电平转换芯片的使能脚没拉高。
好了,这一章的内容就到这儿。I2C和UART是嵌入式通信的基石,搞懂了它们,后面写驱动就顺风顺水了。下一章咱们聊聊传感器数据手册怎么看,那才是真正考验功力的地方。