找回密码
 立即注册
查看: 94|回复: 0

电子温度计程序又扩展了新功能,增加了按键设置温度控制功能

[复制链接]
  • 打卡等级:偶尔看看I
  • 打卡总天数:11
  • 最近打卡:2026-04-10 01:15:45
已绑定手机

12

主题

8

回帖

98

积分

注册会员

积分
98
发表于 2026-4-5 21:26:05 | 显示全部楼层 |阅读模式
针对昨天的温控程序,又扩展了新功能,增加了按键设置温度控制功能,可以用在温度控制领域,喜欢的坛友来这里看看程序。

简要说明一下程序的接线方式和功能:

硬件:
    MCU: STC32G12K128
温度传感器: DS18B20
       显示: 4位共阴数码管
       按键: 2个(加温、降温)
  引脚连接:
       DS18B20数据线: P1.7
       数码管段选A-H: P2.0-P2.7
       数码管位选1-4: P3.0-P3.3
       红色LED: P0.0
      绿色LED: P0.1
       加温按键: P4.2
       降温按键: P4.1
  功能:
       1. 实时显示当前温度
      2. 按键设置温度阈值(0-50度)
      3. 当温度>设定温度时,红灯亮
      4. 当温度<=设定温度时,绿灯亮



#include "STC32G.h"
#include <intrins.h>

/*============================================================================
* 引脚定义
============================================================================*/
// DS18B20数据引脚定义
sbit DS18B20_DQ = P1^7;  // DS18B20数据线
sbit LED_R = P0^0;       // 红色指示灯(温度过高)
sbit LED_G = P0^1;       // 绿色指示灯(温度正常/过低)

// 按键引脚定义
sbit KEY_UP = P4^2;      // 加温按键
sbit KEY_DOWN = P4^1;    // 降温按键

// 数码管段选引脚定义 (P2端口)
sbit SEG_A = P2^0;  // 数码管A段
sbit SEG_B = P2^1;  // 数码管B段
sbit SEG_C = P2^2;  // 数码管C段
sbit SEG_D = P2^3;  // 数码管D段
sbit SEG_E = P2^4;  // 数码管E段
sbit SEG_F = P2^5;  // 数码管F段
sbit SEG_G = P2^6;  // 数码管G段
sbit SEG_DP = P2^7; // 数码管小数点

// 数码管位选引脚定义 (P3端口)
sbit DIG1 = P3^0;  // 第1位数码管
sbit DIG2 = P3^1;  // 第2位数码管
sbit DIG3 = P3^2;  // 第3位数码管
sbit DIG4 = P3^3;  // 第4位数码管

/*============================================================================
* 常量定义
============================================================================*/
// 数码管编码表 (共阴极,0-9,带特殊字符)
// 格式:DP G F E D C B A
unsigned char code SEG_CODE[] = {
    0x3F,  // 0: 00111111
    0x06,  // 1: 00000110
    0x5B,  // 2: 01011011
    0x4F,  // 3: 01001111
    0x66,  // 4: 01100110
    0x6D,  // 5: 01101101
    0x7D,  // 6: 01111101
    0x07,  // 7: 00000110
    0x7F,  // 8: 01111111
    0x6F,  // 9: 01101111
    0x40,  // -: 01000000 (负号)
    0x00,  // 空: 00000000
    0x39,  // C: 00111001
    0x76,  // H: 01110110
    0x63,  // o: 01100011
    0x38,  // L: 00111000
    0x00   // 灭: 00000000
};

// 数码管位选控制码
unsigned char code DIGIT_CODE[] = {
    0xFE,  // 11111110 - 第1位
    0xFD,  // 11111101 - 第2位
    0xFB,  // 11111011 - 第3位
    0xF7   // 11110111 - 第4位
};

/*============================================================================
* 全局变量
============================================================================*/
unsigned int temp_value;      // 当前温度值(放大10倍,如25.5度存储为255)
signed char temp_sign = 1;    // 温度符号:1为正,-1为负
unsigned int set_temp = 250;  // 设定温度值(放大10倍,默认25.0度=250)
bit temp_high = 0;           // 温度过高标志
bit display_flag = 0;        // 显示更新标志
bit temp_change_flag = 0;     // 温度变化标志
bit blink_flag = 0;          // 闪烁标志位
bit set_mode = 0;            // 设置模式标志

// 显示缓冲区(存放4位数码管要显示的数字)
unsigned char display_buf[4] = {0, 0, 0, 0};
unsigned char dot_position = 0;  // 小数点位置(0:无小数点,1:第1位,2:第2位,3:第3位)

// 按键相关变量
unsigned int key_delay = 0;      // 按键延时计数
bit key_flag = 0;               // 按键标志
bit key_up_pressed = 0;         // 加温按键按下标志
bit key_down_pressed = 0;       // 降温按键按下标志
unsigned int set_display_count = 0;  // 设置显示计数
unsigned char blink_count = 0;  // 闪烁计数

/*============================================================================
* 延时函数
============================================================================*/

/**
* @brief 微秒级延时
* @param us 微秒数
*/
void Delay_us(unsigned int us)
{
    while(us--)
    {
        _nop_(); _nop_(); _nop_(); _nop_();
        _nop_(); _nop_(); _nop_(); _nop_();
    }
}

/**
* @brief 毫秒级延时
* @param ms 毫秒数
*/
void Delay_ms(unsigned int ms)
{
    unsigned int i, j;
    for(i = 0; i < ms; i++)
    {
        for(j = 0; j < 1140; j++)
        {
            _nop_();
        }
    }
}

/*============================================================================
* 系统初始化函数
============================================================================*/

/**
* @brief GPIO引脚初始化
*/
void GPIO_Init(void)
{
    // P2端口配置为推挽输出(数码管段选)
    P2M0 = 0xFF;  // 设置为推挽输出模式
    P2M1 = 0x00;
    P2 = 0x00;    // 初始输出低电平,关闭所有段
   
    // P3.0-P3.3配置为推挽输出(数码管位选)
    P3M0 = 0x0F;  // P3.0-P3.3推挽输出
    P3M1 = 0x00;
    P3 |= 0x0F;   // 初始输出高电平,关闭所有位
   
    // P1.7配置为准双向口(DS18B20)
    P1M0 &= ~0x80;
    P1M1 &= ~0x80;
    P1 |= 0x80;   // 初始输出高电平
   
    // P4.1-P4.2配置为输入模式(按键)
    // 将P4口全部8个引脚都配置为准双向口
    P4M0 = 0x00;  // 设置P4.7-P4.0的M0位全为0
    P4M1 = 0x00;  // 设置P4.7-P4.0的M1位全为0
    P4 = 0xFF;    // 设置P4口全为高电平,使能内部上拉
   
    // LED初始化
    P0M0 &= ~0x03;
    P0M1 &= ~0x03;
    LED_R = 0;    // 红色LED初始熄灭
    LED_G = 0;    // 绿色LED初始熄灭
}

/**
* @brief 定时器0初始化(用于数码管扫描和按键检测)
*/
void Timer0_Init(void)
{
    AUXR &= 0x7F;     // 定时器时钟12T模式
    TMOD &= 0xF0;     // 设置定时器模式
    TMOD |= 0x01;     // 定时器0模式1,16位定时器
   
    TL0 = 0x00;       // 设置定时初值
    TH0 = 0xEE;       // 1ms定时(12MHz晶振)
   
    TF0 = 0;          // 清除TF0标志
    TR0 = 1;          // 定时器0开始计时
   
    ET0 = 1;          // 允许定时器0中断
    EA = 1;           // 开总中断
}

/*============================================================================
* DS18B20驱动程序
============================================================================*/

/**
* @brief DS18B20初始化
* @return 0:初始化失败 1:初始化成功
*/
unsigned char DS18B20_Init(void)
{
    unsigned char flag = 0;
   
    // 主机拉低总线480-960us
    DS18B20_DQ = 0;
    Delay_us(600);
    DS18B20_DQ = 1;
   
    // 释放总线,等待15-60us
    Delay_us(30);
   
    // 检测从机响应脉冲
    if(DS18B20_DQ == 0)
    {
        flag = 1;  // DS18B20存在
    }
    else
    {
        flag = 0;  // DS18B20不存在
    }
   
    // 等待从机释放总线
    while(!DS18B20_DQ);
   
    return flag;
}

/**
* @brief DS18B20写一个字节
* @param dat 要写入的数据
*/
void DS18B20_WriteByte(unsigned char dat)
{
    unsigned char i;
   
    for(i = 0; i < 8; i++)
    {
        // 开始写时序
        DS18B20_DQ = 0;
        _nop_(); _nop_();  // 延时约2us
        
        // 写入1位数据
        DS18B20_DQ = dat & 0x01;
        Delay_us(60);      // 保持60us
        
        // 释放总线
        DS18B20_DQ = 1;
        _nop_(); _nop_();  // 延时约2us
        
        // 准备下一位
        dat >>= 1;
    }
   
    // 写入完成后延时
    Delay_us(2);
}

/**
* @brief DS18B20读一个字节
* @return 读取到的数据
*/
unsigned char DS18B20_ReadByte(void)
{
    unsigned char i, dat = 0;
   
    for(i = 0; i < 8; i++)
    {
        // 主机拉低总线开始读时序
        DS18B20_DQ = 0;
        _nop_(); _nop_();  // 延时约2us
        
        // 释放总线
        DS18B20_DQ = 1;
        _nop_(); _nop_();  // 延时约2us
        
        // 读取数据
        dat >>= 1;
        if(DS18B20_DQ)
        {
            dat |= 0x80;
        }
        
        // 保持读时序
        Delay_us(60);
        
        // 释放总线等待下一位
        DS18B20_DQ = 1;
    }
   
    return dat;
}

/**
* @brief 读取温度值
* @return 温度值(实际值×10,如25.5返回255)
*/
signed int DS18B20_Read_Temperature(void)
{
    unsigned char tempL, tempH;
    signed int temp_value;
    float temperature;
   
    // 初始化DS18B20
    if(DS18B20_Init() == 0)
    {
        return 0x7FFF;  // 读取失败,返回特殊值
    }
   
    // 跳过ROM指令
    DS18B20_WriteByte(0xCC);
   
    // 启动温度转换
    DS18B20_WriteByte(0x44);
   
    // 延时等待转换完成(最大750ms)
    Delay_ms(750);
   
    // 重新初始化DS18B20
    if(DS18B20_Init() == 0)
    {
        return 0x7FFF;  // 读取失败
    }
   
    // 跳过ROM指令
    DS18B20_WriteByte(0xCC);
   
    // 读取温度寄存器指令
    DS18B20_WriteByte(0xBE);
   
    // 读取温度值(低字节、高字节)
    tempL = DS18B20_ReadByte();
    tempH = DS18B20_ReadByte();
   
    // 合成16位温度值
    temp_value = tempH;
    temp_value <<= 8;
    temp_value |= tempL;
   
    // 判断温度正负
    if(temp_value & 0xF800)  // 高5位为1表示负数
    {
        temp_sign = -1;  // 负温度
        temp_value = ~temp_value + 1;  // 取补码
    }
    else
    {
        temp_sign = 1;   // 正温度
    }
   
    // 计算实际温度值(保留1位小数)
    temperature = temp_value * 0.0625;
    temp_value = (signed int)(temperature * 10);
   
    return temp_value;
}

/*============================================================================
* 按键处理函数
============================================================================*/

/**
* @brief 按键扫描
*/
void Key_Scan(void)
{
    static bit key_up_last = 1;   // 上次按键状态
    static bit key_down_last = 1;
    static unsigned int key_up_count = 0;  // 按键计数
    static unsigned int key_down_count = 0;
   
    // 检测加温按键
    if(KEY_UP == 0)  // 按键按下
    {
        if(++key_up_count >= 20)  // 20ms消抖
        {
            if(key_up_last == 1 && set_mode == 1)  // 检测下降沿且处于设置模式
            {
                if(set_temp < 500)  // 限制在50.0度
                {
                    set_temp += 5;  // 每次增加0.5度
                }
                key_up_pressed = 1;
            }
            key_up_last = 0;
        }
    }
    else  // 按键释放
    {
        key_up_last = 1;
        key_up_count = 0;
    }
   
    // 检测降温按键
    if(KEY_DOWN == 0)  // 按键按下
    {
        if(++key_down_count >= 20)  // 20ms消抖
        {
            if(key_down_last == 1 && set_mode == 1)  // 检测下降沿且处于设置模式
            {
                if(set_temp > 0)  // 限制在0度
                {
                    set_temp -= 5;  // 每次减少0.5度
                }
                key_down_pressed = 1;
            }
            key_down_last = 0;
        }
    }
    else  // 按键释放
    {
        key_down_last = 1;
        key_down_count = 0;
    }
}

/*============================================================================
* 显示函数
============================================================================*/

/**
* @brief 设置当前温度显示缓冲区
* @param value 要显示的值(实际值×10)
* @param sign 符号:1为正,-1为负
*/
void Set_Display_Buffer(signed int value, signed char sign)
{
    unsigned int abs_value;
   
    // 取绝对值
    if(value < 0)
    {
        abs_value = -value;
    }
    else
    {
        abs_value = value;
    }
   
    // 分离各位数字
    display_buf[0] = abs_value % 10;          // 十分位(小数第一位)
    display_buf[1] = (abs_value / 10) % 10;   // 个位
    display_buf[2] = (abs_value / 100) % 10;  // 十位
    display_buf[3] = (abs_value / 1000) % 10; // 百位
   
    // 设置小数点位置(在个位后)
    dot_position = 1;
   
    // 符号处理
    if(sign < 0)
    {
        // 负号显示在最高位
        display_buf[3] = 10;  // 显示"-"
    }
    else
    {
        // 正数,如果百位为0则不显示
        if(display_buf[3] == 0)
        {
            display_buf[3] = 11;  // 不显示
            
            // 如果十位也为0则不显示
            if(display_buf[2] == 0)
            {
                display_buf[2] = 11;  // 不显示
            }
        }
    }
}

/**
* @brief 设置设定温度显示缓冲区
*/
void Set_SetTemp_Display_Buffer(void)
{
    display_buf[0] = (set_temp % 100) % 10;      // 十分位
    display_buf[1] = (set_temp % 100) / 10;     // 个位
    display_buf[2] = (set_temp / 100) % 10;      // 十位
    display_buf[3] = 15;  // 显示"L"表示设置值
   
    // 设置小数点
    dot_position = 1;
}

/**
* @brief 温度比较和LED控制
*/
void Temp_Compare_Control(void)
{
    // 温度比较
    if((signed int)temp_value > (signed int)set_temp)
    {
        // 当前温度 > 设定温度,红灯亮
        LED_R = 1;
        LED_G = 0;
        temp_high = 1;
    }
    else
    {
        // 当前温度 <= 设定温度,绿灯亮
        LED_R = 0;
        LED_G = 1;
        temp_high = 0;
    }
}

/*============================================================================
* 数码管扫描显示函数(定时器中断调用)
============================================================================*/
void Display_Scan(void)
{
    static unsigned char digit = 0;  // 当前显示的位
    unsigned char seg_data;
   
    // 在设置模式下,设置值会闪烁显示
    if(set_mode)
    {
        if(++blink_count >= 250)  // 500ms闪烁周期
        {
            blink_flag = ~blink_flag;
            blink_count = 0;
        }
        
        // 闪烁期间关闭显示
        if(blink_flag)
        {
            P2 = 0x00;  // 关闭所有段
            switch(digit)
            {
                case 0: DIG1 = 1; break;
                case 1: DIG2 = 1; break;
                case 2: DIG3 = 1; break;
                case 3: DIG4 = 1; break;
            }
            digit++;
            if(digit >= 4) digit = 0;
            return;
        }
    }
   
    // 获取当前位要显示的数字编码
    seg_data = SEG_CODE[display_buf[digit]];
   
    // 如果需要显示小数点
    if(dot_position == digit)
    {
        seg_data |= 0x80;  // 点亮小数点
    }
   
    // 设置段选数据
    P2 = seg_data;
   
    // 先关闭所有数码管
    DIG1 = 1;
    DIG2 = 1;
    DIG3 = 1;
    DIG4 = 1;
   
    // 选择要显示的位
    switch(digit)
    {
        case 0: DIG1 = 0; break;  // 第1位(个位/十分位)
        case 1: DIG2 = 0; break;  // 第2位(十位/个位)
        case 2: DIG3 = 0; break;  // 第3位(百位/十位)
        case 3: DIG4 = 0; break;  // 第4位(符号/百位)
    }
   
    // 切换到下一位
    digit++;
    if(digit >= 4)
    {
        digit = 0;
    }
}

/*============================================================================
* 定时器0中断服务函数
============================================================================*/
void Timer0_ISR(void) interrupt 1
{
    static unsigned int temp_count = 0;
    static unsigned int set_count = 0;
    static unsigned int key_scan_count = 0;
   
    // 重装定时器初值(1ms)
    TL0 = 0x00;
    TH0 = 0xEE;
   
    // 数码管扫描(每1ms扫描1位)
    Display_Scan();
   
    // 按键扫描(每5ms扫描一次)
    if(key_scan_count++ >= 5)
    {
        key_scan_count = 0;
        Key_Scan();
    }
   
    // 温度读取计数(每500ms读取一次温度)
    if(temp_count++ >= 500)
    {
        temp_count = 0;
        display_flag = 1;  // 设置温度读取标志
    }
   
    // 设置模式自动退出计时
    if(set_mode)
    {
        if(set_display_count++ >= 3000)  // 3秒后自动退出设置模式
        {
            set_display_count = 0;
            set_mode = 0;
        }
    }
}

/*============================================================================
* 主函数
============================================================================*/
void main(void)
{
    signed int current_temp = 0;
    signed int last_temp = 0;
    unsigned int no_key_count = 0;  // 无按键计数
   
    // 系统初始化
    GPIO_Init();      // GPIO引脚初始化
    LED_R = 0;       // 红色LED初始熄灭
    LED_G = 0;       // 绿色LED初始熄灭
    Timer0_Init();    // 定时器0初始化
   
    // 初始显示"----"
    display_buf[0] = 10;  // "-"
    display_buf[1] = 10;  // "-"
    display_buf[2] = 10;  // "-"
    display_buf[3] = 10;  // "-"
    dot_position = 0;     // 无小数点
   
    // 首次温度读取
    current_temp = DS18B20_Read_Temperature();
    if(current_temp != 0x7FFF)
    {
        Set_Display_Buffer(current_temp, temp_sign);
        temp_value = current_temp;
        Temp_Compare_Control();
    }
   
    // 主循环
    while(1)
    {
        // 检查是否需要读取温度
        if(display_flag)
        {
            display_flag = 0;  // 清除标志
            
            // 读取温度
            current_temp = DS18B20_Read_Temperature();
            
            // 如果读取成功
            if(current_temp != 0x7FFF)
            {
                temp_value = current_temp;
               
                // 温度有变化才更新显示
                if(current_temp != last_temp)
                {
                    last_temp = current_temp;
                    if(!set_mode)  // 非设置模式下才显示当前温度
                    {
                        Set_Display_Buffer(current_temp, temp_sign);
                    }
                }
               
                // 温度比较和LED控制
                Temp_Compare_Control();
            }
            else
            {
                // 读取失败,显示错误
                if(!set_mode)
                {
                    display_buf[0] = 13;  // "H"
                    display_buf[1] = 12;  // "C"
                    display_buf[2] = 10;  // "-"
                    display_buf[3] = 10;  // "-"
                    dot_position = 0;
                }
            }
        }
        
        // 检测长按按键进入设置模式
        if(KEY_UP == 0 && KEY_DOWN == 0)  // 两个按键同时按下
        {
            if(++no_key_count >= 2000)  // 长按2秒
            {
                no_key_count = 0;
                set_mode = ~set_mode;  // 切换设置模式
                set_display_count = 0;  // 重置设置计时
                blink_flag = 0;         // 重置闪烁标志
                blink_count = 0;
               
                if(set_mode)
                {
                    // 进入设置模式,显示设定值
                    Set_SetTemp_Display_Buffer();
                }
                else
                {
                    // 退出设置模式,显示当前温度
                    Set_Display_Buffer(temp_value, temp_sign);
                }
            }
        }
        else
        {
            no_key_count = 0;
        }
        
        // 在设置模式下,如果有按键操作,重置设置计时
        if(set_mode && (key_up_pressed || key_down_pressed))
        {
            set_display_count = 0;
            blink_flag = 0;
            blink_count = 0;
            key_up_pressed = 0;
            key_down_pressed = 0;
            Set_SetTemp_Display_Buffer();  // 更新显示设置值
        }
    }
}


增加了两个设置按键

增加了两个设置按键

设置控制温度25度

设置控制温度25度

当前温度小于设置温度亮绿灯

当前温度小于设置温度亮绿灯

当前温度大于设置温度亮红灯

当前温度大于设置温度亮红灯
1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-5-11 11:32 , Processed in 0.099860 second(s), 42 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表