代码的自我更新

这段代码是基于51单片机(如STC89C52)的一个典型的倒计时定时器程序。它包含了按键扫描、数码管显示(动态扫描)、LED控制以及定时器中断功能。

代码整体逻辑是清晰的(采用了“大循环+时间片”轮询架构),但存在几个严重的编译错误和逻辑Bug,会导致程序无法编译或运行不正常。

以下是详细的问题分析和修改后的完整代码:

1. 主要问题分析

  1. 致命的编译错误(特殊字符):

    • Timer0_Init 函数中,ET0 = 1;EA = 1; 前面和注释中包含了不可见的非ASCII字符(通常是从网页或文档复制时带入的全角空格 )。这会导致编译器报错 Syntax Error
  2. 逻辑错误:switch-case 穿透:

    • Key_Proc 函数的 case 2 后面缺少 break;。这意味着按下“按键2”后,会执行完代码,紧接着错误地执行 case 3 的代码。
  3. 命名笔误:

    • 数码管处理函数写成了 Sey_Proc(应该是 Seg_Proc),变量也写成了 Sey_Slow_Down。虽然不影响编译(只要一致就行),但为了代码规范,建议修改。
  4. 倒计时逻辑隐患:

    • 在中断中,Timer_Count-- 当计数为0时,unsigned char 会溢出变成 255,然后你马上重置为0。虽然现象上看起来是停在0,但逻辑上建议用 if(Timer_Count > 0) 来判断是否需要减。

2. 修改后的完整代码

我已经修复了上述错误,清理了特殊字符,并添加了必要的注释。

C

/*头文件声明区域*/
#include <REGX52.H>
/* 注意:Key.h 和 Seg.h 需要你自己工程里有这两个文件,否则会报错 */
#include "Key.h" 
#include "Seg.h"

/*变量声明区域*/
unsigned char Key_Slow_Down; // 按键减速专用变量 10ms
unsigned char Seg_Slow_Down; // 数码管减速专用变量 500ms (原 Sey_Slow_Down)
unsigned char Key_Val, Key_Down, Key_Old; // 按键扫描专用变量
unsigned char Seg_Pos; // 数码管扫描变量
unsigned char Seg_Buf[6] = {10, 10, 10, 10, 10, 10}; // 数码管显示数据存放数组

/* 状态变量 */
unsigned char Seg_Mode; // 0-显示模式 1-设置模式
unsigned int Timer_1000ms; // 1000ms计数
unsigned char Timer_Count = 30; // 倒计时当前值
bit System_Flag; // 0-暂停 1-开始

unsigned char Set_Dat[3] = {15, 30, 60}; // 设置可选参数
unsigned char Set_Dat_Index = 1; // 参数索引

unsigned int Timer_500ms; // 500ms闪烁标志
bit Seg_Flag; // 数码管闪烁标志位

/*按键处理函数*/
void Key_Proc()
{
    if(Key_Slow_Down) return;
    Key_Slow_Down = 1; // 进入按键减速,需要在中断中清零

    Key_Val = Key_Read(); // 读取按下的键码值
    Key_Down = Key_Val & (Key_Val ^ Key_Old); // 捕捉下降沿
    Key_Old = Key_Val; // 辅助扫描

    switch(Key_Down)
    {
        case 1: // 启动
            if(Seg_Mode == 0)
                System_Flag = 1;
            break;

        case 2: // 复位/重置
            if(Seg_Mode == 0)
            {
                System_Flag = 0; // 建议复位时先暂停
                Timer_Count = Set_Dat[Set_Dat_Index];
            }
            break; // 【修复】之前漏掉了break,会导致穿透

        case 3: // 切换模式 (显示 <-> 设置)
            if(Seg_Mode == 1) // 如果是从设置模式退回到显示模式
            {
                Timer_Count = Set_Dat[Set_Dat_Index]; // 更新倒计时时间
                System_Flag = 0; // 切换回主界面时通常暂停等待开始
            }
            Seg_Mode ^= 1; // 切换模式
            break;

        case 4: // 修改设置参数
            if(Seg_Mode == 1)
            {
                if(++Set_Dat_Index == 3)
                    Set_Dat_Index = 0;
            }
            break;
    }
}

/*数码管信息处理函数*/
void Seg_Proc() // 【修复】名称规范化
{
    if(Seg_Slow_Down) return;
    Seg_Slow_Down = 1; 

    Seg_Buf[0] = Seg_Mode + 1; // 首位显示模式:1-运行界面,2-设置界面

    if(Seg_Mode == 0) // 系统处于显示界面
    {
        Seg_Buf[4] = Timer_Count / 10 % 10;
        Seg_Buf[5] = Timer_Count % 10;
    }
    else // 系统处于设置界面
    {
        if(Seg_Flag == 1) // 闪烁效果
        {
            Seg_Buf[4] = Set_Dat[Set_Dat_Index] / 10 % 10;
            Seg_Buf[5] = Set_Dat[Set_Dat_Index] % 10;
        }
        else
        {
            Seg_Buf[4] = 10; // 10代表熄灭(取决于你的Seg.h定义)
            Seg_Buf[5] = 10;
        }
    }
}

/* LED和蜂鸣器处理函数 */
void Led_Proc()
{
    // 当倒计时归零且不在设置模式时
    if(Timer_Count == 0 && Seg_Mode == 0)
    {
        P1 = 0x00; // 所有LED全亮
        P2_3 = 0;  // 蜂鸣器/继电器开启
    }
    else
    {
        P1 = 0xFF; // LED全灭
        P2_3 = 1;  // 蜂鸣器关闭
    }
}

/*定时器0初始化函数*/
void Timer0_Init(void) // 1毫秒@12.000MHz
{
    TMOD &= 0xF0;
    TMOD |= 0x01;
    TL0 = 0x18;
    TH0 = 0xFC;
    TF0 = 0;
    TR0 = 1;
    ET0 = 1; // 【修复】删除了这里的非法隐藏字符
    EA = 1;  // 【修复】删除了这里的非法隐藏字符
}

/*中断服务函数*/
void Timer0Server() interrupt 1
{
    TL0 = 0x18;
    TH0 = 0xFC;
    
    // 时间片处理
    if(++Key_Slow_Down == 10) Key_Slow_Down = 0;
    if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0; // 修改为Seg
    
    // 数码管刷新 (每1ms刷新一位,假设是6位共阴/共阳)
    if(++Seg_Pos == 6) Seg_Pos = 0;
    Seg_Disp(Seg_Pos, Seg_Buf[Seg_Pos]); // 需要 Seg.h 支持

    // 倒计时逻辑
    if(System_Flag == 1)
    {
        if(++Timer_1000ms == 1000)
        {
            Timer_1000ms = 0;
            if(Timer_Count > 0) // 【优化】防止溢出
            {
                Timer_Count--;
            }
            else
            {
                // 倒计时结束,停止系统
                System_Flag = 0; 
            }
        }
    }

    // 闪烁标志翻转 (500ms)
    if(++Timer_500ms == 500)
    {
        Timer_500ms = 0;
        Seg_Flag ^= 1;
    }
}

/*主函数程序*/
void main()
{
    Timer0_Init();
    
    while(1)
    {
        Key_Proc();
        Seg_Proc();
        Led_Proc();
    }
}

3. 给你的建议

  1. 关于头文件: 代码中使用了 <Key.h><Seg.h>。这两个不是标准库文件,而是你自己(或你的模板)写的。请确保你的工程文件夹里有 Key.c, Key.h, Seg.c, Seg.h 并且已经添加到工程目录(Project Source Group)中,否则编译会报错 Source file not found

  2. 关于中断刷新: Seg_Disp 放在中断里是非常好的做法(保证显示不闪烁)。

  3. 调试技巧: 如果你发现LED灯乱闪或者程序跑飞,大概率是因为 Timer0_Init 里的那些看不见的字符导致配置没生效,直接复制我上面修改后的代码即可。