系统实现目标

设计并实现一个基于 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);              //复位脉冲至少480us        DQ = 1;                     //电阻上拉        delay10us(6);               //15-60us        CY = DQ;                    //ds18b20应答脉冲给CY        delay10us(42);              //至少480us    }} // 向DS18B20写入一个字节void DS18B20_WriteByte(unsigned char dat) {     unsigned char i;        for(i = 0; i < 8; i++) {        DQ = 0;                  // 拉低总线        _nop_(); _nop_();      // 延时2us                if(dat & 0x01) {         // 写1            DQ = 1;            Delay_us(60);        } else {                 // 写0            Delay_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;               //读取1        delay10us(6);               //读完当前数据    }    return dat;}  // 读取DS18B20温度值float DS18B20_ReadTemperature() {                  unsigned int TPH,temp;                           //温度高八位     unsigned char TPL;     float        temperature;        DS18B20_Init();                // 18b20初始化    DS18B20_WriteByte(0xCC);        // 跳过rom    DS18B20_WriteByte(0x44);        // 启动18b20转换    while (!DQ);                    // 等待转换完    DS18B20_Init();                // 18b20初始化    DS18B20_WriteByte(0xCC);        // 跳过rom    DS18B20_WriteByte(0xBE);        // 读缓冲器    TPL = DS18B20_ReadByte();       // 读低8位    TPH = DS18B20_ReadByte();       // 读高8位        // 正确组合温度数据    temp = (TPH << 8) | TPL;        // DS18B20温度格式:高5位为符号位和整数部分,低11位为小数部分    // 温度值 = temp * 0.0625    temperature = 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=0    LCD_RW = 0;          // 写操作时,RW=0    LCD_EN = 0;          // EN初始化为0    P0 = cmd;            // 将指令写入P0口    delay_ms(1);         // 延时1ms    LCD_EN = 1;          // EN拉高    delay_ms(1);         // 延时1ms    LCD_EN = 0;          // EN拉低    delay_ms(2);         // 延时2ms} // 写数据函数void LCD_WriteData(unsigned char dat) {    LCD_RS = 1;          // 写数据时,RS=1    LCD_RW = 0;          // 写操作时,RW=0    LCD_EN = 0;          // EN初始化为0    P0 = dat;            // 将数据写入P0口    delay_ms(1);         // 延时1ms    LCD_EN = 1;          // EN拉高    delay_ms(1);         // 延时1ms    LCD_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工作在方式2   SCON = 0x50;   // 串口工作在方式1,允许接收        TH1 = 0xFD;    // 装载初值,9600bps对应的初值   TL1 = 0xFD;   TR1 = 1;       // 启动定时器1   EA = 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);              //复位脉冲至少480us        DQ = 1;                     //电阻上拉        delay10us(6);               //15-60us        CY = DQ;                    //ds18b20应答脉冲给CY        delay10us(42);              //至少480us    }} // 向DS18B20写入一个字节void DS18B20_WriteByte(unsigned char dat) {     unsigned char i;        for(i = 0; i < 8; i++) {        DQ = 0;                  // 拉低总线        _nop_(); _nop_();      // 延时2us                if(dat & 0x01) {         // 写1            DQ = 1;            Delay_us(60);        } else {                 // 写0            Delay_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;               //读取1        delay10us(6);               //读完当前数据    }    return dat;}  // 读取DS18B20温度值float DS18B20_ReadTemperature() {                  unsigned int TPH,temp;                           //温度高八位     unsigned char TPL;     float        temperature;        DS18B20_Init();                // 18b20初始化    DS18B20_WriteByte(0xCC);        // 跳过rom    DS18B20_WriteByte(0x44);        // 启动18b20转换    while (!DQ);                    // 等待转换完    DS18B20_Init();                // 18b20初始化    DS18B20_WriteByte(0xCC);        // 跳过rom    DS18B20_WriteByte(0xBE);        // 读缓冲器    TPL = DS18B20_ReadByte();       // 读低8位    TPH = DS18B20_ReadByte();       // 读高8位        // 正确组合温度数据    temp = (TPH << 8) | TPL;        // DS18B20温度格式:高5位为符号位和整数部分,低11位为小数部分    // 温度值 = temp * 0.0625    temperature = 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=0    LCD_RW = 0;          // 写操作时,RW=0    LCD_EN = 0;          // EN初始化为0    P0 = cmd;            // 将指令写入P0口    delay_ms(1);         // 延时1ms    LCD_EN = 1;          // EN拉高    delay_ms(1);         // 延时1ms    LCD_EN = 0;          // EN拉低    delay_ms(2);         // 延时2ms} // 写数据函数void LCD_WriteData(unsigned char dat) {    LCD_RS = 1;          // 写数据时,RS=1    LCD_RW = 0;          // 写操作时,RW=0    LCD_EN = 0;          // EN初始化为0    P0 = dat;            // 将数据写入P0口    delay_ms(1);         // 延时1ms    LCD_EN = 1;          // EN拉高    delay_ms(1);         // 延时1ms    LCD_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工作在方式2   SCON = 0x50;   // 串口工作在方式1,允许接收        TH1 = 0xFD;    // 装载初值,9600bps对应的初值   TL1 = 0xFD;   TR1 = 1;       // 启动定时器1   EA = 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°时,发光二极管会点亮。调整传感器的温度值就会触发报警。

                                      上位机软件可以通过串口设置阈值,具体步骤是通过串口发送阈值到单片机:

                                      图片

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

                                      Logo

                                      更多推荐