news 2026/10/20 5:06:51

基于STM32和DS18B20的温度报警系统设计与实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于STM32和DS18B20的温度报警系统设计与实现

简介:这是一套完整的嵌入式温度报警系统课程设计资料,基于STM32F401微控制器和DS18B20数字温度传感器,适合高校学生及嵌入式开发初学者。资源内容涵盖程序设计、电路仿真与设计报告三大部分,能够帮助读者掌握从硬件连接、驱动编写到界面显示与报警控制的完整流程。压缩包共一百九十个文件,大小约九点二七兆字节,主要包含Keil5工程中的C语言源程序与头文件、编译生成的目标文件与十六进制文件、Proteus仿真原理图文件,以及详细的课程设计文档,文件组织清晰,便于按需查阅。目前已有12670人学习下载,参考价值较高;该设计实现了温度实时测量、液晶屏显示、预警阈值手动设置、LED指示灯报警,并支持负一百到正一百摄氏度范围内的预警功能。配套设计报告包括需求分析、硬件设计、软件流程和测试结果,配合仿真图可直观验证整体系统工作情况,为课程设计答辩或二次开发提供了扎实基础。

1. 从课设题目到能稳定报警的实物:这套基于 STM32 的温度报警系统值钱在哪

把 DS18B20 的单总线时序抠对,这套基于 STM32 的温度报警设计基本就成了一半。作为嵌入式课程设计里出现频率最高的题目之一,它麻雀虽小五脏俱全:DS18B20 负责采集温度,STM32F103C8T6 负责解析单总线数据、判定越限,OLED 实时显示当前值,超限后继电器吸合、蜂鸣器鸣叫。不少同学第一次跑通都会撞见同样的怪现象——温度莫名读出 85 度,继电器在阈值附近咔咔乱跳,这些其实都有规律可循。这篇笔记从硬件选型讲到驱动源码,再到烧录调试和避坑记录,适合正在赶嵌入式课设进度的同学照着重现,也适合想把这套框架扩展成温度记录仪的开发者。项目本身不复杂,真正值钱的是那套 GPIO 模拟单总线时序的位操作模板,换成其他单总线传感器一样能复用。

2. 硬件选型与通信基础:DS18B20 单总线方案在课程设计里的取舍

2.1 传感器三选一:NTC、LM75 与 DS18B20

课程设计里最常用的三种温度采集方案是 NTC 热敏电阻、LM75 数字温度传感器和 DS18B20。NTC 得走 ADC 通道,要自己搭分压电路、查表或者套 Steinhart-Hart 公式做标定,模拟量还会受电源纹波和走线干扰,做出来精度一般、工作量却不小。LM75 走 I2C 接口,代码好写,但测温范围只有 -25 到 +100 度,课堂演示有余,拿来模拟高温报警场景偏窄。DS18B20 是 1-Wire 单总线方案,只占一根 GPIO,不需要 ADC,测温范围 -55 到 +125 度,12 位分辨率下 LSB 是 0.0625 度,-10 到 +85 度范围内典型误差 ±0.5 度,课程设计里用它最容易出效果。

方案接口精度表现主要踩坑点
NTC + ADC模拟采样依赖标定分压电阻、滤波、查表
LM75I2C数字直接读地址冲突、量程偏窄
DS18B201-Wire 单总线典型 ±0.5 度时序敏感、转换等待

三个方案都是合法选题,但 DS18B20 能同时展示时序协议、位操作和状态机思维,答辩时有得讲,后续挂多个传感器也顺理成章,所以这套设计最终选了它。

2.2 单总线协议要点:复位脉冲、写时隙与读时隙

1-Wire 总线只有一根数据线,主机和从机靠时序区分 0 和 1。DS18B20 上电后数据默认存在暂存器里,主机要完成一次读温度,必须走三步:复位脉冲、发 ROM 命令、发功能命令。复位是主机把线拉低至少 480us 再释放,从机在 60~240us 内回一个存在脉冲;跳过 ROM 用 0xCC,启动转换用 0x44,读暂存器用 0xBE。12 位分辨率下转换时间固定 750ms,这段时间里从机不响应任何命令。

读写位时序是这套驱动最容易翻车的地方。主机写 1 时拉低 1~15us 然后释放,剩下时间交给上拉电阻把线拉高;写 0 时得持续拉低 60~120us。读时序是主机先拉低 1~15us 释放,然后必须在 15us 内采样,因为从机只会把线拉低一小段时间来写 0,写 1 则什么都不做。15us 对 F103 的 GPIO 翻转速度来说绰绰有余,但对中断响应速度就是个考验,第 5 章会专门讲这个问题。

2.3 硬件清单与接线定义

按课程设计的常见配置,这套系统需要的物料和接线如下表。核心板用 STM32F103C8T6 最小系统板,OLED 用 SSD1306 的 128x64 I2C 模块,继电器和蜂鸣器都买模块而不是裸元件,省去自己搭驱动电路的时间。

器件型号/规格数量接入位置
主控STM32F103C8T6 最小系统板1-
温度传感器DS18B20 TO-921数据线 PA0
显示SSD1306 OLED 128x64 I2C1SCL-PB8,SDA-PB9
执行器5V 继电器模块,低电平触发1IN-PB1
报警有源蜂鸣器模块1IO-PB10
输入轻触按键,上拉低有效3PA1、PA2、PA3
调试ST-Link 或 USB-TTL1SWD 或 PA9/PA10

DS18B20 的数据线一定要在板外并一颗 4.7kΩ 上拉电阻到 3.3V,否则读回来的全是 0x00。如果手头没有独立上拉,可以换用开发板自带的 DS18B20 接口,很多 F103 核心板已经把这颗上拉电阻做在板上了。OLED 模块用软件 I2C 驱动,PB8/PB9 这两个脚不跟串口冲突,后续接调试线也方便。

3. 工程结构与核心模块拆解:一次温度采集从发起读到报出的完整链路

3.1 工程目录结构和代码分层

这套工程的典型布局是课程设计最常见的四层结构:USER 放 main 和中断向量,HARDWARE 放传感器和模块驱动,SYSTEM 放 delay 和 usart 这类基础服务,CORE 放启动文件。用 CubeMX 生成的 HAL 工程会自动多出 Drivers 和 Core 目录,但 HARDWARE 这一层是你自己建的,驱动文件可以原样复制过去。

temperature_alarm/ ├── USER/ │ ├── main.c # 主循环与报警状态机 │ ├── stm32f10x_it.c # 中断服务函数 │ └── system_stm32f10x.c # 时钟与系统初始化 ├── HARDWARE/ │ ├── ds18b20.c/.h # 单总线驱动 │ ├── oled.c/.h # SSD1306 软件 I2C 驱动 │ ├── key.c/.h # 按键消抖与事件 │ └── beep.c/.h # 蜂鸣器、继电器控制 ├── SYSTEM/ │ ├── delay.c/.h # delay_us / delay_ms │ └── usart.c/.h # 串口打印重定向 ├── CORE/ │ └── startup_stm32f10x_md.s └── OBJ/ # 编译输出

这个分层的意义在于把业务和驱动拆开。main.c 只关心什么时候读温、什么时候报警,不关心位时序;ds18b20.c 只负责把线上电平变成温度数字。后面你要升级成 HAL 工程或者换做主控,HARDWARE 层可以直接搬走,不用改主循环。

3.2 底层驱动:复位脉冲与读写字节的 GPIO 模拟

DS18B20 驱动的核心是三个函数:复位、写字节、读字节。先看复位函数,它除了拉低总线,还要切换 GPIO 方向去读从机的存在脉冲。

// ds18b20.c —— 复位与存在脉冲检测 uint8_t DS18B20_Reset(void) { uint8_t presence = 1; GPIO_InitTypeDef gpio = {0}; gpio.Mode = GPIO_MODE_OUTPUT_PP; // 先切推挽输出 gpio.Pull = GPIO_NOPULL; gpio.Speed = GPIO_SPEED_HIGH; gpio.Pin = DS18B20_PIN; HAL_GPIO_Init(DS18B20_PORT, &gpio); HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_RESET); delay_us(500); // 拉低 500us 产生复位信号 HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_SET); delay_us(60); // 释放总线,等从机应答窗口 gpio.Mode = GPIO_MODE_INPUT; // 切输入,读存在脉冲 gpio.Pull = GPIO_PULLUP; HAL_GPIO_Init(DS18B20_PORT, &gpio); if (HAL_GPIO_ReadPin(DS18B20_PORT, DS18B20_PIN) == GPIO_PIN_RESET) { presence = 0; // 从机拉低应答,检测成功 } delay_us(420); // 凑满复位时序总时长 return presence; }

这里有个容易被忽略的细节:主机释放总线到检测从机应答之间等了 60us,因为存在脉冲要在释放后 60~240us 内出现。如果 delay_us 的实现依赖定时器中断,复位期间来一个 SysTick 中断,电平就会被意外拉高几个微秒,导致复位失败。常见做法是位级时序内临时关中断,读完再开,后面避坑章节会细说。

写字节和读字节是一对镜像操作,低位先出,每个位都是一个完整的时隙。

// 写一个字节:低位在前 void DS18B20_WriteByte(uint8_t data) { for (uint8_t i = 0; i < 8; i++) { HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_RESET); if (data & 0x01) // 写 1:拉低 1~15us 后释放 { delay_us(5); HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_SET); delay_us(60); } else // 写 0:持续拉低至少 60us { delay_us(65); HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_SET); } delay_us(2); // 时隙间恢复时间 data >>= 1; } } // 读一个字节:低位在前,在时隙内采样 uint8_t DS18B20_ReadByte(void) { uint8_t data = 0; for (uint8_t i = 0; i < 8; i++) { data >>= 1; HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_RESET); delay_us(2); // 拉低 2us 后释放 HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_SET); delay_us(8); // 释放后约 10us 处采样 if (HAL_GPIO_ReadPin(DS18B20_PORT, DS18B20_PIN) == GPIO_PIN_SET) { data |= 0x80; } delay_us(50); // 补齐读时隙的总时长 } return data; }

写 1 和写 0 的区别只在低电平持续时间:5us 还是 65us。读字节的关键是采样点必须落在释放后的 15us 窗口内,这里固定在 10us 左右。两个函数里的延时参数只要满足数据手册的窗口区间即可,不要求绝对精确,但必须稳定。

3.3 温度换算与数据手册里的 85 度陷阱

读温度的上层函数负责把暂存器前两个字节拼成 16 位整数,再换算成摄氏度。

// 读一次完整温度:启动转换 -> 等待 750ms -> 读暂存器 float DS18B20_ReadTemperature(void) { int16_t raw; uint8_t low, high; DS18B20_Reset(); DS18B20_WriteByte(0xCC); // 跳过 64 位 ROM 匹配 DS18B20_WriteByte(0x44); // 启动温度转换 delay_ms(750); // 12 位分辨率固定转换时间 DS18B20_Reset(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); // 读暂存器(9 个字节) low = DS18B20_ReadByte(); high = DS18B20_ReadByte(); raw = (int16_t)((high << 8) | low); return raw * 0.0625f; // 12 位分辨率,LSB 对应 0.0625 度 }

12 位温度数据存在暂存器第 0、1 字节,低字节在前。高字节的最高位是符号位,int16_t 强转后负数自动按补码解释,乘 0.0625f 就能得到零下温度。举两个例子:0x0550 换算出来是 85.0 度,0x0191 换算出来是 25.0625 度。85.0 是 DS18B20 上电时暂存器的默认值,不是真实温度,所以主流程里第一次读温最好丢弃,否则 OLED 上会固定显示一个 85 度,看起来就像传感器坏了。

提示:0x44 转换命令发出后,12 位分辨率要等 750ms。如果不等就直接发 0xBE 读暂存器,读到的还是上一次转换结果。这也是"第一次显示 85 度、之后温度不变"的另一个常见原因。

3.4 报警判定与 OLED、继电器联动:阈值滞回是防抖关键

主循环的逻辑可以抽象成一个带滞回窗口的状态机。上限报警和下限报警各用一个标志位,进入报警和解除报警分别落在不同的阈值点上。

#define HIGH_ALARM_TEMP 45.0f #define LOW_ALARM_TEMP 5.0f #define ALARM_HYSTERESIS 2.0f // 滞回窗口 2 度 int main(void) { HAL_Init(); SystemClock_Config(); // 72MHz 主频 GPIO_Init(); // 继电器、蜂鸣器、按键 OLED_Init(); // SSD1306 初始化 USART1_Init(); // 调试串口 float temp = 0; uint8_t high_alarm = 0, low_alarm = 0; DS18B20_ReadTemperature(); // 丢弃上电后的第一次假读 while (1) { temp = DS18B20_ReadTemperature(); // 上限报警:触发要 >=45,解除要 <=43 if (temp >= HIGH_ALARM_TEMP) { if (!high_alarm) { high_alarm = 1; Relay_On(); Buzzer_On(); } } else if (temp <= HIGH_ALARM_TEMP - ALARM_HYSTERESIS) { if (high_alarm) { high_alarm = 0; Buzzer_Off(); Relay_Off(); } } // 下限报警逻辑同理,temp <= 5 触发,temp >= 7 解除 OLED_ShowTemp(temp, high_alarm, low_alarm); delay_ms(500); // 500ms 轮询周期 } }

滞回窗口的意义在继电器上体现得最明显:如果没有 2 度的死区,温度在 45.0 附近小幅波动时,继电器会以秒级频率反复吸合,蜂鸣器一响一停,触点寿命和听觉体验都很难受。进入报警要求温度爬到 45,解除报警要回落到 43,中间这段不动作,相当于给系统加了一个硬件上做不到的迟滞。

OLED 刷新放在 500ms 周期里做,不要提到 100ms。SSD1306 软件 I2C 一次全屏刷新要占掉好几毫秒,刷太快反而吃掉主循环时间,而 DS18B20 的 750ms 转换又是阻塞的,整体实时性全靠这个轮询周期来平衡。

4. 编译烧录与实物调试:把工程从源码变成会叫的实物

4.1 开发环境搭建与工程导入

用 MDK-ARM 打开工程根目录下的 .uvprojx 文件,确认器件选的是 STM32F103C8T6,然后在 Options for Target 的 C/C++ 页把 C99 打开。第一次打开如果报找不到器件,先在 Pack Installer 里把 F1 系列器件包装上,不然启动文件那栏全是问号。工程里如果用到了 printf 输出浮点温度,还要在 Target 页勾上 Use MicroLIB,否则 sprintf 的浮点格式化会直接编不过。

如果你的工程是用 CubeMX 生成的 HAL 工程,结构会多出 Drivers 和 Core 两级目录,但 HARDWARE 层那几个驱动文件是能直接复制进去的。课程设计里两种做法都常见,标准外设库工程把启动文件和库文件都摆好了,环境上反而少折腾;HAL 工程后续想接 CubeMX 的外设图形化配置更方便,看你导师的验收习惯选一个就行。

4.2 关键初始化配置:时钟树、GPIO 与微秒延时

F103 的时钟配置是整个工程的地基。外部 8MHz 晶振经过 PLL 九倍频到 72MHz 系统主频,APB1 总线必须二分频到 36MHz,因为它的外设上限是 36MHz;APB2 保持 72MHz,让 GPIO、USART1、SPI 这些外设跑满速。Flash 等待周期要设成 2,这是 72MHz 下的硬性要求。

void SystemClock_Config(void) { RCC_OscInitTypeDef osc = {0}; RCC_ClkInitTypeDef clk = {0}; // 外部 8MHz 晶振,PLL 9 倍频到 72MHz osc.OscillatorType = RCC_OSCILLATOR_TYPE_HSE; osc.HSEState = RCC_HSE_ON; osc.PLL.PLLState = RCC_PLL_ON; osc.PLL.PLLSource = RCC_PLLSOURCE_HSE; osc.PLL.PLLMUL = RCC_PLL_MUL9; HAL_RCC_OscConfig(&osc); clk.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; clk.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; clk.AHBCLKDivider = RCC_SYSCLK_DIV1; // HCLK = 72MHz clk.APB1CLKDivider = RCC_HCLK_DIV2; // APB1 = 36MHz clk.APB2CLKDivider = RCC_HCLK_DIV1; // APB2 = 72MHz HAL_RCC_ClockConfig(&clk, FLASH_LATENCY_2); }

FLASH_LATENCY_2 漏掉的典型症状是程序在随机位置 HardFault,一会儿能跑一会儿跑飞,很难排查。APB1 上还挂着定时器,二分频后定时器时钟反而倍频到 72MHz,后面接 PWM 蜂鸣器时算周期要用这个数。

单总线驱动里的 delay_us 不建议用 HAL_Delay,因为它最小单位是毫秒。我一般直接用内核 DWT 计数器做微秒延时,不受 SysTick 中断影响:

// 使能 DWT 周期计数器,放在 main 开头执行一次 CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; void delay_us(uint32_t us) { uint32_t start = DWT->CYCCNT; uint32_t ticks = us * 72; // 72MHz 主频,1us = 72 个周期 while ((DWT->CYCCNT - start) < ticks); }

参数里的 72 就是 72MHz 主频换算出来的,改了系统时钟这里也要同步改。按键、继电器、蜂鸣器的 GPIO 初始化就一句话:按键配输入上拉,继电器的 IN 和蜂鸣器 IO 配推挽输出,记得检查继电器模块是高电平触发还是低电平触发,不少模块默认低电平触发,代码里逻辑要反过来写。

4.3 烧录验证与串口辅助调试过程

ST-Link 连 SWD 四根线:SWDIO、SWCLK、GND、3.3V。烧录前把 BOOT0 跳线拨到 0,很多同学卡在这一步:BOOT0 在 1 位置时芯片进系统存储器引导,代码烧进去也不执行,OLED 一片黑,拨回 0 按一下复位键就好。烧录成功后按下面的清单逐项验证,每项都能对上号再去改业务逻辑:

  1. 串口打印 reset ok,确认 DS18B20 在总线上应答;
  2. 首次显示的 85.0 度属于正常现象,丢弃第一次读数;
  3. 用手握 DS18B20,温度上升;松开自然回落;
  4. 把报警阈值设到当前室温以下,继电器应吸合、蜂鸣器应鸣叫;
  5. 拔掉 DS18B20,OLED 显示异常值,插回并复位后恢复。

串口辅助调试是这套系统排查问题的眼睛。USART1 重定向 printf 后,可以在每次读温后打印原始暂存器值和换算结果,对比 OLED 显示是否一致。尤其当温度出现跳变时,串口日志能帮你判断是传感器数据问题还是显示刷新问题,比盯着屏幕猜快得多。

5. 避坑记录:从偶尔失灵到稳定报警的五条排查实录

这套系统跑起来不难,难的是跑稳。下面五条是课程设计阶段最常见的坑,每条都按现象、原因、解决三步说清楚,看起来像玄学的问题,其实都能在时序和逻辑上找到实锤。

5.1 温度固定显示 85.0 度

现象:上电后 OLED 永远显示 85.0,怎么捂传感器温度都不变。原因:85.0 是 DS18B20 上电时暂存器里的默认值 0x0550 换算结果。程序如果只发了一次转换命令就立刻读,读到的还是上一次转换结果;或者转换命令根本没发出去——复位没成功,后续所有命令全部无效。解决:主循环开始前先做一次完整读温并丢弃结果,让芯片完成第一次有效转换;同时在复位函数里检查存在脉冲,检测不到就在串口打印 reset error,把问题暴露在日志里而不是藏在显示里。

5.2 温度偶尔跳变几十度

现象:平时读数稳定,但 OLED 刷新或按键操作后,偶尔冒出一个离谱值。原因:DS18B20 的位时隙对微秒级延时敏感。主循环里 OLED 软件 I2C 在逐位翻转电平,中间一旦被 SysTick 或串口中断打断,读时隙的 15us 采样点就会错过,把 0 读成 1,温度瞬间跳变几十度。解决:把"复位-发命令-读字节"这一段整体包进临界区,执行前 __disable_irq(),执行完 __enable_irq();OLED 刷新和按键扫描放到读温之后串行执行,不要在单总线操作期间抢 CPU。

5.3 继电器在阈值附近反复吸合咔咔响

现象:温度在 45.0 附近小幅波动时,继电器十几秒内反复吸合断开。原因:报警逻辑里用了单点比较,temp >= 45 触发、temp < 45 解除,触发点和解除点挨在一起,只要测量值抖动就会来回切换。这不是继电器的问题,是逻辑缺少滞回。解决:加 2 度滞回窗口,触发条件用 temp >= 45,解除条件用 temp <= 43,中间 2 度是死区。实测带 1 度滞回就有明显改善,温度记录场景建议 0.5~1 度,报警场景用 2 度以上更稳。

5.4 OLED 白屏或花屏

现象:上电后 OLED 不显示,或者第一行出现乱码。原因:三个方向排查——I2C 地址不对,SSD1306 有 0x3C 和 0x3D 两种地址;软件 I2C 翻转太快,超过 400kHz 后模块丢数据;初始化序列之前没留上电稳定时间。解决:写一个 I2C 扫描函数把两个地址都试一遍;软件 I2C 的 SCL/SDA 每次翻转中间加 1~2us 延时;OLED_Init 前先 HAL_Delay(100)。另外 F1 的硬件 I2C 模块状态机比较脆弱,很多人踩过坑,课程设计里软件 I2C 更省心,别为了省 GPIO 硬啃硬件 I2C。

5.5 温度显示正常但蜂鸣器不响

现象:继电器动作了,OLED 也显示报警了,蜂鸣器却一声不吭。原因:蜂鸣器有无源和有源之分。有源蜂鸣器内部带振荡电路,给高电平就响;无源蜂鸣器需要 1~4kHz 的 PWM 驱动,直接拉高只会听到轻微的"嗒"一声,或者完全没声音。解决:买模块时确认是"有源蜂鸣器模块";如果手头已经买了无源的,就把驱动改成定时器 PWM,用 TIM2 输出 2kHz、占空比 50% 的方波,报警时启动通道输出,解除时关闭通道。代码逻辑不变,只多一个 PWM 初始化。

6. 二次开发思路:把固定阈值报警器改成可配置的迷你温度记录仪

课设交了之后,我一般会再往前推一步:把固定阈值改成可掉电保存的配置,再给温度打上时间标签。改造核心是借用 F103 内部 Flash 的最后一页存配置结构体,新增按键进入设置模式改阈值,改完写 Flash,下次上电直接从 Flash 载入。F103C8T6 有 64KB Flash,最后一页起始地址是 0x0800FC00,存一个配置结构体绰绰有余。

// 配置结构体,按 4 字节对齐存储 typedef struct { uint32_t magic; // 0xA5A5A5A5 标记配置有效 float high_threshold; // 高报警阈值 float low_threshold; // 低报警阈值 } alarm_cfg_t; void AlarmCfg_Save(alarm_cfg_t *cfg) { HAL_FLASH_Unlock(); FLASH_EraseInitTypeDef erase = {0}; uint32_t page_error = 0; erase.TypeErase = FLASH_TYPEERASE_PAGES; erase.PageAddress = 0x0800FC00; erase.NbPages = 1; HAL_FLASHEx_Erase(&erase, &page_error); // 写入必须以 32 位为单位,float 地址强转后访问 HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x0800FC00, cfg->magic); HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x0800FC04, *(uint32_t *)&cfg->high_threshold); HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 0x0800FC08, *(uint32_t *)&cfg->low_threshold); HAL_FLASH_Lock(); }

上电时读 magic,对不上就用默认的 45/5 度,对得上就载入存下来的值,这一步就是掉电不丢失的"后悔药"。验证方法很直接:把高阈值改成 30 度,按保存键、掉电、重启,OLED 上显示的阈值还是 30 度,说明 Flash 读回成功。再往后扩展,可以按同样套路加 DS18B20 的 CRC8 校验、多传感器挂同一条总线轮询,或者把 750ms 阻塞等待改成非阻塞状态机。从那以后我每次往项目里加新传感器,都会先做一遍配置存取自检再谈业务逻辑,这个习惯帮我省掉了很多看似玄学的重启丢配置问题。这篇笔记如果能让你少踩一个坑,就值得了,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 5:06:41

Hadoop、HDFS与Hive的关系解析:一条SQL的分布式完整旅程

做大数据这行的人&#xff0c;几乎都遇到过同样的尴尬&#xff1a;学了 Hadoop&#xff0c;千辛万苦把日志文件传进 HDFS&#xff0c;然后发现数据确实“躺”进去了&#xff0c;但想算点东西还得手动写 MapReduce&#xff0c;代码又长又难调。后来听别人说用 Hive&#xff0c;写…

作者头像 李华
网站建设 2026/10/20 5:06:40

中文电子病历实体与事件抽取的临床适配实践

1. 项目概述&#xff1a;这不是一次普通的NLP竞赛任务&#xff0c;而是一场直面临床真实痛点的硬仗 “CCKS2020 任务三&#xff1a;面向中文电子病历的医疗实体及事件抽取”——光看这个标题&#xff0c;你可能只觉得它是个学术竞赛里的标准命名。但如果你在某三甲医院信息科跟…

作者头像 李华
网站建设 2026/10/20 5:06:20

AI重塑终端:从IDE到智能编程界面的开发工作流变革

最近大模型编程的讨论里&#xff0c;“终端”这个词的出现频率高得吓人。我自己的开发主战场也在不知不觉中从IDE挪回了终端——不是怀旧&#xff0c;而是AI把终端变成了真正意义上的智能编程界面。这篇文章不打算站队“终端干掉IDE”&#xff0c;而是想认真拆一拆&#xff1a;…

作者头像 李华
网站建设 2026/10/20 5:06:06

Flink Kafka Connector源码剖析:数据流动模型与精确一次

把 Flink 的 Kafka Connector 源码从头到尾啃过一遍之后&#xff0c;我最大的感受是&#xff1a;很多线上问题并不是参数配置错了&#xff0c;而是我们根本没搞懂连接器的数据流动模型。团队里 A 同学跑来问我&#xff0c;为什么某个任务一直重复消费&#xff1b;B 同学说&…

作者头像 李华
网站建设 2026/10/20 5:05:44

AI面试八股文-17_17-AI 编程工具与自主 Coding Agent 面试题

pdf版下载链&#xff1a;https://pan.quark.cn/s/9cc742e9c2a8 17 AI 编程工具与 Coding Agent&#xff1a;原理、评测与工程实践 更新&#xff1a; 2026-10-12 难度&#xff1a; ⭐⭐⭐⭐ 考点&#xff1a; Coding Agent、Harness、沙箱、权限、Skills、长任务、评测与工具选…

作者头像 李华