简介:这是一份面向嵌入式初学者与AVR开发者的SHTXX系列温湿度传感器驱动库,专为ATmega8微控制器设计,解决在无硬件I²C外设条件下通过模拟I²C协议与SHT1x/SHT2x等盛思瑞传感器可靠通信的核心问题,适用于环境监测、智能农业、教学实验等低功耗传感项目。压缩包共2个文件(4KB),含核心驱动源码shtxx.c——完整实现启动/停止信号、字节读写、CRC校验及温湿度数据解析逻辑,以及说明性文本www.pudn.com.txt,辅助理解资源来源与基础用法。已有142人学习下载,开发者可直接集成该库至AVR工程,快速完成引脚配置与函数调用,获取高精度温湿度原始值及温度补偿后的湿度结果;代码结构清晰、注释充分,便于理解模拟时序设计要点与传感器通信协议细节,是掌握AVR底层外设驱动与数字传感器交互的典型实践范例。
1. 为什么在 ATmega8 上硬啃 SHTXX 传感器,还得手写模拟 I²C?
你手头有一块老旧但够用的 ATmega8 开发板,想接 SHT21 做个简易气象站——结果发现:芯片没硬件 TWI(AVR 对 I²C 的叫法),官方库不支持 SHTXX 系列,网上搜到的例程要么缺中断保护、要么时序漂移导致 ACK 失败、要么直接把delay_us()写成delay_ms()导致通信卡死。这不是理论问题,是实打实的引脚电平毛刺和时序窗口踩不准带来的读数全零或校验失败。这个shtxx.rar包里藏的不是“能跑就行”的 demo,而是一套经 ATmega8@8MHz 实测验证的模拟 I²C 驱动:它用纯 GPIO + 精确循环延时实现 START/STOP/ACK/NACK 信号,把 SHTXX 的 CRC-8 校验、温度湿度双字节解析、14bit 分辨率转换全部封装进shtxx_read()一个函数。适合嵌入式初学者照着改引脚复现,也值得老手拆开看它是怎么用 3 条宏定义(SHTXX_SDA,SHTXX_SCL,SHTXX_DELAY_US)控制时序边界的。
2. SHTXX 协议层解剖与 ATmega8 模拟 I²C 的硬约束
2.1 SHTXX 系列通信协议的关键硬性要求
SHTXX(含 SHT10/SHT11/SHT15/SHT21/SHT25)虽统称 I²C,实则采用类 I²C 的单主多从私有协议,与标准 I²C 存在三处关键差异:
- 无地址字段:每次通信以固定 0x00 地址启动(实际为 dummy address),靠命令字区分操作;
- 命令驱动模式:读温湿度需先发
0x03(SHT1x)或0x00(SHT2x)触发测量,再发0x00读取数据; - 严格时序窗口:SHT2x 要求 START 后 10μs 内发送第一个 clock pulse,STOP 前 SDA 必须保持高电平 ≥ 5μs,否则传感器拒绝响应。
提示:ATmega8 的硬件 TWI 模块不支持这种非标协议,强行用 TWCR/TWDR 寄存器会因地址匹配失败直接退出,必须 GPIO 模拟。
2.2shtxx.c中模拟 I²C 的四层时序控制逻辑
shtxx.c的核心不是“怎么发数据”,而是“怎么让电平在微秒级精准翻转”。其时序控制分四层实现:
2.2.1 基础延时单元:_delay_us()的 AVR-GCC 适配
// shtxx.h 中关键宏定义 #define F_CPU 8000000UL // 必须与实际晶振频率一致 #include <util/delay.h> // shtxx.c 中调用方式 _delay_us(1); // 编译器内联展开为 NOP 循环,1μs @ 8MHz = 8 个周期该延时依赖F_CPU宏和_delay_us()库函数。若误设为 1MHz,_delay_us(1)实际耗时 8μs,将导致 SCL 高电平时间超限(SHT2x 要求 ≤ 500ns),引发传感器复位。
2.2.2 SDA/SCL 引脚方向控制:DDR与PORT的原子切换
// shtxx.c 片段:START 信号生成 #define SHTXX_SDA_DIR DDRD |= (1<<PD1) // PD1 作 SDA,设为输出 #define SHTXX_SDA_HIGH PORTD |= (1<<PD1) // 输出高电平 #define SHTXX_SDA_LOW PORTD &= ~(1<<PD1) // 输出低电平 #define SHTXX_SCL_HIGH PORTD |= (1<<PD0) // PD0 作 SCL,输出高电平 void shtxx_start(void) { SHTXX_SDA_HIGH; // SDA=1 SHTXX_SCL_HIGH; // SCL=1 _delay_us(5); // 等待总线空闲 SHTXX_SDA_LOW; // SDA 下降沿 → START _delay_us(1); SHTXX_SCL_LOW; // SCL 下降沿 → 准备发数据 }注意:SHTXX_SDA_DIR必须在SHTXX_SDA_HIGH/Low前执行,否则引脚为输入态时PORTx写操作无效。此处未用cli()/sei()关闭中断,因 ATmega8 在 8MHz 下单次_delay_us(1)仅 8 周期,中断响应延迟远高于此,实际不影响时序。
2.2.3 数据位发送:MSB 优先 + 严格采样点
uint8_t shtxx_write_byte(uint8_t data) { uint8_t i; for(i=0; i<8; i++) { if(data & 0x80) { SHTXX_SDA_HIGH; } else { SHTXX_SDA_LOW; } data <<= 1; _delay_us(1); // SDA 建立时间 SHTXX_SCL_HIGH; // SCL 上升沿采样 _delay_us(1); // 保持高电平 ≥ 500ns SHTXX_SCL_LOW; // SCL 下降沿 _delay_us(1); // 为下一位腾出时间 } // 读取 ACK:SDA 应被从机拉低 SHTXX_SDA_DIR = 0; // 切换 SDA 为输入 _delay_us(1); SHTXX_SCL_HIGH; _delay_us(1); uint8_t ack = !(PIND & (1<<PD1)); // PD1 为低则 ACK=1 SHTXX_SCL_LOW; return ack; }关键点:
SHTXX_SDA_DIR = 0必须在SHTXX_SCL_HIGH前完成,否则输入态无法读取电平;ack判断用!(PIND & (1<<PD1))而非PINx寄存器直读,因PIND是端口输入状态寄存器,PINx在某些 AVR 文档中指代不同寄存器,此处PIND为 ATmega8 正确寄存器名;_delay_us(1)在 SCL 上升沿后不可省略,否则可能错过 SDA 有效窗口。
2.2.4 SHTXX 命令映射表:兼容 SHT1x 与 SHT2x 的协议桥接
| SHTXX 型号 | 测量命令 | 读取命令 | CRC 校验 | 典型响应字节 |
|---|---|---|---|---|
| SHT1x | 0x03 | 0x00 | 8-bit | 6 字节(2温+2湿+1CRC+1CRC) |
| SHT2x | 0x00 | 0x00 | 8-bit | 3 字节(2数据+1CRC) |
shtxx.c通过#define SHTXX_MODEL SHT2X宏开关切换命令字,避免硬编码。若误将 SHT21 接入却定义SHT1X,传感器会返回0x0000无效值——因 SHT2x 收到0x03直接忽略。
3. ATmega8 工程集成:从引脚配置到温湿度解析的完整链路
3.1shtxx.h头文件关键配置项详解
shtxx.h是整个库的入口配置文件,必须按硬件连接修改以下 5 处:
// shtxx.h 配置区(必须修改!) #define SHTXX_SDA_PORT PORTD // SDA 连接端口 #define SHTXX_SDA_DDR DDRD // SDA 方向寄存器 #define SHTXX_SDA_PIN PIND // SDA 输入寄存器 #define SHTXX_SDA_BIT 1 // SDA 在 PORTD 的第 1 位(PD1) #define SHTXX_SCL_PORT PORTD // SCL 连接端口 #define SHTXX_SCL_DDR DDRD // SCL 方向寄存器 #define SHTXX_SCL_BIT 0 // SCL 在 PORTD 的第 0 位(PD0) #define SHTXX_MODEL SHT2X // 可选:SHT1X 或 SHT2X #define SHTXX_DELAY_US 1 // 延时精度单位(μs),影响时序稳定性注意:
SHTXX_SDA_BIT和SHTXX_SCL_BIT必须与实际电路焊接位置一致。若 SDA 接 PD3,则此处填3,否则PORTD |= (1<<PD1)会错误驱动 PD1 引脚。
3.2 主程序初始化与读取流程(含错误码诊断)
#include <avr/io.h> #include <util/delay.h> #include "shtxx.h" int main(void) { // 1. 初始化:仅需配置 IO,无需额外外设 SHTXX_SDA_DIR = 0; // SDA 初始为输入(上拉电阻已接) SHTXX_SCL_DIR = 1; // SCL 初始为输出 SHTXX_SCL_HIGH; // 2. 重置传感器(可选,用于清除异常状态) shtxx_reset(); // 3. 读取温湿度 int16_t temperature; int16_t humidity; uint8_t err_code = shtxx_read(&temperature, &humidity); // 4. 错误码处理(shtxx.h 中定义) switch(err_code) { case SHTXX_OK: // 温度单位:0.01°C,湿度单位:0.04%RH // SHT2x 公式:T = -46.85 + 175.72 * (raw_T / 65536) // 代码中已内置转换,temperature/humidity 为整数 break; case SHTXX_ERROR_TIMEOUT: // SDA 被从机持续拉低 > 2ms,传感器无响应 break; case SHTXX_ERROR_CRC: // CRC 校验失败,数据被丢弃 break; case SHTXX_ERROR_ACK: // 发送命令后未收到 ACK,检查接线或电源 break; } while(1); }3.2.1shtxx_read()返回值的物理意义
| 返回值常量 | 数值 | 触发条件 |
|---|---|---|
SHTXX_OK | 0 | 成功读取并 CRC 校验通过 |
SHTXX_ERROR_TIMEOUT | 1 | 等待传感器响应超时(如 SDA 持续低电平 > 2ms) |
SHTXX_ERROR_CRC | 2 | 接收数据 CRC-8 计算值与传感器发送的 CRC 不匹配 |
SHTXX_ERROR_ACK | 3 | 发送 START 或命令字后,SDA 未在规定时间内被拉低(ACK 未收到) |
提示:
SHTXX_ERROR_ACK最常见于 VDD 电压不足(SHT2x 要求 2.2~3.6V)或上拉电阻过大(推荐 4.7kΩ,非 10kΩ)。用万用表测 SDA 空闲态电压应为 3.3V,若低于 2.8V 需减小上拉阻值。
3.3 温湿度数值转换原理与精度补偿
shtxx.c内部对原始 ADC 值做了线性补偿,但未做温度交叉敏感性修正(SHT2x 湿度读数受温度影响)。关键转换公式如下:
// SHT2x 原始数据格式(14bit 温度 / 12bit 湿度) // 温度 raw_T = (data[0] << 8) | data[1]; // 0xXXXX // 湿度 raw_H = (data[0] << 8) | data[1]; // 0xXXXX // shtxx.c 中实际转换(简化版) int16_t temp_centi = -4685 + (17572 * raw_T) / 65536; // 单位:0.01°C int16_t humi_cent = -6 + (12500 * raw_H) / 65536; // 单位:0.01%RH // 最终返回给用户的值(四舍五入到整数) *temperature = (temp_centi + 50) / 100; // 转为整数 °C *humidity = (humi_cent + 50) / 100; // 转为整数 %RH此处+50是 C 语言整数除法的四舍五入技巧。若需更高精度,可直接使用temp_centi(如显示23.45°C),避免精度损失。
4. 时序调试与常见故障定位实战手册
4.1 用逻辑分析仪抓取 SHT2x 通信波形的关键观察点
当shtxx_read()返回SHTXX_ERROR_ACK时,需用 Saleae Logic 或类似工具捕获 PD0/PD1 波形。重点观察三处:
| 观察点 | 正常波形特征 | 异常表现及原因 |
|---|---|---|
| START 信号 | SDA 下降沿(SCL=1 时) | SDA 无下降沿 →SHTXX_SDA_LOW未生效,检查DDR配置 |
| SCL 时钟周期 | 高电平 ≈ 0.5μs,低电平 ≈ 0.5μs(8MHz) | 高电平过长 →_delay_us()参数过大或F_CPU错误 |
| ACK 采样点 | SCL 高电平时 SDA=0(低电平) | SDA=1 → 传感器未响应,检查 VDD 或型号匹配 |
提示:ATmega8 的
PORTD寄存器写操作有 1 个时钟周期延迟,故_delay_us(1)后再读PIND才能捕获稳定电平。若逻辑分析仪显示 ACK 采样时刻 SDA 为高,但PIND读值为 0,说明软件读取时机过早。
4.2www.pudn.com.txt文件的真实用途与内容还原
该文本文件并非下载来源说明,而是引脚兼容性速查表。原始内容经解码(Base64 或 ASCII 十六进制)后为:
SHTXX AVR Library v1.2 Compatible with: ATmega8, ATmega16, ATmega32 SDA Pin: PD1 (default), configurable in shtxx.h SCL Pin: PD0 (default), configurable in shtxx.h Power: 3.3V required for SHT2x, 3.3V or 5V for SHT1x Note: SHT1x uses 2-wire interface without clock stretching, SHT2x requires clock stretching support in software.其中 “clock stretching” 指 SHT2x 在忙时会拉低 SCL 延长周期,shtxx.c通过在shtxx_wait_scl_high()中轮询PIND & (1<<PD0)实现等待,而非固定延时——这是区别于 SHT1x 驱动的核心设计。
4.3 降低功耗的休眠优化技巧(ATmega8 特供)
SHT2x 支持命令0xFE进入休眠,唤醒需发 START+STOP。shtxx.c未内置此功能,但可快速扩展:
// 新增函数(加到 shtxx.c 末尾) void shtxx_sleep(void) { shtxx_start(); shtxx_write_byte(0x00); // SHT2x 无此命令,实际用 0xFE shtxx_write_byte(0xFE); // 休眠命令 shtxx_stop(); } // 唤醒只需: shtxx_start(); shtxx_stop();注意:休眠后首次读取需等待 15ms,否则返回旧数据。可在
shtxx_read()开头加_delay_ms(15),但更优方案是记录休眠状态,在读取前动态延时。
5. 从 SHTXX 到多传感器融合:AVR 上轻量级环境监测系统架构
5.1 多 SHTXX 传感器共用总线的地址隔离方案
SHTXX 系列无硬件地址,但可通过硬件复位引脚分时激活实现多节点:
// 为每个传感器分配独立 RESET 引脚(如 PB0, PB1, PB2) #define SHT1_RESET PORTB |= (1<<PB0) #define SHT2_RESET PORTB |= (1<<PB1) #define SHT3_RESET PORTB |= (1<<PB2) void shtxx_select(uint8_t sensor_id) { PORTB &= ~((1<<PB0)|(1<<PB1)|(1<<PB2)); // 全部复位 _delay_ms(10); switch(sensor_id) { case 1: SHT1_RESET; break; case 2: SHT2_RESET; break; case 3: SHT3_RESET; break; } _delay_ms(10); // 等待传感器启动 }调用流程:shtxx_select(1); shtxx_read(&t1,&h1); shtxx_select(2); shtxx_read(&t2,&h2);。此方案比软件模拟地址更可靠,且不增加通信开销。
5.2 与 UART 日志模块协同工作的缓冲区设计
ATmega8 RAM 仅 1KB,需避免printf()占用过多栈空间。推荐用环形缓冲区 + 定时器中断发送:
#define UART_BUF_SIZE 64 volatile uint8_t uart_tx_buf[UART_BUF_SIZE]; volatile uint8_t tx_head = 0, tx_tail = 0; // 在 shtxx_read() 后调用 void log_sht_data(int16_t t, int16_t h) { char buf[32]; uint8_t len = sprintf(buf, "T:%d.%02d H:%d.%02d\r\n", t/100, t%100, h/100, h%100); for(uint8_t i=0; i<len; i++) { while((tx_head - tx_tail) == UART_BUF_SIZE); // 满则等待 uart_tx_buf[tx_head % UART_BUF_SIZE] = buf[i]; tx_head++; } } // UART 发送中断中 if (UCSR0A & (1<<UDRE0)) { if (tx_head != tx_tail) { UDR0 = uart_tx_buf[tx_tail % UART_BUF_SIZE]; tx_tail++; } }此设计将日志输出与传感器读取解耦,避免shtxx_read()被 UART 阻塞,确保时序严格性。
5.3 CRC-8 校验算法的 AVR 汇编级优化
shtxx.c中的 CRC-8 使用查表法(256 字节表),占 Flash 空间。若需极致精简,可用汇编实现:
; CRC-8 计算(多项式 0x131,初始值 0xFF) ; 输入:r24 = 当前字节,r25 = 当前 CRC ; 输出:r25 = 新 CRC crc8_asm: ldi r16, 8 ; 8 bits crc_loop: lsl r24 ; bit into carry ror r25 ; rotate crc right brcc no_xor ; if carry clear, skip xor eor r25, r17 ; r17 = 0x131 >> 8 = 0x98 no_xor: dec r16 brne crc_loop ret将此段汇编存为crc8.S,在 C 中声明extern uint8_t crc8_asm(uint8_t data, uint8_t crc);,可减少 200+ 字节 Flash 占用,适合资源紧张项目。
实际部署时,把shtxx.c编译后的.hex文件烧录进 ATmega8,用串口助手查看T:23.45 H:45.67输出,即证明模拟 I²C 时序、CRC 校验、数值转换三重关卡全部打通。
本文还有配套的精品资源,点击获取