
简介面向嵌入式开发者的一份ADT75数字温度传感器驱动源代码解决Linux平台下与ADI公司ADT75通信所需的初始化、温度读取、数据转换和错误处理等问题适用于工业自动化、环境监测、电子设备冷却系统等对温度控制有要求的场景。资源包为RAR压缩格式整体约3KB仅包含1个C语言源文件adt75.c代码非常精简便于快速分析、移植也可作为驱动开发的参考模板。该驱动覆盖I²C/SPI接口的读写时序、传感器工作模式配置、寄存器操作、二进制温度数据到可读摄氏度值的转换逻辑以及通信异常检测和必要的校准手段基本囊括了接入该传感器的主要环节。已有87人浏览学习适合需要为嵌入式项目集成温度监控功能的软硬件开发者可在理解源码后根据实际平台调整总线接口与参数快速完成传感器模块的驱动适配和功能验证。1. 一块 ADT75 驱动源码比对着手册自己造轮子快得多做嵌入式硬件最烦的一件事板上要测温器件选了 ADI 的 ADT75连上 I²C 总线之后却发现没有现成驱动。这时候拿到一份 adt75.rar里面是 adt75.c 和配套头文件等于把初始化到读温度的核心步骤都写好了。这份资源本质是 ADT75 数字温度传感器的 C 语言驱动源码覆盖 I²C 通信、温度换算、错误处理这三件事。适合两类人一类是裸机单片机工程师想移植到自己的 I²C 框架另一类是 Linux 下做 sensor 驱动拿它当寄存器级参考。下面我把它拆开讲从协议到移植坑最后给验证方法。2. 先把传感器协议讲透地址、寄存器与数据格式2.1 地址怎么定A0/A1 引脚与 0x48ADT75 是标准的 I²C 从设备7 位地址的高 5 位固定低 2 位由 A1、A0 两个引脚的电平决定。最常见的接法是两个引脚都接地这时候器件地址是 0x487 位地址加读写位之后读操作发 0x91写操作发 0x90。如果你的板子上同时挂了多颗 ADT75就要通过 A0/A1 组合把地址错开最多可以接四颗。我一般拿到一块板子第一件事不是看代码而是用万用表量 A0、A1 引脚的静态电平确认地址再写驱动。这个看起来琐碎却是我踩过最多坑的地方地址写错后面所有读操作都返回 0xFF你还以为传感器坏了。另外 ADT75 的 A0/A1 引脚内部没有上拉悬空的话电平不确定要么外部接上下拉电阻要么直接接电源或地别让它飘着。2.2 三个关键寄存器温度值、配置、阈值ADT75 的数据手册上寄存器不多但真正决定驱动正确性的就三个温度值寄存器地址 0x00、配置寄存器地址 0x01、还有 TOS 和 THYST 两个阈值寄存器地址 0x03 和 0x02。温度值寄存器是 16 位只读里面放的是 12 位温度数据配置寄存器控制关机、比较模式/中断模式、极性、故障排队阈值寄存器在过温报警场景下才有意义普通测温驱动不写它也能跑。这里要特别强调 12 位这个数字。ADT75 的分辨率是 0.0625°C也就是一个 LSB 代表 1/16 度。温度数据在寄存器里主要是左对齐摆放的高字节的 8 位是整数部分的高 8 位低字节的高 4 位是小数部分低 4 位一般是 0。如果你把 16 位原始值直接拿来用数值会比你预期的温度大 16 倍这是新手最容易翻车的地方后面避坑章节我细说。2.3 adt75.c 驱动骨架初始化、读取、错误处理打开 adt75.c 之后你会发现它的结构比想象中简单核心函数不外乎三个模块初始化函数负责校验 I²C 通信、写入配置寄存器、把传感器从关机状态唤醒数据读取函数负责发寄存器地址、读两个字节并执行温度换算错误处理则体现在返回值的设计上比如通信失败返回负数数据异常返回特定错误码。常见 adt75.c 的初始化函数里会有一个 I²C 通信自检的环节比如对一个非只读寄存器先写后读如果写进去的值读出来不一致就认为通信链路有问题。这个做法在裸机环境里特别实用。拿到源码后我建议先画一张数据流图从存储读寄存器地址、写器件地址、时钟信号到接收的数据字节、符号位判断你会发现驱动真正的逻辑就几十行剩下的时间都花在调试总线时序上。3. 把 adt75.c 跑起来裸机移植与 Linux 驱动两条路线3.1 裸机 I²C 移植寄存器封装与温度换算如果你用的是 STM32、NXP 这类单片机直接把 adt75.c 里的 I²C 收发函数替换成你平台上的 HAL 接口就可以。关键是理清楚四个操作的封装写一个字节、读一个字节、写两个字节、读两个字节。ADT75 的读温度流程是先发送 0x00 寄存器地址然后主机产生重复起始条件接着读两个字节最后发送 NACK 并停止。下面这段是我移植时常用的简化代码假设底层 i2c_write 和 i2c_read 已经封装好分别传器件地址、数据缓冲区、长度#define ADT75_ADDR_DEFAULT 0x48 #define ADT75_REG_TEMP 0x00 #define ADT75_REG_CONFIG 0x01 int adt75_init(uint8_t dev_addr) { uint8_t cfg 0x00; /* 上电默认连续转换、比较模式 */ int ret; /* 写配置寄存器0x00 表示使用默认的连续转换模式 */ ret i2c_write(dev_addr, ADT75_REG_CONFIG, cfg, 1); if (ret 0) return -1; return 0; } int adt75_read_temp_raw(uint8_t dev_addr, uint16_t *raw) { uint8_t reg ADT75_REG_TEMP; uint8_t buf[2] {0}; /* 先写寄存器地址再用重复起始条件读两个字节 */ if (i2c_write(dev_addr, reg, 1, 0) 0) return -1; if (i2c_read(dev_addr, buf, 2, 1) 0) return -1; /* 12 位数据左对齐右移 4 位得到真正的温度原始值 */ *raw ((uint16_t)buf[0] 8 | buf[1]) 4; return 0; }逻辑说明i2c_write 的第一个参数是 7 位器件地址第二个参数是寄存器地址指针第三个是数据指针第四个是长度最后一个参数表示是否发送停止条件。读温度时先写寄存器地址这一步通常不发送停止条件紧跟着发重复起始条件再连续读两个字节读最后一个字节之前主机发 NACK 通知从机不再接收数据。参数说明dev_addr 是 7 位地址不带读写位也就是 0x48 而不是 0x90/0x91。raw 经过右移 4 位之后是 12 位无符号值范围 0 到 4095。后面换算成摄氏度的公式单独讲。要注意这段代码假设了你的平台在写一个字节时会自动处理 ACK/NACK如果平台接口行为不一样需要在适配层补齐。3.2 温度换算公式从 12 位原始码到摄氏度原始码拿到之后下一步是换算。ADT75 的 12 位数据是二进制补码格式bit11 是符号位。正温度时原始码直接乘以 0.0625 就是摄氏度负温度时要先减掉 4096 再做乘法。0x000 代表 0°C0x010 代表 1°C0xFF0 代表 -1°C也就是原始码 4080 减去 4096 等于 -16再乘 0.0625 得到 -1°C。float adt75_raw_to_celsius(uint16_t raw12) { int16_t signed_val; /* bit11 为 1 表示负温度补码转有符号数 */ if (raw12 0x0800) signed_val (int16_t)raw12 - 4096; else signed_val (int16_t)raw12; return signed_val * 0.0625f; }逻辑说明0x0800 是 12 位数据里的符号位掩码4096 是 2 的 12 次方。把原始码判一下符号位再乘以 LSB 值就能得到带符号的浮点温度单位摄氏度。正温度下最大能表示到 127.9375°C负温度下能表示到 -55°C正好和手册标称的范围对得上。参数说明如果你把这段函数用在 Linux 驱动里通常不会直接返回 float而是返回毫摄氏度整数避免浮点运算。做法是 signed_val 乘 1000 再除 16等于 signed_val 乘 62.5。我建议保留 float 版本用于调试线上版本改整数。另外 0.0625 这个系数不是猜的它是 12 位 ADC 下 1/16 度分辨率对应的 LSB 值改分辨率的话系数要跟着改。3.3 Linux 下挂到 i2c 总线简化版客户端驱动写法Linux 下如果你不想写完整的 hwmon 驱动可以用 i2c-dev 接口从用户空间直接操作适合原型验证。做法是打开 /dev/i2c-1用 ioctl 设置从设备地址然后通过 SMBUS 读字节命令读取温度寄存器。先把功能性验证跑通再考虑写成内核驱动。#include linux/i2c-dev.h #include sys/ioctl.h #include fcntl.h int fd open(/dev/i2c-1, O_RDWR); ioctl(fd, I2C_SLAVE, 0x48); /* 设置从设备地址 */ uint8_t reg 0x00; uint8_t buf[2] {0}; write(fd, reg, 1); /* 写寄存器指针 */ read(fd, buf, 2); /* 连续读两个字节 */ close(fd);逻辑说明这段代码走的是 i2c-dev 用户空间通道write 发送寄存器地址read 执行重复起始条件并读回两个字节。它最大的价值是不需要编译内核模块拿到板上就能跑适合先确认硬件链路有没有问题。参数说明/dev/i2c-1 对应总线的编号你需要根据板子上的实际总线号修改。I2C_SLAVE 的地址同样是 7 位地址 0x48不带读写位。刚才 adt75.c 里的温度换算函数可以直接套到这个场景先读原始码再换算。注意用户空间 i2c-dev 在部分内核版本上对重复起始条件的处理有差异如果 read 返回的数据不对先检查是不是被中间 stop 条件打断了。4. 避坑ADT75 驱动移植最常见的五个坑与排查4.1 读回来的温度全是 0xFF现象用 i2cdetect 能扫到设备或者地址是确定的但读温度值寄存器返回两个字节全是 0xFF。原因SDA/SCL 上拉电阻缺失或上拉太弱线电平被拉不上去也可能是传感器供电没起来VCC 对地电压不足。0xFF 这个特征意味着总线上所有数据位都被外部拉高典型是上拉或者供电问题。解决先用示波器或万用表量 SDA、SCL 的静态电平正常空闲状态应该是高电平接近 VCC。再用 i2cdetect 扫描确认总线号对不对。还没解决就把 ADT75 焊盘重新补焊一遍我曾经遇到过虚焊导致地址引脚接触不良的案例现象一模一样。4.2 负温度显示成 4095 或乱跳现象环境温度是 -5°C但驱动报出来 4095 或者一个很大的正数温度曲线在过零点附近跳动。原因把 12 位补码数据当成无符号整数直接乘 0.0625。负温度在补码表示下高位是 1无符号解读会得到一个接近 4096 的数再乘 0.0625 就成了 255°C。解决严格按 3.2 的公式先判断 bit11 再减 4096。这里有一个血泪经验不要只测常温就交付一定要把传感器丢进冰水混合物里验证一次负温路径否则负温代码可能一直没被执行过。4.3 温度读数比实际大 16 倍现象室温 25°C驱动显示 400°C比例刚好是 16 倍。原因ADT75 的 12 位温度数据是左对齐存放在 16 位寄存器里的也就是低字节的低 4 位是无效的 0。如果你把两个字节拼起来当 16 位整数直接用等于把 12 位数据人为放大了 16 倍。解决拼完 16 位之后先右移 4 位再走换算公式。验证方法也简单读出原始 16 位值看最低 4 位是不是一直是 0是的话就说明对齐方式吻合。这个坑我在第一次移植 adt75.c 时踩过后来我养成了习惯任何温度传感器驱动先打印原始寄存器值看一眼数据分布再写换算逻辑。4.4 配置寄存器写完不生效现象初始化里写了配置寄存器回头再读还是默认值 0x00或者写入关机位之后传感器没有停止转换。原因配置寄存器的写入被 NACK 了但没有检查返回值。有些 I²C 平台接口丢 ACK 错误连续写多个字节时如果时序不满足从机会拒收后续数据。另一个常见原因是初始化函数在写之前没有确认总线空闲总线被别的设备占用时数据根本没发出去。解决每次写配置寄存器后都读回来比对写 0x00 读回 0x00写 0x80 读回 0x80逐位校验。如果发现问题在写操作前后加小的延时并检查有没有把停止条件发对。这不是玄学多数是总线速率和从机反应时间不匹配。4.5 过温中断乱触发现象配置了 TOS 和 THYST 之后OS 引脚在温度没有异常时频繁拉低或者已经在阈值之上却不触发。原因TOS 是上限阈值THYST 是迟滞下限。正确的触发逻辑是温度超过 TOS 触发回落到 THYST 以下才解除。如果你只配了 TOS 没配 THYSTTHYST 默认值可能是 0°C温度刚到几十度附近就会造成状态翻转混乱。解决把 THYST 和 TOS 的配置顺序固定下来先写 THYST 再写 TOS并且明确你在用的是比较模式还是中断模式。比较模式下 OS 引脚电平持续反映过温状态中断模式下需要读寄存器清除。具体哪个 bit 对应什么模式以数据手册的配置寄存器位定义为准不同 ADI 温度传感器的布局略有差异。5. 进阶校准、滤波与验证让驱动读数真正可信5.1 多次采样取中位数工业现场的温度信号噪声不大但 I²C 读数偶发毛刺是正常的。我更推荐对连续 5 次采样取中位数而不是平均值。中位数可以完全丢掉一个跳变的坏点平均值却会被它拉偏。对 12 位数据来说这几乎不消耗 CPU。uint16_t adt75_read_median(uint8_t dev_addr, uint8_t samples) { uint16_t buf[samples]; for (uint8_t i 0; i samples; i) { adt75_read_temp_raw(dev_addr, buf[i]); } /* 简单插入排序后取中间值 */ for (uint8_t i 1; i samples; i) { uint16_t key buf[i]; int8_t j i - 1; while (j 0 buf[j] key) { buf[j 1] buf[j]; j--; } buf[j 1] key; } return buf[samples / 2]; }逻辑说明samples 次读原始码排序后取中间值屏蔽单点毛刺。注意这里的排序对象如果是未经符号展开的 12 位原始码温度高低关系和原始码大小在正温区是一致的负温区会有偏差所以排序应该在符号展开后的有符号数值上进行。参数说明samples 建议取 5 或 7取太少滤波效果差取太多采样周期变长。排序前先把原始码转成有符号 12 位值再排排序代码里比较的是有符号数最后再乘 0.0625。这个函数返回的仍是原始码需要在调用处再走一次换算。5.2 校准与验证流程驱动能跑只是第一步读数准不准要靠校准来保证。我习惯的做法是准备冰水混合物做 0°C 参考、恒温设备或数字温度计做室温参考把 ADT75 和参考源放在同一环境里稳定 10 分钟分别记录读数算出偏差表。如果偏差是固定偏移直接在换算结果里加上修正值即可。验证时用三个温度点做最小二乘拟合检查线性度是否符合预期。ADT75 在 -55°C 到 125°C 范围内线性度本身不错如果拟合出来有明显弯曲不要怀疑传感器先怀疑驱动里寄存器读错了或者供电电压异常。从那以后我每次移植完温度传感器驱动都会把 0°C、室温、最高工作温度三个点完整测一遍再交付这个习惯帮我挡掉了至少三次现场返工希望帮到你。本文还有配套的精品资源点击获取