时间轴
时间轴
2025-12-23
init
本文介绍了Linux驱动笔记中的单总线(One-Wire)协议,阐述了其仅需一根信号线即可同时传输时钟与数据、支持双向通信的特点,以及上拉电阻、多设备唯一64位地址识别等硬件组成。文章详细说明了单总线的通信步骤,包括初始化、ROM操作命令和功能命令,并强调主机必须严格遵循时序。同时,以温度传感器DS18B20为例,介绍了其9至12位可调分辨率、-55°C至+125°C测量范围、±0.4°C精度、全球唯一序列号及多点分布式测温能力,并说明了其引脚配置、封装类型和通过数据线供电的灵活方式。
Linux 驱动笔记
| 目录 | 链接 |
|---|---|
| 1. Linux 驱动框架 | |
| 2. Linux 驱动加载逻辑 | |
| 3. 字符设备基础 | |
| 4. 并发与竞争 | |
| 5. 高级字符设备进阶 | |
| 6. 中断 | |
| 7. 平台总线 | |
| 8. 设备树 | |
| 9. 设备模型 | |
| 10. 热插拔 | |
| 11. pinctrl 子系统 | |
| 12. gpio 子系统 | |
| 13. 输入子系统 | |
| 14. 单总线 | |
| 15. I2C | |
| 16. SPI | |
| 17. UART | |
| 18. PWM | |
| 19. RTC | |
| 20. Watchdog | |
| 21. CAN | |
| 22. 网络设备 | |
| 23. ADC | |
| 24. IIO | |
| 25. USB | |
| 26. LCD |
单总线简介
单总线概述
单总线(One-Wire)是一种串行通信协议和硬件总线,用于在电子设备之间传输数据和控制信号。它是由独立的芯片制造商 Dallas Semiconductor 开发的,并且在多种应用中得到了广泛应用。
与 SPI、I2C 等串行数据通信方式不同,单总线的特点是只需要一根信号线,既可以传输时钟又可以传输数据,而且数据是双向的。所以单总线具有节省 IO 口、结构简单、便于扩展和维护等特点。
单总线用于各种应用,包括温度传感器、湿度传感器、EEPROM 存储器、时钟等。它在许多领域中得到了广泛应用,例如工业自动化、家庭自动化、物联网和电子设备监测等。本篇使用温度传感器 DS18B20 进行举例学习单总线。

- 信号线:单总线使用一根信号线进行数据传输和通信。这根线被称为数据线,也是提供电源的线路。单总线上的所有设备都连接到这根信号线上。
- 上拉电阻:单总线需要一个上拉电阻连接到信号线和电源之间,以确保在没有设备发送数据时,信号线上的电平保持为高电平(逻辑 1)。上拉电阻的值通常在 4.7 千欧姆到 10 千欧姆之间(GPIO 设置为上拉)。
- 设备:单总线支持多个设备连接到信号线上。每个设备都具有唯一的 64 位地址,通过这个地址来识别和选择通信的目标设备。设备可以是各种类型的传感器、存储器、时钟等。
- 处理器:处理器是单总线上的控制器,处理器负责发送命令,读取响应和控制单总线上的从设备
单总线的通信步骤
单总线是主从结构,当主机呼叫从机时,从机才会应答,所以主机必须严格遵循单总线的命令时序。如果命令时序不对,则器件不会响应。
单总线的通信步骤通常包括以下几个阶段。
- 初始化:通信开始之前,主设备会发送初始化信号来确保单总线上没有其他设备正在通信。初始化信号是一个特定的序列,通常是将数据线拉低一段时间然后释放。
- ROM 操作命令
- 功能命令
DS18B20 介绍
参考:
芯片概述
DS18B20 是一种数字温度传感器芯片,提供 9 到 12 位分辨率的温度测量,可以通过可编程非易失性存储单元实现温度的下限和上限报警。
它是基于单总线通信协议的设备,只需要一根信号线和一根地线。
DS18B20 能够以较高的精度测量温度,精确到 0.0625°C(12 位分辨率时)。它具有广泛的测量范围,通常介于 -55°C 到 +125°C 之间。
DS18B20 芯片可以通过单总线从主设备获取供电,也可以通过外部电源进行供电。这使得它在一些低功耗应用中能够灵活选择供电方式。
每个 DS18B20 都会有一个全球唯一的 64 位序列号,可以将多个 DS18B20 串联在同一根单总线上进行组网,只需要一个处理器就可以控制分布在大面积区域中的多颗 DS18B20。这种组网方式特别适合 HVAC 环境控制、建筑、设备、粮情测温和工业测温以及过程监测控制等应用领域。
基本性能
DS18B20 是一款数字温度传感器芯片,具有以下基本性能特点:
- 采用单总线接口仅需一个端口引脚进行通信
- 每颗芯片具有全球唯一的 64 位的序列号
- 具有多点分布式测温功能无需外围元器件
- 可通过数据线供电,供电电压范围为 2.5V ~ 5.5V
- 温度测量范围为 -55°C 到 +125°C
- 在 -10°C ~ +70°C 范围内精确度为±0.4°C
- 温度分辨率 9-12 位可选
- 最高 12 位精度下,温度转换速度小于 750ms
- 具有用户自定义的非易失性温度报警设置
- 报警搜索命令识别并标识超过程序设定温度的器件
- 超强静电保护能力:HBM 8000V MM 800V
- 可提供贴片的 MSOP8,SOP8 封装和 3 脚的 TO-92、TO-92S 封装。
引脚配置和封装
DS18B20 芯片引脚配置:
- VDD:供电引脚,用于提供芯片的正电源。
- DQ:数据引脚,用于单总线通信和数据传输。
- GND:地引脚,连接芯片的地(负电源)。
DS18B20 芯片可用于不同的封装类型,其中最常见的封装是 TO-92 封装和 TO-92-3 封装。这些封装都是具有三个引脚的小型封装,适用于直插式安装和表面贴装。
TO-92 封装是一种常见的小型塑料封装,引脚按照顺序排列,依次为 VDD、DQ 和 GND。
TO-92-3 封装与 TO-92 封装类似,但引脚顺序略有不同。TO-92-3 封装的引脚顺序为 GND、DQ 和 VDD。
除了这些常见的封装类型,DS18B20 还可以在其他封装类型中使用,例如 SOT-23 封装和TO-263 封装等,这些封装类型可能具有不同的引脚排列和尺寸。如下图所示:


内部结构
DS18b20 是一种数字温度传感器芯片,其内部结构主要包括以下组成部分,如下图所示:

- 温度传感器:DS18B20 内部集成了温度传感器,用于测量环境的温度。传感器通常基于基准电压的变化来检测温度,并将其转换为数字信号。
- A/D 转换器:DS18B20 芯片内部包含了一种模数转换器(A/D 转换器),用于将传感器测量到的模拟温度值转换为相应的数字表示。这使得温度数据能够以数字形式进行处理和传输。
- 存储器:DS18B20 芯片还具有内部存储器,用于存储配置信息和温度测量结果。存储器可以存储唯一的 64 位地址、温度分辨率和其他相关设置。
- 控制逻辑:DS18B20 芯片包含了控制逻辑电路,用于管理温度测量、通信和其他相关功能。控制逻辑协调各个部分的操作,并与主设备进行通信。
- 单总线接口:DS18B20 采用了单总线通信协议,其内部结构包括一条数据线和一个上拉电阻,用于与主设备进行通信。单总线接口简化了连接和通信的布线,使得多个 DS18B20 传感器能够方便地串联在同一条总线上。
Memory Map
64-Bit Lasered ROM Code

| 部分 | 含义 |
|---|---|
| 28 | 家族代码(DS18B20 固定是 0x28) |
| FF 1C 46 93 16 03 | 唯一序列号 |
| 5A | CRC 校验 |
Memory
存储器由 9 个字节组成,其分配如下表所示

DS18B20 自带 CRC 校验, CRC 用多项式 X⁸ + X⁵ + X⁴ + 1, 主机必须自己重新算 CRC, 两个 CRC 相等 → 数据正确, 不等 → 说明通信错误, 芯片不会自动阻止错误
寄存器介绍
- 温度寄存器(Temperature Register):温度寄存器存储了最近一次温度测量的结果。它是一个 16 位的寄存器,包含了温度值的原始数据。通过读取温度寄存器中的数据,并结合分辨率设置,可以计算出实际的温度值。
- 配置寄存器(Configuration Register):配置寄存器用于设置 DS18B20 的工作模式和温度分辨率。它是一个 8 位的寄存器,每个位对应一个配置选项。通过写入配置寄存器,可以选择温度分辨率、触发温度转换和使用电源供电模式等。
配置寄存器
配置寄存器用于设置 DS18B20 的工作模式和温度分辨率。它是一个 8 位的寄存器,每个位对应一个配置选项。如下图所示

注意:上电默认设置 R0=1, R1=1(12 位精度)。
精度和转换时间之间有直接关系。配置寄存器的位 5 和位 6(R0 和 R1)用于设置分辨率,位 7 和位 0 到 4 被器件保留,禁止写入。温度分辨率设置表如下所示:

温度寄存器
DS18B20 芯片的温度寄存器是一个 16 位的寄存器,用于存储最近一次温度测量的原始数据。温度寄存器的位布局如下所示:

温度寄存器的最低有效位(LSB)是 位,表示温度的最小精度为 0.0625°C。其他位依次表示更高的温度精度,分别为:
- (0.125)
- (0.25)
- (0.5)
- (1)
- (2)
- (4)
- (8)
DS18B20 芯片的温度寄存器中存储的原始数据可以通过以下步骤计算出实际温度值:
- 从温度寄存器读取的 16 位数据可以解释为一个有符号整数,其中最高位(MSB)表示符号位。如果符号位为 0,表示正温度;如果符号位为 1,表示负温度。
- 取出温度寄存器中的位 4 到位 15,共 12 位,表示温度的整数值(已含符号信息)。
- 将这 12 位数据与符号位组合成一个有符号整数。
- 根据所选择的温度分辨率,将有符号整数乘以相应的分辨率因子,以获得实际的温度值。
- 当温度大于 0 时,符号位为 0,将这 12 位数据直接解释为有符号整数(高位补 0),再乘以分辨率因子即可得到实际的温度。
- 当温度小于 0 时,符号位为 1,这 12 位数据以补码形式存储,取其绝对值(低 11 位取反加一),乘以分辨率因子后再取负即可得到实际的温度。
举个例子,假设选择了 12 位的温度分辨率,并从温度寄存器读取的数据为 0x1FFF。
0x1FFF 的二进制表示为:0001 1111 1111 1111。最高位为 0,表示正温度。取出低 11 位:111 1111 1111。将这 11 位与符号位组合,得到有符号整数为:0111 1111 1111(对应 0x07FF)。
对于 12 位的温度分辨率,分辨率因子为 0.0625°C。将有符号整数 0x07FF乘以分辨率因子:0x07FF * 0.0625 = 127.9375°C。
因此,从温度寄存器读取的数据 0x1FFF 对应着约 127.94°C 的实际温度值。
指令介绍
根据 DS18B20 的通讯协议,主机(单片机)控制 DS18B20 完成温度转换必须经过三个步骤:
- 每一次读写之前都要对 DS18B20 进行复位操作
- 复位成功后发送一条 ROM 指令
- 最后发送 RAM 指令,这样才能对 DS18B20 进行预定的操作。
复位要求主 CPU 将数据线下拉 500 微秒,然后 释放,当 DS18B20 收到信号后等待 16~60 微秒左右,后发出 60~240 微秒的存在低脉冲,主 CPU 收到此信号表示复位成功。
ROM Command
在主机检测到存在脉冲之后,它可以发送一个 ROM 命令。这些命令作用于每个从设备唯一的 64 位 ROM 编码,使主机在 1-Wire 总线上存在多个设备时,能够选中特定的一个设备。
这些命令还允许主机:
- 判断总线上有多少设备
- 确认设备类型
- 检测是否有设备触发了报警状态
一共有 5 种 ROM 命令,每条命令长度都是 8 位。
在发送 DS18B20 的功能命令之前,主机必须先发送一个合适的 ROM 命令。
| 指令 | 约定代码 | 功能 |
|---|---|---|
| Search ROM | 0xF0 | 用于确定挂接在同一总线上 DS18B20 的个数和识别 64 位 ROM 地址。为操作各器件作好准备。 |
| Read ROM | 0x33 | 读 DS18B20 温度传感器 ROM 中的编码(即 64 位地址) |
| Match ROM | 0x55 | 发出此命令之后,接着发出 64 位 ROM 编码,访问单总线上与该编码相对应的 DS18B20 使之作出响应,为下一步对该 DS18B20 的读写作准备。 |
| Skip ROM | 0xCC | 忽略 64 位 ROM 地址,直接向 DS18B20 发温度变换命令。适用于单片工作。 |
| Alarm Search | 0xEC | 执行后只有温度超过设定值上限或下限的片子才做出响应。 |
Search ROM [F0h]
系统上电后,主机必须识别总线上所有从设备的 ROM 编码,这样才能知道:
- 有多少设备
- 每个设备的类型
主机通过一种“排除法”完成这个过程,需要重复执行Search ROM 命令 + 数据交换多次,直到找出所有设备。
如果总线上只有一个设备,可以用更简单的Read ROM [33h]代替 Search ROM。
每完成一次 Search ROM 过程,主机都必须回到通信流程的第 1 步(初始化)。
Read ROM [33h]
这个命令 只能在总线上只有一个从设备时使用。
它允许主机直接读取该设备的 64 位 ROM 编码,而不需要执行 Search ROM 过程。
如果总线上有多个设备却使用这个命令:
- 所有设备会同时响应
- 会发生数据冲突
Match ROM [55h]
主机发送Match ROM + 64 位 ROM 编码就可以精确选中某一个设备。
只有 ROM 编码完全匹配的设备才会响应后续功能命令,其他设备都会等待下一次 reset。
适用于:
- 多设备总线
- 单设备总线
Skip ROM [CCh]
这个命令会:
- 同时选中总线上所有设备
- 不需要发送 ROM 编码
例如Skip ROM+Convert T [44h]可以让所有 DS18B20 同时开始温度转换。
注意Skip ROM+Read Scratchpad [BEh]只能在总线上只有一个设备时使用。
如果总线上有多个设备却使用这个命令:
- 所有设备会同时响应
- 会发生数据冲突
Alarm Search [ECh]
这个命令和 Search ROM 的操作方式一样,
但只有触发报警标志的设备 才会响应。它用于检测最近一次温度转换中是否有设备超出报警阈值
每完成一次 Alarm Search 过程,主机必须回到初始化步骤。
ROM Commands 时序图

Function Command

Convert T [44h]
这个命令启动一次温度测量。
转换完成后:
- 温度结果写入 scratchpad 的 2 字节温度寄存器
- 芯片进入低功耗空闲状态
如果是 寄生供电模式:
- 主机在发命令后 10µs 内
- 必须开启强上拉
- 持续整个转换时间 tCONV
如果是 外部供电模式:
主机可以不断读时间槽:
- 返回 0 → 还在转换
- 返回 1 → 转换完成
寄生供电时不能这样查询,因为总线被强上拉。
Write Scratchpad [4Eh]
允许主机写 3 个字节到 scratchpad:
- 第 1 字节 → TH(byte 2)
- 第 2 字节 → TL(byte 3)
- 第 3 字节 → 配置寄存器(byte 4)
数据必须:
- 低位先传(LSB first)
- 三个字节必须一次写完再 reset,否则数据可能损坏。
Read Scratchpad [BEh]
主机读取 scratchpad 内容:
- 从 byte 0 的最低位开始
- 一直读到 byte 8(CRC)
如果只需要部分数据:
- 可以随时 reset 终止读取
Copy Scratchpad [48h]
把 scratchpad 的:
12 | TH / TL / 配置寄存器(byte 2,3,4) |
复制到 EEPROM。
寄生供电模式下:
- 10µs 内必须开启强上拉
- 至少持续 10ms
Recall E2 [B8h]
从 EEPROM 读取:
1 | TH / TL / 配置 |
重新加载到 scratchpad。
主机可以查询状态:
- 0 → 正在恢复
- 1 → 完成
这个操作在上电时会自动执行,所以一上电 scratchpad 就有有效数据
Read Power Supply [B4h]
检测供电方式:
主机发命令后读一个时间槽:
- 总线被拉低 → 寄生供电
- 总线保持高 → 外部供电
Function Commands 时序图

操作举例
单总线上只有一个 DS18b20
总线上只有一个 DS18B20,并且它使用寄生供电。主机向该 DS18B20 的 scratchpad 写入 TH、TL 和配置寄存器,然后读取 scratchpad 并重新计算 CRC 来验证数据。之后主机把 scratchpad 的内容复制到 EEPROM。
| 主机模式 | 数据(低位先传) | 说明 |
|---|---|---|
| Tx | Reset | 主机发送复位脉冲 |
| Rx | Presence | DS18B20 返回存在脉冲 |
| Tx | CCh | 主机发送 Skip ROM(选中总线上所有设备) |
| Tx | 4Eh | 主机发送 Write Scratchpad |
| Tx | 3 字节数据 | 写入 TH、TL 和配置寄存器 |
| Tx | Reset | 主机再次复位 |
| Rx | Presence | DS18B20 返回存在脉冲 |
| Tx | CCh | Skip ROM |
| Tx | BEh | Read Scratchpad |
| Rx | 9 字节数据 | 读取 scratchpad + CRC 并校验 |
| Tx | Reset | 主机复位 |
| Rx | Presence | DS18B20 返回存在脉冲 |
| Tx | CCh | Skip ROM |
| Tx | 48h | Copy Scratchpad |
| Tx | 强上拉 ≥ 10ms | 写 EEPROM 期间强上拉供电 |
单总线上有多个 DS18B20
总线上有多个 DS18B20,并且它们使用寄生供电。主机对其中某一个指定的 DS18B20 启动温度转换,然后读取它的 scratchpad,并重新计算 CRC 来验证数据是否正确。
| 主机模式 | 数据(低位先传) | 说明 |
|---|---|---|
| Tx | Reset | 主机发送复位脉冲 |
| Rx | Presence | DS18B20 返回存在脉冲 |
| Tx | 55h | 主机发送 Match ROM 命令 |
| Tx | 64 位 ROM 码 | 主机指定目标 DS18B20 |
| Tx | 44h | 主机发送 Convert T(开始测温) |
| Tx | 强上拉保持高电平 | 主机在整个转换时间 tCONV 内提供强上拉供电 |
| Tx | Reset | 主机再次发送复位脉冲 |
| Rx | Presence | DS18B20 返回存在脉冲 |
| Tx | 55h | 主机再次发送 Match ROM |
| Tx | 64 位 ROM 码 | 再次选中同一设备 |
| Tx | BEh | 主机发送 Read Scratchpad |
| Rx | 9 字节数据 | 主机读取 scratchpad(含 CRC),并自行计算 CRC 校验 |
通信时序
DS18B20 使用一种严格的 1-Wire 通信协议来保证数据完整性。该协议定义了几种信号类型:
reset pulsepresence pulsewrite 0write 1read 0read 1
1-Wire 总线上所有数据都是 LSB first(低位先传)
复位时序
1-Wire总线上的所有通信都以一个 初始化序列 开始。
这个初始化序列包括:
- 由主机发送的一个 复位脉冲(reset pulse)
- 随后由从设备发送的 存在脉冲(presence pulse)
存在脉冲的作用是告诉主机:总线上有从设备(例如 DS18B20),并且它们已经准备好通信。

在初始化过程中:
- 主机发送复位脉冲,主机将 1-Wire 总线拉低 至少 480µs
- 主机释放总线,进入接收模式,释放后,总线被 5kΩ 上拉电阻拉高
- DS18B20 检测到上升沿,它等待 15µs ~ 60µs
- DS18B20 发送存在脉冲,将总线拉低 60µs ~ 240µs
- 总线由上拉电阻拉高(RESISTOR PULLUP)
读写时序
- 主机在 写时隙 中向 DS18B20 写数据,
- 在 读时隙 中从 DS18B20 读取数据。
在 1-Wire 总线上:每一个时隙只传输 1 bit 数据。

Write Time Slots
写时隙有两种:
- Write 1(写 1)
- Write 0(写 0)
主机用:Write 1 时隙写入逻辑 1,Write 0 时隙写入逻辑 0

所有写时隙必须满足:
- 每个时隙 ≥ 60µs 且 ≤ 120us
- 相邻时隙之间恢复时间 ≥ 1µs
两种写时隙都由主机:先把 1-Wire 总线拉低开始
写 1 的规则
主机拉低总线后:必须在 15µs 内释放
释放后:5kΩ 上拉电阻会把总线拉高
写 0 的规则
- 主机拉低总线后:持续保持低电平 ≥ 60µs
DS18B20 会在一个采样窗口检测总线状态:在主机开始写时隙后的 15µs ~ 60µs 之间,如果这段时间:
- 总线是高电平 → 写入 1
- 总线是低电平 → 写入 0
Read Time Slots
DS18B20 只能在读时隙里发数据。所以:
- 主机想读数据,必须主动创建读时隙
- 从机不能自己说话
这是一种:主机驱动的同步通信
主机在这些命令后会立刻读:
- Read Scratchpad [BEh]
- Read Power Supply [B4h]
另外还能用读时隙查询状态:
- Convert T [44h](温度转换是否完成)
- Recall E2 [B8h]

读时隙由主机发起:
- 主机拉低总线 ≥ 1µs
- 主机释放总线
- DS18B20 在这个窗口发 0 或 1
- 发 1 → 什么都不做 → 线被上拉 → 高电平
- 发 0 → 主动拉低总线,(DS18B20 在时隙末尾释放总线,上拉电阻恢复高电平)
整个 Read Slot 要 ≥ 60µs 长,时隙间恢复 ≥ 1µs
因此DS18B20 的数据在读时隙开始后 15µs 内有效,所以主机必须:在 15µs 内完成采样

示例
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 | // ROM Command// Function Commandstruct ds18b20 { dev_t dev_num; struct cdev cdev; struct class *class; struct device *dev; struct gpio_desc *gpiod; struct fasync_struct *fa; u8 precision; // wait_queue_head_t wq;};int ds18b20_reset(struct gpio_desc *gpiod){ int timeout; // host send reset pulse gpiod_direction_output(gpiod, 0); udelay(500); // >= 480 us // host release the bus gpiod_direction_input(gpiod); udelay(60); // 15 us ~ 60 us // ds18b20 send presence pulse timeout = 240; while (gpiod_get_value(gpiod)) { // 60 us ~ 240 us if (timeout == 0) goto err; udelay(1); timeout--; } // resistor pullup timeout = 240; while (!gpiod_get_value(gpiod)) { // 60 us ~ 240 us if (timeout == 0) goto err; udelay(1); timeout--; } udelay(480); return 0;err: return -EIO;}void ds18b20_write_bit(struct gpio_desc *gpiod, char bit){ gpiod_direction_output(gpiod, 0); if (bit) { udelay(6); gpiod_direction_input(gpiod); udelay(80); // 60 ~ 120 } else { udelay(86); gpiod_direction_input(gpiod); } udelay(1);}void ds18b20_write_byte(struct gpio_desc *gpiod, char byte){ int i; for (i = 0; i < 8; i++) ds18b20_write_bit(gpiod, (byte >> i) & 0x1);}u8 ds18b20_read_bit(struct gpio_desc *gpiod){ char bit; gpiod_direction_output(gpiod, 0); udelay(2); // 1 us ~ 15 us, as smaller as possible gpiod_direction_input(gpiod); udelay(12); // wait for 12 us then read value bit = gpiod_get_value(gpiod); udelay(50); // >= 45us return bit;}u8 ds18b20_read_byte(struct gpio_desc *gpiod){ int i; char value = 0; for (i = 0; i < 8; i++) value |= ds18b20_read_bit(gpiod) << i; return value;}int ds18b20_open(struct inode *inode, struct file *file){ struct ds18b20 *drv_data = container_of(inode->i_cdev, struct ds18b20, cdev); //struct gpio_desc *gpiod = drv_data->gpiod; file->private_data = drv_data; // 精度设置为12 drv_data->precision = 12; pr_info("%s success, default precision is %d\n", __func__, drv_data->precision); return 0;}int ds18b20_release(struct inode *inode, struct file *file){ pr_info("%s\n", __func__); return 0;}ssize_t ds18b20_read(struct file *file, char __user *buf, size_t size, loff_t *offset){ char ls_byte, ms_byte; struct ds18b20 *drv_data = file->private_data; struct gpio_desc *gpiod = drv_data->gpiod; int16_t raw_temp; size_t count = min(sizeof(int16_t), size); int ret; // 复位 ret = ds18b20_reset(gpiod); if (ret < 0) { pr_err("%s: reset fail\n", __func__); return ret; } // 只有一个ds18b20, 发送Skip ROM ds18b20_write_byte(gpiod, ROM_CMD_SKIP_ROM); // Function Command, 发送Convert T ds18b20_write_byte(gpiod, FUNC_CMD_CONVERT_T); switch (drv_data->precision) { case 12: msleep(750); break; case 11: msleep(375); break; case 10: msleep(188); break; case 9: msleep(94); break; default: pr_err("unsupported precision\n"); return -EFAULT; } // 复位 ret = ds18b20_reset(gpiod); if (ret < 0) { pr_err("%s: reset fail\n", __func__); return ret; } // 只有一个ds18b20, 发送Skip ROM ds18b20_write_byte(gpiod, ROM_CMD_SKIP_ROM); // Function Command, 发送Read Scratchpad ds18b20_write_byte(gpiod, FUNC_CMD_READ_SCRATCHPAD); // read val ls_byte = ds18b20_read_byte(gpiod); ms_byte = ds18b20_read_byte(gpiod); raw_temp = (int16_t)(((u16)ms_byte << 8) | ls_byte); pr_info("raw_temp is %d\n", raw_temp); if (copy_to_user(buf, &raw_temp, count) != 0) return -EFAULT; // TODO CRC return count;}long ds18b20_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ char ls_byte, ms_byte; struct ds18b20 *drv_data = file->private_data; struct gpio_desc *gpiod = drv_data->gpiod; int16_t raw_temp; int ret; switch (cmd) { case DS_CMD_START: // 复位 ret = ds18b20_reset(gpiod); if (ret < 0) { pr_err("%s: ds18b20 reset failed\n", __func__); return ret; } // 只有一个ds18b20, 发送Skip ROM ds18b20_write_byte(gpiod, ROM_CMD_SKIP_ROM); // Function Command, 发送Convert T ds18b20_write_byte(gpiod, FUNC_CMD_CONVERT_T); switch (drv_data->precision) { case 12: msleep(750); break; case 11: msleep(375); break; case 10: msleep(188); break; case 9: msleep(94); break; default: pr_err("unsupported precision\n"); return -EFAULT; } break; case DS_CMD_SET_PREC: break; case DS_CMD_GET_TEMP: // 复位 ret = ds18b20_reset(gpiod); if (ret < 0) { pr_err("%s: reset fail\n", __func__); return ret; } // 只有一个ds18b20, 发送Skip ROM ds18b20_write_byte(gpiod, ROM_CMD_SKIP_ROM); // Function Command, 发送Read Scratchpad ds18b20_write_byte(gpiod, FUNC_CMD_READ_SCRATCHPAD); // read val ls_byte = ds18b20_read_byte(gpiod); ms_byte = ds18b20_read_byte(gpiod); raw_temp = (int16_t)(((u16)ms_byte << 8) | ls_byte); pr_info("raw_temp is %d\n", raw_temp); // if (copy_to_user((void *)arg, &raw_temp, sizeof(int16_t)) != 0) // return -EFAULT; if(put_user(raw_temp, (int16_t __user *)arg)) return -EFAULT; break; default: pr_err("unknown ioctl cmd\n"); return -EFAULT; } return 0;}int ds18b20_fasync(int fd, struct file *file, int on){ struct ds18b20 *drv_data = file->private_data; return fasync_helper(fd, file, on, &drv_data->fa);}struct file_operations ds18b20_fops = { .owner = THIS_MODULE, .open = ds18b20_open, .release = ds18b20_release, .read = ds18b20_read, .llseek = no_llseek, .unlocked_ioctl = ds18b20_unlocked_ioctl,};static int ds18b20_probe(struct platform_device *pdev){ int ret; //struct device *dev = &pdev->dev; struct ds18b20 *ds18b20; ds18b20 = kzalloc(sizeof(*ds18b20), GFP_KERNEL); if (!ds18b20) { ret = -ENOMEM; goto err_kzalloc; } ret = alloc_chrdev_region(&ds18b20->dev_num, 0, 1, "ds18b20"); if (ret < 0) goto err_chrdev_region; cdev_init(&ds18b20->cdev, &ds18b20_fops); ds18b20->cdev.owner = THIS_MODULE; ret = cdev_add(&ds18b20->cdev, ds18b20->dev_num, 1); if (ret < 0) goto err_cdev; ds18b20->class = class_create(THIS_MODULE, "one-wire"); if (IS_ERR(ds18b20->class)) { ret = PTR_ERR(ds18b20->class); goto err_class; } // /sys/class/one-wire/ds18b20 ds18b20->dev = device_create(ds18b20->class, NULL, ds18b20->dev_num, NULL, "ds18b20"); if (IS_ERR(ds18b20->dev)) { ret = PTR_ERR(ds18b20->dev); goto err_device_create; } ds18b20->gpiod = gpiod_get_optional(&pdev->dev, "ds18b20", GPIOD_OUT_HIGH); if (IS_ERR_OR_NULL(ds18b20->gpiod)) { if (ds18b20->gpiod == NULL) ret = -ENODEV; else if (IS_ERR(ds18b20->gpiod)) ret = PTR_ERR(ds18b20->gpiod); goto err_get_gpio; } platform_set_drvdata(pdev, ds18b20); return 0;err_get_gpio: device_destroy(ds18b20->class, ds18b20->dev_num);err_device_create: class_destroy(ds18b20->class);err_class: cdev_del(&ds18b20->cdev);err_cdev: unregister_chrdev_region(ds18b20->dev_num, 1);err_chrdev_region: kfree(ds18b20);err_kzalloc: return ret;}static int ds18b20_remove(struct platform_device *pdev){ struct ds18b20 *ds18b20 = platform_get_drvdata(pdev); gpiod_put(ds18b20->gpiod); device_destroy(ds18b20->class, ds18b20->dev_num); cdev_del(&ds18b20->cdev); class_destroy(ds18b20->class); unregister_chrdev_region(ds18b20->dev_num, 1); kfree(ds18b20); return 0;}const struct of_device_id match_table[] = { { .compatible = "even629,ds18b20" },};struct platform_driver ds18b20_pdrv = { .driver = { .name = "ds18b20", .owner = THIS_MODULE, .of_match_table = match_table, }, .probe = ds18b20_probe, .remove = ds18b20_remove,};module_platform_driver(ds18b20_pdrv);MODULE_LICENSE("GPL");MODULE_AUTHOR("even629<asqwgo@outlook.com>");MODULE_DESCRIPTION("This is a test sample for ds18b20"); |

