【嵌入式外设精学】Day12|I2C:上拉、起停条件、读写寄存器
今天解决:两根线怎么收发数据?读传感器寄存器的标准步骤?
目录
1. 问题
- 为什么 I2C 只有 SDA、SCL?
- 读寄存器为什么好像要写一次再读一次?
2. 原理
2.1 硬件三要素
| 开漏 | 只拉低,高靠上拉 |
| 上拉电阻 | 接在 SDA、SCL 上 |
| 共地 | 所有设备地相连 |
2.2 起始和停止
| START | SCL 为高时,SDA 下降 |
| STOP | SCL 为高时,SDA 上升 |
| 数据变化 | 只能在 SCL 低 时改 SDA |
2.3 读写寄存器(标准两步)
写: START → 器件地址(写) → 寄存器地址 → 数据… → STOP
读: START → 器件地址(写) → 寄存器地址 → 重复 START → 器件地址(读) → 数据… → NACK → STOP
“先写寄存器号再读”是行业惯例;重复 START 表示还没结束,总线仍归自己。
3. 代码示例
#include "stm32f4xx.h"
#include <stdint.h>
/* 用硬件 I2C1 读一个寄存器(HAL/标准库风格示意) */
int i2c_read_reg(uint8_t dev7, uint8_t reg, uint8_t *val)
{
I2C_GenerateSTART(I2C1, ENABLE);
/* 等 EV5 */
I2C_Send7bitAddress(I2C1, (uint16_t)(dev7 << 1), I2C_Direction_Transmitter);
/* 等地址应答 */
I2C_SendData(I2C1, reg);
/* 等发完 */
I2C_GenerateSTART(I2C1, ENABLE); /* 重复起始 */
I2C_Send7bitAddress(I2C1, (uint16_t)(dev7 << 1), I2C_Direction_Receiver);
/* 最后一字节读完发 NACK,再 STOP */
I2C_GenerateSTOP(I2C1, ENABLE);
*val = I2C_ReceiveData(I2C1);
return 0;
}
(完整应答、超时、错误恢复请按库手册补全;步骤就是上面“原理”那两行。)
4. 常见误区
| 只用内部上拉 | 一般不够,要外部电阻 |
| 数据可以在 SCL 高时随便变 | 会被当成起停 |
| 读寄存器只发读地址 | 要先写寄存器号 |
| 无超时死等 | 从机挂死会卡程序 |
5. 自测
6. 答案
作者:shuifanyu 标签:I2C 起始停止 寄存器读写



