# 从脉冲到数据:51单片机IIC时序模拟与GXHT3L连续模式驱动实现
IIC协议是嵌入式开发中连接传感器、存储芯片的核心串行协议,然而51单片机多数型号不集成硬件IIC控制器。软件模拟时序成为开发者必须跨越的门槛。本文以温湿度传感器GXHT3L为载体,从起始信号、应答机制、字节传输的底层脉冲出发,完整呈现IIC时序的软件模拟方法,并解析GXHT3L连续转换模式下的数据读取与CRC校验。
---
## 一、IIC总线时序的软件模拟
IIC总线由SCL(时钟线)与SDA(数据线)构成,**所有时序信号均通过两根I/O口的电平变化组合产生**。
**起始信号**:SCL高电平期间,SDA由高电平切换至低电平。
```c
sbit sda = P0^1;
sbit scl = P0^2;
void i2c_start() {
scl = 1;
sda = 1;
delay10us(); // 建立时间 >4.7μs
sda = 0; // 下降沿,产生起始条件
delay10us(); // 保持时间 >4μs
scl = 0; // 拉低SCL,准备传输数据
}
```
**停止信号**:SCL高电平期间,SDA由低电平切换至高电平。
```c
void i2c_stop() {
sda = 0;
scl = 1;
delay10us(); // 停止信号建立时间
sda = 1; // 上升沿,产生停止条件
delay10us(); // 总线空闲时间
}
```
**字节写入**:高位先行,每1位数据在SCL低电平时设置SDA,SCL高电平时被从机读取。
```c
void i2c_write_byte(unsigned char dat) {
unsigned char i;
for(i = 0; i < 8; i++) {
scl = 0;
if(dat & 0x80) sda = 1;
else sda = 0;
dat <<= 1;
scl = 1; // 从机在SCL高电平期间读取SDA
delay10us();
}
scl = 0;
}
```
**应答检测**:第9个时钟周期,主机释放SDA(置1),检测从机是否将SDA拉低。
```c
unsigned char i2c_wait_ack() {
unsigned char ack;
sda = 1; // 释放总线
scl = 1;
delay10us();
ack = sda; // 0表示应答,1表示非应答
scl = 0;
return ack;
}
```
<"b8.a8k1.org.cn"><"x0.a8k1.org.cn"><"r4.a8k1.org.cn">
**字节读取**:主机释放SDA,每个SCL高电平周期读取一位。
```c
unsigned char i2c_read_byte() {
unsigned char i, val = 0;
sda = 1; // 释放总线
for(i = 0; i < 8; i++) {
val <<= 1;
scl = 1;
if(sda) val |= 0x01;
scl = 0;
}
return val;
}
```
> 延时时长直接影响IIC通信速率。10μs延时对应约50kHz速率,若需400kHz快速模式,延时需压缩至1.25μs以内。**时钟频率应严格匹配从机规格**,GXHT3L最高支持1MHz。
---
## 二、GXHT3L传感器驱动实现
GXHT3L是中科银河芯开发的单芯片温湿度传感器,集成16位ADC,**I2C地址由ADDR引脚电平决定**:接低电平时地址0x44,接高电平时地址0x45。
### 2.1 连续转换模式启动
连续转换模式下,传感器以2.5ms周期自主测量,主机可直接读取最新数据。启动命令为`0x2C10`:
```c
void GXHT3L_start_continuous() {
i2c_start();
i2c_write_byte(0x44 << 1 | 0); // 地址+写位
i2c_wait_ack();
i2c_write_byte(0x2C); // 命令高字节
i2c_wait_ack();
i2c_write_byte(0x10); // 命令低字节(高重复性)
i2c_wait_ack();
i2c_stop();
}
```
### 2.2 温湿度数据读取
连续模式下读取数据无需再次发送测量命令,直接发起读事务即可。**数据格式为“温度高8位+温度低8位+CRC+湿度高8位+湿度低8位+CRC”**。
```c
void GXHT3L_read_data(float *temp, float *hum) {
unsigned char buf[6];
unsigned int rawT, rawH;
i2c_start();
i2c_write_byte(0x44 << 1 | 1); // 地址+读位
if(i2c_wait_ack() == 0) { // 从机应答
buf[0] = i2c_read_byte(1); // 温度高8位,发送ACK
buf[1] = i2c_read_byte(1); // 温度低8位
buf[2] = i2c_read_byte(1); // 温度CRC
buf[3] = i2c_read_byte(1); // 湿度高8位
buf[4] = i2c_read_byte(1); // 湿度低8位
buf[5] = i2c_read_byte(0); // 湿度CRC,发送NACK
}
i2c_stop();
// CRC校验(实现略,多项式0x31)
if(!check_crc(buf, 2, buf[2])) return;
if(!check_crc(buf+3, 2, buf[5])) return;
<"k6.a8k1.org.cn"><"p3.a8k1.org.cn"><"m7.a8k1.org.cn">
rawT = (buf[0] << 8) | buf[1];
rawH = (buf[3] << 8) | buf[4];
*temp = (175.0 * (float)rawT / 65535.0) - 45.0; // 温度转换
*hum = 100.0 * (float)rawH / 65535.0; // 湿度转换
}
```
**数据转换公式**源于传感器手册:温度范围-45℃至+130℃,对应数字量0~65535;湿度范围0~100%RH,线性映射。
### 2.3 CRC校验的必要性
GXHT3L为每个16位数据附加8位CRC校验码,多项式为`x^8 + x^5 + x^4 + 1`(0x31)。**在强干扰工业环境中,跳过CRC校验可能导致数十摄氏度的异常跳变**。校验算法可复用SHT3x系列的标准实现。
---
## 三、连续模式与单次模式的选择
| 模式 | 启动方式 | 数据获取延迟 | 功耗 | 适用场景 |
|--------------|------------------------------|--------------|--------|------------------------------|
| 单次转换模式 | 每次测量需发0x2C10命令 | ≥15ms | 较低 | 电池供电、低频采集 |
| 连续转换模式 | 启动后自主测量,直接读数据 | <3ms | 较高 | 实时监控、动态响应 |
**连续模式下,读取频率不应高于传感器内部更新速率(约2.5ms/次)**,否则可能读到同一笔未更新的数据。
---
## 四、常见问题与调试要点
1. **总线死锁**:从机异常时可能持续拉低SDA。模拟时序需设计**总线恢复机制**——发送9个以上SCL时钟迫使从机释放总线。
2. **延时精度**:51单片机`nop()`约1μs(12MHz晶振),可用`_nop_()`函数构建微秒级延时,避免使用`delay10ms()`这类毫秒级函数降低通信速率。
3. **地址左移混淆**:传感器手册地址0x44是**7位器件地址**,IIC通信字节需左移1位,最低位补R/W。初学者易直接发送0x44导致寻址失败。
4. **ACK/NACK时机**:读取最后一字节后**必须发送NACK**,否则从机认为主机仍需更多数据,持续占用总线。
---
软件模拟IIC的实质,是将协议规范转化为**精确的电平序列**。理解起始、停止、应答、字节读写的脉冲关系后,任何IIC从机的驱动都只是命令序列的排列组合。GXHT3L的连续模式读取,正是这一方法论的典型实践——以稳定的底层时序为基,逐字节组装出完整的数据通路。