news 2026/9/14 14:16:19

STC单片机驱动DS18B20温度传感器+ST7735 TFT屏显示完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STC单片机驱动DS18B20温度传感器+ST7735 TFT屏显示完整指南

简介:面向STC单片机开发者和嵌入式初学者,此压缩包完整展示了如何将STC单片机与ST7735驱动1.8寸TFT屏及DS18B20数字温度传感器结合,搭建实时环境温度显示系统。资源围绕底层驱动、通信协议和界面显示展开,包含C语言源码、头文件、工程配置文件及演示视频,覆盖从单总线读取温度到TFT屏幕绘制的全过程,适合学习1-Wire通信、SPI/LCD驱动和单片机外设协同开发的读者参考。包内共111个文件,以c/h源码、obj/lst中间文件、uvproj/uvopt工程配置为主,另有hex烧录文件、map/m51链接映射及mp4演示录屏,整体约38.96MB,结构清晰,既能直接查看源码逻辑,也可导入Keil工程重新编译。目前已有631人学习,文件内还包含PCA、定时器、按键等模块,可帮助读者理解系统初始化与任务调度,是入门STC图形化温度监测项目的实用参考。

1. STC单片机驱动DS18B20和ST7735 TFT屏,这一组合难点在哪

一个环境温度显示器,最省事的方案是STM32加现成库,但放到STC单片机上,难点就变了:DS18B20靠一条IO口做单总线通信,时序窗口窄到微秒级,STC89C52这类12T单片机连硬件SPI都没有,ST7735这块1.8寸屏的刷新还要靠IO口去模拟。同样一个温度值,在串口打印容易,让它稳定显示到TFT彩屏上,中间隔着两条链路:一条是DS18B20的读写时序,另一条是ST7735的信号时序。这篇内容把这两条链路的连接、驱动写法、显示格式和排错顺序完整理一遍,适合用51内核做仪表显示,或者想把串口数据搬到TFT液晶屏上的开发者。

2. STC单片机与ST7735 TFT屏的硬件连接和SPI基础

2.1 STC89C52RC和STC15系列,驱动ST7735的差别在哪

先做选型。STC89C52RC是12T内核,没有硬件SPI;STC15系列是1T内核,部分型号带硬件SPI,但很多从51转过来的项目仍然用软件模拟SPI。晶振建议选11.0592MHz而不是12MHz:一方面串口波特率误差小,另一方面STC89C52在11.0592MHz下一个机器周期约1.085us,计算DS18B20时序延时更容易。如果是STC15系列1T内核,同样的DelayUs函数实际延时只有STC89C52的约1/12,不改延时函数直接跑DS18B20,十有八九读不回温度。

STC单片机复位电路在这里也起作用:单片机复位时所有IO口恢复成准双向高电平,如果屏幕的RST引脚和单片机复位脚直接并联,复位期间屏幕也会跟着复位。等单片机跑起来去初始化屏幕时,时序已经错乱了。我一般用单独的IO口控制屏幕RST,这样上电顺序完全由程序掌控。

2.2 引脚分配:DS18B20的一条IO和ST7735的五条信号线

下面是一套常用连接,使用STC89C52RC时P1口接屏幕、P2.0接温度传感器。

模块信号STC89C52RC引脚说明
DS18B20DQP2.0外部接4.7kΩ上拉到VCC
ST7735SCLKP1.0串行时钟
ST7735MOSIP1.1主机输出数据
ST7735CSP1.2片选,低有效
ST7735DCP1.3数据/命令选择
ST7735RSTP1.4复位,低有效
ST7735BLKP1.5背光控制

ST7735的核心电压是3.3V,而STC89C52的IO口是5V电平。现在市面上多数1.8寸TFT模块自带稳压和电平转换,可以直接5V供电;但裸屏或精简模块没有,这时候SCLK和MOSI必须串1kΩ电阻限流再做电平匹配,强行直连会缩短屏幕寿命。DS18B20可以5V供电,DQ引脚也是5V电平,和STC匹配没问题。

2.3 软件SPI的最小实现

ST7735的SPI只需要单向通信:SCK空闲低电平,MOSI在SCK上升沿被采样,MSB先出。没有硬件SPI时,发送一个字节就是循环8次:先拉低SCK,再置位MOSI,最后拉高SCK。

sbit LCD_SCL = P1^0; sbit LCD_SDA = P1^1; sbit LCD_CS = P1^2; sbit LCD_DC = P1^3; sbit LCD_RST = P1^4; sbit LCD_BLK = P1^5; void DelayUs(unsigned int us) { while(us--) { _nop_(); _nop_(); } } void Lcd_WriteByte(unsigned char dat) { unsigned char i; for(i = 0; i < 8; i++) { LCD_SCL = 0; if(dat & 0x80) LCD_SDA = 1; else LCD_SDA = 0; LCD_SCL = 1; dat <<= 1; } }

Lcd_WriteByte每次先把SCL拉低,给MOSI留出建立时间,再拉高形成上升沿,ST7735就在这个上升沿采样数据。注意先拉低再拉高,而不是先置数据再翻转,因为SCK空闲低电平是ST7735的默认极性。初始化完成后如果屏幕完全没有反应,先用万用表量SCK在空闲时是不是低电平,这是最快排查点。

2.4 命令与数据的区别,ST7735的DC引脚

Lcd_WriteByte只负责往总线送数据,至于这个字节是命令还是参数,由DC引脚决定。DC为低时主机发送的是命令,为高时发送的是数据。初始化序列里大量出现“一个命令后面跟几个参数”的格式,就是靠DC引脚切换的。

void Lcd_WrCmd(unsigned char cmd) { LCD_CS = 0; LCD_DC = 0; Lcd_WriteByte(cmd); LCD_CS = 1; } void Lcd_WrData(unsigned char dat) { LCD_CS = 0; LCD_DC = 1; Lcd_WriteByte(dat); LCD_CS = 1; }

CS在整个字节传输期间拉到低电平,确保同一时刻只有这块屏幕在接收数据。如果板子上并联了多块SPI设备,CS选错会导致其他设备也收到命令,屏幕出现随机乱画。这个底层函数后面所有显示操作都会复用,值得单独建一个lcd_driver.c放起来。

3. DS18B20时序与STC单片机的精确读写

3.1 单总线时序:复位脉冲与存在脉冲

DS18B20是单总线设备,所有通信都由主机发起。第一步是复位时序:主机拉低总线480至960us,然后释放并等待15至60us,此时DS18B20会拉低总线60至240us,产生一个存在脉冲,表示设备在线。

时序阶段最小值典型值最大值
主机拉低480us500us960us
释放后等待15us60us60us
存在脉冲低电平60us240us
sbit DQ = P2^0; unsigned char DS18B20_Reset(void) { unsigned char flag; DQ = 0; DelayUs(500); DQ = 1; DelayUs(60); flag = DQ; DelayUs(400); return flag; }

flag在存在脉冲期间读到0,说明传感器在线;读到1说明总线上没有设备或线路没接对。这里有个STC特有的细节:STC89C52的IO口是准双向结构,拉高后可以靠内部弱上拉读输入,不用切换方向;但STC15系列如果配置成开漏输出,释放总线后必须把IO口切回高阻输入,否则读到的永远是低电平,存在脉冲判断会失效。

3.2 STC单片机上的写时隙与读时隙

复位成功后,每次读写都是一个独立的时隙。写1:主机拉低总线,15us内释放,靠外部上拉拉高;写0:主机拉低总线并保持60至120us再释放。读时隙:主机拉低1us后释放,在15us内采样总线电平。这三个操作的区别只在电平持续时间上。

void DS18B20_WriteBit(unsigned char bitval) { DQ = 0; if(bitval) DQ = 1; DelayUs(20); DQ = 1; } unsigned char DS18B20_ReadBit(void) { unsigned char bitval; DQ = 0; _nop_(); DQ = 1; bitval = DQ; DelayUs(20); return bitval; }

读时序里最容易出错的是采样点:DQ = 1之后必须立刻读,不能加长延时。_nop_()在11.0592MHz下大约1us,DS18B20在主机释放总线后开始驱动总线,采样窗口一般在15us内。如果在这里换成DelayUs(10)再去读,大概率读到1,因为传感器已经释放总线了。

字节读写就是把位操作循环8次:

void DS18B20_WriteByte(unsigned char dat) { unsigned char i; for(i = 0; i < 8; i++) { DS18B20_WriteBit(dat & 0x01); dat >>= 1; } } unsigned char DS18B20_ReadByte(void) { unsigned char i, dat = 0; for(i = 0; i < 8; i++) { dat >>= 1; if(DS18B20_ReadBit()) dat |= 0x80; } return dat; }

LSB先出,这是单总线的固定格式。读写字节之前先算好时间:一个时隙约60us,一个字节8个时隙,关中断时间不到1ms,不会影响主循环。

3.3 完整的温度转换命令序列

DS18B20的每一次温度转换都需要两条命令链。第一条链是启动转换:复位、跳过ROM、发0x44。第二条链是读取结果:复位、跳过ROM、发0xBE,再连续读9字节暂存器。

unsigned char DS18B20_StartConvert(void) { if(DS18B20_Reset()) return 1; DS18B20_WriteByte(0xCC); // 跳过ROM DS18B20_WriteByte(0x44); // 启动温度转换 return 0; }

0xCC是跳过ROM命令。总线上只有一个传感器时,可以不读64位序列号直接操作。如果总线上挂了多个DS18B20,就必须先用0x33读ROM拿到每个设备的序列号,再用0x55匹配ROM,否则所有传感器会同时应答,数据互相冲突。

转换期间总线可以释放,单片机去做屏幕刷新。12位分辨率下转换时间典型值750ms,上限约750ms。转换完成后,通过读暂存器把温度取回来:

int DS18B20_GetRawTemperature(void) { int raw; DS18B20_Reset(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); raw = DS18B20_ReadByte() | (DS18B20_ReadByte() << 8); return raw; }

严格的做法是把剩余7字节读完,用CRC8校验,多项式是X8+X5+X4+1。工程上如果只需要显示温度,可以只读前两字节后直接复位总线,省掉CRC计算时间。传感器刚上电时暂存器里是0x0550,也就是85°C,如果读出这个值,先别怀疑传感器坏了。

3.4 把温度寄存器换算成定点数,不用浮点

DS18B20的12位温度数据中,LSB对应0.0625°C,数据以16位有符号数存储。换算成“温度乘以100”的定点数,可以完全避开浮点运算,因为STC89C52没有浮点单元,软件浮点慢且占用大量代码空间。

int DS18B20_GetTemperatureX100(void) { int raw; unsigned int temp; int result; raw = DS18B20_GetRawTemperature(); if(raw & 0x8000) { raw = ~raw + 1; // 负数取补码 temp = (unsigned int)raw * 625 / 100; result = -(int)temp; } else { temp = (unsigned int)raw * 625 / 100; result = (int)temp; } return result; // 单位:0.01摄氏度 }

raw * 625 / 100等价于raw * 0.0625 * 100:先乘以625再除以100,整个过程都是整数运算。返回的result比如2531就代表25.31°C。负数先取补码再计算,最后把负号补回去。显示时用result / 100拿整数部分,用result % 100拿小数部分。

提示:如果读出的温度始终是85°C,大概率不是传感器坏了,而是复位时序没把传感器唤醒,读到的只是暂存器上电默认值。先去查复位脉冲宽度,再用示波器看DQ引脚上有没有存在脉冲。

4. ST7735初始化、坐标设置与温度数值显示

4.1 屏幕初始化:ST7735S的最小命令序列

ST7735S这类TFT液晶屏上电后要先经过延时、唤醒、设置方向、设置颜色格式,最后才能显示。下面是一组能跑通的最小序列:

void Lcd_Init(void) { LCD_RST = 0; DelayMs(120); LCD_RST = 1; DelayMs(120); Lcd_WrCmd(0x01); // 软件复位 DelayMs(150); Lcd_WrCmd(0x11); // 退出睡眠 DelayMs(120); Lcd_WrCmd(0x36); // MADCTL:显示方向 Lcd_WrData(0xC8); Lcd_WrCmd(0x3A); // COLMOD:颜色格式 Lcd_WrData(0x05); // RGB565 Lcd_WrCmd(0x29); // 开启显示 DelayMs(20); }
命令参数作用
0x01软件复位,清空内部寄存器
0x11退出睡眠模式,必须等待120ms
0x360xC8设置扫描方向与RGB顺序
0x3A0x05像素格式为RGB565,每像素2字节
0x29打开显示输出

0x36的参数0xC8是我常用的横屏方向。bit7控制行扫描方向,bit6控制列扫描方向,bit5控制行列交换。如果画面出现镜像,只翻转bit6或bit7,不要整个字节乱试。0x3A设成0x05是16位色,也就是RGB565,屏幕内部每个像素点占两个字节。

SLPOUT到DISPON之间的延时非常重要,用STC单片机控制RST引脚而不是直接并联复位电路,就是为了让屏幕在单片机IO口稳定后再走初始化流程。如果你用的是IPS TFT LCD面板,SLPOUT后的延时建议提到150ms,IPS面板对初始化时序比普通TFT更挑剔。

4.2 设置绘图窗口:为什么1.8寸屏会偏2个像素

ST7735内部的GRAM尺寸比1.8寸面板的可见区域大一圈,常见的误差是X方向偏移2个像素、Y方向偏移1个像素。不处理这个偏移,画出来的内容整体错位。设置窗口用0x2A列地址命令和0x2B行地址命令,之后用0x2C开始写显存数据。

void Lcd_SetWindow(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2) { Lcd_WrCmd(0x2A); Lcd_WrData(x1 + 2); Lcd_WrData(y1 + 1); Lcd_WrData(x2 + 2); Lcd_WrData(y2 + 1); Lcd_WrCmd(0x2B); Lcd_WrData(x1); Lcd_WrData(y1); Lcd_WrData(x2); Lcd_WrData(y2); Lcd_WrCmd(0x2C); }

0x2A0x2B的参数都是先发高字节再发低字节,但在128x160这种小分辨率下,直接用8位宽度传参即可。偏移量加在列地址上:x1 + 2x2 + 2。如果屏幕四边出现彩条或边缘错位,就把+2+1互换测试,不同批次的面板偏移不完全一样。

4.3 颜色填充函数

窗口设置完成后,往0x2C连续写像素数据,屏幕就会按行列顺序填充颜色。颜色数据是RGB565格式,两个字节的发送顺序和初始化里的BGR位有关:

void Lcd_FillColor(unsigned int color, unsigned int count) { while(count--) { Lcd_WrData(color >> 8); Lcd_WrData(color & 0xFF); } }

color >> 8先发高字节,color & 0xFF后发低字节。如果发现红色和蓝色对调,问题出在0x36的BGR位:把0xC8改成0x08,红色和蓝色分量就会互换回来。这个测试用纯红色0xF800填充全屏最直观。

4.4 显示温度数值:字模拆分与局部刷新

显示温度需要把整数、小数、符号分别查字模,再定位到屏幕的固定区域。我这里用8x16的ASCII字模,每个字符占8列16行像素,取模软件按字节纵向取模即可。

void Lcd_ShowTemp(unsigned char x, unsigned char y, int temp) { unsigned char buf[7]; unsigned char i, len = 0; int t; if(temp < 0) { buf[len++] = '-'; t = -temp; } else { t = temp; } buf[len++] = t / 100 + '0'; // 整数部分 buf[len++] = '.'; buf[len++] = (t / 10) % 10 + '0'; // 十分位 buf[len++] = t % 10 + '0'; // 百分位 buf[len++] = 'C'; for(i = 0; i < len; i++) { Lcd_ShowChar(x, y, buf[i]); x += 8; } }

这里用了一个buf数组拼字符串,先处理负号,再处理整数、小数点、小数位。后面刷新只需要对这块区域重画,不用每次清全屏。如果每次读温度都Lcd_FillColor全屏清一次,屏幕会持续闪烁,因为750ms的转换周期和显示刷新周期在视觉上直接打架,肉眼能看出明显闪动。

局部刷新的做法是在Lcd_ShowTemp之前先调用Lcd_SetWindow固定温度显示区域,然后整个函数只往这块区域填充即可。主循环结构一般是:启动转换,等待期间刷新屏幕,读取温度并显示,如此循环。

5. 联调校准:STC单片机读不到温度与显示乱码的快速定位

5.1 三个典型症状的排查顺序

现象优先检查的方向常见根因
屏幕白屏或花屏RST引脚上电时序SLPOUT后延时不足,或初始化数据发错
温度固定显示85°C复位脉冲宽度和上拉电阻DS18B20没被正常唤醒
数字闪烁或数值乱跳读写期间是否被中断打断定时器或串口中断抢占单总线时序

白屏时用示波器抓SCK和MOSI,看初始化命令是否在正确时间发出。ST7735没有MISO回读路径,一旦数据线接错,屏幕不会给你任何错误提示。温度85°C的场景,先量P2.0上拉到VCC的电阻,再检查DS18B20_Reset的返回值,返回1表示总线上根本没有设备。

5.2 中断与临界区

DS18B20的时隙窗口最窄在15us左右,如果在读时隙期间发生串口中断或定时器中断,回来之后采样点已经错过。最直接的保护是在读温度前关闭总中断:

EA = 0; temp = DS18B20_GetTemperatureX100(); EA = 1;

一次完整读取关中断时间在1ms以内,对串口接收缓冲来说完全可以接受。但要注意关中断期间不要去调DelayMs这类长延时函数,否则整个主循环都会被卡住。STC15系列如果开了看门狗,关中断超过喂狗周期会直接复位,需要把喂狗语句放在关中断之前。

5.3 用示波器抓单总线波形

把探头挂在DQ引脚,触发方式选下降沿。正常波形应该是:一段约500us的低电平复位脉冲,释放后出现一个短促的低电平存在脉冲,之后是一串整齐的60us左右读写时隙。如果你看到一个写时隙里出现两个下降沿,多半是延时偏短,传感器把前一个时隙误判成了复位脉冲。

如果没有示波器,可以用LED做粗测:初始化后让DQ输出高电平,如果传感器在线,LED会保持高亮而不是闪烁,这个判断比读寄存器还快。

5.4 温度校准

DS18B20出厂有校准,但线缆电阻、传感器自热都会带来零点几度的偏差。程序里预留一个偏移宏,校准后统一叠加:

#define TEMP_OFFSET_X100 20 result += TEMP_OFFSET_X100;

TEMP_OFFSET_X100的单位是0.01°C,20代表0.20°C。校准方法是把传感器和水银温度计放在同一环境,静置10分钟后读差值,然后填入宏。注意传感器要远离电源芯片和屏幕背光,这两个热源会让显示值比环境温度高出不少,偏移量只能补偿固定误差,补偿不了布局造成的持续自热。

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

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

STC15W204S PCA PWM详解:寄存器配置与舵机控制实战

简介&#xff1a;面向STC单片机开发者&#xff0c;特别是使用STC15W204S进行PWM输出的工程师&#xff0c;这份代码包提供了基于PCA模块实现PWM生成与端口切换的完整工程方案&#xff0c;可解决PWM占空比配置、时钟源选择及多通道输出等常见问题&#xff0c;适用于电机调速、LED…

作者头像 李华
网站建设 2026/9/14 14:14:47

2026招聘风向:学历贬值,作品集与项目经验成新标尺

1. 从2025年底的几份面试评价表说起&#xff1a;风向真的变了2025年11月&#xff0c;我帮一家客户公司做年终招聘复盘&#xff0c;顺手统计了28份进入终面的候选人评价表。其中14位本科毕业于985/211或QS前50院校&#xff0c;另外14位来自普通一本甚至二本院校。让我意外的是&a…

作者头像 李华
网站建设 2026/9/14 14:14:00

PHP话费充值源码部署实战:通道对接、回调验签与订单状态机

简介&#xff1a;面向PHP开发者和网络服务创业者的完整话费充值运营源码包&#xff0c;基于PHP构建用户充值、订单管理、后台管理等核心流程&#xff0c;源码经过测试可直接部署&#xff0c;适合需要快速搭建充值业务平台或深入学习支付接口对接的开发者。包内共1859个文件&…

作者头像 李华
网站建设 2026/9/14 14:13:21

PHP与MySQL Web开发实战:环境配置、索引设计与PDO安全编程

简介&#xff1a;面向Web开发初学者与需要快速上手中后端配合的开发者&#xff0c;这份教程围绕PHP与MySQL的协同应用展开&#xff0c;讲解如何使用PHP的MySQLi或PDO扩展建立数据库连接、执行SQL查询、处理结果集与错误&#xff0c;并通过表单提交、动态内容生成等场景展示两者…

作者头像 李华