跳转至

onewire - 单总线协议库

作者:王棚嶙

一、概述

OneWire(单总线)是一种由Dallas Semiconductor(现Maxim Integrated)开发的通信协议,它只需要一根数据线(加上地线)即可实现双向通信。这种协议广泛应用于温度传感器(如DS18B20)、存储器、ADC/DAC等设备。LuatOS支持OneWire协议,允许开发者方便地与各种单总线设备进行通信。

1.1 OneWire协议简介

  • 协议特点:单线双向通信,节省IO资源
  • 通信速率:标准模式16kbps,高速模式142kbps
  • 拓扑结构:总线型拓扑,支持多设备并联
  • 设备类型:温度传感器、存储器、ADC、DAC等
  • 供电方式:支持寄生供电和外部供电

1.2 硬件接口特性

  • GPIO映射:可配置任意GPIO作为OneWire总线
  • 上拉电阻:需要4.7kΩ上拉电阻到VCC(3.3V)
  • 总线电容:建议总线电容不超过1000pF
  • 电缆长度:标准模式下可达100米

1.3 设备地址说明

OneWire设备地址由8字节组成:

--[[
设备地址格式:
字节0:家族码(Family Code)
字节1-6:序列号(Serial Number)
字节7:CRC校验码

常见家族码:
0x10:DS18S20温度传感器
0x28:DS18B20温度传感器  
0x22:DS1822温度传感器
0x42:DS28EA00温度传感器
]]

二、核心示例

1、核心示例是指:使用本库文件提供的核心 API,开发的基础业务逻辑的演示代码;

2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;

3、更加完整和详细的 demo,请参考 LuatOS 仓库 中各个产品目录下的 demo/onewire;

-- 示例一
-- 初始化OneWire总线
onewire.init(0)  -- 初始化硬件单总线ID为0
-- 配置时序参数
onewire.timing(0, false, 0, 480, 70, 70, 410, 70, 10, 10, 15, 70)  -- DS18B20标准时序

-- 读取DS18B20温度(假设已知设备存在)
onewire.reset(0, true)  -- 复位总线并检测设备

-- 发送温度转换命令
onewire.tx(0, 0x44, false, false, false)  -- Convert T命令
sys.wait(750)  -- 等待转换完成(12位精度需要750ms)

-- 读取温度数据
onewire.reset(0, true)  -- 再次复位
onewire.tx(0, 0xBE, false, false, false)  -- Read Scratchpad命令
local succ, data = onewire.rx(0, 9, false, false, false)  -- 读取9字节数据

if succ and data and #data == 9 then
    -- 计算温度(简化的整数部分)
    local temp = data:byte(1) + (data:byte(2) * 256)
    if temp > 32767 then temp = temp - 65536 end  -- 处理负温度
    temp = temp * 0.0625  -- DS18B20的分辨率为0.0625°C
    log.info("温度", "读取成功", "温度:", temp, "°C")
end


-- 示例二
-- 初始化总线
onewire.init(0)  -- 初始化硬件单总线ID为0
onewire.timing(0, false, 0, 480, 70, 70, 410, 70, 10, 10, 15, 70)  -- DS18B20标准时序

-- 定义温度轮询函数
local function pollTemperature()
    -- 启动温度转换
    onewire.reset(0, true)  -- 复位总线
    onewire.tx(0, 0x44, false, false, false)  -- 发送Convert T命令给所有设备
    sys.wait(100)  -- 等待转换

    -- 读取温度(跳过ROM命令,读取第一个设备)
    onewire.reset(0, true)  -- 再次复位
    onewire.tx(0, 0xCC, false, false, false)  -- Skip ROM命令
    onewire.tx(0, 0xBE, false, false, false)  -- Read Scratchpad命令
    local succ, temp_data = onewire.rx(0, 2, false, false, false)  -- 读取2字节温度数据

    if succ and temp_data and #temp_data == 2 then
        local temp = temp_data:byte(1) + temp_data:byte(2) * 256
        if temp > 32767 then temp = temp - 65536 end
        temp = temp * 0.0625
        log.info("温度传感器", temp_data:toHex(), temp, "°C")
    end
end

-- 启动定时器轮询
sys.timerLoopStart(pollTemperature, 2000)  -- 每2秒轮询一次


-- 示例三
-- 初始化OneWire
onewire.init(0)  -- 初始化硬件单总线ID为0
onewire.timing(0, false, 0, 480, 70, 70, 410, 70, 10, 10, 15, 70)  -- DS18B20标准时序

-- 定义总线状态检查函数
local function checkBusStatus()
    -- 检查总线状态
    local present = onewire.reset(0, true)
    if present then
        log.info("OneWire", "总线正常,设备存在")

        -- 尝试读取设备ROM ID(使用Read ROM命令0x33)
        onewire.reset(0, true)  -- 复位
        onewire.tx(0, 0x33, false, false, false)  -- Read ROM命令
        local succ, rom_data = onewire.rx(0, 8, false, false, false)  -- 读取8字节ROM数据

        if succ and rom_data and #rom_data == 8 then
            log.info("设备ROM", rom_data:toHex())
        else
            log.warn("OneWire", "读取设备ROM失败")
        end
    else
        log.warn("OneWire", "总线异常,无设备响应")
    end
end

-- 启动状态监控定时器
sys.timerLoopStart(checkBusStatus, 5000)  -- 每5秒检查一次

三、常量详解

核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;

onewire核心库无常量

四、函数详解

4.1 onewire.init(id)

功能

初始化OneWire总线;

注意事项

初始化硬件单总线,每个ID对应一个硬件单总线;

参数

id

参数含义:OneWire总线ID
数据类型:number
取值范围:0~7
是否必选:是;
注意事项:每个ID对应一个独立的OneWire总线,如果只有一条则随意填写;
参数示例:0

返回值

无返回值

示例

-- 初始化OneWire总线0
onewire.init(0)
log.info("onewire", "初始化完成")

4.2 onewire.timing(id, is_tick, clk_div, tRSTL, tRSTH, tPDHIGH, tPDLOW, tSLOT, tStart, tLOW1, tRDV, tREC)

功能

配置OneWire总线时序参数,用于匹配不同的单总线设备;

注意事项

1、如果不配置,默认情况下是直接匹配DS18B20;

2、标准模式通信距离更远,高速模式速度更快;

3、需要在初始化后调用;

4、影响所有后续通信的时序;

5、从Maxim Integrated官网或其他芯片供应商处获取对应传感器的官方数据手册,重点关注"Timing Diagrams"或"Electrical Characteristics"章节,来查找时序图,芯片手册定义了芯片的理论能力范围;

6、DS18S20/DS18B20/DS1822/DS28EA00,这四种温度传感器请参考第六章节;

参数

id

参数含义:OneWire总线ID
数据类型:number
取值范围:0~7
是否必选:是;
注意事项:必须是已初始化的总线ID
参数示例:0

is_tick

参数含义:后续时序参数是否是tick
数据类型:boolean
取值范围:true-tickfalse-单位是μs
是否必选:是;
注意事项:除非具体平台有特殊要求,一般是μs
参数示例:false

clk_div

参数含义:tick参数下的分频系数
数据类型:number
取值范围:建议分频到1tick=1个μs
是否必选:是;
注意事项:如果μs参数,本参数忽略;
参数示例:0

tRSTL

参数含义:reset拉低总时间
数据类型:number
取值范围:有效的时序值;
是否必选:是;
注意事项:单位根据is_tick决定
参数示例:500

tRSTH

参数含义:reset释放总时间
数据类型:number
取值范围:有效的时序值;
是否必选:是;
注意事项:单位根据is_tick决定
参数示例:500

tPDHIGH

参数含义:reset释放到开始探测时间
数据类型:number
取值范围:有效的时序值;
是否必选:是;
注意事项:单位根据is_tick决定
参数示例:15

tPDLOW

参数含义:reset探测时间
数据类型:number
取值范围:有效的时序值;
是否必选:是;
注意事项:单位根据is_tick决定
参数示例:240

tSLOT

参数含义:通信总有效时间;
数据类型:number
取值范围:有效的时序值;
是否必选:是;
注意事项:单位根据is_tick决定
参数示例:65

tStart

参数含义:通信start信号时间,一般就是开头拉低;
数据类型:number
取值范围:有效的时序值;
是否必选:是;
注意事项:单位根据is_tick决定
参数示例:1

tLOW1

参数含义:start信号到允许写的时间
数据类型:number
取值范围:有效的时序值;
是否必选:是;
注意事项:单位根据is_tick决定
参数示例:15

tRDV

参数含义:start信号到允许读的时间
数据类型:number
取值范围:有效的时序值;
是否必选:是;
注意事项:单位根据is_tick决定
参数示例:15

tREC

参数含义:通信结束前恢复时间;
数据类型:number
取值范围:有效的时序值;
是否必选:是;
注意事项:单位根据is_tick决定
参数示例:2

返回值

无返回值

示例

-- 配置单总线时序匹配DS18B20,保留了点余量
onewire.timing(0, false, 0, 500, 500, 15, 240, 65, 1, 15, 15, 2)
log.info("onewire", "时序配置成功")

4.3 onewire.reset(id, need_ack)

功能

发送复位脉冲并检测设备是否存在;

注意事项

1、复位脉冲的使用策略:

- 设备初始化阶段:建议发送复位脉冲确保设备就绪;

- 关键命令前:如温度转换、ROM操作等建议复位;

- 连续数据传输:已知设备在线可省略复位以提高效率;

- 错误恢复:通信失败时应重新发送复位脉;

2、返回值表示是否有设备响应;

3、会重置总线上的所有设备;

参数

id

参数含义:OneWire总线ID
数据类型:number
取值范围:0~7
是否必选:是;
注意事项:必须是已初始化的总线ID
参数示例:0

need_ack

参数含义:是否需要检测应答信号;
数据类型:boolean
取值范围:true-需要检测,false-不需要检测;
是否必选:是;
注意事项:true需要检测false不需要
参数示例:true

返回值

local present = onewire.reset(id, need_ack)

有一个返回值 present

present

含义说明:设备存在状态;
数据类型:boolean
取值范围:true或false
注意事项:检测到应答或无需检测返回true,失败返回false
返回示例:true

示例

-- 复位总线并检测设备
local present = onewire.reset(0, true)
if present then
    log.info("onewire", "检测到设备")
else
    log.warn("onewire", "未检测到设备")
end

4.4 onewire.bit(id, send1bit)

功能

硬件单总线发送或接收1bit;

注意事项

1、可以发送或接收单个位;

2、发送单个位时,send1bit参数为要发送的值;

3、接收单个位时,send1bit参数为nil或留空;

参数

id

参数含义:OneWire总线ID
数据类型:number
取值范围:0~7
是否必选:是;
注意事项:必须是已初始化的总线ID
参数示例:0

send1bit

参数含义:发送bit的电平1高电平,0低电平,留空或者其他值则是读1bit
数据类型:number/nil
取值范围:01
是否必选:否;
注意事项:发送时为01,接收时为nil
参数示例:1

返回值

local result = onewire.bit(id, send1bit)

有一个返回值 result

result

含义说明:操作结果;
数据类型:int
取值范围:如果是发送则忽略结果,如果是接收则是接收到的电平;
注意事项:发送模式返回0,接收模式返回接收到的位值(01)
返回示例:1

示例

-- 发送1bit高电平
onewire.bit(0, 1)
-- 读取1bit数据
local bit_value = onewire.bit(0)

4.5 onewire.tx(id, data, is_msb, need_reset, need_ack)

功能

发送数据到OneWire设备;

注意事项

1、支持发送整数、字符串或zbuff数据;

2、如果是整数则是1个字节数据,如果是zbuff则是从开头到指针前的数据;

3、可以控制是否需要先发送reset信号;

参数

id

参数含义:OneWire总线ID
数据类型:number
取值范围:0~7
是否必选:是;
注意事项:必须是已初始化的总线ID
参数示例:0

data

参数含义:要发送的数据;
数据类型:int/string/zbuff
取值范围:有效的数据;
是否必选:是;
注意事项:如果是int则是1个字节数据,如果是zbuff则是从开头到指针前的数据
参数示例:0x33

is_msb

参数含义:是否需要先发送MSB
数据类型:boolean
取值范围:true-是,false-不是;
是否必选:否;
注意事项:默认情况下都是false
参数示例:false

need_reset

参数含义:是否需要先发送reset
数据类型:boolean
取值范围:true-需要,false-不需要;
是否必选:否;
注意事项:true需要检测false不需要(默认),必须满足以下条件才能省略reset
         1、总线状态稳定 - 没有设备插入/移除
         2、设备已初始化 - 之前的reset已成功建立通信
         3、时序要求宽松 - 不是严格的协议要求场景
         4、错误容忍度高 - 允许偶尔的通信失败;
参数示例:true

need_ack

参数含义:是否需要检测应答信号;
数据类型:boolean
取值范围:true-需要检测,false-不需要检测;
是否必选:否;
注意事项:true需要检测false不需要(默认);
参数示例:true

返回值

local result = onewire.tx(id, data, is_msb, need_reset, need_ack)

有一个返回值 result

result

含义说明:发送结果;
数据类型:boolean
取值范围:true或false
注意事项:检测到应答或无需检测返回true,失败或参数错误返回false
返回示例:true

示例

-- 复位并检测ACK,接收到ACK后发送0x33
local succ = onewire.tx(0, 0x33, false, true, true)
-- 复位后发送0x33,无视从机是否ACK
local succ = onewire.tx(0, 0x33, false, true, false)
-- 直接发送0x33
local succ = onewire.tx(0, 0x33)

4.6 onewire.rx(id, len, cmd, buff, is_msb, need_reset, need_ack)

功能

硬件单总线读取N字节数据;

注意事项

1、可以返回字符串或zbuff数据;

2、如果是zbuff,则是从开头到指针前的数据;

3、可以控制是否需要先发送reset信号;

参数

id

参数含义:OneWire总线ID
数据类型:number
取值范围:0~7
是否必选:是;
注意事项:必须是已初始化的总线ID
参数示例:0

len

参数含义:需要读取的字节数量;
数据类型:number
取值范围:1~256
是否必选:是;
注意事项:接收数据长度,不能超过缓冲区大小;
参数示例:8

cmd

参数含义:在读取前发送命令,可以填nil不发送任何命令
数据类型:int/nil
取值范围:有效的命令或nil
是否必选:否;
注意事项:如果需要在读取前发送命令,可以指定命令字节;
参数示例:0x33

buff

参数含义:接收数据缓存,接收前会清空整个缓存,如果填nil则输出字符串
数据类型:zbuff/nil
取值范围:有效的zbuff或nil
是否必选:否;
注意事项:如果填nil则返回字符串数据
参数示例:zbuff(8)

is_msb

参数含义:是否需要先接收MSBtrue是false不是,默认情况下都是false
数据类型:boolean
取值范围:true或false
是否必选:否;
注意事项:默认情况下都是false
参数示例:false

need_reset

参数含义:是否需要先发送resettrue需要检测false不需要
数据类型:boolean
取值范围:true或false
是否必选:否;
注意事项:true需要检测false不需要(默认),必须满足以下条件才能省略reset
         1、总线状态稳定 - 没有设备插入/移除
         2、设备已初始化 - 之前的reset已成功建立通信
         3、时序要求宽松 - 不是严格的协议要求场景
         4、错误容忍度高 - 允许偶尔的通信失败;;
参数示例:true

need_ack

参数含义:是否需要检测应答信号,true需要检测false不需要检测
数据类型:boolean
取值范围:true或false
是否必选:否,默认值false
注意事项:
         1. 应答信号检测原理:
         - 主机发送480-960μs复位脉冲
         - 等待15-60μs后检测设备是否拉低总线60-240μs
         - 设备拉低总线表示"存在并准备好通信",这就是应答信号

         2. true-需要检测应答信号:
         - 适用场景:设备发现、错误诊断、关键通信
         - 优点:确保设备在线,通信可靠性高
         - 缺点:增加通信时间和系统开销
         - 典型应用:DS18B20初始化ROM命令序列

         3. false-不需要检测应答信号:
         - 适用场景:已知设备稳定在线、连续数据传输
         - 优点:通信速度快,系统开销小
         - 风险:无法检测设备是否存在,可能通信失败
         - 典型应用:温度数据连续读取、状态轮询

         4. 使用建议:
           客户需根据自己的使用场景来决定要不要检测
         - 设备初始化阶段:need_ack=true
         - 稳定通信阶段:可设置为need_ack=false
         - 通信失败时:切换回need_ack=true进行诊断
         - 多设备环境:建议保持need_ack=true
         - 读数据时:建议保持need_ack=true
参数示例:true

返回值

local succ, rx_data = onewire.rx(id, len, cmd, buff, is_msb, need_reset, need_ack)

有两个返回值 succ 和 rx_data

succ

含义说明:检测到应答或无需检测返回true,失败或者参数错误返回false
数据类型:boolean
取值范围:true或false
注意事项:true-成功,false-失败;
返回示例:true

rx_data

含义说明:接收到的数据;
数据类型:string
取值范围:接收到的数据;
注意事项:如果buff填nil,则接收数据从这里输出;
返回示例:"\x01\x02\x03"

示例

-- 直接接收8个字节
local succ, rx_data = onewire.rx(0, 8)
-- 先发送reset,检查ack信号,发送0x33,接收8个字节,这是DS18B20读ROM ID标准流程
local succ, rx_data = onewire.rx(0, 8, 0x33, buf, nil, true, true)

4.7 onewire.debug(id, onoff)

功能

单总线调试开关;

注意事项

1、开启后会输出详细的调试信息;

2、有助于排查通信问题;

3、量产环境建议关闭;

参数

id

参数含义:HW模式是硬件单总线编号,如果只有一条则随意填写;
数据类型:number
取值范围:0~7
是否必选:是;
注意事项:必须是已初始化的总线ID
参数示例:0

onoff

参数含义:调试开关;
数据类型:boolean
取值范围:true或false
是否必选:是;
注意事项:true-打开调试,false-关闭调试;
参数示例:true

返回值

无返回值

示例

-- 打开调试模式
onewire.debug(0, true)
-- 关闭调试模式
onewire.debug(0, false)

4.8 onewire.deinit(id)

功能

关闭单总线;

注意事项

1、释放相关资源;

2、关闭后不能再使用该总线,除非重新初始化;

3、建议在程序退出或不再需要时调用;

参数

id

参数含义:单总线ID编号
数据类型:number
取值范围:0~7
是否必选:是;
注意事项:必须是已初始化的总线ID
参数示例:0

返回值

无返回值

示例

-- 关闭总线0
onewire.deinit(0)

五、产品支持列表

支持 LuatOS 开发的所有产品都支持onewire核心库。

六、参考资料

DS18B20 datasheet:DS18B20

DS18S20 datasheet:DS18S20

DS1822 datasheet:DS1822

DS28EA00 datasheet:DS28EA00