温度采集器系统技术文档

温度采集器系统技术文档

:clipboard: 目录

  1. 项目概述
  2. 硬件配置
  3. 系统架构
  4. 驱动层详解
  5. 应用层详解
  6. 功能说明
  7. 使用指南
  8. 开发指南

项目概述

基本信息

  • 项目名称:温度采集器
  • 适用场景:蓝桥杯单片机竞赛
  • 单片机型号:AT89C52 (MCS-51系列)
  • 晶振频率:12MHz
  • 开发环境:Keil uVision

功能特点

  • :white_check_mark: 温度数据输入(0-85℃,支持小数点)
  • :white_check_mark: 实时温度显示
  • :white_check_mark: 温度上下限参数设置(10-70℃)
  • :white_check_mark: LED状态指示(超限/正常/偏低/错误)
  • :white_check_mark: PWM亮度控制(10级)
  • :white_check_mark: 按键防抖与长按检测
  • :white_check_mark: 闪烁反馈机制

硬件配置

引脚分配

外设 引脚 功能描述
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)

功能说明

  • 6位共阳数码管显示
  • 动态扫描刷新
  • 支持小数点显示

段码表定义

// 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)

功能说明

  • 8位LED独立控制
  • 采用状态缓存机制
  • 低电平点亮

代码实现

// 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点亮

使用指南

基本操作流程

:one: 温度输入操作

步骤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亮:温度<下限

:two: 参数设置操作

步骤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点亮

:three: 错误处理

情况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

许可证

本项目仅供学习交流使用。


文档结束