当前位置: 首页 > news >正文

告别串口打印:用STM32 HAL库+DS18B20做个OLED屏显温度计(Keil工程开源)

STM32 HAL库实战:打造OLED屏显温度计(附完整Keil工程)

在嵌入式开发中,将传感器数据可视化是提升用户体验的关键一步。想象一下,当你的温度监测设备不再依赖笨重的串口调试工具,而是通过一块精致的OLED屏幕实时显示数据,这种即时的视觉反馈会让整个项目瞬间生动起来。本文将带你用STM32 HAL库驱动DS18B20温度传感器和SSD1306 OLED显示屏,构建一个完整的温度监测系统。

1. 硬件选型与工程搭建

1.1 核心硬件组件

这个项目的硬件架构非常简单,只需要三个关键部件:

  • 主控芯片:STM32F103C8T6(蓝色pill开发板)
  • 温度传感器:DS18B20(防水型封装)
  • 显示模块:0.96寸OLED(SSD1306驱动,I2C接口)

选择STM32F103的原因在于其广泛的社区支持和丰富的HAL库资源,而DS18B20的单总线协议和OLED的I2C接口可以最大限度减少IO占用。以下是硬件连接示意图:

STM32引脚外设连接备注
PA0DS18B20 DATA需接4.7K上拉电阻
PB6OLED SCLI2C1时钟线
PB7OLED SDAI2C1数据线

1.2 开发环境配置

在Keil MDK中新建工程时,需要特别注意以下配置项:

// 在CubeMX中的关键配置: 1. 启用I2C1(标准模式,100kHz) 2. 配置PA0为GPIO输出(DS18B20数据线) 3. 开启Systick定时器(用于us级延时)

提示:建议先单独测试DS18B20和OLED模块的功能,确保每个部件正常工作后再进行集成。

2. DS18B20驱动优化

2.1 单总线协议实现

DS18B20的经典驱动通常包含大量延时函数,这在结合OLED显示时可能导致屏幕刷新卡顿。我们通过以下优化提升系统响应速度:

// 优化后的延时函数(基于Systick) void DS18B20_Delay(uint16_t us) { uint32_t ticks = us * (SystemCoreClock / 1000000); uint32_t start = SysTick->VAL; while((start - SysTick->VAL) < ticks); }

关键改进点:

  • 取消全局中断禁用,改为关键段保护
  • 增加超时检测机制,避免总线死锁
  • 采用HAL库标准的GPIO操作函数

2.2 温度数据滤波处理

工业级应用中,简单的单次采样往往不够可靠。我们实现了一个滑动平均滤波器:

#define FILTER_SIZE 5 float TempFilter(float new_temp) { static float buffer[FILTER_SIZE] = {0}; static uint8_t index = 0; float sum = 0; buffer[index++] = new_temp; if(index >= FILTER_SIZE) index = 0; for(int i=0; i<FILTER_SIZE; i++){ sum += buffer[i]; } return sum / FILTER_SIZE; }

3. OLED界面设计

3.1 SSD1306驱动集成

使用开源u8g2库可以快速实现OLED驱动,以下是适配STM32 HAL的初始化代码:

// u8g2初始化结构体 u8g2_t u8g2; void OLED_Init(void) { u8g2_Setup_ssd1306_i2c_128x64_noname_f( &u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_stm32_gpio_and_delay ); u8g2_InitDisplay(&u8g2); u8g2_SetPowerSave(&u8g2, 0); }

3.2 温度显示UI设计

一个专业的温度显示界面应该包含以下元素:

  • 当前温度值(大号字体)
  • 温度变化趋势图
  • 极值记录
  • 单位标识(°C/°F)

实现代码示例:

void DrawTempScreen(float temp) { char buf[20]; u8g2_ClearBuffer(&u8g2); u8g2_SetFont(&u8g2, u8g2_font_logisoso32_tf); sprintf(buf, "%.1f", temp); u8g2_DrawStr(&u8g2, 10, 40, buf); u8g2_SetFont(&u8g2, u8g2_font_unifont_t_symbols); u8g2_DrawGlyph(&u8g2, 90, 40, 0x00b0); // 度符号 u8g2_DrawStr(&u8g2, 102, 40, "C"); u8g2_SendBuffer(&u8g2); }

4. 系统整合与性能优化

4.1 任务调度策略

为了避免温度采样影响显示流畅度,我们采用状态机架构:

typedef enum { STATE_MEASURE_START, STATE_MEASURE_WAIT, STATE_DISPLAY_UPDATE } SystemState; void MainTask(void) { static SystemState state = STATE_MEASURE_START; static uint32_t timer = 0; static float current_temp = 0; switch(state){ case STATE_MEASURE_START: DS18B20_Start(); state = STATE_MEASURE_WAIT; timer = HAL_GetTick(); break; case STATE_MEASURE_WAIT: if(HAL_GetTick() - timer > 750){ // DS18B20转换需要750ms current_temp = DS18B20_Get_Temp(); state = STATE_DISPLAY_UPDATE; } break; case STATE_DISPLAY_UPDATE: DrawTempScreen(current_temp); state = STATE_MEASURE_START; break; } }

4.2 低功耗设计

对于电池供电的应用,我们可以进一步优化功耗:

  1. 在温度稳定时降低采样频率
  2. 使用OLED的局部刷新功能
  3. 配置STM32进入低功耗模式
void EnterLowPowerMode(void) { // 配置GPIO为模拟输入(最低功耗) GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_All; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 进入STOP模式(保留RAM内容) HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); }

5. 进阶功能扩展

5.1 温度报警功能

通过STM32的GPIO驱动蜂鸣器或LED,实现超温报警:

#define TEMP_THRESHOLD 28.0f void CheckTempAlert(float temp) { static uint8_t alert_state = 0; if(temp > TEMP_THRESHOLD){ if(!alert_state){ HAL_GPIO_WritePin(BUZZER_GPIO_Port, BUZZER_Pin, GPIO_PIN_SET); alert_state = 1; } }else{ if(alert_state){ HAL_GPIO_WritePin(BUZZER_GPIO_Port, BUZZER_Pin, GPIO_PIN_RESET); alert_state = 0; } } }

5.2 多传感器网络

单总线协议的优势在于可以挂载多个DS18B20。修改搜索ROM命令即可实现多点测温:

void DS18B20_SearchRom(uint8_t *rom_code) { DS18B20_Rst(); DS18B20_Check(); DS18B20_Write_Byte(0xF0); // Search ROM命令 for(int i=0; i<8; i++){ rom_code[i] = DS18B20_Read_Byte(); } }

在实际项目中,我发现OLED的刷新速率与温度采样周期需要精细平衡。当采样间隔设置为1秒时,屏幕刷新会显得非常流畅,而将温度滤波窗口设为5个样本可以有效消除数据抖动。

http://www.jsqmd.com/news/946998/

相关文章:

  • 树莓派新手必看:用手机热点替代电脑,户外也能玩转(附VNC配置)
  • 踩坑实录:poi-tl处理Word模板分页与图片时,我遇到的3个坑及解决方案
  • AI编程祛魅:从功能幻觉到零故障工作流的实战指南
  • 【Azure App Service】应用服务中的SNAT (Source Network Address Translation 源网络地址转化)
  • 【深入理解计算机系统】第一章(计算机系统漫游)笔记
  • 彻底理清 B+ 树页分裂与页合并对大批量写入 MySQL分库分表与分区表的设计抉择 数据时吞吐量的影响路径
  • ssm员工在线知识培训考试平台(10153)
  • 从Copilot到Agent:我的团队如何用ChatDev在3天内“自动化”了一个内部工具
  • AD软件大电流布线必备:一招把Top层铺铜“变成”阻焊开窗,告别焊盘锡量不足的烦恼
  • Python 爬虫进阶技巧:元数据 meta 标签提取辅助爬虫页面判重
  • 保姆级教程:在嵌入式Linux上实战I3C SDR模式的热加入与带内中断(附代码避坑)
  • 拆解Botsch经典算法:手写半边结构,一步步实现Isotropic Remeshing(附C++代码)
  • 深入GL3224固件升级工具:如何手动添加Flash芯片支持(以Winbond W25Q16为例)
  • NarratoAI完整教程:三步掌握AI视频解说制作神器
  • ESP8266从联网到传数据:一条AT指令搞定WiFi连接与TCP通信(实战避坑)
  • 用STM32F103C8T6搞定74HC165扩展16个按键(附完整代码和接线图)
  • Harness Engineering:Agent自主决策审计
  • Android混合开发避坑指南:WebView与H5通信的5种姿势与安全实践
  • 2026降AIGC革命:AI率92%暴降至5%!实测10款降AI率工具!薅羊毛技巧!
  • 别再用BertModel直接喂给Chroma了!手写一个EmbeddingFunction解决HuggingFaceEmbeddings离线调用难题
  • AUTOSAR SPI实战避坑:同步调用Spi_SyncTransmit阻塞了CPU?试试异步Spi_AsyncTransmit提升效率
  • 深入探秘 Golang 源码中 channel 管道通信的真正设计意图与边界
  • 用MATLAB批量生成卫星TLE文件:STK11自动化脚本实战(附完整代码)
  • DDD-013:仓储(Repository)
  • Python 爬虫进阶技巧:批量解析 html 实体转义字符还原原始文本
  • Xcode 15开发者的终端效率手册:除了CMD+R运行,你的快捷键还缺这一块
  • 从Demo到量产:Davinci工程添加自定义模块与变体文件的完整指南(以BRS模块为例)
  • 告别WebView黑盒:用Chrome DevTools调试Android混合开发页面(附Androidx-WebKit实战)
  • 钢材表面缺陷检测实战工程:含NEU-DET数据集与YOLOv5/v8多版本训练配置
  • 2026深度测评10款降AI率软件红黑榜!优缺点全曝光,达标率直接对标行业天花板