- 打卡等级:偶尔看看I
- 打卡总天数:11
- 最近打卡:2026-04-10 01:15:45
已绑定手机
注册会员
- 积分
- 98
|
针对昨天的温控程序,又扩展了新功能,增加了按键设置温度控制功能,可以用在温度控制领域,喜欢的坛友来这里看看程序。
简要说明一下程序的接线方式和功能:
硬件:
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度
-
当前温度小于设置温度亮绿灯
-
当前温度大于设置温度亮红灯
1
喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
-
+1
楼主威武~
|