2301_81441287 2025-11-29 11:56 采纳率: 50%
浏览 3

关于51单片机ds18b20温度传感器的设计问题

我在做一个基于51单片机温度传感器的设计,是当测定温度超过设定温度时候,会进入报警模式,但是现在的问题是,进入报警模式蜂鸣器能够正常报警,但是从报警模式退出以后,蜂鸣器持续鸣响,只有摁下任意摁键以后才停止鸣响。以下是我的代码:

#include <reg51.h>
#include <intrins.h>

#define LED P0          // 数码管段码接口
sbit DQ = P3^7;         // DS18B20数据线
sbit BEEP = P1^3;       // 蜂鸣器报警
sbit ALARM_LED = P2^1;  // 灯光报警LED
sbit SHCP = P2^3;       // 74HC595移位时钟
sbit STCP = P2^2;       // 74HC595锁存时钟
sbit DS = P2^4;         // 74HC595数据线

/*========== 步进电机引脚 ==========*/
sbit IN1 = P1^4;
sbit IN2 = P1^5;
sbit IN3 = P1^6;
sbit IN4 = P1^7;

/*========== 按键引脚 ==========*/
sbit KEY1 = P1^0;   // 设置模式-调低温度 / 手动-正转
sbit KEY2 = P1^1;   // 设置模式-调高温度 / 手动-反转
sbit KEY3 = P1^2;   // 进入/退出设置模式

/*========== 数码管段码 ==========*/
unsigned char code dofly_DuanMa[] =
{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff};

unsigned char code dofly_WeiMa[] =
{0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};

/*========== 变量区域(新增蜂鸣器控制变量) ==========*/
int temperature = 0;
int set_temperature = 300;    // 30.0℃(放大10倍存储,0.5℃对应数值+5)
bit set_mode = 0;             // 0=正常模式 1=设置模式
bit alarm_flag = 0;
bit direction = 0;            // 0=正转 1=反转
bit motor_manual = 0;         // 是否处于手动控制电机模式
unsigned char phase_index = 0;
unsigned char key3_cnt = 0;   // KEY3消抖计数
unsigned char key1_cnt = 0;   // KEY1消抖计数
unsigned char key2_cnt = 0;   // KEY2消抖计数

unsigned char code phase_table[8] =
{0x08,0x0C,0x04,0x06,0x02,0x03,0x01,0x09};

// 新增蜂鸣器控制变量
unsigned int beep_counter = 0;        // 蜂鸣器计时计数器
unsigned char beep_interval = 50;     // 鸣叫间隔(初始50ms)
#define MIN_BEEP_INTERVAL 5           // 最小间隔(5ms)
#define MAX_BEEP_INTERVAL 50          // 最大间隔(50ms)
unsigned char temp_diff = 0;          // 实际温度与设定温度差值(℃)

/*========== 延时函数 ==========*/
void DelayUs2x(unsigned char t){ while(--t); }
void delay_ms(unsigned int ms){ unsigned int i,j;for(i=ms;i>0;i--) for(j=114;j>0;j--);}

/*========== DS18B20 ==========*/
bit Init_DS18B20(void){
    bit dat;
    DQ = 1; DelayUs2x(5);
    DQ = 0; DelayUs2x(200); DelayUs2x(200);
    DQ = 1; DelayUs2x(50);
    dat = DQ; DelayUs2x(25);
    return dat;
}

void WriteOneChar(unsigned char dat){
    unsigned char i;
    for(i=8;i>0;i--){
        DQ = 0; DQ = dat & 0x01;
        DelayUs2x(25);
        DQ = 1; dat >>= 1;
    }
    DelayUs2x(25);
}

unsigned char ReadOneChar(void){
    unsigned char i,dat=0;
    for(i=8;i>0;i--){
        DQ = 0; dat >>= 1; DQ = 1;
        if(DQ) dat |= 0x80;
        DelayUs2x(25);
    }
    return dat;
}

int ReadTemperature(void){
    unsigned char a,b;
    int t;
    Init_DS18B20(); WriteOneChar(0xCC); WriteOneChar(0x44); delay_ms(10);
    Init_DS18B20(); WriteOneChar(0xCC); WriteOneChar(0xBE);
    a = ReadOneChar(); b = ReadOneChar();
    t = b; t <<= 8; t = t | a;
    return (int)(t * 0.625); // 放大10倍,精确到0.1℃
}

/*========== 步进电机驱动 ==========*/
void step_motor_drive(void){
    IN1 = (phase_table[phase_index]&0x08)?1:0;
    IN2 = (phase_table[phase_index]&0x04)?1:0;
    IN3 = (phase_table[phase_index]&0x02)?1:0;
    IN4 = (phase_table[phase_index]&0x01)?1:0;

    if(direction == 0){
        phase_index++;
        if(phase_index>=8) phase_index=0;
    }
    else{
        if(phase_index==0) phase_index=7;
        else phase_index--;
    }
}

/*========== 74HC595 显示函数 ==========*/
void HC595_send(unsigned char dat){
    unsigned char i;
    for(i=0;i<8;i++){
        SHCP=0; DS=dat&0x80; dat<<=1; SHCP=1;
    }
    STCP=0;  _nop_();  STCP=1;
}

void DisplayTemperature(void){
    unsigned char temp_buf[4];
    int temp_val = set_mode ? set_temperature : temperature;

    temp_buf[0] = temp_val/1000;          // 千位(温度通常0-99℃,此处用于消隐)
    temp_buf[1] = (temp_val%1000)/100;    // 百位(十位温度)
    temp_buf[2] = (temp_val%100)/10;      // 十位(个位温度)
    temp_buf[3] = temp_val%10;            // 个位(小数位)

    // 显示千位(消隐)
    LED = (temp_buf[0]==0)? dofly_DuanMa[16]:dofly_DuanMa[temp_buf[0]];
    HC595_send(dofly_WeiMa[7]); delay_ms(1); HC595_send(0);

    // 显示百位(十位温度)
    LED = dofly_DuanMa[temp_buf[1]];
    HC595_send(dofly_WeiMa[6]); delay_ms(1); HC595_send(0);

    // 显示十位(个位温度,带小数点)
    LED = dofly_DuanMa[temp_buf[2]]&0x7F;
    HC595_send(dofly_WeiMa[5]); delay_ms(1); HC595_send(0);

    // 显示个位(小数位)
    LED = dofly_DuanMa[temp_buf[3]];
    HC595_send(dofly_WeiMa[4]); delay_ms(1); HC595_send(0);

    // 显示℃符号
    LED = dofly_DuanMa[12];
    HC595_send(dofly_WeiMa[3]); delay_ms(1); HC595_send(0);
}

/*========== 按键处理(核心修改部分) ==========*/
void key_scan(void){
    // KEY3:短按进入/退出设置模式(消抖处理)
    if(KEY3 == 0){
        key3_cnt++;
        if(key3_cnt >= 20){ // 约400ms消抖
            set_mode = ~set_mode; // 切换设置模式
            BEEP = 1; // 进入/退出设置模式时立即关闭蜂鸣器
            beep_counter = 0; // 重置蜂鸣器计数器
            key3_cnt = 0;
        }
    }
    else key3_cnt = 0;

    // 设置模式:按键1调低温度(步长0.5℃),按键2调高温度(步长0.5℃)
    if(set_mode == 1){
        motor_manual = 0; // 设置模式下禁用手动电机
        IN1 = IN2 = IN3 = IN4 = 0; // 停止电机

        // KEY1:调低温度(最小10.0℃,防止过低)
        if(KEY1 == 0){
            key1_cnt++;
            if(key1_cnt >= 50){ // 长按约1s触发(防误触)
                if(set_temperature >= 100){ // 10.0℃下限
                    set_temperature -= 5; // 0.5℃步长(对应数值-5)
                }
                key1_cnt = 0;
            }
        }
        else key1_cnt = 0;

        // KEY2:调高温度(最大99.5℃,防止过高)
        if(KEY2 == 0){
            key2_cnt++;
            if(key2_cnt >= 50){ // 长按约1s触发(防误触)
                if(set_temperature <= 995){ // 99.5℃上限
                    set_temperature += 5; // 0.5℃步长(对应数值+5)
                }
                key2_cnt = 0;
            }
        }
        else key2_cnt = 0;
    }
    // 正常模式:保持原手动电机控制逻辑
    else{
        static unsigned int k1=0,k2=0;

        // KEY1 长按 → 正转
        if(KEY1==0){
            k1++;
            if(k1 > 10){ 
                direction = 0;     // 正转
                motor_manual = 1;  // 开启手动电机模式
            }
        }else k1 = 0;

        // KEY2 长按 → 反转
        if(KEY2==0){
            k2++;
            if(k2 > 10){ 
                direction = 1;     // 反转
                motor_manual = 1;  // 开启手动电机模式
            }
        }else k2 = 0;

        // 都松开 → 关闭手动模式,停止电机
        if(KEY1==1 && KEY2==1){
            motor_manual = 0;
            IN1 = IN2 = IN3 = IN4 = 0;
        }
    }
}

/*========== 蜂鸣器控制(与前序代码一致,核心修改) ==========*/
void beep_control_fixed(void) {
    
    // 触发条件:正常模式+实际温度>设定温度
    if(set_mode == 0 && temperature > set_temperature) {
        alarm_flag = 1;
        // 计算实际温度差值(℃,因温度放大10倍存储,需除以10)
        temp_diff = (temperature - set_temperature) / 10;
        if(temp_diff < 1) temp_diff = 1; // 最小差值1℃,避免间隔为0
        
        // 温度越高,鸣叫间隔越小(50ms~5ms)
        beep_interval = MAX_BEEP_INTERVAL - (temp_diff * 2);
        if(beep_interval < MIN_BEEP_INTERVAL) {
            beep_interval = MIN_BEEP_INTERVAL;
        }
        
        // 蜂鸣器脉冲控制(鸣叫=beep_interval ms,停止=beep_interval ms)
        beep_counter++;
        if(beep_counter <= beep_interval) {
            BEEP = 0; // 低电平鸣叫(根据硬件调整,若不响则改为1)
            ALARM_LED = 0; // LED同步亮
        } else if(beep_counter <= beep_interval * 2) {
            BEEP = 1; // 高电平停止
            ALARM_LED = 1; // LED同步灭
        } else {
            beep_counter = 0; // 重置计数器
        }
    } else {
        // 未触发条件:关闭蜂鸣器和LED
        alarm_flag = 0;
        BEEP = 1;
        ALARM_LED = 1;
        beep_counter = 0;
    }
}

/*========== 主程序(修改报警逻辑为新蜂鸣器控制) ==========*/
void main(void){
    unsigned char temp_count = 0;

    // 初始化:蜂鸣器和LED默认关闭
    BEEP = 1;
    ALARM_LED = 1;
    IN1 = IN2 = IN3 = IN4 = 0;

    while(1){
        key_scan();  // 按键扫描(含设置模式逻辑)

        // 1秒更新一次温度
        if(++temp_count >= 50){
            temp_count = 0;
            temperature = ReadTemperature(); // 读取温度(100ms一次,50次=5秒?此处保留原逻辑,可按需调整为100次=10秒)
        }

        beep_control_fixed(); // 调用新蜂鸣器控制函数(替代原Alarm_Process)

        // 无报警时才允许手动控制电机
        if(!alarm_flag && !set_mode){ // 设置模式下禁用手动电机
            if(motor_manual){
                step_motor_drive();   // 手动转动
            }
            else{
                IN1 = IN2 = IN3 = IN4 = 0;  // 松开按键 → 停止
            }
        } else if(alarm_flag && !set_mode) {
            // 报警时自动控制电机正转
            direction = 0;
            step_motor_drive();
        }

        DisplayTemperature();// 显示温度(设置模式显示设定值,正常模式显示实测值)
        delay_ms(20);
    }
}

请帮我看看什么问题。谢谢大家

  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2025-11-29 12:09
    关注

    中午好🌞🌞🌞
    本答案参考通义千问

    你描述的问题是:当温度超过设定值后,蜂鸣器进入报警模式,但在退出报警模式后,蜂鸣器仍然持续鸣响,只有按下任意按键才能停止。

    这个问题的根本原因在于蜂鸣器的状态没有被正确重置,即使在报警状态结束后,BEEP引脚仍保持低电平(即蜂鸣器一直响)。


    问题分析

    1. 报警触发逻辑

      • 当温度高于设定值时,alarm_flag = 1,此时进入报警模式。
      • 蜂鸣器被设置为开启状态(BEEP = 0)。
    2. 报警退出逻辑

      • 在退出报警模式时,虽然 alarm_flag = 0,但未对 BEEP 进行显式关闭操作,导致蜂鸣器状态未改变。
    3. 按键处理逻辑

      • 按键事件中,可能只做了“停止蜂鸣器”的动作,而未做“恢复初始状态”的操作。

    解决方案

    1. 显式控制蜂鸣器状态

    • 报警模式退出后,必须将 BEEP = 1;(关闭蜂鸣器)。
    • 正常模式下,确保 BEEP = 1;(关闭)。

    2. 修改报警逻辑

    • 在判断温度是否超限时,设置 alarm_flag = 1;
    • 在退出报警时,设置 alarm_flag = 0; 并关闭蜂鸣器。

    3. 按键处理中加入蜂鸣器控制

    • 在按键处理函数中,除了关闭蜂鸣器外,还应确保它处于关闭状态。

    修改后的代码片段(重点部分加粗)

    // 原有报警逻辑
    if (temperature > set_temperature) {
        alarm_flag = 1;
        BEEP = 0;  // 开启蜂鸣器
    } else {
        alarm_flag = 0;
        BEEP = 1;  // **显式关闭蜂鸣器**
    }
    
    // 在退出报警模式的逻辑中(例如在主循环中)
    if (!alarm_flag) {
        BEEP = 1;  // **确保退出报警时蜂鸣器关闭**
    }
    

    完整修复建议

    步骤一:在主循环中添加蜂鸣器状态检查

    void main() {
        // 初始化代码...
    
        while(1) {
            temperature = ReadTemperature();
            
            if (temperature > set_temperature) {
                alarm_flag = 1;
                BEEP = 0;  // 打开蜂鸣器
            } else {
                alarm_flag = 0;
                BEEP = 1;  // **关闭蜂鸣器**
            }
    
            // 其他逻辑...
        }
    }
    

    步骤二:在按键处理中添加蜂鸣器控制

    void KeyScan() {
        // 按键消抖处理...
    
        if (KEY1 == 0) {  // KEY1按下
            key1_cnt++;
            if (key1_cnt >= 10) {
                key1_cnt = 0;
                // 设置温度调低
                if (set_mode) {
                    set_temperature -= 5;
                    if (set_temperature < 0) set_temperature = 0;
                }
                // **关闭蜂鸣器**
                BEEP = 1;
            }
        }
    
        if (KEY2 == 0) {  // KEY2按下
            key2_cnt++;
            if (key2_cnt >= 10) {
                key2_cnt = 0;
                // 设置温度调高
                if (set_mode) {
                    set_temperature += 5;
                    if (set_temperature > 999) set_temperature = 999;
                }
                // **关闭蜂鸣器**
                BEEP = 1;
            }
        }
    
        if (KEY3 == 0) {  // KEY3按下
            key3_cnt++;
            if (key3_cnt >= 10) {
                key3_cnt = 0;
                set_mode = !set_mode;
                // **关闭蜂鸣器**
                BEEP = 1;
            }
        }
    }
    

    总结

    关键点:

    • 务必在报警退出时显式关闭蜂鸣器(BEEP = 1;)
    • 在所有按键处理中,都要考虑蜂鸣器状态的复位
    • 不要依赖默认状态,要主动控制硬件状态

    最终建议

    如果你希望进一步优化,可以引入一个全局变量 beep_on = 0; 来统一控制蜂鸣器状态,并在主循环中根据该变量决定是否开启蜂鸣器:

    bit beep_on = 0;
    
    void main() {
        while(1) {
            temperature = ReadTemperature();
    
            if (temperature > set_temperature) {
                beep_on = 1;
            } else {
                beep_on = 0;
            }
    
            BEEP = beep_on ? 0 : 1;  // 根据标志控制蜂鸣器
        }
    }
    

    这样能更清晰地管理蜂鸣器状态,避免出现“遗留”状态。


    如需我帮你整合完整的代码或调试其他功能,请继续提供更多信息!

    评论

报告相同问题?

问题事件

  • 创建了问题 11月29日