Flipper Zero 1-Wire 温度计示例解析:在 Unleashed 固件中用 DS18B20 构建完整应用
2026/9/13 7:27:54 网站建设 项目流程

Flipper Zero 1-Wire 温度计示例解析:在 Unleashed 固件中用 DS18B20 构建完整应用

【免费下载链接】unleashed-firmwareFlipper Zero Unleashed Firmware项目地址: https://gitcode.com/GitHub_Trending/un/unleashed-firmware

本篇技术指南以 Unleashed 固件仓库中的 example_thermo 示例应用 为骨架,完整讲解如何将 DS18B20 数字温度传感器通过 1-Wire 总线连接到 Flipper Zero 的外部 GPIO,并跑通一个集温度采集、屏幕绘制、按键响应、后台线程与本地化显示于一体的示例程序。读完本文,你将掌握 1-Wire 主机库的初始化与通信流程、DS18B20 温区寄存器解析方法、通过fbt编译启动 FAP 的完整命令,以及如何更换数据引脚并理解外置上拉电阻的硬件前提。

示例应用概览与源码入口

example_thermo是一个 FlipperAppType.EXTERNAL 类型的示例应用(参见 application.fam),应用标识为example_thermo,入口函数为example_thermo_main,需要依赖gui服务,栈大小为 1KB,属于Examples分类。它的核心作用有两个层面:

  • 演示 DS18B20 的 1-Wire 驱动用法:包括复位、写命令、读暂存器(Scratchpad)、CRC 校验与温度值换算;
  • 演示完整应用的四个基本功:GUI 绘制(ViewPort + Canvas)、输入事件处理(消息队列)、线程(FuriThread 标志量协作)、本地化(locale 公制/英制单位切换)。

主程序源码位于 example_thermo.c,执行流程为:example_thermo_main→ 分配上下文example_thermo_context_alloc→ 运行主循环example_thermo_run→ 释放资源example_thermo_context_free。这种「分配上下文 → 运行循环 → 释放资源」三段式结构是 Flipper 应用的标准范式。

电气连接:DS18B20 与 Flipper 外部 GPIO

在启动应用之前,需要按下表将传感器与 Flipper 的 GPIO 排针相连(对应引脚位置见 furi_hal_resources.h 中定义的引脚映射):

DS18B20Flipper 引脚
VDD9
GND18
DQ17

两条重要注意事项:

  • NOTE 1:GND 也可以使用排针上的 8 号或 11 号引脚,三者电气等价;
  • NOTE 2:默认引脚 17(即gpio_ibutton,映射到 iButton 端口,见 furi_hal_resources.c)内部已带有上拉能力;一旦改用其他引脚,必须在 DQ 与 VDD(9 号脚)之间外接一个 4.7kΩ 上拉电阻。

从源码层面看,这一约束与 1-Wire 总线的电气特性直接相关:总线空闲时必须被上拉至高电平,时序操作依赖主机拉低与释放的组合。在 one_wire_host_start 中,引脚被初始化为GpioModeOutputOpenDrain(开漏输出),开漏结构本身不具备拉高能力,因此外部上拉电阻是总线正确工作的物理前提。

启动应用:fbt 命令行全流程

启动该演示应用需要依次完成以下步骤:

  1. 确保 Flipper 中已插入 SD 卡(示例以 FAP 形式运行,需要 SD 卡存放应用文件);
  2. 用 USB 数据线将 Flipper 连接到电脑;
  3. 在终端中运行编译并启动命令:
./fbt launch APPSRC=example_thermo

fbt是 Unleashed 固件的统一构建工具(位于仓库根目录,脚本入口 fbt),launch子命令会完成 FAP 的交叉编译、通过 USB 部署到设备并从 CLI 侧启动该应用。若需要先构建而不启动,可使用./fbt fap_example_thermo之类目标;开发调试的更多用法可参考 fbt.md 与 AppManifests.md。运行后屏幕会显示Thermometer Demo标题与「Connect thermometer to GPIO pin 17」提示,温度读数实时刷新,按 Back 键退出。

更换数据引脚:THERMO_GPIO_PIN 宏详解

示例允许通过修改宏来更换 1-Wire 数据引脚,可选值如下(源码注释与 furi_hal_resources.h 中均可查证):

/* Possible GPIO pin choices: - gpio_ext_pc0 - gpio_ext_pc1 - gpio_ext_pc3 - gpio_ext_pb2 - gpio_ext_pb3 - gpio_ext_pa4 - gpio_ext_pa6 - gpio_ext_pa7 - gpio_ibutton */ #define THERMO_GPIO_PIN (gpio_ibutton)

修改要点:

  • 这些符号均为const GpioPin类型的外部引脚描述符,定义于 furi_hal_resources.c,例如gpio_ext_pa6对应GPIOA端口的LL_GPIO_PIN_6
  • gpio_ibutton外,其余引脚没有内置上拉电阻,更换后必须外接 4.7kΩ 上拉电阻到 9 号脚,否则总线无法工作;
  • 修改后需重新编译并启动应用,屏幕上的提示文字会自动更新为对应的引脚号——这是通过furi_hal_resources_get_ext_pin_number(&THERMO_GPIO_PIN)动态获取引脚编号实现的(见 example_thermo.c)。

值得注意的是,部分可选引脚在系统其他功能中被复用,例如gpio_ext_pa6/gpio_ext_pa7同时被调试输出与 PWM 使用(参见 furi_hal_os.c 与 furi_hal_pwm.c),选择引脚时需避免与已启用的外设冲突。

1-Wire 主机库:底层实现与关键 API

示例依赖的one_wire库位于 lib/one_wire,由主机端(one_wire_host.h)、从机端(one_wire_slave)与 Maxim CRC(maxim_crc.h)组成,构建配置见 SConscript。

时序参数:软件模拟 1-Wire 协议

one_wire_host通过 GPIO 位操作软件模拟 1-Wire 时序,没有依赖硬件外设。在 one_wire_host.c 中定义了三种时序表(基于 Maxim Application Note 126):

  • normal(标准模式,默认):写 1 低电平 9µs/高电平 64µs,写 0 低电平 64µs,复位脉冲 480µs,存在检测采样 70µs 后延迟 410µs;
  • overdrive(超速模式):所有时序缩短到约 1/8,复位脉冲仅 70µs;
  • tm01x(面向 TM01x 器件):复位脉冲 740µs,存在检测 140µs。

可通过onewire_host_set_overdrive/onewire_host_set_timings_default/onewire_host_set_timings_tm01x切换。示例使用默认时序,即可满足 DS18B20 标准速率通信。

常用 API 与调用关系

API作用底层实现要点
onewire_host_alloc(gpio_pin)按引脚分配主机实例,初始化搜索状态校验指针、置默认时序,见 one_wire_host.c
onewire_host_start初始化引脚为开漏输出GpioModeOutputOpenDrain,见 one_wire_host.c
onewire_host_reset总线复位并检测存在脉冲拉低 480µs 后释放,采样返回是否有设备应答,见 one_wire_host.c
onewire_host_write写一个字节(LSB 先行)逐位调用onewire_host_write_bit
onewire_host_read_bytes连续读取多个字节逐字节调用onewire_host_read
onewire_host_search总线设备 ROM 搜索(支持条件搜索模式)实现标准三态位搜索算法,见 one_wire_host.c
onewire_host_stop释放引脚恢复为GpioModeAnalog,见 one_wire_host.c

示例场景只有单个传感器,因此在每次事务前调用onewire_host_reset后直接发送 Skip ROM(0xCC)即可,无需执行 ROM 搜索;若总线上挂接多个 1-Wire 器件,则需改用onewire_host_search获取 8 字节 ROM 地址后再按地址寻址。

CRC 校验:maxim_crc8

DS18B20 暂存器第 9 字节是 Maxim 校验和,示例用 maxim_crc8 对前 8 个字节计算并与设备返回的 CRC 比对,匹配才采信数据。该实现为逐位多项式(多项式0x8C)算法,初始值为MAXIM_CRC8_INIT(0)。

温度读取协议:从命令序列到数值换算

读取流程在example_thermo_reader_thread_callback(后台线程)中以 1000ms 周期循环执行(宏UPDATE_PERIOD_MS),分两阶段完成。

阶段一:请求转换(request)

example_thermo_request_temperature在临界区(FURI_CRITICAL_ENTER/EXIT)内完成三条命令:

  1. onewire_host_reset—— 复位总线,若返回 false 说明无设备,直接放弃并置has_device = false
  2. DS18B20_CMD_SKIP_ROM0xCC)—— 跳过 ROM 寻址,针对单设备总线;
  3. DS18B20_CMD_CONVERT0x44)—— 启动温度转换。

DS18B20 完成一次转换最长需要约 750ms,因此线程随后通过furi_thread_flags_wait(ReaderThreadFlagExit, FuriFlagWaitAny, UPDATE_PERIOD_MS)既等待转换完成,又等待退出信号;超时(FuriFlagErrorTimeout)说明 1 秒已到、转换就绪,继续读取;收到ReaderThreadFlagExit标志则退出线程。

阶段二:读取暂存器并解析(read)

example_thermo_read_temperature同样在临界区内执行,最多重试 10 次:

  1. 复位总线,发送 Skip ROM,再发送DS18B20_CMD_READ_SCRATCHPAD0xBE)准备读取;
  2. onewire_host_read_bytes读取 9 字节暂存器内容,结构体DS18B20Scratchpad依次为:temp_lsbtemp_msb、高/低报警寄存器、配置寄存器、3 字节保留、CRC;
  3. maxim_crc8校验前 8 字节与第 9 字节 CRC,不匹配则重试,10 次全失败则置has_device = false
  4. 校验通过后按 DS18B20 数据手册(示例源码引用了官方数据手册)换算温度。

温度数值换算详解

换算逻辑位于 example_thermo.c,核心步骤如下:

  • 分辨率提取resolution_mode = (config >> 5) & 0x03,对应 9/10/11/12 位分辨率(12 位为默认上电值,步进 0.0625°C);
  • 小数位掩码decimal_mask = (0x0F << (3 - resolution_mode)) & 0x0F,用于屏蔽低分辨率时无效的低位;
  • 整数部分integer_part = (temp_msb << 4) | (temp_lsb >> 4)
  • 小数部分decimal_part = temp_lsb & decimal_mask,每 1/16 位(LSB)代表 0.0625°C,故temp_abs = integer_part + decimal_part / 16.f
  • 符号判断(temp_msb & 0xF0) != 0表示负温,最终赋值为-temp_abstemp_abs

这段代码将原始寄存器字节还原为带符号浮点摄氏度,是整个示例最具教学价值的部分,可对照 DS18B20 数据手册第 6 页温度寄存器格式逐行核对。

GUI、输入与线程模型

屏幕绘制

绘制回调example_thermo_draw_callback在每次刷新时全量重绘:标题Thermometer Demo、分隔线、提示连接引脚号,随后根据has_device状态显示温度读数或-- No data --。温度显示通过snprintf格式化为%+.1f(保留一位小数并带符号),例如Temperature: +23.5C

本地化单位切换

示例是 locale-aware 的:根据locale_get_measurement_unit()(见 locale.h)判断单位制:

  • LocaleMeasurementUnitsMetric:直接显示摄氏温度,单位C
  • LocaleMeasurementUnitsImperial:调用locale_celsius_to_fahrenheit换算为华氏,单位F
  • 其他取值触发furi_crash("Illegal measurement units")作为防御性断言。

用户可在 Flipper 的Settings → System → Units中切换单位制,重进应用即可看到温度显示在 °C/°F 间变化,直观验证本地化机制。

输入与主循环

输入回调example_thermo_input_callback由 GUI 线程触发,只做一件事:把InputEvent投入容量为 8 的消息队列(furi_message_queue_alloc(8, sizeof(InputEvent)))。主循环example_thermo_run阻塞在furi_message_queue_get上,仅响应InputTypeShort类型的短按,当按键为InputKeyBack时置退出标志并跳出循环。退出时依次执行:向读取线程置ReaderThreadFlagExit标志 →furi_thread_join等待线程结束 →onewire_host_stop释放总线 →power_enable_otg(power, false)关闭外引脚电源。

外部引脚供电

example_thermo_run在启动时调用power_enable_otg(context->power, true)为外部 GPIO 排针提供 5V 输出(API 声明见 power.h,实现位于 power_api.c),退出时再关闭。这解释了 DS18B20 的 VDD 可直接接 Flipper 引脚 9 供电的原因;若自行接线改用外部电源,则需相应调整供电配置。

从示例到实战:关键注意事项小结

  1. 上拉电阻是硬前提:除默认的gpio_ibutton(引脚 17)外,其余数据引脚都必须外接 4.7kΩ 上拉至 VDD,否则复位检测与读写时序均不可靠;
  2. 临界区保护:1-Wire 位时序对时序抖动敏感,所有总线事务(复位、读写)都应放在FURI_CRITICAL_ENTER/EXIT临界区内执行,防止被其他线程打断导致时序错乱;
  3. 转换延时匹配:请求转换后需等待至多 750ms 再读取,示例用UPDATE_PERIOD_MS = 1000的线程标志等待同时兼顾退出响应,是「周期任务 + 优雅退出」的典型写法;
  4. CRC 校验不可省:1-Wire 在长线上易受干扰,读取暂存器后务必校验第 9 字节 CRC,失败应重试而不是直接采信;
  5. 引脚复用冲突:更换数据引脚前先确认该引脚未被调试输出、PWM、红外等其他外设占用(可在 furi_hal_resources.c 与对应外设驱动中核对)。

在此基础上,你可以把示例的读取逻辑抽取为可复用模块,接入自己的温度监控、数据记录或 BadUSB 联动的应用场景。

【免费下载链接】unleashed-firmwareFlipper Zero Unleashed Firmware项目地址: https://gitcode.com/GitHub_Trending/un/unleashed-firmware

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询