资讯动态

Linux下TMP75AIDR温度传感器驱动移植:基于lm75驱动与设备树

发布时间:2026/10/22 21:49:32 来源:尧图企业网站定制
如果你手头正好有一片 TMP75AIDR 温度传感器想在 Linux 环境下把它顺利跑起来第一反应千万别急着去搜所谓官方驱动包。在绝大多数嵌入式项目里这个活儿用内核自带的 lm75 驱动家族就能办完只要做好三件事确认内核配置、补设备树节点、加载模块并验证读数。所谓 Linux 设备驱动移植很多时候并不是从寄存器读写开始手搓一个驱动而是把软硬件链路打通、把现成代码用到正确的位置上。这篇就按我第一次做 TMP75AIDR 驱动移植时的实际路径来拆适合刚接触 I2C 传感器驱动的朋友也适合已经调过几次设备树、想快速搞定温度采集的工程师。1. 项目概述与整体设计1.1 TMP75AIDR 到底是什么芯片TMP75AIDR 是一颗数字温度传感器走 I2C 接口兼容 SMBus内部带 12 位 ADC温度分辨率能做到 0.0625°C/LSB。供电电压范围比较宽2.7V 到 5.5V 都能工作因此常被用在嵌入式控制板上直接测量板级温度或者配合 MCU/Linux 主机做环境温度监控。芯片本身封装很小引脚也不多最需要关注的其实是地址配置。它有两个地址引脚 A0、A1、A2或者三个不同批次引脚定义略有差异AIDR 后缀属于工业级版本这些引脚的电平组合决定了芯片在 I2C 总线上的 7 位地址。默认情况下如果这三个引脚都接地地址就是 0x48这是我最常用的一个地址。也就是说总线上的 I2C 地址由高四位 1001 加低三位 A2/A1/A0 组成稍微算一下就能得到 0x48 到 0x4F 的完整地址范围。测量范围方面这种工业级版本一般标称可以覆盖 -40°C 到 125°C在常温区间比如 -10°C 到 85°C 内精度表现会更好误差通常在 ±1°C 左右。手册里不同温度窗口的精度指标不同具体以你拿到的手册批次为准。移植驱动前先看一眼寄存器结构也有必要寄存器 00h 是温度数据寄存器01h 是配置寄存器02h/03h 分别存的是高温阈值和低温迟滞阈值。温度换算那块我后面细讲。1.2 为什么优先选 lm75 驱动这条路线Linux 内核的 hwmon 子系统里有个非常经典的驱动文件drivers/hwmon/lm75.c它不只是为原版 LM75 芯片服务的。打开这个文件里的 of_match_table 就能看到它兼容了一大批LM75 协议兼容的温度传感器其中明确包含了ti,tmp75这个 compatible 字符串。TMP75 和 TMP75AIDR 在寄存器布局和读取语义上基本一致所以内核驱动可以直接拿来用。选择内核自带驱动而不是厂商提供的外挂驱动有几个很实际的原因。第一lm75 驱动已经在内核社区经过了很多年的验证寄存器读写、符号扩展、温度换算这些基础逻辑都被反复打磨过出问题的概率比我临时写的驱动低得多。第二它天然接入 sysfs 和 hwmon 框架驱动加载成功后用户态直接用cat /sys/class/hwmon/hwmon0/temp1_input就能拿到温度值完全不用自己写应用层协议解析。第三它和设备树配合得很好我在设备树里声明一个 compatible 为ti,tmp75的节点内核的 I2C 子系统就会自动把驱动和设备绑定起来。所以这次的移植工作本质上是适配 验证不是从零开发。理解这一点之后整个项目的设计思路就清晰了我先梳理内核配置再编写设备树节点接着编译加载驱动最后用各种工具确认温度读数。如果硬件地址有变化我只要调整设备树中的 reg 属性即可。1.3 移植规划从硬件接线到用户态数据在动手敲设备树之前我建议先把三条信息确认清楚芯片挂在哪条 I2C 总线上、总线编号是多少、芯片引脚上的地址电平是什么。这三个信息直接决定设备树节点写在哪个控制器下、reg 填什么值。我这次的实际场景是某嵌入式主板上的 i2c-2 总线挂了一颗 TMP75AIDR芯片 A0/A1/A2 全部接低地址为 0x48。于是规划的任务清单是先在内核配置里打开 lm75 驱动的编译开关再在设备树里给 i2c-2 增加一个tmp7548节点然后编译并替换设备树加载 lm75 模块最后通过 hwmon 接口读取温度并和参考温度计做对比。整个过程不涉及修改驱动源码只有一处地方可能需要微调就是当芯片地址不是 0x48 时改设备树的 reg。这样的规划方式也适用于其他 I2C 温度传感器比如 TMP275、TMP102 这些型号原理相同先确认内核有没有现成兼容驱动如果没有再考虑自己写一个轻量的 hwmon 驱动。2. 内核与设备树侧的准备工作2.1 内核配置先把 lm75 驱动编译开关打开首先是内核配置。lm75 驱动对应的 Kconfig 选项是CONFIG_SENSORS_LM75在 menuconfig 里大致路径是 Device Drivers → Hardware Monitoring support 下面的 LM75 compatible 相关选项。不同内核版本菜单显示可能略有差异但关键词都是 LM75。这个选项可以编成y直接编入内核也可以编成m生成独立模块。我的习惯是先编成模块调试阶段方便反复加载卸载确认稳定后再改成y固化进去这样也能减少启动后手动加载模块的步骤。如果之前内核已经编译过修改配置后一定要重新编译内核或模块别只编译设备树就完事否则驱动根本不存在。配置完成后建议检查一下生成的 .config 里确实有CONFIG_SENSORS_LM75y或m这一行。编译内核或模块后如果选的是m会得到lm75.ko文件。把它放到目标板的模块目录或者使用内核编译流程里的modules_install安装然后执行depmod -a更新模块依赖。这里有个我踩过的坑有时候为了省事直接在目标板上用 modprobe 加载模块结果提示找不到符号或版本不匹配。大多数情况是因为模块编译用的内核源码版本和当前运行内核版本不一致或者交叉编译环境没配对。最稳妥的方式是按你开发板厂商提供的 SDK 流程把内核和模块一起编译、一起部署。2.2 设备树节点写法与地址计算设备树是整个移植过程里最容易出问题、也最值得认真检查的部分。我最终的节点写法是这样的i2c2 { status okay; clock-frequency 100000; tmp7548 { compatible ti,tmp75; reg 0x48; }; };先说reg 0x48这个值怎么来的。TMP75AIDR 的 I2C 地址是 7 位格式是1001 A2 A1 A0。如果 A0/A1/A2 都接地二进制就是1001000换算成十六进制就是 0x48。如果 A2 接高而其他接地地址变成1001100也就是 0x4C。硬件上怎么接就填对应的值。很多开发板为了兼容不同模块会预留地址跳线或者焊盘调试时先拿万用表量一下引脚电平最保险。compatible ti,tmp75对应的是事件驱动的匹配字符串也对应内核源码里of_device_id表。建议整个字符串原样保留不要自己改成tmp75aidr之类的东西因为驱动匹配靠的就是这个名字。补充一点老版本内核的设备树绑定文档里写的是ti,tmp75但某些内核版本也接受tmp75之类的旧写法如果发现驱动没有 probe可以查一下当前内核源码里到底支持哪些字符串。i2c2表示挂在第二条 I2C 控制器上。具体编号要根据你的平台手册确定有的平台叫i2c1、i2c3不要照抄我这里的写法。如果总线上已经有时钟或者又有挂载其他设备节点加在同一个控制器下面即可。2.3 编译、部署与初步确认设备树改好后按平台标准流程编译 dtb。比如使用 ARM 交叉编译环境时通常是执行make dtbs或者直接走 SDK 的打包脚本。编译完成后建议先反编译一遍生成的 dtb确认新节点确实编译进去了。用dtc工具反编译的命令大致是dtc -I dtb -O dts -o output.dts your-board.dtb然后在 output.dts 里搜tmp75能看到节点内容就说明设备树层没问题。把 dtb 刷进目标板启动如果之前选了模块方式启动后执行modprobe lm75然后查看 I2C 总线上设备是否被创建ls /sys/bus/i2c/devices/正常会多出一个类似2-0048的目录。2是 I2C 总线号0048是芯片地址。再进一步确认ls -l /sys/bus/i2c/devices/2-0048/driver如果驱动成功绑定这个软链接会指向 lm75 驱动目录比如显示.../drivers/lm75。到这一步驱动和设备树的基本链路就算通了。3. 驱动适配与核心实现细节3.1 I2C 驱动与设备树节点的匹配过程不要把它想得太玄学。Linux 内核对 I2C 设备匹配有两种常见方式一种是驱动里注册了i2c_device_id表按名字匹配另一种是注册了of_match_table按设备树里的 compatible 匹配。lm75 驱动两种都有所以我的设备树节点一创建内核的 I2C core 就开始遍历已注册的 I2C 驱动拿设备树里的 compatible 字符串去和驱动的 of_match_table 比对一旦对上就调用驱动的 probe 函数。lm75 驱动源码里大致是这样static const struct of_device_id lm75_of_match[] { { .compatible ti,tmp75 }, {} }; MODULE_DEVICE_TABLE(of, lm75_of_match);匹配成功后probe 里做的事情有分配设备私有数据结构、设置 I2C 客户端、检查芯片是否存在或做一次初始化读取、注册 hwmon 设备并且创建对应的 sysfs 属性文件。用户态看到的/sys/class/hwmon/hwmon0/temp1_input就是这一步创建的。这里我想强调一个思路理解匹配流程不是为了背代码而是为了排查问题。如果driver软链接没出现先确认 compatible 字符串是否完全一致再确认这个平台上 I2C 控制器的时钟有没有开启。很多时候驱动没绑定不是代码问题而是设备树节点没挂在正确的控制器下面或者该控制器没被使能。3.2 温度数据怎么从寄存器变成用户态读数TMP75AIDR 内部温度寄存器是 12 位有效数据存放在两个字节里。读取回来的原始值本质是有符号数最高位是符号位。12 位二进制每个 LSB 对应的温度变化是 0.0625°C也就是 1/16 摄氏度。所以温度计算公式很简单实际温度 寄存器值 × 0.0625°C但这个乘法不能直接用浮点跑尤其是内核态里能省则省。lm75 驱动里用整数运算完成了换算把寄存器的高 16 位有符号值做一次右移再乘以 500 再除以 8 之类的技巧反正最后输出的单位是毫摄氏度。sysfs 里返回给用户态的temp1_input就是毫摄氏度比如读出 28375代表 28.375°C。用户态读取一条命令就够了cat /sys/class/hwmon/hwmon0/temp1_input如果你想更直观地看到三位小数可以用 shell 简单换算awk {printf %.3f\n, $1/1000} /sys/class/hwmon/hwmon0/temp1_input这里有一个细节容易忽略芯片上电后默认就是连续转换模式驱动不需要显式配置就会持续更新温度寄存器。所以读到的数值会是实时变化的而不是某个固定初值。如果发现读数一动不动就要怀疑芯片是不是被配置成了单次转换模式或者硬件上根本没有正常工作。3.3 配置寄存器与精度取舍配置寄存器最简单也最实用。它控制转换分辨率、单次/连续转换模式、ALERT 引脚极性等。默认值通常是连续转换模式分辨率 12 位。12 位模式下的转换时间会长一些但也只是几百毫秒量级对温度监控场景完全够用。如果你追求更快的转换周期可以把分辨率降到 9 位对应 LSB 变成 0.5°C转换时间能缩短到几十毫秒但精度也牺牲得多一般不推荐。我的建议是开发调试阶段保持默认不碰配置寄存器先把链路跑通。等温度读数正常了再根据实际需要决定要不要改。比如某些产品对温度变化响应速度要求高可以尝试降低分辨率如果需要温度告警可以配置高温阈值寄存器和迟滞寄存器再配合 ALERT 输出引脚。这些东西在 lm75 驱动里不一定暴露了配置接口改起来可能得在设备树或者驱动初始化里加参数所以不要一上来就把配置寄存器玩坏否则后面排查时干扰因素会变多。4. 实操验证与调试记录4.1 先确认驱动真的绑上了移植过程中我习惯按四步确认避免上来就 miss 一个环节。第一步看总线上有没有这个地址的设备。用i2cdetect扫一下i2cdetect -y 2如果总线上确实有芯片地址 0x48 那格会显示48。如果显示--要么地址不对要么芯片没供电要么上拉电阻有问题也可能是总线编号不对。第二步看 sysfs 里有没有生成2-0048目录。第三步看驱动链接是否指向 lm75。第四步确认 hwmon 类目录存在cat /sys/class/hwmon/hwmon0/name输出应该是lm75。这一步能帮你确认你说的 hwmon0 到底是不是这颗芯片因为系统里可能还有 CPU 温度传感器、板载其他传感器它们也会注册 hwmon 节点。如果有多个就逐个 cat name找出标签是 lm75 的那个。4.2 从用户态读温度并做连续监测驱动绑定正常后温度读取非常简单。先读一次cat /sys/class/hwmon/hwmon0/temp1_input比如输出 28687说明当前温度 28.687°C。为了观察变化趋势我经常用watch命令连续刷新watch -n 1 cat /sys/class/hwmon/hwmon*/temp1_input注意这里用了通配符方便在不确定哪个 hwmon 是目标时一次看到所有温度传感器输出。实际调的时候最好还是指定具体路径避免看混了。如果有多个传感器节点可以用cat /sys/class/hwmon/hwmon*/name和temp*组合判断。顺手做个简单验证把手指按到芯片封装上或者用电烙铁靠近但不接触温度读数应该逐渐上升移开后慢慢回落到环境温度。如果读数变化太快或者几乎不变化说明要么热耦合不好要么芯片根本没在测量状态。4.3 精度评估与参考温度计做对比系统集成阶段光看到数据在变还不够我还建议做一次简单精度验证。把 TMP75AIDR 和一个经过校准的参考温度计放到同一个恒温块里或者至少放在热容量接近的同一块金属表面等温度稳定后同时记录两边的读数。记录个十几分钟看差值是否在手册给出的精度范围内。我一般会把采样脚本写成这样便于落到 csv 里while true; do echo $(date %s) $(cat /sys/class/hwmon/hwmon0/temp1_input) sleep 2 done再结合参考温度计的数据对比做进一步修正。如果发现固定偏差比如整体偏高 0.3°C先别急着改代码检查一下 PCB 布局是不是让传感器太靠近发热元器件或者散热焊盘有没有连到大面积铜皮。很多时候误差的来源不是芯片本身而是传感器所在位置的环境温度并非你想要测的对象。这个环节能省就省的话后续产品测试时会花更多时间在解释为什么温度不对上。5. 常见问题与排查技巧实录5.1 驱动加载存在但设备节点找不到这是最经常遇到的第一类问题。设备树里写了节点modprobe lm75也提示加载成功但/sys/bus/i2c/devices/下就是看不到2-0048。优先检查三件事。第一设备树是否真的编译进当前启动的 dtb 里了。有时候改的是源码编译的却是另一个设备树文件启动加载的文件还是旧版本。用dtc反编译后搜索节点就能确认。第二I2C 控制器是否使能。很多 SoC 的控制器默认关闭要在设备树里设置status okay。第三总线号是否对。不同平台 I2C 控制器编号可能从 0 开始也可能从 1 开始你以为挂在 i2c2实际硬件上芯片在 i2c3设备树节点写在 i2c2 下面自然找不到。拿i2cdetect -l列出系统实际的总线列表再对着原理图确认芯片的 SCL/SDA 接到了哪组引脚上。5.2 地址设置错误与总线冲突如果 i2cdetect 能看到芯片但设备树节点 reg 和实际地址不一致驱动不会 probe。反过来如果 reg 等于某个地址而那个地址上挂的其实是另一个设备也会出乱子。常见的 I2C 地址冲突发生在板载 EEPROM、RTC 等设备身上它们很多也占据 0x48 到 0x4F 这一段。地址问题排查就是要会算。前面说过1001 A2 A1 A0是地址格式A0/A1/A2 的具体接法决定最终地址。举例来说A0 接高电平、A1/A2 接地时地址是 0x49A2 接高电平时则是 0x4C。有些模块上芯片的地址引脚已经被固定你可能只能改软件里的 reg 值。如果是自己画板预留跳线最好以后扩展多片传感器时还能灵活错开地址。总线上同时挂多颗 TMP75可以分别把 A0/A1/A2 接不同电平这样每颗地址不同设备树里写多个不同 reg 的节点就行。5.3 温度读数不动或乱跳的处理方向读数一动不动先确认是不是芯片处于单次转换模式。如果之前用i2cset把配置寄存器里的 SHOT 位置了 1芯片就只转换一次之后温度寄存器不再更新必须重新触发转换。临时调试可以用 i2cset 改回去但别乱改其他位。如果是全新上电就有问题更可能是硬件虚焊、SDA/SCL 颠倒、供电异常要拿示波器去看 I2C 通信波形。读数乱跳的原因又不一样。最常见的是供电纹波大、I2C 总线上拉电阻阻值不合适、线缆太长。上拉电阻阻值太大会导致信号上升沿变缓高速读取时数据错位太小又会加重总线负载。一般 I2C 总线用 4.7kΩ 或 2.2kΩ 常见具体要看总线上挂了多少设备。另外如果传感器旁边有大电流器件切换或者地平面不连续也会在温度数据里看到毛刺。这种问题不是驱动能解决的得回到硬件设计上处理。5.4 用 i2c-tools 做绕过驱动的底层诊断当 sysfs 读不到或读数明显异常时i2c-tools 是很好的底层诊断工具。先拿i2cdetect -y -r 2确认设备地址再用i2cdump看寄存器原始值i2cdump -y 2 0x48命令输出会按照字节显示寄存器内容。温度寄存器是 00h配置寄存器是 01h。结合数据手册对照配置寄存器当前值就能判断芯片是否处于连续转换模式。如果想单独读温度寄存器i2cget也能做i2cget -y 2 0x48 0x00 w注意w会按 word 读取不同芯片和不同工具版本对字节序的处理可能不同所以看到的数据要和手册仔细对照不要急着下结论。底层诊断的价值在于它能帮你区分问题在硬件层面还是软件层面。只要地址能扫到、寄存器能读那驱动加载不上大概率是设备树或内核配置问题如果地址都扫不到那就是硬件链路的问题回头查供电、上拉、接线才是正道。在调试过程中如果临时用i2cset改了配置寄存器一定记得记录原始值调完恢复默认。特别是 ALERT 相关位和比较器模式位改坏了会影响后续行为而这些问题往往隐藏得比较深会浪费不少时间。我自己做这类移植时最深的体会是其实内核已经替你把 90% 的读写逻辑做完了真正花时间的通常不是驱动代码而是设备树、地址、总线拓扑、供电这些外围因素。先把基础环境验证到地址能扫到、寄存器能读这一层再考虑驱动绑定的事情。后续如果想扩展比如同时挂多片 TMP75AIDR或者把芯片换成同系列的其他型号也只需要调整设备树里的节点和地址基本不用改动内核驱动这个路线还是可以长期复用的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑