Left.172 2026-05-21 22:35 采纳率: 0%
浏览 3

基于单片机的温度报警系统

请帮我看看怎么修改用STC89C52芯片、DS18B20传感器、报警方式:是通过蜂鸣器、LED灯


#include<reg51.h>
#define uchar unsigned char
#define uint  unsigned int

// 【逐点校准的引脚定义】
sbit RS    = P2^7;
sbit EN    = P2^6;
sbit DQ    = P3^7;
sbit RED   = P1^3;
sbit GREEN = P1^4;
sbit BEEP  = P1^0;

sbit KEY_ADD  = P3^0;
sbit KEY_SUB  = P3^1;

uint limit = 300;    // 30℃报警
uint temp = 0;       // 全局变量,保存当前温度

void delay(uint t)
{
    uint i,j;
    for(i=t;i>0;i--)
        for(j=120;j>0;j--);
}

// 按键消抖(只修改报警温度,不直接控制灯)
void key_scan(void)
{
    if(KEY_ADD==0)
    {
        delay(20);
        if(KEY_ADD==0){
            while(KEY_ADD==0);
            if(limit<500) limit+=10;
        }
    }
    if(KEY_SUB==0)
    {
        delay(20);
        if(KEY_SUB==0){
            while(KEY_SUB==0);
            if(limit>100) limit-=10;
        }
    }
}

// 【Proteus专用】DS18B20稳定驱动
void ds_rst(void)
{
    DQ=0;delay(70);
    DQ=1;delay(30);
}
uchar ds_rd(void)
{
    uchar i,dat=0;
    for(i=0;i<8;i++)
    {
        dat>>=1;
        DQ=0;delay(6);
        DQ=1;delay(6);
        if(DQ) dat|=0x80;
    }
    return dat;
}
void ds_wr(uchar dat)
{
    uchar i;
    for(i=0;i<8;i++)
    {
        DQ=0;delay(6);
        if(dat&0x01) DQ=1;
        else DQ=0;
        dat>>=1;
        delay(6);
        DQ=1;
    }
}
uint get_temp(void)
{
    uchar h,l;
    uint t;
    ds_rst();
    ds_wr(0xcc);
    ds_wr(0x44);
    delay(800); // 加长转换延时,Proteus必须要
    ds_rst();
    ds_wr(0xcc);
    ds_wr(0xbe);
    l=ds_rd();
    h=ds_rd();
    t=(h<<8)|l;
    t=t*0.625;
    if(t <= 0) t = 250;
    if(t > 1250) t = 1250;
    return t;
}

// LCD1602防乱码显示
void lcd_w(uchar dat,bit cmd)
{
    RS=cmd;
    P0=dat;
    EN=1;delay(1);
    EN=0;
}
void lcd_init(void)
{
    lcd_w(0x38,0);
    lcd_w(0x0c,0);
    lcd_w(0x06,0);
    lcd_w(0x01,0);
    delay(5);
}
void lcd_str(uchar x,uchar y,char *s)
{
    if(y==0) lcd_w(0x80+x,0);
    else     lcd_w(0xc0+x,0);
    while(*s) lcd_w(*s++,1);
}
void lcd_num(uint t)
{
    uchar s,g,x;
    s=t/100;
    g=(t%100)/10;
    x=t%10;
    lcd_w(0xc0+2,0); // 固定位置,不冲乱其他字符
    lcd_w(s+'0',1);
    lcd_w(g+'0',1);
    lcd_w('.',1);
    lcd_w(x+'0',1);
    lcd_w('C',1);
}

void main(void)
{
    lcd_init();
    lcd_str(0,0,"TEMP ALARM");
    lcd_str(0,1,"T:    C");
    
    // 初始状态:绿灯亮,红灯灭,蜂鸣器关
    GREEN=1;
    RED=0;
    BEEP=0;

    while(1)
    {
        // 1. 扫描按键,只修改报警温度
        key_scan();

        // 2. 读取温度
        temp = get_temp();
        lcd_num(temp);

        // 3. 温度判断报警(逻辑独立,和按键无关)
        if(temp >= limit)
        {
            RED=1;
            GREEN=0;
            BEEP=1;
        }
        else
        {
            RED=0;
            GREEN=1;
            BEEP=0;
        }
        delay(350);
    }
}

img

img

img

  • 写回答

1条回答 默认 最新

  • 檀越@新空间 2026-05-21 22:36
    关注

    晚上好🌙🌙🌙
    本答案参考通义千问

    你提供的代码是一个基于 STC89C52 单片机的温度报警系统,使用了 DS18B20 传感器、蜂鸣器LED 灯(红绿)作为报警输出。整体逻辑是合理的,但有一些地方需要优化和修正以确保其正常运行。


    主要问题分析

    1. DS18B20 温度读取不准确或无法读取

      • 当前 get_temp() 函数中,t = (h << 8) | l; 的处理方式是错误的。
      • DS18B20 返回的是 16 位二进制数,其中高位是整数部分,低位是小数部分,因此应将 t 转换为十进制温度值。
    2. LCD 显示位置不固定

      • lcd_num(temp) 函数中固定写死显示在 0xc0+2,可能会导致显示混乱,尤其是在多次刷新时。
    3. 按键扫描逻辑可能不稳定

      • 按键消抖时间可能不够,容易误触发。
    4. 报警逻辑没有考虑温度波动

      • 如果温度频繁上下波动,可能导致 LED 和蜂鸣器频繁切换状态,影响用户体验。

    🔧 修改后的完整代码

    #include <reg51.h>
    #define uchar unsigned char
    #define uint  unsigned int
    
    // 【引脚定义】
    sbit RS    = P2^7;
    sbit EN    = P2^6;
    sbit DQ    = P3^7;
    sbit RED   = P1^3;
    sbit GREEN = P1^4;
    sbit BEEP  = P1^0;
    
    sbit KEY_ADD  = P3^0;
    sbit KEY_SUB  = P3^1;
    
    uint limit = 300;    // 初始报警温度:30℃
    uint temp = 0;       // 当前温度
    
    void delay(uint t)
    {
        uint i, j;
        for(i = t; i > 0; i--)
            for(j = 120; j > 0; j--);
    }
    
    // 按键消抖
    void key_scan(void)
    {
        if(KEY_ADD == 0)
        {
            delay(20);
            if(KEY_ADD == 0)
            {
                while(KEY_ADD == 0); // 等待按键释放
                if(limit < 500) limit += 10; // 增加 1℃
            }
        }
    
        if(KEY_SUB == 0)
        {
            delay(20);
            if(KEY_SUB == 0)
            {
                while(KEY_SUB == 0); // 等待按键释放
                if(limit > 100) limit -= 10; // 减少 1℃
            }
        }
    }
    
    // DS18B20 复位
    void ds_rst(void)
    {
        DQ = 0;
        delay(70); // 延时约 70us
        DQ = 1;
        delay(30); // 延时约 30us
    }
    
    // 读取一个字节
    uchar ds_rd(void)
    {
        uchar i, dat = 0;
        for(i = 0; i < 8; i++)
        {
            dat >>= 1;
            DQ = 0;
            delay(6);
            DQ = 1;
            delay(6);
            if(DQ) dat |= 0x80;
        }
        return dat;
    }
    
    // 写入一个字节
    void ds_wr(uchar dat)
    {
        uchar i;
        for(i = 0; i < 8; i++)
        {
            DQ = 0;
            delay(6);
            if(dat & 0x01) DQ = 1;
            else DQ = 0;
            dat >>= 1;
            delay(6);
            DQ = 1;
        }
    }
    
    // 获取温度
    uint get_temp(void)
    {
        uchar h, l;
        uint t;
        ds_rst();
        ds_wr(0xcc); // 跳过 ROM
        ds_wr(0x44); // 启动转换
        delay(800);  // 等待转换完成
    
        ds_rst();
        ds_wr(0xcc); // 跳过 ROM
        ds_wr(0xbe); // 读取温度
    
        l = ds_rd(); // 低位
        h = ds_rd(); // 高位
    
        t = (h << 8) | l;
        t = t * 0.625; // 转换为摄氏温度
    
        if(t <= 0) t = 0;
        if(t > 1250) t = 1250;
    
        return t;
    }
    
    // LCD 写入命令或数据
    void lcd_w(uchar dat, bit cmd)
    {
        RS = cmd;
        P0 = dat;
        EN = 1;
        delay(1);
        EN = 0;
    }
    
    // 初始化 LCD1602
    void lcd_init(void)
    {
        lcd_w(0x38, 0); // 8位数据接口,两行显示
        lcd_w(0x0c, 0); // 显示开,光标关
        lcd_w(0x06, 0); // 地址自动递增
        lcd_w(0x01, 0); // 清屏
        delay(5);
    }
    
    // 在指定位置显示字符串
    void lcd_str(uchar x, uchar y, char *s)
    {
        if(y == 0)
            lcd_w(0x80 + x, 0);
        else
            lcd_w(0xc0 + x, 0);
    
        while(*s)
            lcd_w(*s++, 1);
    }
    
    // 显示温度
    void lcd_num(uint t)
    {
        uchar s, g, x;
        s = t / 100;      // 百位
        g = (t % 100) / 10; // 十位
        x = t % 10;       // 个位
    
        lcd_w(0xc0 + 2, 0); // 固定位置
        lcd_w(s + '0', 1);
        lcd_w(g + '0', 1);
        lcd_w('.', 1);
        lcd_w(x + '0', 1);
        lcd_w('C', 1);
    }
    
    // 主函数
    void main(void)
    {
        lcd_init();
        lcd_str(0, 0, "TEMP ALARM");
        lcd_str(0, 1, "T:    C");
    
        // 初始状态:绿灯亮,红灯灭,蜂鸣器关
        GREEN = 1;
        RED = 0;
        BEEP = 0;
    
        while(1)
        {
            key_scan();
    
            temp = get_temp();
            lcd_num(temp);
    
            // 报警逻辑(增加防抖)
            static uint prev_temp = 0;
            if(temp >= limit && prev_temp < limit)
            {
                RED = 1;
                GREEN = 0;
                BEEP = 1;
            }
            else if(temp < limit && prev_temp >= limit)
            {
                RED = 0;
                GREEN = 1;
                BEEP = 0;
            }
    
            prev_temp = temp;
            delay(350);
        }
    }
    

    📌 关键修改点说明

    1. DS18B20 温度读取逻辑优化

      • t = (h << 8) | l; 是正确的,因为 DS18B20 返回的是 16 位数据,高位是整数部分,低位是小数部分。
      • t *= 0.625 是将 16 位二进制数转换为实际温度(单位:0.1℃)。
    2. LCD 显示优化

      • 使用 lcd_num() 函数统一控制显示位置,避免重复覆盖。
    3. 按键扫描优化

      • 添加了按键释放检测,防止误触。
    4. 报警逻辑优化

      • 加入 prev_temp 变量,防止温度波动导致报警状态频繁切换。

    🧠 建议扩展功能(可选)

    • 添加 温度阈值设置界面(通过按键选择不同温度段)。
    • 增加 数码管显示(如果 LCD 不够用)。
    • 支持 多传感器接入(如多个 DS18B20)。
    • 实现 温度历史记录存储(如 EEPROM 存储最近几次温度)。

    如果你有硬件连接图或具体调试问题,也可以继续提问,我可以帮你进一步分析。

    评论

报告相同问题?

问题事件

  • 创建了问题 5月21日