温度采集器系统技术文档
目录
- 项目概述
- 硬件配置
- 系统架构
- 驱动层详解
- 应用层详解
- 功能说明
- 使用指南
- 开发指南
项目概述
基本信息
- 项目名称:温度采集器
- 适用场景:蓝桥杯单片机竞赛
- 单片机型号:AT89C52 (MCS-51系列)
- 晶振频率:12MHz
- 开发环境:Keil uVision
功能特点
温度数据输入(0-85℃,支持小数点)
实时温度显示
温度上下限参数设置(10-70℃)
LED状态指示(超限/正常/偏低/错误)
PWM亮度控制(10级)
按键防抖与长按检测
闪烁反馈机制
硬件配置
引脚分配
| 外设 |
引脚 |
功能描述 |
| 4×4矩阵键盘 |
P3.0-P3.7 |
行列扫描(列P3.0-P3.3,行P3.4-P3.7) |
| 8位LED |
P1.0-P1.7 |
LED显示输出(低电平点亮) |
| 6位数码管 |
P0口 |
数据输出 |
| 数码管控制 |
P2.6 |
段选锁存 |
| 数码管控制 |
P2.7 |
位选锁存 |
硬件原理图
┌─────────────────────────────────────────────┐
│ AT89C52 │
│ │
│ P3.0-P3.3 ──────► 键盘列输入 │
│ P3.4-P3.7 ──────► 键盘行检测 │
│ │
│ P1.0-P1.7 ──────► 8位LED(低电平亮) │
│ │
│ P0.0-P0.7 ──────► 数码管段码数据 │
│ P2.6 ──────► 数码管段选锁存 │
│ P2.7 ──────► 数码管位选锁存 │
└─────────────────────────────────────────────┘
系统架构
分层架构设计
┌──────────────────────────────────────────────┐
│ 应用层 (User/main.c) │
│ • 按键处理 (Key_Proc) │
│ • 数码管处理 (Seg_Proc) │
│ • LED处理 (Led_Proc) │
└──────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────┐
│ 驱动层 (Driver/*.c) │
│ • 键盘驱动 (Key.c) │
│ • 数码管驱动 (Seg.c) │
│ • LED驱动 (Led.c) │
└──────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────┐
│ 硬件抽象层 (REGX52.H) │
│ • P0, P1, P2, P3 寄存器 │
│ • 定时器寄存器 │
└──────────────────────────────────────────────┘
文件组织结构
demo5/
├── Driver/ # 底层驱动模块
│ ├── Key.h / Key.c # 4×4矩阵键盘驱动
│ ├── Led.h / Led.c # 8位LED驱动
│ └── Seg.h / Seg.c # 6位数码管驱动
└── User/ # 用户应用层
└── main.c # 主程序(451行)
驱动层详解
1. 键盘驱动模块 (Key.c)
功能说明
- 4×4矩阵键盘扫描
- 采用列扫描行检测法
- 返回键值:1-16
代码实现
// Driver/Key.c
#include "Key.h"
unsigned char Key_Read()
{
unsigned char temp = 0;
// 第1列扫描 (P3.0=0)
P3_0 = 0; P3_1 = 1; P3_2 = 1; P3_3 = 1;
if(P3_4 == 0) temp = 1; // 按键1
if(P3_5 == 0) temp = 2; // 按键2
if(P3_6 == 0) temp = 3; // 按键3
if(P3_7 == 0) temp = 4; // 按键4
// 第2列扫描 (P3.1=0)
P3_0 = 1; P3_1 = 0; P3_2 = 1; P3_3 = 1;
if(P3_4 == 0) temp = 5; // 按键5
if(P3_5 == 0) temp = 6; // 按键6
if(P3_6 == 0) temp = 7; // 按键7
if(P3_7 == 0) temp = 8; // 按键8
// 第3列扫描 (P3.2=0)
P3_0 = 1; P3_1 = 1; P3_2 = 0; P3_3 = 1;
if(P3_4 == 0) temp = 9; // 按键9
if(P3_5 == 0) temp = 10; // 按键10
if(P3_6 == 0) temp = 11; // 按键11 (小数点)
if(P3_7 == 0) temp = 12; // 按键12 (确认/切换)
// 第4列扫描 (P3.3=0)
P3_0 = 1; P3_1 = 1; P3_2 = 1; P3_3 = 0;
if(P3_4 == 0) temp = 13; // 按键13 (切换参数)
if(P3_5 == 0) temp = 14; // 按键14 (加)
if(P3_6 == 0) temp = 15; // 按键15 (减)
if(P3_7 == 0) temp = 16; // 按键16 (清除)
return temp;
}
键盘布局
┌─────┬─────┬─────┬─────┐
│ 1 │ 5 │ 9 │ 13 │ ← 切换参数
├─────┼─────┼─────┼─────┤
│ 2 │ 6 │ 10 │ 14 │ ← 加
├─────┼─────┼─────┼─────┤
│ 3 │ 7 │ 11 │ 15 │ ← 减
├─────┼─────┼─────┼─────┤
│ 4 │ 8 │ 12 │ 16 │ ← 清除
└─────┴─────┴─────┴─────┘
↑ ↑ ↑ ↑
数字 数字 . 确认
2. 数码管驱动模块 (Seg.c)
功能说明
段码表定义
// Driver/Seg.c
// 字码表:0-9, 熄灭, G(6d), A(77), -(40), C(39), P(73)
unsigned char Seg_Dula[] = {
0x3f, // 0
0x06, // 1
0x5b, // 2
0x4f, // 3
0x66, // 4
0x6d, // 5
0x7d, // 6
0x07, // 7
0x7f, // 8
0x6f, // 9
0x00, // 10 - 熄灭
0x6d, // 11 - G (字母)
0x77, // 12 - A (字母T的头部显示)
0x40, // 13 - 横杠(-)
0x39, // 14 - C (字母d的头部显示)
0x73 // 15 - P (字母S的头部显示)
};
// 位码表:从右到左,第1-6位
unsigned char Seg_Wela[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf};
显示函数
// Driver/Seg.c
void Seg_Disp(unsigned char wela, dula, point)
{
// 消影处理
P0 = 0x00;
P2_6 = 1; // 段选锁存脉冲
P2_6 = 0;
// 位选
P0 = Seg_Wela[wela];
P2_7 = 1; // 位选锁存脉冲
P2_7 = 0;
// 段选(带小数点判断)
if(point == 1)
P0 = Seg_Dula[dula] | 0x80; // 点亮小数点(DP)
else
P0 = Seg_Dula[dula];
P2_6 = 1; // 段选锁存脉冲
P2_6 = 0;
}
数码管段码原理
a
───
f │ │ b
─g─
e │ │ c
─── • dp
d
段码位分配:
dp g f e d c b a
7 6 5 4 3 2 1 0
3. LED驱动模块 (Led.c)
功能说明
代码实现
// Driver/Led.c
#include <Led.h>
void Led_Disp(unsigned char addr, enable)
{
static unsigned char temp = 0x00; // LED状态缓存
static unsigned char temp_old = 0xff; // 旧状态缓存
// 位操作:设置/清除对应位
if(enable)
temp |= 0x01 << addr; // 置位(点亮)
else
temp &= ~(0x01 << addr); // 清位(熄灭)
// 只有状态变化时才更新硬件
if(temp != temp_old)
{
P1 = ~temp; // 取反输出(低电平点亮)
temp_old = temp;
}
}
LED功能分配
LED0 (P1.0) → 温度超过上限 (红灯,亮度等级3)
LED1 (P1.1) → 温度在范围内 (绿灯,亮度等级6)
LED2 (P1.2) → 温度低于下限 (蓝灯,亮度等级9)
LED3 (P1.3) → 数据错误标志 (黄灯)
LED4-7 → 预留扩展
应用层详解
1. 主程序结构 (main.c)
全局变量定义
// User/main.c - 按键相关变量
unsigned char Key_Val, Key_Down, Key_Old, Key_Up; // 按键状态检测
unsigned char Key_Slow_Down; // 按键减速器(10ms)
// 数码管相关变量
unsigned char Seg_Buf[6] = {10,10,10,10,10,10}; // 显示缓冲区
unsigned char Seg_Point[6] = {0,0,0,0,0,0}; // 小数点缓冲区
unsigned char Seg_Pos; // 扫描位置
unsigned int Seg_Slow_Down; // 数码管减速器(50ms)
// LED相关变量
unsigned char ucLed[8] = {0,0,0,0,0,0,0,0}; // LED状态数组
unsigned char Led_Pos; // LED扫描位置
unsigned char Led_Level; // 亮度等级(3/6/9)
unsigned char Led_PWM; // PWM计数器
// 温度数据相关
unsigned char Seg_Dis_Mode; // 显示模式 0/1/2
unsigned char Temperature_Input[3] = {13,13,13}; // 输入缓冲
unsigned char Temperature_Real[3] = {0,0,0}; // 实际温度(百十个位)
unsigned char Temperature_Data; // 当前温度值
unsigned char TMAX; // 温度上限
unsigned char TMIN; // 温度下限
unsigned char Temperature_Setting[4] = {3,0,2,0}; // 参数设置(默认30℃/20℃)
// 标志位
bit Num_Input; // 数字输入完成标志
bit Point_Input; // 小数点输入标志
bit Flag_0; // 模式0闪烁标志(250ms周期)
bit Flag_2; // 模式2闪烁标志(250ms周期)
bit Data_Error; // 数据错误标志
2. 按键处理函数 (Key_Proc)
按键防抖与边沿检测
void Key_Proc()
{
if (Key_Slow_Down) return; // 10ms减速
Key_Slow_Down = 1;
Key_Val = Key_Read(); // 读取键值
Key_Down = Key_Val & (Key_Val ^ Key_Old); // 下降沿检测
Key_Up = Key_Old & (Key_Val ^ Key_Old); // 上升沿检测
Key_Old = Key_Val; // 保存旧值
// ... 按键处理逻辑
}
模式0:数字输入处理
// 按键1-10:数字输入
if((Key_Down > 0) && (Key_Down <= 10))
{
if(Seg_Dis_Mode == 0) // 仅在输入模式有效
{
if(Temperature_Input_Index < 3) // 最多3位
{
Temperature_Input[Temperature_Input_Index] = Key_Down - 1;
Num_Input = 1; // 标记已输入数字
Temperature_Input_Index++;
}
}
}
// 按键11:小数点输入
if((Key_Down == 11) && (Num_Input == 1) && (Point_Input == 0))
{
if(Temperature_Input_Index > 0) // 必须先有数字
{
Num_Input = 0;
Point_Input = 1;
Temperature_Input_Point[Temperature_Input_Index - 1] = 1;
}
}
按键16:数据确认与四舍五入
if(Key_Down == 16)
{
if(Seg_Dis_Mode == 0)
{
// 确定小数点位置
for(i=0; i<3; i++)
{
if(Temperature_Input_Point[i] == 1) Point_Wei = i;
if(Point_Input == 0) Point_Wei = 2; // 无小数点=整数
}
// 根据小数点位置进行四舍五入
switch(Point_Wei)
{
case 0: // 格式:x.xx → 保留整数,四舍五入
Temperature_Real[1] = 0;
Temperature_Real[2] = Temperature_Input[0];
if(Temperature_Input[1] >= 5) // 十分位≥5则进位
{
Temperature_Real[2]++;
if(Temperature_Real[2] == 10) // 进位处理
{
Temperature_Real[2] = 0;
Temperature_Real[1]++;
}
}
break;
case 1: // 格式:xx.x → 保留一位小数
Temperature_Real[1] = Temperature_Input[0];
Temperature_Real[2] = Temperature_Input[1];
if(Temperature_Input[2] >= 5) // 百分位≥5则进位
{
Temperature_Real[2]++;
if(Temperature_Real[2] == 10)
{
Temperature_Real[2] = 0;
Temperature_Real[1]++;
if(Temperature_Real[1] == 10)
{
Temperature_Real[1] = 0;
Temperature_Real[0]++;
}
}
}
break;
case 2: // 无小数点 → 清除数据重新输入
// 重置所有输入状态
Temperature_Input_Index = 0;
Num_Input = 0;
Point_Input = 0;
// ... 清除缓冲区
break;
}
// 数据验证:范围0-85℃
Temperature_Input_Data = Temperature_Real[0]*100 +
Temperature_Real[1]*10 +
Temperature_Real[2];
if((Temperature_Input_Data >= 0) &&
(Temperature_Input_Data <= 85) &&
(Num_Input == 1))
{
Seg_Dis_Mode = 1; // 切换到显示模式
Temperature_Data = Temperature_Input_Data;
}
else
{
// 数据非法,重置输入
// ... 清除缓冲区
}
}
else
{
// 其他模式下按16键返回输入模式
Seg_Dis_Mode = 0;
// ... 重置状态
}
}
按键12:模式切换
if(Key_Down == 12)
{
if(Seg_Dis_Mode == 1) // 显示模式 → 设置模式
{
Seg_Dis_Mode = 2;
Temperature_Setting_Index = 0; // 默认修改上限
}
else if(Seg_Dis_Mode == 2) // 设置模式 → 显示模式
{
TMAX = Temperature_Setting[0]*10 + Temperature_Setting[1];
TMIN = Temperature_Setting[2]*10 + Temperature_Setting[3];
// 参数合法性校验:上限≥下限
if(TMAX >= TMIN)
{
// 保存有效参数
for(k=0; k<4; k++)
{
Temperature_Setting_Old[k] = Temperature_Setting[k];
}
Data_Error = 0;
}
else
{
// 参数非法,恢复旧值
for(k=0; k<4; k++)
{
Temperature_Setting[k] = Temperature_Setting_Old[k];
}
Data_Error = 1; // 点亮错误LED
}
Seg_Dis_Mode = 1;
}
}
按键13:参数位切换
if(Key_Down == 13)
{
Temperature_Setting_Index += 2; // 0→2→0循环
if(Temperature_Setting_Index == 4)
Temperature_Setting_Index = 0;
// 索引0→修改上限(TMAX)
// 索引2→修改下限(TMIN)
}
按键14/15:参数调整与长按加速
// 按键14:单次加1
if(Key_Down == 14)
{
if(Seg_Dis_Mode == 2)
{
Temperature_Setting[Temperature_Setting_Index + 1]++;
// 个位进位处理
if(Temperature_Setting[Temperature_Setting_Index + 1] == 10)
{
Temperature_Setting[Temperature_Setting_Index + 1] = 0;
Temperature_Setting[Temperature_Setting_Index]++;
}
// 上限保护:70℃
if((Temperature_Setting[Temperature_Setting_Index] == 7) &&
(Temperature_Setting[Temperature_Setting_Index + 1] == 1))
{
Temperature_Setting[Temperature_Setting_Index] = 7;
Temperature_Setting[Temperature_Setting_Index + 1] = 0;
}
}
}
// 按键14长按:快速加1(500ms后触发)
if(Key_Old == 14)
{
if(Seg_Dis_Mode == 2)
{
if(Key_Long0 >= 500) // 长按阈值500ms
{
// 与单次加1逻辑相同
Temperature_Setting[Temperature_Setting_Index + 1]++;
// ... 进位与保护逻辑
}
}
}
// 按键15:减1(逻辑与加1对称)
if(Key_Down == 15)
{
if(Seg_Dis_Mode == 2)
{
Temperature_Setting[Temperature_Setting_Index + 1]--;
// 下限保护:10℃
if((Temperature_Setting[Temperature_Setting_Index] == 1) &&
(Temperature_Setting[Temperature_Setting_Index + 1] == 255)) // 借位
{
Temperature_Setting[Temperature_Setting_Index] = 1;
Temperature_Setting[Temperature_Setting_Index + 1] = 0;
}
// 个位借位处理
if(Temperature_Setting[Temperature_Setting_Index + 1] == 255)
{
Temperature_Setting[Temperature_Setting_Index + 1] = 9;
Temperature_Setting[Temperature_Setting_Index]--;
}
}
}
3. 数码管显示函数 (Seg_Proc)
void Seg_Proc()
{
if (Seg_Slow_Down) return; // 50ms减速
Seg_Slow_Down = 1;
switch (Seg_Dis_Mode)
{
case 0: // 输入模式:显示 "T xxx"
Seg_Buf[0] = 12; // 字母T(实际为A)
Seg_Buf[1] = 10; // 熄灭
Seg_Buf[2] = 10; // 熄灭
Seg_Buf[3] = Temperature_Input[0];
Seg_Point[3] = Temperature_Input_Point[0];
Seg_Buf[4] = Temperature_Input[1];
Seg_Point[4] = Temperature_Input_Point[1];
Seg_Buf[5] = Temperature_Input[2];
// 闪烁效果:当前输入位
if(Flag_0) // 250ms周期翻转
{
if(Temperature_Input_Index < 2)
{
Seg_Buf[Temperature_Input_Index + 3] = 10; // 熄灭=闪烁
}
if(Temperature_Input_Index == 2)
Seg_Buf[5] = 10;
}
break;
case 1: // 显示模式:显示 "d xx.x"
Seg_Buf[0] = 14; // 字母d(实际为C)
Seg_Buf[1] = 10;
Seg_Buf[2] = 10;
Seg_Buf[3] = 10;
Seg_Point[3] = 0;
Seg_Point[4] = 0;
Seg_Buf[4] = Temperature_Real[1]; // 十位
Seg_Buf[5] = Temperature_Real[2]; // 个位
break;
case 2: // 设置模式:显示 "S xx xx"
Seg_Buf[0] = 15; // 字母S(实际为P)
Seg_Buf[1] = 10;
Seg_Buf[2] = Temperature_Setting[0]; // 上限十位
Seg_Buf[3] = Temperature_Setting[1]; // 上限个位
Seg_Buf[4] = Temperature_Setting[2]; // 下限十位
Seg_Buf[5] = Temperature_Setting[3]; // 下限个位
// 闪烁效果:当前修改的参数
if(Flag_2) // 250ms周期翻转
{
if(Temperature_Setting_Index == 0) // 修改上限
{
Seg_Buf[2] = 10;
Seg_Buf[3] = 10;
}
if(Temperature_Setting_Index == 2) // 修改下限
{
Seg_Buf[4] = 10;
Seg_Buf[5] = 10;
}
}
break;
}
}
显示格式示例
模式0 (输入模式):
┌──┬──┬──┬──┬──┬──┐
│ T│ │ │ 3│2.│5 │ ← 输入32.5
└──┴──┴──┴──┴──┴──┘
↑
当前位闪烁
模式1 (显示模式):
┌──┬──┬──┬──┬──┬──┐
│ d│ │ │ │ 3│ 2│ ← 显示32℃
└──┴──┴──┴──┴──┴──┘
模式2 (设置模式):
┌──┬──┬──┬──┬──┬──┐
│ S│ │ 3│ 0│ 2│ 0│ ← 上限30℃,下限20℃
└──┴──┴──┴──┴──┴──┘
↑↑
修改上限时闪烁
4. LED处理函数 (Led_Proc)
void Led_Proc()
{
// LED0:温度超过上限(红灯,亮度等级3)
if(Temperature_Data > TMAX)
{
Led_Level = 3; // 30%占空比
ucLed[0] = 1;
}
else
{
ucLed[0] = 0;
}
// LED1:温度在范围内(绿灯,亮度等级6)
if((Temperature_Data >= TMIN) && (Temperature_Data <= TMAX))
{
Led_Level = 6; // 60%占空比
ucLed[1] = 1;
}
else
{
ucLed[1] = 0;
}
// LED2:温度低于下限(蓝灯,亮度等级9)
if(Temperature_Data < TMIN)
{
Led_Level = 9; // 90%占空比
ucLed[2] = 1;
}
else
{
ucLed[2] = 0;
}
// LED3:数据错误标志(黄灯)
if(Data_Error == 1)
{
ucLed[3] = 1;
}
else
{
ucLed[3] = 0;
}
}
LED亮度PWM实现
亮度等级3: ███░░░░░░░ (30%占空比)
亮度等级6: ██████░░░░ (60%占空比)
亮度等级9: █████████░ (90%占空比)
PWM周期:10ms (定时器中断1ms × 10)
5. 定时器中断系统
Timer0初始化 (1ms中断周期)
void Timer0Init(void) // @12MHz
{
TMOD &= 0xF0; // 清除定时器0模式位
TMOD |= 0x01; // 设置为模式1(16位定时器)
// 计算定时值:65536 - 12000 = 53536 = 0xD120
// 但代码中使用 0xFC18 (64536) 约1ms
TL0 = 0x18;
TH0 = 0xFC;
TF0 = 0; // 清除溢出标志
TR0 = 1; // 启动定时器0
ET0 = 1; // 使能定时器0中断
EA = 1; // 使能全局中断
}
Timer0中断服务函数
void Timer0Server() interrupt 1
{
// 重装定时值
TL0 = 0x18;
TH0 = 0xFC;
// 1. 按键减速:每10ms扫描一次
if(++Key_Slow_Down == 10)
Key_Slow_Down = 0;
// 2. 数码管减速:每50ms处理一次
if(++Seg_Slow_Down == 50)
Seg_Slow_Down = 0;
// 3. 数码管动态扫描:每1ms切换下一位
if(++Seg_Pos == 6)
Seg_Pos = 0;
Seg_Disp(Seg_Pos, Seg_Buf[Seg_Pos], Seg_Point[Seg_Pos]);
// 4. LED扫描位切换
if(++Led_Pos == 8)
Led_Pos = 0;
// 5. 闪烁标志翻转:每250ms
if(++Time250 == 250)
{
Time250 = 0;
if(Seg_Dis_Mode == 0) Flag_0 ^= 1; // 模式0闪烁
if(Seg_Dis_Mode == 2) Flag_2 ^= 1; // 模式2闪烁
}
// 6. 长按检测
if(Key_Old == 14)
Key_Long0++;
else
Key_Long0 = 0;
if(Key_Old == 15)
Key_Long1++;
else
Key_Long1 = 0;
// 7. LED PWM亮度控制
if(++Led_PWM == 10)
Led_PWM = 0;
if(Led_PWM < Led_Level)
Led_Disp(Led_Pos, ucLed[Led_Pos]); // 点亮
else
Led_Disp(Led_Pos, 0); // 熄灭
}
定时器任务时间轴
时间轴(ms) | 任务
─────────────┼──────────────────────────
每 1ms │ • 数码管扫描切换
│ • LED扫描切换
│ • 长按计时累加
│ • PWM计数累加
─────────────┼──────────────────────────
每 10ms │ • 按键扫描(防抖)
─────────────┼──────────────────────────
每 50ms │ • 数码管显示处理
─────────────┼──────────────────────────
每 250ms │ • 闪烁标志翻转
─────────────┼──────────────────────────
每 500ms │ • 长按触发阈值
6. 主函数
void main()
{
Timer0Init(); // 初始化定时器
while(1) // 主循环
{
Key_Proc(); // 按键处理
Seg_Proc(); // 数码管显示处理
Led_Proc(); // LED处理
}
}
功能说明
工作模式流程图
┌──────────────┐
│ 模式0 │
│ 温度输入 │ 按键1-10:数字输入
│ 显示:T xxx │ 按键11:小数点
└──────┬───────┘ 按键16:确认
│ 按键16
▼ (数据合法)
┌──────────────┐
│ 模式1 │
│ 数据显示 │ 按键12:进入设置
│ 显示:d xx │
└──────┬───────┘
│ 按键12
▼
┌──────────────┐
│ 模式2 │
│ 参数设置 │ 按键13:切换参数
│ 显示:S xxxx│ 按键14:加
└──────┬───────┘ 按键15:减
│ 按键12 按键12:保存
▼ (参数合法)
模式1
数据流转示意图
┌─────────────┐
│ 用户输入 │
│ (键盘) │
└──────┬──────┘
│
▼
┌─────────────────────────────┐
│ Temperature_Input[3] │ ← 原始输入缓冲
│ Temperature_Input_Point[3] │ ← 小数点标记
└──────┬──────────────────────┘
│ 四舍五入处理
▼
┌─────────────────────────────┐
│ Temperature_Real[3] │ ← 处理后温度
└──────┬──────────────────────┘
│ 范围校验(0-85℃)
▼
┌─────────────────────────────┐
│ Temperature_Data │ ← 当前有效温度
└──────┬──────────────────────┘
│
├──► Led_Proc() ──► LED状态指示
│
└──► Seg_Proc() ──► 数码管显示
参数设置流程
┌─────────────┐
│ 默认参数 │
│ TMAX=30℃ │
│ TMIN=20℃ │
└──────┬──────┘
│
▼
┌─────────────┐
│ 按键13 │ ← 切换上限/下限
│ 选择参数 │
└──────┬──────┘
│
▼
┌─────────────┐
│ 按键14/15 │ ← 增加/减少
│ 调整数值 │ (长按加速)
└──────┬──────┘
│
▼
┌─────────────┐
│ 按键12 │
│ 保存参数 │
└──────┬──────┘
│
├──► 参数合法(TMAX≥TMIN) ──► 保存成功
│
└──► 参数非法(TMAX<TMIN) ──► 恢复旧值 + LED3点亮
使用指南
基本操作流程
温度输入操作
步骤1:系统上电,进入输入模式(显示 "T ---")
步骤2:输入温度值
- 示例1:输入整数温度 25℃
按键顺序:2 → 5 → 16
显示过程:T 2__ → T 25_ → d 25
- 示例2:输入小数温度 36.8℃
按键顺序:3 → 6 → 11(.) → 8 → 16
显示过程:T 3__ → T 36_ → T 36. → T 36.8 → d 37
(注:小数会四舍五入显示为整数)
- 示例3:输入一位小数 8.5℃
按键顺序:8 → 11(.) → 5 → 16
显示过程:T 8__ → T 8._ → T 8.5 → d 9
(注:5舍入进位)
步骤3:观察LED状态
- LED0亮:温度>上限
- LED1亮:温度正常
- LED2亮:温度<下限
参数设置操作
步骤1:在显示模式下,按键12进入设置模式(显示 "S 3020")
↑ ↑
上限30℃ 下限20℃
步骤2:调整上限(默认选中)
- 按键14:增加(70℃上限保护)
- 按键15:减少(10℃下限保护)
- 长按:快速调整(500ms后触发)
示例:调整上限为35℃
按键14 × 5次:30→31→32→33→34→35
显示:S 3520
步骤3:切换到下限调整
- 按键13:切换参数(上限↔下限)
显示:S 3520 (下限部分闪烁)
步骤4:调整下限
示例:调整下限为18℃
按键15 × 2次:20→19→18
显示:S 3518
步骤5:保存参数
- 按键12:保存并返回显示模式
- 参数校验:
√ TMAX ≥ TMIN → 保存成功
× TMAX < TMIN → 恢复旧值,LED3点亮
错误处理
情况1:输入温度超出范围(>85℃或<0℃)
→ 系统自动重置,返回输入模式
情况2:参数设置非法(上限<下限)
→ 保留旧参数,LED3点亮警告
情况3:输入错误,想重新输入
→ 按键16清除,重新开始
开发指南
编译环境配置
Keil uVision设置
1. 打开项目文件:User/温度采集器.uvproj
2. 目标配置 (Target Options)
- Device: Atmel -> AT89C52
- Xtal(MHz): 12.0
3. 输出配置 (Output)
- Name of Executable: Objects/温度采集器
- [√] Create HEX File
4. C51编译选项
- Optimization Level: 8 (Speed)
- [√] Global Register Coloring
5. 下载配置
- 使用STC-ISP或Keil仿真器
代码移植指南
1. 移植到其他51系列单片机
// 修改晶振频率(如使用11.0592MHz)
void Timer0Init(void) // @11.0592MHz
{
TMOD &= 0xF0;
TMOD |= 0x01;
TL0 = 0x66; // 重新计算定时值
TH0 = 0xFC;
// ... 其他不变
}
2. 适配不同的硬件接口
// 如LED使用P2口
// Led.c修改:
void Led_Disp(unsigned char addr, enable)
{
// ... 逻辑不变
P2 = ~temp; // 原来是P1
}
// 如键盘使用P1口
// Key.c修改:
unsigned char Key_Read()
{
P1_0 = 0; P1_1 = 1; P1_2 = 1; P1_3 = 1;
if(P1_4 == 0) temp = 1;
// ... 其他类似修改
}
3. 功能扩展建议
// 扩展1:添加蜂鸣器报警
void Buzzer_Proc()
{
if(Temperature_Data > TMAX)
{
Buzzer = 1; // 温度超限鸣叫
}
else
{
Buzzer = 0;
}
}
// 扩展2:添加温度传感器(如DS18B20)
unsigned char Read_Temperature()
{
unsigned char temp;
// DS18B20读取逻辑
return temp;
}
void main()
{
Timer0Init();
while(1)
{
Key_Proc();
Seg_Proc();
Led_Proc();
Buzzer_Proc(); // 添加蜂鸣器处理
// 定期读取传感器
if(Read_Sensor_Flag)
{
Temperature_Data = Read_Temperature();
Read_Sensor_Flag = 0;
}
}
}
// 扩展3:添加EEPROM存储参数
#include <eeprom.h>
void Save_Parameters()
{
EEPROM_Write(0x00, TMAX);
EEPROM_Write(0x01, TMIN);
}
void Load_Parameters()
{
TMAX = EEPROM_Read(0x00);
TMIN = EEPROM_Read(0x01);
// 参数校验
if(TMAX < 10 || TMAX > 70) TMAX = 30;
if(TMIN < 10 || TMIN > 70) TMIN = 20;
}
调试技巧
1. 使用Keil仿真器
断点位置建议:
- main.c:47 Key_Proc()入口 → 观察按键值
- main.c:290 Seg_Proc()入口 → 观察显示缓冲
- main.c:349 Led_Proc()入口 → 观察LED状态
- main.c:401 Timer0中断入口 → 观察定时准确性
监视变量建议:
- Key_Val, Key_Down → 按键状态
- Seg_Buf[0..5], Seg_Point[0..5] → 显示数据
- Temperature_Data, TMAX, TMIN → 温度参数
- Seg_Dis_Mode → 当前模式
2. 常见问题排查
问题1:数码管不显示或显示混乱
→ 检查Seg_Pos是否正常循环(0-5)
→ 检查P2.6、P2.7锁存信号是否正常
→ 确认Seg_Dula[]数组索引没有越界
问题2:按键无响应
→ 检查Key_Slow_Down是否正常减速
→ 确认P3口电平是否正常
→ 验证Key_Read()返回值是否正确
问题3:LED亮度不正常
→ 检查Led_Level值(应为3/6/9)
→ 确认PWM计数器Led_PWM正常循环(0-9)
→ 验证定时器中断是否正常运行
问题4:闪烁频率不对
→ 检查Time250计数是否准确
→ 确认定时器初值是否正确(1ms周期)
→ 验证Flag_0和Flag_2翻转逻辑
问题5:参数保存失败
→ 检查TMAX和TMIN的大小关系
→ 确认Temperature_Setting[]数组值合法
→ 验证保存逻辑中的数组拷贝是否正确
性能优化建议
1. 减少中断服务函数执行时间
// 优化前:每次都扫描8个LED
void Timer0Server() interrupt 1
{
// ... 其他代码
for(i=0; i<8; i++)
{
Led_Disp(i, ucLed[i]);
}
}
// 优化后:每次只扫描1个LED(已采用)
void Timer0Server() interrupt 1
{
// ... 其他代码
if(++Led_Pos == 8) Led_Pos = 0;
if(Led_PWM < Led_Level)
Led_Disp(Led_Pos, ucLed[Led_Pos]);
else
Led_Disp(Led_Pos, 0);
}
2. 优化减速处理
// 减速机制说明:避免主循环过快执行
void Key_Proc()
{
if (Key_Slow_Down) return; // 未到10ms,直接返回
Key_Slow_Down = 1; // 重置标志
// ... 按键处理
}
// 定时器中每10ms清零一次Key_Slow_Down
3. 内存优化
// 当前内存使用情况:
// - 全局变量:约60字节
// - 堆栈:约50字节
// - 代码:约2KB
// 优化建议:
// 1. 复用临时变量(如i, j, k)
// 2. 使用位变量替代字节变量(已采用bit类型)
// 3. 数组尽量使用unsigned char而非int
附录
A. 完整引脚定义表
| 引脚 |
功能 |
方向 |
说明 |
| P3.0 |
键盘列1 |
输出 |
扫描列,输出0时检测 |
| P3.1 |
键盘列2 |
输出 |
扫描列,输出0时检测 |
| P3.2 |
键盘列3 |
输出 |
扫描列,输出0时检测 |
| P3.3 |
键盘列4 |
输出 |
扫描列,输出0时检测 |
| P3.4 |
键盘行1 |
输入 |
按键检测,低电平有效 |
| P3.5 |
键盘行2 |
输入 |
按键检测,低电平有效 |
| P3.6 |
键盘行3 |
输入 |
按键检测,低电平有效 |
| P3.7 |
键盘行4 |
输入 |
按键检测,低电平有效 |
| P1.0-P1.7 |
LED0-7 |
输出 |
LED控制,低电平点亮 |
| P0.0-P0.7 |
数码管段码 |
输出 |
段码数据输出 |
| P2.6 |
段选锁存 |
输出 |
上升沿锁存段码 |
| P2.7 |
位选锁存 |
输出 |
上升沿锁存位码 |
B. 按键功能速查表
| 按键 |
功能 |
适用模式 |
备注 |
| 1-10 |
数字0-9 |
模式0 |
输入温度值 |
| 11 |
小数点 |
模式0 |
必须先输入数字 |
| 12 |
确认/保存 |
模式1→2, 模式2→1 |
模式切换 |
| 13 |
切换参数 |
模式2 |
上限↔下限 |
| 14 |
增加 |
模式2 |
长按加速 |
| 15 |
减少 |
模式2 |
长按加速 |
| 16 |
确认/清除 |
模式0 |
确认输入或返回模式0 |
C. 数码管段码对照表
| 索引 |
字符 |
段码(HEX) |
二进制 |
说明 |
| 0 |
0 |
0x3F |
00111111 |
数字0 |
| 1 |
1 |
0x06 |
00000110 |
数字1 |
| 2 |
2 |
0x5B |
01011011 |
数字2 |
| 3 |
3 |
0x4F |
01001111 |
数字3 |
| 4 |
4 |
0x66 |
01100110 |
数字4 |
| 5 |
5 |
0x6D |
01101101 |
数字5 |
| 6 |
6 |
0x7D |
01111101 |
数字6 |
| 7 |
7 |
0x07 |
00000111 |
数字7 |
| 8 |
8 |
0x7F |
01111111 |
数字8 |
| 9 |
9 |
0x6F |
01101111 |
数字9 |
| 10 |
熄灭 |
0x00 |
00000000 |
全灭 |
| 11 |
G |
0x6D |
01101101 |
字母G |
| 12 |
A(T) |
0x77 |
01110111 |
显示为T字头 |
| 13 |
- |
0x40 |
01000000 |
横杠 |
| 14 |
C(d) |
0x39 |
00111001 |
显示为d字头 |
| 15 |
P(S) |
0x73 |
01110011 |
显示为S字头 |
D. 系统参数配置表
| 参数名称 |
默认值 |
范围 |
单位 |
说明 |
| 温度上限(TMAX) |
30 |
10-70 |
℃ |
可调整 |
| 温度下限(TMIN) |
20 |
10-70 |
℃ |
可调整 |
| 温度输入范围 |
- |
0-85 |
℃ |
硬件限制 |
| 按键扫描周期 |
10 |
- |
ms |
防抖时间 |
| 数码管刷新周期 |
50 |
- |
ms |
显示处理 |
| 闪烁周期 |
250 |
- |
ms |
视觉效果 |
| 长按触发时间 |
500 |
- |
ms |
快速调整 |
| PWM周期 |
10 |
- |
ms |
LED亮度 |
E. 错误代码说明
| 错误代码 |
LED3状态 |
原因 |
解决方法 |
| E001 |
点亮 |
参数设置非法(TMAX<TMIN) |
重新设置参数 |
| E002 |
- |
温度输入超范围(>85℃) |
重新输入有效值 |
| E003 |
- |
温度输入超范围(<0℃) |
重新输入有效值 |
版本历史
| 版本 |
日期 |
修改内容 |
| v1.0 |
2026-01-30 |
初始版本,实现基本功能 |
| v1.1 |
2026-01-31 |
优化按键长按逻辑 |
作者信息
- 项目类型:蓝桥杯单片机竞赛项目
- 技术支持:参考蓝桥杯官方文档
- 开发工具:Keil uVision 5
许可证
本项目仅供学习交流使用。
文档结束