I2C — 主机轮询通信(HAL 库)
固件库: PY32F0xx_Firmware V1.5.0
芯片: PY32F030
源文件:PUYA/PY32F0xx_Firmware_V1.5.0/Projects/PY32F030-STK/Example/I2C/I2C_TwoBoard_CommunicationMaster_Polling/Src/main.c
功能简介
I2C(Inter-Integrated Circuit)是一种两线制(SCL 时钟 + SDA 数据)、半双工、一主多从的串行总线,常用于连接 EEPROM、传感器、OLED 屏等。
本示例演示主机(Master)工作方式:
- 主机先向从机地址
0xA0发送 15 字节; - 再向同一地址 接收 15 字节(示例用两块板回环对接,从机把收到的数据原样发回);
- CPU 用轮询方式等待每笔传输完成(
HAL_I2C_Master_Transmit/Receive内部即轮询); - 收发一致则点亮绿灯,不一致则闪烁报警。
虽然工程里函数名带
Master,实际传输由HAL_I2C_Master_Transmit/Receive完成,内部是轮询等待,符合“主机轮询通信”的教学目标。
硬件准备
- 两块 PY32F030 开发板(一主一从),或用一块板把 SDA/SCL 自连做回环
- SCL、SDA 各接 4.7kΩ 上拉电阻到 VDD(I2C 开漏总线必须上拉)
- 交叉连接:主 SCL↔从 SCL,主 SDA↔从 SDA,共地
- 串口/LED 用于观察结果
完整代码(主机部分)
|
|
代码分段讲解
1. 地址说明
I2C_ADDRESS = 0xA0 是含读写位的 8 位地址(即 7 位地址 0x50 左移 1 位)。HAL 的 HAL_I2C_Master_Transmit/Receive 内部会自动根据读/写把最低位置 0/1,调用时直接传 0xA0 即可(底层会有兼容处理)。实际对接外部芯片时,请以其数据手册给出的 7 位地址为准。
2. 速率与占空比
ClockSpeed = 100000:标准模式 100kbps;可改400000进入快速模式。- 快速模式下
DutyCycle控制 SCL 高低电平比(16_9或2)。
3. 轮询方式
HAL_I2C_Master_Transmit(..., Timeout) 内部不断查询状态寄存器直到完成或超时,期间 CPU 被占用,简单但效率低。对性能敏感的场景可改用 IT(中断)或 DMA 版本(同目录有 I2C_TwoBoard_CommunicationMaster_IT / _DMA)。
4. 时钟拉伸
NoStretchMode = DISABLE 表示允许从机在忙时拉低 SCL(时钟拉伸),这是 I2C 规范允许的流控机制,绝大多数从机需要。
实验现象
- 按下用户键后,主机发送
1~15; - 若从机把数据原样返回,主机收到一致数据,绿灯常亮;
- 若数据不一致或总线异常,绿灯以 500ms 周期闪烁报警。
常见问题
-
总线卡死 / 收不到数据?
- SCL、SDA 必须有上拉电阻(4.7kΩ 到 VDD),开漏总线无上拉无法拉高。
- 检查接线:SCL↔SCL、SDA↔SDA、主从共地。
- 确认从机地址与数据手册一致(区分 7 位 / 8 位地址)。
-
HAL_I2C_Master_Transmit返回HAL_BUSY/HAL_TIMEOUT?- 总线可能被某次异常拉低(SDA 卡在低)。可尝试复位或从机重新上电。
- 适当增大
Timeout(如 5000ms)。
-
写 EEPROM 后马上读不到?
- EEPROM 写周期约 5ms,需在写后延时再读。
-
如何只读单字节传感器(如温湿度)?
- 先
HAL_I2C_Master_Transmit发寄存器地址,再HAL_I2C_Master_Receive读数据(即“写寄存器指针 + 读数据”组合)。
- 先
小结
- I2C 主机轮询三步:初始化
HAL_I2C_Init→Master_Transmit发 →Master_Receive收。 - 总线必须上拉,地址要与从机匹配,主从共地。
- 轮询版代码最简单,适合初学;追求效率改用 IT/DMA 版。
拓展应用
- 驱动 OLED(SSD1306)、AT24Cxx EEPROM、各类 I2C 传感器(BMP280、SHT30)。
- 一主多从:用不同从机地址挂多个设备,轮流访问。
- 用
HAL_I2C_Mem_Write/Read直接读写带寄存器的器件(如I2C_MEMADD_SIZE_8BIT)。 - 把轮询升级为中断/DMA,释放 CPU。