单片机开发快速入门:使用DS18B20温度传感器设计温控系统
系统实现目标
设计并实现一个基于 Proteus 和 51 单片机的温度传感器系统,系统需具备如下功能:
1、实时温度监测与显示
系统通过DS18B20温度传感器进行环境温度的实时采集,应用LCD1602显示器实时显示温度数值。
2、阈值报警机制
当检测温度超过预设阈值时,系统自动触发报警功能,通过点亮发光二极管(LED)发出警示信号。
3、双向串口通信功能
系统采用串行通信接口与模拟上位机软件建立双向数据交互:
-
上位机接收单片机发送的实时温度数据
-
上位机可通过串口设置阈值
系统实现了温度监测、阈值报警及人机交互的完整闭环,为工业控制、环境监测等应用提供了模拟实现场景。
温度传感器
温度传感器是一种将温度物理量转换为可处理电信号的装置,其核心功能在于实现温度-电信号的转换。
早期使用的是模拟传感器,如热敏电阻,随着环境温度的变化,它的阻值也发生线性变化,用处理器采集电阻两端的电压,然后根据转换公式就可以计算出当前环境温度。现代的温度传感器已经走向数字化,美国DALLAS半导体公司推出的数字化温度传感器DS18B20采用单总线协议,即与单片机接口仅需占用一个I/O端口,无需任何外部元件,直接将环境温度转换为数字信号。
DS18B20 采用单总线接口,仅需将数据引脚(DQ)与单片机的 IO 口(图中为 P1.2 引脚)连接。电路中包含 VCC 供电、GND 接地。以及 4.7K 电阻 R3(上拉电阻,确保总线电平稳定)。

对于单总线结构的DS18B20的读写控制必须严格按照时序图进行编程, 通过单片机的I/O端口直接驱动DS18B20的DQ信号的交流端. 通过调用精确的延时子程序, 来对其准确时序控制。 DS18B20的读时序图如下图所示:

DS18B20的读时序分为读“0”和读“1”两种情况,具体如下:
读“0”时序:控制器先将单总线拉低至少1μs,然后释放总线。DS18B20会在控制器释放总线后,将总线拉低并保持60~120μs,此期间控制器采样总线为低电平,即读取到“0”。随后总线会被释放回高电平。
读“1”时序:控制器同样先将单总线拉低至少1μs,释放总线后,DS18B20会在15μs内将总线拉低一个短暂脉冲(约15μs),之后释放总线使其恢复高电平。控制器在释放总线后的15μs左右采样总线,此时总线为高电平,即读取到“1”。读操作之间的恢复时间(tREC)需大于1μs。
实验用电子元件
电路用电子元件见下表:
|
元件名称 |
型号 |
搜素词 |
设置值 |
|
51单片机 |
AT89C52 |
AT89C52 |
/ |
|
温度传感器 |
DS18B20 |
DS18B20 |
/ |
|
液晶显示器 |
LCD1602 |
LM016L |
/ |
|
虚拟串口 |
COMPIM |
COMPIM |
/ |
|
发光二极管 |
/ |
LED-RED |
/ |
|
排阻 |
/ |
RESPACK-8 |
/ |
|
电阻 |
/ |
RES |
|
|
电源 |
POWER |
/ |
5V |
|
接地 |
GROUND |
/ |
/ |
|
晶振 |
/ |
CRYSTAL |
12MHZ |
|
电容 |
/ |
CAP |
22pf |
电路设计
下图为电路设计图。

晶振电路
U1的18脚(XTAL2)和19脚(XTAL1)之间连接晶振X2(11.0592MHZ),晶振两端分别通过电容C3(22pF)和C4(22pF)接地,构成稳定的时钟振荡电路。
复位电路
U1的9脚(RST)通过电阻R2(1kΩ)接地,同时通过电容C5(10μF)连接至VCC,形成上电复位电路,保证芯片上电后可靠复位。
温度传感器电路
DS18B20的VCC引脚(3脚)接+5V,GND引脚(1脚)接地,DQ引脚(2脚)通过上拉电阻R3(4.7kΩ)连接至U1的P1.2引脚(4脚),实现单总线通信。
温度显示电路
LCD的D0-D7引脚(7-14脚)分别对应连接至U1的P0.0-P0.7引脚(39-32脚),实现8位数据传输。排阻RP1的引脚1接5V电源,其余引脚(2-9)分别连接至LCD的D7-D0引脚,起到上拉电阻作用,确保P0口输出的高电平稳定。
LCD的RS引脚(4脚)连接至U1的P2.0引脚(21脚),RW引脚(5脚)连接至P2.1引脚(22脚),E引脚(6脚)连接至P2.2引脚(23脚),用于控制LCD的读写操作和命令/数据选择。
LCD的VDD引脚(2脚)接+5V,VSS引脚(1脚)接地。
串口通信电路
COMPIM的RXD引脚连接至P3.0(单片机的RXD引脚),COMPIM的TXD引脚连接至P3.1(单片机的TXD引脚)。
打开虚拟串口配置对话框,按下图给出的参数进行配置,也可以自己定义参数。这里配置的参数一定要和在电脑端配置的VSPD虚拟串口参数一致。

LED(D1)的阳极通过限流电阻R1(220Ω)连接至VCC,阴极连接至U1的P1.0引脚(2脚),实现通过P1.0控制LED的亮灭。
配置单片机主频
单片机主频配置为11.0592MHz。
软件设计
1、软件整体流程
下图为软件整体流程图。

主控模块是程序的核心模块,负责调度温度模块、显示模块和通信模块,并负责报警功能。
软件整体工作流程描述如下:
主控模块运行后,先调用温度模块、显示模块和通信模块的初始化函数,初始化温度传感器、LCD液晶显示器和虚拟串口,并初始化阈值变量。
外部设备初始化完成后,主控模块进入循环体,在循环体内重复执行下面的流程:
(1)调用温度模块提供的函数获取已采集的温度数据;
(2)调用显示模块提供的显示函数,在LCD液晶显示器上显示温度数据;
(3)调用通信模块提供的发送函数,将温度数据发送到串口,上位机软件从串口接收温度数据;
(4)比较阈值与温度数据,若温度数据大于阈值,点亮发光二极管。
在通信模块内,启用UART_ISR()中断,接收上位机设置的阈值,将阈值赋值给主控模块的阈值变量。
2、通用模块
模块代码名称:common.h、common.c
模块实现一些通用函数,方便其它模块调用。该模块定义了一些通用数据类型和宏操作,并实现了基于软件循环的毫秒级、微秒级延时函数
模块源代码C头文件:
#ifndef __COMMON_H__#define __COMMON_H__#include "reg52.h"#include// 类型重定义typedef unsigned char uint8;typedef unsigned int uint16;typedef unsigned long uint32;sbit LCD_ALARM = P1^0;// 位操作宏#define BIT(x) (1 << (x))#define SET_BIT(x, y) ((x) |= BIT(y))#define CLR_BIT(x, y) ((x) &= ~BIT(y))#define GET_BIT(x, y) (((x) >> (y)) & 0x01)// ms延时函数void Delay_ms(unsigned int ms);// us延时函数void Delay_us(unsigned int us);void delay10us(unsigned char n);#endif // __COMMON_H__
模块源代码C实现文件:
#include "common.h"// 延时函数void Delay_ms(unsigned int ms) {unsigned int i, j;for (i = ms; i > 0; i--)for (j = 112; j > 0; j--);}// 微秒级延时函数void Delay_us(unsigned int us) {while (us--) {// 11.0592MHz下,每个循环约1.085us_nop_(); _nop_(); _nop_(); _nop_();_nop_(); _nop_(); _nop_(); _nop_();}}/******************************************************************函数功能:延时函数11.0592MHZ 延时约10us*****************************************************************/void delay10us(unsigned char n) //误差 -0.234375us{unsigned char i;while (n--){i = 2;while (--i);}}
3、温度模块
模块代码名称:temperature.h、temperature.c。
实现 DS18B20 温度传感器的驱动功能。

模块源代码C头文件:
#ifndef __TEMPERATURE_H__#define __TEMPERATURE_H__// DS18B20 引脚定义sbit DQ = P1^2; // DQ引脚连接到P1.2// 函数声明void DS18B20_Init();void DS18B20_WriteByte(unsigned char dat);unsigned char DS18B20_ReadByte();float DS18B20_ReadTemperature();#endif // __TEMPERATURE_H__
模块源代码C实现文件:
#include "common.h"#include "temperature.h"// DS18B20复位函数void DS18B20_Init() {CY = 1;while (CY){DQ = 0; //总线拉低delay10us(48); //复位脉冲至少480usDQ = 1; //电阻上拉delay10us(6); //15-60usCY = DQ; //ds18b20应答脉冲给CYdelay10us(42); //至少480us}}// 向DS18B20写入一个字节void DS18B20_WriteByte(unsigned char dat) {unsigned char i;for(i = 0; i < 8; i++) {DQ = 0; // 拉低总线_nop_(); _nop_(); // 延时2usif(dat & 0x01) { // 写1DQ = 1;Delay_us(60);} else { // 写0Delay_us(60);DQ = 1;}dat >>= 1; // 右移一位}DQ = 1; // 确保总线释放}// 从DS18B20读取一个字节unsigned char DS18B20_ReadByte() {unsigned char i;unsigned dat = 0;for (i=0; i<8; i++) //8位数据{dat >>= 1;DQ = 0; //拉低总线_nop_(); //至少1us_nop_();DQ = 1; //单片机释放总线_nop_(); //等待总线稳定_nop_();if (DQ)dat |= 0x80; //读取1delay10us(6); //读完当前数据}return dat;}// 读取DS18B20温度值float DS18B20_ReadTemperature() {unsigned int TPH,temp; //温度高八位unsigned char TPL;float temperature;DS18B20_Init(); // 18b20初始化DS18B20_WriteByte(0xCC); // 跳过romDS18B20_WriteByte(0x44); // 启动18b20转换while (!DQ); // 等待转换完DS18B20_Init(); // 18b20初始化DS18B20_WriteByte(0xCC); // 跳过romDS18B20_WriteByte(0xBE); // 读缓冲器TPL = DS18B20_ReadByte(); // 读低8位TPH = DS18B20_ReadByte(); // 读高8位// 正确组合温度数据temp = (TPH << 8) | TPL;// DS18B20温度格式:高5位为符号位和整数部分,低11位为小数部分// 温度值 = temp * 0.0625temperature = temp * 0.0625f;return temperature;}
4、显示模块
模块代码名称:lcd.h、lcd.c。
LCD1602 显示屏驱动模块,用于在 51 单片机等微控制器上控制 LCD1602 显示时间和日期。模块提供了完整的 LCD1602 显示屏驱动功能。

模块源代码C头文件:
#ifndef __LCD_H__#define __LCD_H__// LCD1602 引脚定义sbit LCD_RS = P2^0; // RS引脚连接到P2.0sbit LCD_RW = P2^1; // R/W引脚连接到P2.1sbit LCD_EN = P2^2; // E引脚连接到P2.2// 函数声明void delay_ms(unsigned int ms);void LCD_WriteCmd(unsigned char cmd);void LCD_WriteData(unsigned char dat);void LCD_Init();void LCD_SetCursor(unsigned char Line, unsigned char Column);void LCD_ShowChar(unsigned char Line, unsigned char Column, char Char);void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length);void LCD_ShowString(unsigned char Line, unsigned char Column, char *String);void LCD_ShowDegree(unsigned char Line, unsigned char Column);void LCD_ShowTemperature(unsigned char Line, unsigned char Column, float Temperature);#endif // __LCD_H__
模块源代码C实现文件:
#include "common.h"#include "lcd.h"// 写指令函数void LCD_WriteCmd(unsigned char cmd) {LCD_RS = 0; // 写指令时,RS=0LCD_RW = 0; // 写操作时,RW=0LCD_EN = 0; // EN初始化为0P0 = cmd; // 将指令写入P0口delay_ms(1); // 延时1msLCD_EN = 1; // EN拉高delay_ms(1); // 延时1msLCD_EN = 0; // EN拉低delay_ms(2); // 延时2ms}// 写数据函数void LCD_WriteData(unsigned char dat) {LCD_RS = 1; // 写数据时,RS=1LCD_RW = 0; // 写操作时,RW=0LCD_EN = 0; // EN初始化为0P0 = dat; // 将数据写入P0口delay_ms(1); // 延时1msLCD_EN = 1; // EN拉高delay_ms(1); // 延时1msLCD_EN = 0; // EN拉低delay_ms(2); // 延时2ms}// 初始化函数void LCD_Init() {delay_ms(20); // 上电延时LCD_WriteCmd(0x38); // 设置8位数据接口,2行显示,5x7点阵delay_ms(5);LCD_WriteCmd(0x38);delay_ms(5);LCD_WriteCmd(0x38);delay_ms(5);LCD_WriteCmd(0x0C); // 显示开,光标关delay_ms(5);LCD_WriteCmd(0x01); // 清屏delay_ms(5);}// 计算X的Y次方int LCD_Pow(int X, int Y) {unsigned char i;int Result = 1;for(i = 0; i < Y; i++) {Result *= X;}return Result;}// 在LCD1602指定位置显示数字void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length) {unsigned char i;LCD_SetCursor(Line, Column);for(i = Length; i > 0; i--) {LCD_WriteData(Number / LCD_Pow(10, i - 1) % 10 + '0');}}// LCD1602设置光标位置void LCD_SetCursor(unsigned char Line, unsigned char Column) {if(Line == 1) {LCD_WriteCmd(0x80 | (Column - 1)); // 第一行} else if(Line == 2) {LCD_WriteCmd(0x80 | (Column - 1 + 0x40)); // 第二行}}// 在LCD1602指定位置显示一个字符void LCD_ShowChar(unsigned char Line, unsigned char Column, char Char) {LCD_SetCursor(Line, Column);LCD_WriteData(Char);}// 在LCD1602指定位置显示字符串void LCD_ShowString(unsigned char Line, unsigned char Column, char *String) {unsigned char i;LCD_SetCursor(Line, Column);for(i = 0; String[i] != '\0'; i++) {LCD_WriteData(String[i]);}}// 显示摄氏度符号void LCD_ShowDegree(unsigned char Line, unsigned char Column) {LCD_ShowChar(Line, Column, 0xdf); // 显示度数符号的上半部分LCD_ShowChar(Line, Column + 1, 'C'); // 显示字母C}// 在LCD1602上显示温度值void LCD_ShowTemperature(unsigned char Line, unsigned char Column, float Temperature) {unsigned int IntPart;unsigned long DecPart;// 处理负温度if(Temperature < 0) {LCD_ShowChar(Line, Column, '-');Temperature = -Temperature;Column++;} else {LCD_ShowChar(Line, Column, '+');Column++;}// 分离整数部分和小数部分(保留4位小数)IntPart = (unsigned int)Temperature;DecPart = (unsigned long)((Temperature - IntPart) * 10000 + 0.5); // 四舍五入// 显示整数部分(最多3位)if(IntPart >= 100) {LCD_ShowNum(Line, Column, IntPart, 3);Column += 3;} else if(IntPart >= 10) {LCD_ShowNum(Line, Column, IntPart, 2);Column += 2;} else {LCD_ShowNum(Line, Column, IntPart, 1);Column += 1;}// 显示小数点LCD_ShowChar(Line, Column, '.');Column++;// 显示小数部分(4位)LCD_ShowNum(Line, Column, DecPart, 4);Column += 4;// 显示摄氏度符号LCD_ShowDegree(Line, Column);}
5、通信模块
模块代码名称:urat.h、urat.c
模块主要用于通过串口接收用户输入的温度阈值,发送温度数据到上位机软件。

模块源代码C头文件:
#define __URAT_H__#define __URAT_H__void UART_Init();void UART_SendChar(unsigned char dat);void UART_SendString(unsigned char *str);void convertTempToInt();#endif // __URAT_H__
模块源代码C实现文件:
#include "common.h"#include "urat.h"extern int threshold;// 温度数据缓冲区unsigned char tempBuffer[3]; // 存储温度数据,最多2位数字unsigned char tempIndex = 0; // 缓冲区索引bit tempReceived = 0; // 温度数据接收完成标志// 初始化串口,波特率9600bps,晶振11.0592MHzvoid UART_Init() {TMOD |= 0x20; // 定时器1工作在方式2SCON = 0x50; // 串口工作在方式1,允许接收TH1 = 0xFD; // 装载初值,9600bps对应的初值TL1 = 0xFD;TR1 = 1; // 启动定时器1EA = 1; // 允许总中断ES = 1; // 允许串口中断}// 串口发送一个字符void UART_SendChar(unsigned char dat) {SBUF = dat; // 将数据放入发送缓冲器while (!TI); // 等待发送完成TI = 0; // 清除发送完成标志位}// 串口发送字符串void UART_SendString(unsigned char *str) {while (*str != '\0') { // 字符串结束标志UART_SendChar(*str);str++;}}// 串口中断服务函数void UART_ISR() interrupt 4 {unsigned char receiveData;if (RI) { // 接收中断标志位RI = 0; // 清除接收中断标志位receiveData = SBUF; // 读取接收的数据if ((receiveData >= '0' && receiveData <= '9') || receiveData == '\r' || receiveData == '\n') {if (receiveData == '\r' || receiveData == '\n') {// 数据接收完成if (tempIndex > 0) {tempBuffer[tempIndex] = '\0'; // 添加字符串结束符tempReceived = 1; // 设置接收完成标志convertTempToInt();tempIndex = 0; // 重置索引}} else {// 接收数字if (tempIndex < 2) { // 只接收1-2位数字tempBuffer[tempIndex++] = receiveData;} else {// 数据过长,重置tempIndex = 0;UART_SendString("Temperature data too long (max 2 digits)\r\n");}}}}}// 将接收到的字符串转换为整数void convertTempToInt() {if (tempIndex == 1) {// 1位数字threshold = tempBuffer[0] - '0';} else if (tempIndex == 2) {// 2位数字threshold = (tempBuffer[0] - '0') * 10 + (tempBuffer[1] - '0');}}
6、主控模块
模块代码名称:main.c。
主控模块集成了温度传感器、LCD 显示和串口通信模块,实现了一个完整的温度监控系统,具有温度采集、显示、远程监控和报警功能。
模块源代码C实现文件:
#include "common.h"#include "temperature.h"#include "lcd.h"#include "urat.h"// 阈值int threshold;void main() {float temperature;unsigned char code str[] = "Temperature:";unsigned char buffer[16];LCD_Init();UART_Init();LCD_ALARM = 0;threshold = 25;while(1) {LCD_ShowString(1,1,str);temperature = DS18B20_ReadTemperature();// 在第二行第一列显示温度值LCD_ShowTemperature(2, 1, temperature);// 发送温度值到串口UART_SendString(str);// 将浮点数转换为字符串(简化版)if(temperature < 0) {//Uart_SendByte('-');temperature = -temperature;}buffer[0] = (unsigned char)temperature / 10 + '0';buffer[1] = (unsigned char)temperature % 10 + '0';buffer[2] = '.';buffer[3] = (unsigned char)(temperature * 10) % 10 + '0';buffer[4] = (unsigned char)(temperature * 100) % 10 + '0';buffer[5] = (unsigned char)(temperature * 1000) % 10 + '0';buffer[6] = (unsigned char)(temperature * 10000) % 10 + '0';buffer[7] = '\0';UART_SendString(buffer);UART_SendString(" C\n");if( (int)temperature > threshold ) {LCD_ALARM = 0;} else {LCD_ALARM = 1;}Delay_ms(1000); // 每秒读取一次温度}}.
联合调试与功能验证
一、环境准备
1、虚拟串口配置
-
启动VSPD软件,虚拟生成COM1和COM2两个串口
-
在设备管理器中确认无端口冲突(COM1/COM2状态正常)
2、串口调试工具设置
-
打开串口调试助手(如SSCOM)
-
选择VSPD创建的COM2端口
-
参数配置:9600bps/8N1(无校验位/1停止位)
二、验证温度采集与显示
启动Proteus仿真软件,运行单片机程序,程序运行稳定后,温度传感器DS18B20会显示当前的温度值,LCD1602液晶显示器也会显示当前采集的温度。运行效果如下图所示:

同时单片机程序会将温度数据发送到串口,上位机软件可通过串口接收温度数据。

因为DS18B20是仿真器件,传感器采集不到环境温度,但可以通过手动来改变传感器当前的温度值,用于验证程序在温度变化时是否能正常工作。单击传感器两个红色箭头按钮,即可上调和下调温度,如下图:

三、验证报警功能
程序内部设定了一个阈值变量,默认值为25°当温度超过25°时,发光二极管会点亮。调整传感器的温度值就会触发报警。
上位机软件可以通过串口设置阈值,具体步骤是通过串口发送阈值到单片机:

在串口调试助手发送窗口内,输入需要设置的阈值,单击“发送”按钮即可完成阈值设置。
系统实现目标
设计并实现一个基于 Proteus 和 51 单片机的温度传感器系统,系统需具备如下功能:
1、实时温度监测与显示
系统通过DS18B20温度传感器进行环境温度的实时采集,应用LCD1602显示器实时显示温度数值。
2、阈值报警机制
当检测温度超过预设阈值时,系统自动触发报警功能,通过点亮发光二极管(LED)发出警示信号。
3、双向串口通信功能
系统采用串行通信接口与模拟上位机软件建立双向数据交互:
-
上位机接收单片机发送的实时温度数据
-
上位机可通过串口设置阈值
系统实现了温度监测、阈值报警及人机交互的完整闭环,为工业控制、环境监测等应用提供了模拟实现场景。
温度传感器
温度传感器是一种将温度物理量转换为可处理电信号的装置,其核心功能在于实现温度-电信号的转换。
早期使用的是模拟传感器,如热敏电阻,随着环境温度的变化,它的阻值也发生线性变化,用处理器采集电阻两端的电压,然后根据转换公式就可以计算出当前环境温度。现代的温度传感器已经走向数字化,美国DALLAS半导体公司推出的数字化温度传感器DS18B20采用单总线协议,即与单片机接口仅需占用一个I/O端口,无需任何外部元件,直接将环境温度转换为数字信号。
DS18B20 采用单总线接口,仅需将数据引脚(DQ)与单片机的 IO 口(图中为 P1.2 引脚)连接。电路中包含 VCC 供电、GND 接地。以及 4.7K 电阻 R3(上拉电阻,确保总线电平稳定)。

对于单总线结构的DS18B20的读写控制必须严格按照时序图进行编程, 通过单片机的I/O端口直接驱动DS18B20的DQ信号的交流端. 通过调用精确的延时子程序, 来对其准确时序控制。 DS18B20的读时序图如下图所示:

DS18B20的读时序分为读“0”和读“1”两种情况,具体如下:
读“0”时序:控制器先将单总线拉低至少1μs,然后释放总线。DS18B20会在控制器释放总线后,将总线拉低并保持60~120μs,此期间控制器采样总线为低电平,即读取到“0”。随后总线会被释放回高电平。
读“1”时序:控制器同样先将单总线拉低至少1μs,释放总线后,DS18B20会在15μs内将总线拉低一个短暂脉冲(约15μs),之后释放总线使其恢复高电平。控制器在释放总线后的15μs左右采样总线,此时总线为高电平,即读取到“1”。读操作之间的恢复时间(tREC)需大于1μs。
实验用电子元件
电路用电子元件见下表:
|
元件名称 |
型号 |
搜素词 |
设置值 |
|
51单片机 |
AT89C52 |
AT89C52 |
/ |
|
温度传感器 |
DS18B20 |
DS18B20 |
/ |
|
液晶显示器 |
LCD1602 |
LM016L |
/ |
|
虚拟串口 |
COMPIM |
COMPIM |
/ |
|
发光二极管 |
/ |
LED-RED |
/ |
|
排阻 |
/ |
RESPACK-8 |
/ |
|
电阻 |
/ |
RES |
|
|
电源 |
POWER |
/ |
5V |
|
接地 |
GROUND |
/ |
/ |
|
晶振 |
/ |
CRYSTAL |
12MHZ |
|
电容 |
/ |
CAP |
22pf |
电路设计
下图为电路设计图。

晶振电路
U1的18脚(XTAL2)和19脚(XTAL1)之间连接晶振X2(11.0592MHZ),晶振两端分别通过电容C3(22pF)和C4(22pF)接地,构成稳定的时钟振荡电路。
复位电路
U1的9脚(RST)通过电阻R2(1kΩ)接地,同时通过电容C5(10μF)连接至VCC,形成上电复位电路,保证芯片上电后可靠复位。
温度传感器电路
DS18B20的VCC引脚(3脚)接+5V,GND引脚(1脚)接地,DQ引脚(2脚)通过上拉电阻R3(4.7kΩ)连接至U1的P1.2引脚(4脚),实现单总线通信。
温度显示电路
LCD的D0-D7引脚(7-14脚)分别对应连接至U1的P0.0-P0.7引脚(39-32脚),实现8位数据传输。排阻RP1的引脚1接5V电源,其余引脚(2-9)分别连接至LCD的D7-D0引脚,起到上拉电阻作用,确保P0口输出的高电平稳定。
LCD的RS引脚(4脚)连接至U1的P2.0引脚(21脚),RW引脚(5脚)连接至P2.1引脚(22脚),E引脚(6脚)连接至P2.2引脚(23脚),用于控制LCD的读写操作和命令/数据选择。
LCD的VDD引脚(2脚)接+5V,VSS引脚(1脚)接地。
串口通信电路
COMPIM的RXD引脚连接至P3.0(单片机的RXD引脚),COMPIM的TXD引脚连接至P3.1(单片机的TXD引脚)。
打开虚拟串口配置对话框,按下图给出的参数进行配置,也可以自己定义参数。这里配置的参数一定要和在电脑端配置的VSPD虚拟串口参数一致。

LED(D1)的阳极通过限流电阻R1(220Ω)连接至VCC,阴极连接至U1的P1.0引脚(2脚),实现通过P1.0控制LED的亮灭。
配置单片机主频
单片机主频配置为11.0592MHz。
软件设计
1、软件整体流程
下图为软件整体流程图。

主控模块是程序的核心模块,负责调度温度模块、显示模块和通信模块,并负责报警功能。
软件整体工作流程描述如下:
主控模块运行后,先调用温度模块、显示模块和通信模块的初始化函数,初始化温度传感器、LCD液晶显示器和虚拟串口,并初始化阈值变量。
外部设备初始化完成后,主控模块进入循环体,在循环体内重复执行下面的流程:
(1)调用温度模块提供的函数获取已采集的温度数据;
(2)调用显示模块提供的显示函数,在LCD液晶显示器上显示温度数据;
(3)调用通信模块提供的发送函数,将温度数据发送到串口,上位机软件从串口接收温度数据;
(4)比较阈值与温度数据,若温度数据大于阈值,点亮发光二极管。
在通信模块内,启用UART_ISR()中断,接收上位机设置的阈值,将阈值赋值给主控模块的阈值变量。
2、通用模块
模块代码名称:common.h、common.c
模块实现一些通用函数,方便其它模块调用。该模块定义了一些通用数据类型和宏操作,并实现了基于软件循环的毫秒级、微秒级延时函数
模块源代码C头文件:
#ifndef __COMMON_H__#define __COMMON_H__#include "reg52.h"#include// 类型重定义typedef unsigned char uint8;typedef unsigned int uint16;typedef unsigned long uint32;sbit LCD_ALARM = P1^0;// 位操作宏#define BIT(x) (1 << (x))#define SET_BIT(x, y) ((x) |= BIT(y))#define CLR_BIT(x, y) ((x) &= ~BIT(y))#define GET_BIT(x, y) (((x) >> (y)) & 0x01)// ms延时函数void Delay_ms(unsigned int ms);// us延时函数void Delay_us(unsigned int us);void delay10us(unsigned char n);#endif // __COMMON_H__
模块源代码C实现文件:
#include "common.h"// 延时函数void Delay_ms(unsigned int ms) {unsigned int i, j;for (i = ms; i > 0; i--)for (j = 112; j > 0; j--);}// 微秒级延时函数void Delay_us(unsigned int us) {while (us--) {// 11.0592MHz下,每个循环约1.085us_nop_(); _nop_(); _nop_(); _nop_();_nop_(); _nop_(); _nop_(); _nop_();}}/******************************************************************函数功能:延时函数11.0592MHZ 延时约10us*****************************************************************/void delay10us(unsigned char n) //误差 -0.234375us{unsigned char i;while (n--){i = 2;while (--i);}}
3、温度模块
模块代码名称:temperature.h、temperature.c。
实现 DS18B20 温度传感器的驱动功能。

模块源代码C头文件:
#ifndef __TEMPERATURE_H__#define __TEMPERATURE_H__// DS18B20 引脚定义sbit DQ = P1^2; // DQ引脚连接到P1.2// 函数声明void DS18B20_Init();void DS18B20_WriteByte(unsigned char dat);unsigned char DS18B20_ReadByte();float DS18B20_ReadTemperature();#endif // __TEMPERATURE_H__
模块源代码C实现文件:
#include "common.h"#include "temperature.h"// DS18B20复位函数void DS18B20_Init() {CY = 1;while (CY){DQ = 0; //总线拉低delay10us(48); //复位脉冲至少480usDQ = 1; //电阻上拉delay10us(6); //15-60usCY = DQ; //ds18b20应答脉冲给CYdelay10us(42); //至少480us}}// 向DS18B20写入一个字节void DS18B20_WriteByte(unsigned char dat) {unsigned char i;for(i = 0; i < 8; i++) {DQ = 0; // 拉低总线_nop_(); _nop_(); // 延时2usif(dat & 0x01) { // 写1DQ = 1;Delay_us(60);} else { // 写0Delay_us(60);DQ = 1;}dat >>= 1; // 右移一位}DQ = 1; // 确保总线释放}// 从DS18B20读取一个字节unsigned char DS18B20_ReadByte() {unsigned char i;unsigned dat = 0;for (i=0; i<8; i++) //8位数据{dat >>= 1;DQ = 0; //拉低总线_nop_(); //至少1us_nop_();DQ = 1; //单片机释放总线_nop_(); //等待总线稳定_nop_();if (DQ)dat |= 0x80; //读取1delay10us(6); //读完当前数据}return dat;}// 读取DS18B20温度值float DS18B20_ReadTemperature() {unsigned int TPH,temp; //温度高八位unsigned char TPL;float temperature;DS18B20_Init(); // 18b20初始化DS18B20_WriteByte(0xCC); // 跳过romDS18B20_WriteByte(0x44); // 启动18b20转换while (!DQ); // 等待转换完DS18B20_Init(); // 18b20初始化DS18B20_WriteByte(0xCC); // 跳过romDS18B20_WriteByte(0xBE); // 读缓冲器TPL = DS18B20_ReadByte(); // 读低8位TPH = DS18B20_ReadByte(); // 读高8位// 正确组合温度数据temp = (TPH << 8) | TPL;// DS18B20温度格式:高5位为符号位和整数部分,低11位为小数部分// 温度值 = temp * 0.0625temperature = temp * 0.0625f;return temperature;}
4、显示模块
模块代码名称:lcd.h、lcd.c。
LCD1602 显示屏驱动模块,用于在 51 单片机等微控制器上控制 LCD1602 显示时间和日期。模块提供了完整的 LCD1602 显示屏驱动功能。

模块源代码C头文件:
#ifndef __LCD_H__#define __LCD_H__// LCD1602 引脚定义sbit LCD_RS = P2^0; // RS引脚连接到P2.0sbit LCD_RW = P2^1; // R/W引脚连接到P2.1sbit LCD_EN = P2^2; // E引脚连接到P2.2// 函数声明void delay_ms(unsigned int ms);void LCD_WriteCmd(unsigned char cmd);void LCD_WriteData(unsigned char dat);void LCD_Init();void LCD_SetCursor(unsigned char Line, unsigned char Column);void LCD_ShowChar(unsigned char Line, unsigned char Column, char Char);void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length);void LCD_ShowString(unsigned char Line, unsigned char Column, char *String);void LCD_ShowDegree(unsigned char Line, unsigned char Column);void LCD_ShowTemperature(unsigned char Line, unsigned char Column, float Temperature);#endif // __LCD_H__
模块源代码C实现文件:
#include "common.h"#include "lcd.h"// 写指令函数void LCD_WriteCmd(unsigned char cmd) {LCD_RS = 0; // 写指令时,RS=0LCD_RW = 0; // 写操作时,RW=0LCD_EN = 0; // EN初始化为0P0 = cmd; // 将指令写入P0口delay_ms(1); // 延时1msLCD_EN = 1; // EN拉高delay_ms(1); // 延时1msLCD_EN = 0; // EN拉低delay_ms(2); // 延时2ms}// 写数据函数void LCD_WriteData(unsigned char dat) {LCD_RS = 1; // 写数据时,RS=1LCD_RW = 0; // 写操作时,RW=0LCD_EN = 0; // EN初始化为0P0 = dat; // 将数据写入P0口delay_ms(1); // 延时1msLCD_EN = 1; // EN拉高delay_ms(1); // 延时1msLCD_EN = 0; // EN拉低delay_ms(2); // 延时2ms}// 初始化函数void LCD_Init() {delay_ms(20); // 上电延时LCD_WriteCmd(0x38); // 设置8位数据接口,2行显示,5x7点阵delay_ms(5);LCD_WriteCmd(0x38);delay_ms(5);LCD_WriteCmd(0x38);delay_ms(5);LCD_WriteCmd(0x0C); // 显示开,光标关delay_ms(5);LCD_WriteCmd(0x01); // 清屏delay_ms(5);}// 计算X的Y次方int LCD_Pow(int X, int Y) {unsigned char i;int Result = 1;for(i = 0; i < Y; i++) {Result *= X;}return Result;}// 在LCD1602指定位置显示数字void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length) {unsigned char i;LCD_SetCursor(Line, Column);for(i = Length; i > 0; i--) {LCD_WriteData(Number / LCD_Pow(10, i - 1) % 10 + '0');}}// LCD1602设置光标位置void LCD_SetCursor(unsigned char Line, unsigned char Column) {if(Line == 1) {LCD_WriteCmd(0x80 | (Column - 1)); // 第一行} else if(Line == 2) {LCD_WriteCmd(0x80 | (Column - 1 + 0x40)); // 第二行}}// 在LCD1602指定位置显示一个字符void LCD_ShowChar(unsigned char Line, unsigned char Column, char Char) {LCD_SetCursor(Line, Column);LCD_WriteData(Char);}// 在LCD1602指定位置显示字符串void LCD_ShowString(unsigned char Line, unsigned char Column, char *String) {unsigned char i;LCD_SetCursor(Line, Column);for(i = 0; String[i] != '\0'; i++) {LCD_WriteData(String[i]);}}// 显示摄氏度符号void LCD_ShowDegree(unsigned char Line, unsigned char Column) {LCD_ShowChar(Line, Column, 0xdf); // 显示度数符号的上半部分LCD_ShowChar(Line, Column + 1, 'C'); // 显示字母C}// 在LCD1602上显示温度值void LCD_ShowTemperature(unsigned char Line, unsigned char Column, float Temperature) {unsigned int IntPart;unsigned long DecPart;// 处理负温度if(Temperature < 0) {LCD_ShowChar(Line, Column, '-');Temperature = -Temperature;Column++;} else {LCD_ShowChar(Line, Column, '+');Column++;}// 分离整数部分和小数部分(保留4位小数)IntPart = (unsigned int)Temperature;DecPart = (unsigned long)((Temperature - IntPart) * 10000 + 0.5); // 四舍五入// 显示整数部分(最多3位)if(IntPart >= 100) {LCD_ShowNum(Line, Column, IntPart, 3);Column += 3;} else if(IntPart >= 10) {LCD_ShowNum(Line, Column, IntPart, 2);Column += 2;} else {LCD_ShowNum(Line, Column, IntPart, 1);Column += 1;}// 显示小数点LCD_ShowChar(Line, Column, '.');Column++;// 显示小数部分(4位)LCD_ShowNum(Line, Column, DecPart, 4);Column += 4;// 显示摄氏度符号LCD_ShowDegree(Line, Column);}
5、通信模块
模块代码名称:urat.h、urat.c
模块主要用于通过串口接收用户输入的温度阈值,发送温度数据到上位机软件。

模块源代码C头文件:
#define __URAT_H__#define __URAT_H__void UART_Init();void UART_SendChar(unsigned char dat);void UART_SendString(unsigned char *str);void convertTempToInt();#endif // __URAT_H__
模块源代码C实现文件:
#include "common.h"#include "urat.h"extern int threshold;// 温度数据缓冲区unsigned char tempBuffer[3]; // 存储温度数据,最多2位数字unsigned char tempIndex = 0; // 缓冲区索引bit tempReceived = 0; // 温度数据接收完成标志// 初始化串口,波特率9600bps,晶振11.0592MHzvoid UART_Init() {TMOD |= 0x20; // 定时器1工作在方式2SCON = 0x50; // 串口工作在方式1,允许接收TH1 = 0xFD; // 装载初值,9600bps对应的初值TL1 = 0xFD;TR1 = 1; // 启动定时器1EA = 1; // 允许总中断ES = 1; // 允许串口中断}// 串口发送一个字符void UART_SendChar(unsigned char dat) {SBUF = dat; // 将数据放入发送缓冲器while (!TI); // 等待发送完成TI = 0; // 清除发送完成标志位}// 串口发送字符串void UART_SendString(unsigned char *str) {while (*str != '\0') { // 字符串结束标志UART_SendChar(*str);str++;}}// 串口中断服务函数void UART_ISR() interrupt 4 {unsigned char receiveData;if (RI) { // 接收中断标志位RI = 0; // 清除接收中断标志位receiveData = SBUF; // 读取接收的数据if ((receiveData >= '0' && receiveData <= '9') || receiveData == '\r' || receiveData == '\n') {if (receiveData == '\r' || receiveData == '\n') {// 数据接收完成if (tempIndex > 0) {tempBuffer[tempIndex] = '\0'; // 添加字符串结束符tempReceived = 1; // 设置接收完成标志convertTempToInt();tempIndex = 0; // 重置索引}} else {// 接收数字if (tempIndex < 2) { // 只接收1-2位数字tempBuffer[tempIndex++] = receiveData;} else {// 数据过长,重置tempIndex = 0;UART_SendString("Temperature data too long (max 2 digits)\r\n");}}}}}// 将接收到的字符串转换为整数void convertTempToInt() {if (tempIndex == 1) {// 1位数字threshold = tempBuffer[0] - '0';} else if (tempIndex == 2) {// 2位数字threshold = (tempBuffer[0] - '0') * 10 + (tempBuffer[1] - '0');}}
6、主控模块
模块代码名称:main.c。
主控模块集成了温度传感器、LCD 显示和串口通信模块,实现了一个完整的温度监控系统,具有温度采集、显示、远程监控和报警功能。
模块源代码C实现文件:
#include "common.h"#include "temperature.h"#include "lcd.h"#include "urat.h"// 阈值int threshold;void main() {float temperature;unsigned char code str[] = "Temperature:";unsigned char buffer[16];LCD_Init();UART_Init();LCD_ALARM = 0;threshold = 25;while(1) {LCD_ShowString(1,1,str);temperature = DS18B20_ReadTemperature();// 在第二行第一列显示温度值LCD_ShowTemperature(2, 1, temperature);// 发送温度值到串口UART_SendString(str);// 将浮点数转换为字符串(简化版)if(temperature < 0) {//Uart_SendByte('-');temperature = -temperature;}buffer[0] = (unsigned char)temperature / 10 + '0';buffer[1] = (unsigned char)temperature % 10 + '0';buffer[2] = '.';buffer[3] = (unsigned char)(temperature * 10) % 10 + '0';buffer[4] = (unsigned char)(temperature * 100) % 10 + '0';buffer[5] = (unsigned char)(temperature * 1000) % 10 + '0';buffer[6] = (unsigned char)(temperature * 10000) % 10 + '0';buffer[7] = '\0';UART_SendString(buffer);UART_SendString(" C\n");if( (int)temperature > threshold ) {LCD_ALARM = 0;} else {LCD_ALARM = 1;}Delay_ms(1000); // 每秒读取一次温度}}.
联合调试与功能验证
一、环境准备
1、虚拟串口配置
-
启动VSPD软件,虚拟生成COM1和COM2两个串口
-
在设备管理器中确认无端口冲突(COM1/COM2状态正常)
2、串口调试工具设置
-
打开串口调试助手(如SSCOM)
-
选择VSPD创建的COM2端口
-
参数配置:9600bps/8N1(无校验位/1停止位)
二、验证温度采集与显示
启动Proteus仿真软件,运行单片机程序,程序运行稳定后,温度传感器DS18B20会显示当前的温度值,LCD1602液晶显示器也会显示当前采集的温度。运行效果如下图所示:

同时单片机程序会将温度数据发送到串口,上位机软件可通过串口接收温度数据。

因为DS18B20是仿真器件,传感器采集不到环境温度,但可以通过手动来改变传感器当前的温度值,用于验证程序在温度变化时是否能正常工作。单击传感器两个红色箭头按钮,即可上调和下调温度,如下图:

三、验证报警功能
程序内部设定了一个阈值变量,默认值为25°当温度超过25°时,发光二极管会点亮。调整传感器的温度值就会触发报警。
上位机软件可以通过串口设置阈值,具体步骤是通过串口发送阈值到单片机:

在串口调试助手发送窗口内,输入需要设置的阈值,单击“发送”按钮即可完成阈值设置。
更多推荐



所有评论(0)