news 2026/4/26 6:57:33

LCD1602液晶显示屏程序与传感器数据联动显示案例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LCD1602液晶显示屏程序与传感器数据联动显示案例

用LCD1602把传感器数据“说”出来:一个看得见的温湿度监测系统

你有没有过这样的经历?调试一个温湿度采集项目时,串口打印一堆数字来回滚,眼睛都快看花了,却还是搞不清当前环境到底有多湿、多热。这时候要是有个小屏幕,直接告诉你“温度:25°C,湿度:60%”,是不是瞬间清爽了?

别急着上OLED或TFT彩屏——成本高、功耗大、驱动复杂。在很多对价格敏感的小型嵌入式设备里,真正扛大梁的,其实是一个看起来有点“复古”的家伙:LCD1602字符液晶屏

今天我们就来做一个实战案例:让LCD1602实时显示DHT11采集到的温湿度数据。不讲虚的,从硬件连接到代码逻辑,再到常见坑点和优化技巧,手把手带你把“传感器→单片机→显示屏”这条链路打通。


为什么选LCD1602?它真的还值得用吗?

先别急着嫌弃它“老土”。虽然现在满大街都是彩色触摸屏,但在实际工程中,LCD1602依然有它的不可替代性

我们来看一组对比:

特性LCD1602OLEDTFT
成本< ¥5~¥15> ¥30
功耗(不含背光)~1mA~0.05mA~50–100mA
接口方式并行GPIO(4/8位)I2C/SPISPI/RGB并行
显示内容固定ASCII字符图形+文字全彩图形+GUI
自定义能力支持8个自定义字符完全自由绘图支持复杂界面

看出门道了吗?

如果你只需要显示几行固定格式的文字,比如:

Temp: 26 C Humi: 58 %

那完全没必要为了一朵“云”买下整片天空。LCD1602结构简单、驱动直接、稳定性强,特别适合教学实验、工业控制面板、农业监测节点这类对可靠性要求高、预算有限的场景。

更重要的是——它不需要操作系统,不用跑RTOS,甚至不用DMA,STM32、Arduino、51单片机都能轻松驾驭。


硬件怎么接?一张图搞定

我们这个系统的主角有三个:

  • 主控芯片:STM32F103C8T6(蓝 pill 开发板)
  • 传感器:DHT11 温湿度模块
  • 显示器:LCD1602 字符屏(带HD44780控制器)

引脚连接一览

LCD1602引脚功能说明连接到MCU引脚备注
VSSGNDGND必接
VDDVCC (5V)5V必接
V0对比度调节可调电阻中间抽头建议接10kΩ电位器
RS寄存器选择PA0高=数据,低=指令
RW读写控制GND通常只写不读,直接接地
E使能信号PA2上升沿触发
D4–D7数据线(低4位)PB4–PB74位模式
A / K背光电源5V 或 PWM 控制可串联限流电阻

⚠️ 注意:DHT11的数据线建议通过一个10kΩ上拉电阻接到VCC,并尽量缩短走线以减少干扰。

这样一共用了7个GPIO(RS、E + D4~D7),比8位模式省了两个IO,性价比拉满。


软件怎么写?分三步走

整个程序的核心流程非常清晰:

初始化LCD → 显示启动提示 → 循环读取DHT11 → 成功则更新显示,失败则报错

我们一步步拆解。

第一步:让LCD1602“醒过来”

LCD1602上电后不能马上干活,必须按照严格的时序进行初始化,尤其是切换到4位工作模式这一步,稍有差池就会黑屏无响应。

关键步骤如下:

  1. 上电延时15ms
  2. 发送0x03三次(确保进入8位模式)
  3. 发送0x02(切换至4位模式)
  4. 配置显示参数:两行、5x7点阵、开显示、关光标

下面是基于STM32 HAL库的简化实现:

void LCD_Init(void) { delay_ms(15); LCD_CTRL_PORT->BRR = RS_PIN; // 指令模式 LCD_SendNibble(0x03); delay_ms(5); LCD_SendNibble(0x03); delay_us(150); LCD_SendNibble(0x03); LCD_SendNibble(0x02); // 正式进入4位模式 LCD_WriteCommand(0x28); // 4位数据,2行显示,5x7字体 LCD_WriteCommand(0x0C); // 开显示,关光标 LCD_WriteCommand(0x06); // 地址自动+1,画面不动 LCD_WriteCommand(0x01); // 清屏 delay_ms(2); }

其中LCD_SendNibble()是核心函数,负责发送半字节数据并触发E脉冲:

void LCD_SendNibble(uint8_t nibble) { uint32_t temp = LCD_DATA_PORT->ODR & 0xFF0F; // 清除D4-D7 temp |= (nibble & 0x0F) << 4; LCD_DATA_PORT->ODR = temp; LCD_CTRL_PORT->BSRR = EN_PIN; // E上升沿 delay_us(1); LCD_CTRL_PORT->BRR = EN_PIN; // E下降沿 delay_ms(1); }

初始化完成后,就可以愉快地输出文字了。


第二步:从DHT11手里“抢”数据

DHT11是典型的单总线器件,通信靠“握手+打拍子”完成。整个过程需要精确控制毫秒和微秒级延时。

基本流程如下:

  1. MCU拉低数据线至少18ms,唤醒DHT11;
  2. DHT11回应一个80μs的低电平+80μs的高电平;
  3. 开始传输40位数据,每位以50μs低电平开头,高电平长短区分0和1;
    - 高电平持续26–28μs → ‘0’
    - 高电平持续70μs左右 → ‘1’

下面是读取函数的关键逻辑:

int DHT11_Read(void) { uint8_t i, j, data = 0; // 设置PA3为推挽输出 GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 启动信号:拉低18ms以上 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, RESET); delay_ms(18); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, SET); delay_us(30); // 释放总线,等待响应 // 切换为输入模式 GPIO_InitStruct.Mode = GPIO_MODE_INPUT; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 等待DHT11拉低(应答开始) wait_timeout(!HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3), 100); // 应答低 wait_timeout(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3), 100); // 应答高 wait_timeout(!HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3), 100); // 数据0开始 // 读取40位数据 for (i = 0; i < 5; i++) { data = 0; for (j = 0; j < 8; j++) { while (!HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3)); // 等待低电平结束 delay_us(40); // 进入高电平后延迟40us判断 if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3)) { data |= (1 << (7 - j)); } while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3)); // 等待该位结束 } dht11_data[i] = data; } // 校验和检查 if (dht11_data[4] == (dht11_data[0]+dht11_data[1]+dht11_data[2]+dht11_data[3])) { return 1; } return 0; }

📌 提示:wait_timeout(condition, max)是一个带超时保护的等待宏,防止死循环。


第三步:把数据显示出来,还不许闪!

很多人第一次做联动显示时都会犯同一个错误:每次刷新前先清屏。结果就是——屏幕疯狂闪烁,用户体验极差。

问题出在哪?LCD_WriteCommand(0x01)不仅清屏,还会归零地址指针,导致整个画面重绘,视觉上就是“闪一下”。

✅ 正确做法:局部刷新 + 补空格防残留

我们要做的不是“重画整个屏幕”,而是“只改变该变的部分”。

比如这一行:

Humi: 60 %

下次变成:

Humi: 8 %

如果不处理,屏幕上会留下一个“0”,变成“Humi: 80 %”——这就是典型的字符残留

解决办法很简单:写完数字后手动加个空格覆盖旧字符。

void LCD_PrintHumidity(uint8_t humi) { LCD_SetCursor(1, 0); // 第二行第0列 LCD_PrintStr("Humi:"); LCD_PrintNum(humi); LCD_WriteData(' '); // 清除可能残留的% LCD_WriteData('%'); }

同理,温度也可以封装成类似函数。

再配合一个状态机制,避免无效刷新:

uint8_t retry = 0; while (retry < 3 && !DHT11_Read()) { retry++; delay_ms(1000); } if (retry >= 3) { LCD_DisplayError("Sensor Error"); } else { LCD_PrintTemperature(dht11_data[2]); LCD_PrintHumidity(dht11_data[0]); }

刷新频率控制在每2秒一次即可,既保证实时性,又延长传感器寿命(DHT11建议采样间隔≥1秒)。


实战中的那些“坑”,我都替你踩过了

你以为写完代码就能稳定运行?Too young。下面这些经验,全是血泪教训总结出来的。

❗ 坑一:DHT11偶尔读不出数据

现象:有时成功,有时失败,重启就好。

原因:单总线对时序极其敏感,稍微一点延迟偏差就会导致误判。

解决方案
- 使用__NOP()内联汇编代替delay_us()提高精度;
- 在中断密集的系统中,临时关闭中断;
- 加10kΩ上拉电阻增强信号质量;
- 优先使用外部晶振而非内部RC。

❗ 坑二:LCD对比度调不好,要么全黑要么全白

现象:调了半天电位器,还是看不清。

真相:V0脚电压决定了对比度,理想范围是0.5V~1.5V之间。

推荐方案
- 不用手动电位器,改用DAC输出1V左右;
- 或者用固定分压电路(如4.7k + 1k电阻);
- 工业级应用可加入温补电路,因为液晶特性受温度影响较大。

❗ 坑三:背光太亮费电,晚上刺眼

改进思路
- 将背光正极通过N-MOS管连接到MCU的PWM引脚;
- 白天全亮,夜间降为30%亮度;
- 甚至可以结合光敏电阻自动调节。


还能怎么升级?让它更聪明一点

别以为这只是个“玩具级”项目。这个基础架构完全可以扩展出更多实用功能:

🔹 加个按键,切换显示模式

  • 按一下:显示温湿度
  • 再按:显示历史最高/最低值
  • 长按:进入校准模式

🔹 接Wi-Fi上传数据

  • 用ESP-01S模块将数据发到MQTT服务器;
  • 手机APP随时查看记录;
  • 结合继电器实现自动除湿/加热控制。

🔹 自定义图标提升体验

LCD1602支持8个自定义字符,我们可以画一个小小的“水滴”💧表示湿度,“太阳”☀️表示温度:

// 自定义“水滴”图案 uint8_t droplet[8] = { 0b00100, 0b01010, 0b01010, 0b01010, 0b01010, 0b10001, 0b10001, 0b01110 }; LCD_CreateChar(0, droplet); // 存入CGRAM位置0 LCD_WriteData(0); // 显示该图标

从此你的显示不再是冷冰冰的字母,而是有了“表情”的交互界面。


写在最后:小屏幕,大世界

也许你觉得LCD1602已经“过时”了,但我想说的是:技术没有过时,只有是否适用

在这个追求“炫酷UI”的时代,我们反而更需要回归本质——用最简单的方案解决最真实的问题

当你在一个偏远农田的监测站里,看到一块小小的LCD屏稳稳地写着“Temp: 28°C, Humi: 72%”,而整个系统靠太阳能供电一年都不用维护时,你会明白:

有时候,最朴素的显示,才是最有力量的信息传递。

掌握LCD1602与传感器联动的技术,不只是学会了一个外设驱动,更是建立起一种系统思维:如何让物理世界的变化,被人类感知

这正是嵌入式开发的魅力所在。

如果你也在做类似的项目,欢迎留言交流经验。或者告诉我你想下一个点亮什么传感器?PM2.5?土壤湿度?我们可以一起把它“显示”出来。

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

GPT-SoVITS支持RESTful接口吗?自定义服务搭建指南

GPT-SoVITS支持RESTful接口吗&#xff1f;自定义服务搭建指南 在智能语音应用日益普及的今天&#xff0c;越来越多开发者希望将前沿的语音克隆技术快速集成到自己的产品中。比如为虚拟主播赋予真人般的声音、让有声读物自动使用指定音色朗读&#xff0c;甚至用亲人的声音生成陪…

作者头像 李华
网站建设 2026/4/23 8:21:24

XUnity自动翻译插件:游戏语言障碍终极解决方案

XUnity自动翻译插件&#xff1a;游戏语言障碍终极解决方案 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 还在为看不懂日文游戏而烦恼吗&#xff1f;XUnity自动翻译插件正是为你量身打造的终极翻译利器&…

作者头像 李华
网站建设 2026/4/23 15:08:51

利用Proteus 8 Professional下载实现课堂互动仿真教学方案

用Proteus做课堂仿真教学&#xff1f;这招让电子课从“听懂了”变成“我会了”你有没有经历过这样的场景&#xff1a;老师在讲台上讲《单片机原理》&#xff0c;PPT翻到第15页&#xff1a;“我们来看一下AT89C51如何控制LED闪烁。”台下学生点头如捣蒜——“哦&#xff0c;懂了…

作者头像 李华
网站建设 2026/4/23 8:23:02

游戏翻译终极指南:AI技术如何彻底改变你的游戏体验

游戏翻译终极指南&#xff1a;AI技术如何彻底改变你的游戏体验 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 还在为外语游戏中的生涩文本而烦恼吗&#xff1f;XUnity Auto Translator为你提供了一站式的…

作者头像 李华
网站建设 2026/4/24 13:26:11

终极指南:如何用XUnity插件彻底解决游戏语言障碍

终极指南&#xff1a;如何用XUnity插件彻底解决游戏语言障碍 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 还在为外语游戏中的生涩文本而烦恼吗&#xff1f;XUnity Auto Translator作为一款强大的Unity…

作者头像 李华
网站建设 2026/4/24 3:02:06

基于交叉编译工具链的SPI控制器驱动调试技巧

高效调试SPI控制器驱动&#xff1a;交叉编译实战与硬核排错指南你有没有遇到过这样的场景&#xff1f;在嵌入式板子上加载了一个SPI驱动模块&#xff0c;insmod成功了&#xff0c;但一读外设就卡死&#xff1b;dmesg里飘出一行“timeout waiting for completion”&#xff0c;而…

作者头像 李华