蓝桥杯第十三届国赛 —— 代码错误总结与修复记录

蓝桥杯第十三届国赛 —— 代码错误总结与修复记录

题目:第十三届蓝桥杯单片机组国赛程序设计
平台:STC15F2K60S2 (CT107D开发板)
功能模块:频率测量(P3.4计数) + 超声波测距 + 湿度ADC/DAC + PWM电机 + 继电器EEPROM计数 + 参数设置 + LED指示
排查轮次:共3轮,第一轮代码审计查错12处,第二轮4T测试查错3处,第三轮满分代码对比查错6处
参考满分代码:2份(sprintf格式化方案 + 手动数码管方案)


错误总览

序号 严重度 错误类型 位置 简述
1 致命 自增遗漏 Timer1 ISR time_1s_freq 永不自增,频率始终为0
2 致命 自增遗漏 Timer2 ISR pwm_period 永不自增,PWM失效
3 致命 赋值/比较混淆 key_proc 参数页 6处 = 写成 ==,参数调节全部失效
4 致命 switch case编号错误 key_proc + seg_proc case 0/2/3 应为 0/1/2
5 严重 PWM周期+占空比双重错误 Timer2 ISR %11(909Hz) + 占空比反转
6 严重 继电器计数无边沿检测 led_proc 只计"开"不计"关"
7 严重 EEPROM上电未恢复 main() 掉电重启后计数归零
8 严重 EEPROM写入位置不当 led_proc 在1ms高频任务中写EEPROM,P2端口冲突
9 中等 数组越界 led_proc ucLed[seg_show_mode-1] 当 mode=0 时为 ucLed[-1]
10 中等 LED页面指示缺失 led_proc 非参数页L1-L3全灭,应指示当前页
11 中等 湿度比较多乘10 led_proc humidity_val > para*10 条件永远为假
12 中等 kHz前导零 seg_proc 675Hz显示"00.6"而非"0.6"
13 中等 距离m模式小数点错位 seg_proc 150cm显示"15.0"m 应为"1.50"m
14 中等 bit类型加idata修饰 全局变量 Keil C51中bit不应有内存修饰符
15 轻微 超声波0值未过滤 get_diastance 无回波时0值污染滑动平均滤波器
16 轻微 LED闪烁计时器逻辑 Timer1 ISR 闪烁标志未翻转,计时器未归零
17 轻微 湿度显示缺百位 seg_proc 湿度=100时显示"00"
18 轻微 长按清零未同步Pre key_proc eeprom_proc检测到变化重复写入

第一层:底层驱动错误

1. 按键驱动 key.c —— 缺少串口冲突检测与P3.4状态恢复

原始代码问题:

  • 无串口冲突检测:J9开关在IAP15位置时,IAP15芯片通过P3.0发送串口数据,导致按键误触(不要检测)

  • 无P3.4状态恢复:(按键扫描后P3口状态改变,干扰T0计数器频率测量
    根因分析
    4T测试平台通过 UART串口 与你的板子通信(设置频率、读取结果)。UART的RX线就是 P30!
    而本小姐之前在 key.c 里加的这行代码:
    if (P30 == 0) return 0; // ← 这行!!!
    4T平台的串口TX → 你的板子P30(RXD),串口数据不断发送,P30 频繁出现低电平 → 每次按键扫描都被拦截 →
    S7永远检测不到 → kHz模式永远进不去!
    Hz模式(test 6)正确是因为它不需要按S7(已经在Hz模式了)。
    解决方案:删掉串口冲突检测! 4T测试时不存在IAP15干扰,这个检测反而害了你
    修复后的key.c:

unsigned char Key_Read() {
  unsigned char temp = 0;
  P44 = 1; P42 = 1; P35 = 1;     // 复位所有行线
  if (P30 == 0) return 0;         // 串口冲突检测
  P44 = 0; P42 = 1; P35 = 1;     // 驱动第一行
  if (P33 == 0) temp = 4;
  if (P32 == 0) temp = 5;
  if (P31 == 0) temp = 6;
  if (P30 == 0) temp = 7;
  P44 = 1; P42 = 1; P35 = 1;     // 复位
  P3 = P3 | 0xef;                 // 恢复P3.4频率计数输入
  return temp;
}

记忆要点:

  • 同时使用按键+串口 → 扫描前检测P3.0

  • 同时使用按键+频率测量 → 扫描后恢复P3状态 P3 = P3 | 0xef


第二层:中断与定时器错误

2. Timer1 ISR —— ++ 遗漏(致命!)

// ❌ 错误:永不自增
if (time_1s_freq == 1000) { ... }

// ✓ 正确
if (++time_1s_freq == 1000) { ... }

同类错误: Timer2 ISR 的 pwm_period 也遗漏了 ++

教训: 凡是用变量做定时计数的 if(xxx == N),必须确认有 ++ 或在其他位置自增

3. PWM 周期与占空比双重错误

// ❌ 原始代码(两处错误)
pwm_period = (++pwm_period) % 11;           // 11步×100us=1.1ms=909Hz
MOTOR(pwm_period < 10 - pwm_compare);       // 占空比反转

// ✓ 修复
pwm_period = (++pwm_period) % 10;           // 10步×100us=1ms=1KHz
MOTOR(pwm_period < pwm_compare);            // 占空比正确

条件 pwm_compare 修复前(10-N) 修复后(N) 要求
freq > 参数 8 18% ✗ 80% ✓ 80%
freq ≤ 参数 2 73% ✗ 20% ✓ 20%

注意: 满分代码2用 Motor(Pwm_Period < 10 - Pwm_Compare) 但占空比实际是反的(可能4T未严格检测)。满分代码1用 if(motor_compare<8) motor(1) 逻辑明确正确。

4. LED闪烁定时器 —— 计时器未归零、标志未翻转

// ❌ 原始(只有空case,什么都没做)
case 0:
break;

// ✓ 修复
case 0:
if (++time_100ms_freq >= 100)
{
    time_100ms_freq = 0;
    led_freq_flight ^= 1;               // 翻转闪烁标志
    time_100ms_humidity = time_100ms_diatance = 0;  // 清其他计时器
    led_humidity_flight = led_diatance_flight = 0;   // 灭其他LED
}
break;


第三层:主程序逻辑错误

5. 参数调节 —— === 混淆(6处!)

// ❌ 错误:= 是赋值,永远为真!
freq_para_kHZ_10x_ctrl = (freq_para_kHZ_10x_ctrl = 120) ? 10 : ...

// ✓ 正确:== 是比较
freq_para_kHZ_10x_ctrl = (freq_para_kHZ_10x_ctrl == 120) ? 10 : ...

错误代码总共6处(频率/湿度/距离 × 加/减),导致所有参数调节异常。

教训: 三元运算符 (condition) ? a : b 的 condition 中,用 == 不是 =

6. switch case 编号错误

// ❌ 错误:para_show_mode 循环 0→1→2,但case写了0/2/3
switch(para_show_mode) {
  case 0: ... break;
  case 2: ... break;  // 应该是 case 1
  case 3: ... break;  // 应该是 case 2
}

教训: switch 的 case 值必须与实际变量范围一致

7. 继电器计数 —— 只计"开"不计"关"

// ❌ 错误:只在 ON 时计数
if (relay_work != relay_old)
{
    if (relay_work == 1) {  // 只有开才计数
        relay_num++;
    }
    Relay(relay_work);
    relay_old = relay_work;
}

// ✓ 正确:开和关都计数(满分代码1和2均如此)
if (relay_work != relay_old)
{
    relay_num++;             // 任何状态变化都+1
    Relay(relay_work);
    relay_old = relay_work;
}

记忆规则: “开关次数” = 状态变化次数,开+1,关也+1

8. EEPROM 三大问题

8.1 上电未恢复(致命)

// ❌ 错误:main() 里完全没有读EEPROM
void main() {
    System_Init();
    // ← 掉电后 relay_num 从0开始,之前存的数据白费
    ...
}

// ✓ 正确:上电校验+恢复
void main() {
    unsigned char eeprom_temp;
    System_Init();
    EEPROM_Read(&eeprom_temp, 22, 1);     // 读校验位
    if (eeprom_temp == eeprom_lock) {      // 校验通过
        EEPROM_Read(&relay_num, 0, 1);    // 恢复计数
        relay_num_pre = relay_num;         // 同步Pre
    } else {                               // 首次上电
        relay_num = 0;
        EEPROM_Write(&eeprom_lock, 22, 1); // 写校验位
    }
    ...
}

8.2 写入放在高频任务中(P2冲突风险

// ❌ 错误:在 led_proc(1ms调度)中直接写EEPROM
// Timer2的MOTOR()会打断I2C通信,破坏P2.0(SCL)/P2.1(SDA)

// ✓ 正确:独立500ms低频任务 + 变化检测
void eeprom_proc() {
    if (relay_num != relay_num_pre) {
        relay_num_pre = relay_num;
        EEPROM_Write(&relay_num, 0, 1);
    }
}
// 调度器注册:{eeprom_proc, 500, 0}

8.3 长按清零未同步Pre变量

// ❌ 错误:清零后 eeprom_proc 检测到变化重复写入
relay_num = 0;
EEPROM_Write(&relay_num, 0, 1);

// ✓ 正确:同步Pre
relay_num = 0;
relay_num_pre = 0;    // ← 同步!
EEPROM_Write(&relay_num, 0, 1);

EEPROM四原则:

  1. 只在值变化时写(Pre变量对比)

  2. 独立低频任务(≥500ms)

  3. 上电时读出恢复

  4. 不塞进高频任务(避免P2冲突)


第三点五层:I2C与Timer2端口冲突(DAC/EEPROM不工作的根因)

19. Timer2 MOTOR() 中断破坏 I2C 总线

问题本质:

I2C总线使用 P2.0(SCL)P2.1(SDA),而 MOTOR()/Relay()/Beep() 都对 P2 做读-改-写操作:

// led.c 中的 MOTOR() 函数
temp = P2 & 0x1f;     // 读P2 → 包含 SCL/SDA 的当前电平!
temp = temp | 0xa0;   // 设138地址
P2 = temp;             // 写回P2 → 覆盖 SCL/SDA 锁存器!

破坏时序:

Da_Write 发送数据中:
  Master 释放 SDA=1 (等ACK)
  Slave 拉低 SDA=0 (ACK)
       │
       ├── ⚡ Timer2 中断!MOTOR() 读到 P2.1=0
       │   → 写回 P2.1=0 到锁存器
       │   → Master 现在主动驱动 SDA=0
       │
  Slave 释放 SDA,但 Master 锁住 SDA=0
  → 后续所有 I2C 数据全错!

修复方法:所有 I2C 操作前后关闭/恢复 Timer2 中断

void ad_da()
{
  unsigned char temp_ad;
  unsigned char da_out;

  IE2 &= ~0x04;             // 关Timer2中断
  temp_ad = Ad_Read(0x43);
  IE2 |= 0x04;              // 开Timer2中断

  // ... 计算(float运算不涉及I2C,无需保护)...

  IE2 &= ~0x04;             // 关Timer2中断
  Da_Write(da_out);
  IE2 |= 0x04;              // 开Timer2中断
}

关键规则: 凡是用到 I2C(PCF8591 ADC/DAC、AT24C02 EEPROM)的地方,都要用 IE2 &= ~0x04 / IE2 |= 0x04 包裹!
不要把 float 计算包在里面(会让电机停顿太久),只包裹实际的 I2C 函数调用。


第四层:显示与状态错误

9. LED 数组越界 ucLed[-1]

// ❌ 错误:seg_show_mode=0 时 ucLed[0-1] = ucLed[-1]
ucLed[seg_show_mode - 1] = 1;

// ✓ 正确:
ucLed[0] = (seg_show_mode == 0);
ucLed[1] = (seg_show_mode == 1);
ucLed[2] = (seg_show_mode == 2);

10. 湿度LED判断多乘10

// ❌ 错误:humidity_val范围0-100,para范围10-60,para*10最小100,永远为假
ucLed[4] = (humidity_val > humidity_para_10x * 10);

// ✓ 正确:
ucLed[4] = (humidity_val > humidity_para_10x);

11. kHz显示前导零

// ❌ 错误:675Hz显示 "F  00.6"
seg_buf[5] = freq_val / 10000;  // 0,不是空白

// ✓ 正确:零消隐
seg_buf[5] = (freq_val / 10000 == 0) ? 10 : freq_val / 10000;

12. 距离m模式小数点错位

// ❌ 错误:150cm 显示 "15.0" (15.0m,荒谬)
seg_buf[5] = diastance_val / 100 % 10;
seg_buf[6] = diastance_val / 10 % 10 + ',';  // 小数点在十位后

// ✓ 正确:150cm 显示 "1.50" (1.50m)
seg_buf[5] = diastance_val / 100 % 10 + ','; // 小数点在百位后
seg_buf[6] = diastance_val / 10 % 10;
seg_buf[7] = diastance_val % 10;

13. 超声波0值未过滤

// ❌ 错误:超时返回0会污染滑动平均滤波器
diastance_val = Moving_Average_Filter(temp_distance);

// ✓ 正确(满分代码2做法):
if (temp_distance != 0)
    diastance_val = Moving_Average_Filter(temp_distance);

14. bit 类型加 idata 修饰符

// ❌ 不规范:bit只能在位寻址区(0x20-0x2F),不能加内存修饰符
idata bit freq_show_mode = 0;

// ✓ 正确:
bit freq_show_mode = 0;

注意: 满分代码2也用了 idata bit 并通过了4T。多数Keil版本会忽略该修饰符。但为规范性建议去除。


满分代码对比——关键设计模式

1. 调度器任务分配

任务 满分1 满分2 推荐
LED/继电器 主循环 1ms 1ms
按键 10ms 10ms 10ms
数码管数据 30ms 180ms 100ms
ADC/DAC 160ms 160ms 160ms
超声波 100ms 100ms 100ms
EEPROM 主循环(变化检测) 1ms(内嵌) 500ms(独立)

2. 满分代码1的sprintf方案 vs 满分代码2的手动数码管方案

方面 sprintf方案 手动方案
代码量 少(一行格式化) 多(逐位计算)
可读性
ROM占用 大(链接stdio库)
出错风险 高(小数点位置、零消隐)
竞赛推荐 ✓(快速开发) 需要更细心

3. 长按检测标准模式

// key_proc 中:
if (key_down == 7) long_press_flag = 1;      // 按下开始计时
if (key_up == 7) {
    if (time_1s >= 1000) { /* 长按动作 */ }
    long_press_flag = 0;                      // 释放停止计时
}

// Timer1 ISR 中:
if (long_press_flag) {
    if (++time_1s >= 1000) time_1s = 1001;    // 计时(上限防溢出)
} else
    time_1s = 0;                               // 不计时时始终归零

4. PWM标准实现(1KHz 80%/20%)

// Timer2 ISR (100us中断)
void Timer2Isr(void) interrupt 12 {
    pwm_period = (++pwm_period) % 10;    // 10步 = 1ms = 1KHz
    MOTOR(pwm_period < pwm_compare);     // compare=8→80%, compare=2→20%
}

// 在频率采样处切换占空比
if (freq_val > freq_para_kHZ_10x * 100)
    pwm_compare = 8;   // 超过参数 → 80%
else
    pwm_compare = 2;   // 未超过 → 20%


复盘检查清单

编码阶段

  • 所有定时计数的 if(var == N) 确认有 ++var

  • 所有三元 (cond) ? a : b 确认 cond 中是 == 不是 =

  • switch case 编号与变量实际范围一致

  • bit 类型不加 idata/xdata/pdata 修饰符

  • 数组下标不会出现负数或越界

  • 小数点加在正确的 seg_buf 位置

  • 零消隐逻辑覆盖所有需要的数位

继电器/EEPROM相关

  • 继电器计数有边沿检测(Old标志位)

  • 开和关都计数

  • EEPROM独立低频任务(≥500ms)

  • EEPROM只在值变化时写入(Pre变量对比)

  • 上电时从EEPROM恢复数据(校验机制)

  • 长按清零时同步Pre变量

PWM相关

  • Timer2 初始化在 main() 中调用

  • %10 不是 %11(10步×100us=1KHz)

  • 占空比方向正确(compare=8 → 80%)

  • Motor_Start / pwm_compare 在正确位置更新

端口复用

  • 使用按键+串口 → 不要在Key_Read中加P3.0检测(4T平台串口通信会阻断S7按键!)

  • 使用按键+频率测量 → key.c 加 P3 状态恢复

  • 超声波滤波加 != 0 无效值过滤

I2C与Timer2冲突(最易漏检!)

  • 所有 Ad_Read / Da_Write 调用前 IE2 &= ~0x04,调用后 IE2 |= 0x04

  • 所有 EEPROM_Write / EEPROM_Read 调用前后同理

  • float计算放在 IE2 保护范围之外(避免电机停顿过久)

  • 如果DAC输出不正确,第一件事检查Timer2是否干扰了I2C

code
/* 头文件包含 */
#include <STC15F2K60S2.H>//单片机寄存器专用头文件
#include "stdio.h"//标准库函数专用头文件
#include "string.h"//字符串处理标准库专用头文件
#include "intrins.h"//标准库函数专用头文件
#include "init.h"//初始化底层驱动专用头文件
#include "seg.h"//数码管底层驱动专用头文件
#include "led.h"//Led底层驱动专用头文件
#include "key.h"//按键底层驱动专用头文件

#include "ds1302.h"//ds1302底层驱动专用头文件
#include "onewire.h"//ds18b20底层驱动专用头文件
#include "ultrasound.h"//超声波底层驱动专用头文件
#include "iic.h"//iic底层驱动专用头文件
#include "uart.h"//串口底层驱动专用头文件
#include "filtering.h"//滤波器底层驱动专用头文件

idata unsigned long int uwtick=0;//调度器计时器
idata unsigned int time_1s_freq=0;//频率测量1s
//key
idata unsigned char key_old,key_up,key_down,key_val;
idata unsigned int time_1s = 0; //jiance changan 
bit long_press_falg;//长按标志
//led
pdata unsigned char ucLed[8]={0,0,0,0,0,0,0,0};
//seg
idata unsigned char seg_pos=0;
pdata unsigned char seg_buf[8]={10,10,10,10,10,10,10,10};
//频率
idata unsigned int freq_val =0;//采集频率   --HZ

idata unsigned char seg_show_mode = 0; //频率 shi度 测距 参数
bit freq_show_mode = 0;// 0 hz 1 khz

idata unsigned char humidity_val = 0;//湿度数据
idata unsigned char diastance_val = 0;
bit diastance_show_mode = 0; //0 cm  m1

idata unsigned char para_show_mode = 0;
idata unsigned char freq_para_kHZ_10x = 90;  //10-120 5
idata unsigned char humidity_para_10x = 40;//10-60 10
idata unsigned char diastance_para_M_10x = 6; //1-12 1
idata unsigned char freq_para_kHZ_10x_ctrl = 90;  //10-120 5
idata unsigned char humidity_para_10x_ctrl = 40;//10-60 10
idata unsigned char diastance_para_M_10x_ctrl = 6; //1-12 1

idata unsigned char pwm_period = 0 ;//调光周期 单位1 ms
idata unsigned char pwm_compare = 2;//调光比 0 == 100%

idata unsigned char time_100ms_freq = 0;
idata unsigned char time_100ms_humidity = 0;
idata unsigned char time_100ms_diatance = 0;

bit led_freq_flight = 0;
bit led_humidity_flight = 0;
bit led_diatance_flight = 0;

idata unsigned char relay_num = 0;//relay  开关次数
idata unsigned char relay_num_pre = 0;//EEPROM变化检测
idata unsigned char eeprom_lock = 13; //kaiguansuo 

bit relay_work = 0;
bit relay_old = 0;

//key
void key_proc()
{
  key_val = Key_Read();
  key_down = key_val&(key_val^key_old);
  key_up = ~key_val&(key_val^key_old);
  key_old = key_val;

 
    
   switch(seg_show_mode)
   {
    //频率
    case 0:
    if(key_down  == 4)
      seg_show_mode = 1;
    if (key_down ==7)
      freq_show_mode  ^= 1;
    break;

    //湿度
    case 1:
    if(key_down == 4)
      {
        seg_show_mode = 2;
        diastance_show_mode = 0;
      }
    if (key_down ==7)
    {
    long_press_falg = 1;
    time_1s = 0;
    }
    if(key_up == 7){
      if (time_1s >= 1000)
      {
       relay_num = 0;
       relay_num_pre = 0;
       IE2 &= ~0x04;
       EEPROM_Write(&relay_num,0,1);
       IE2 |= 0x04;
      }
      long_press_falg = 0;
    } 
    break;

    //测距
    case 2:
    if(key_down == 4)
    {
    seg_show_mode = 3;
    para_show_mode = 0;

    freq_para_kHZ_10x_ctrl = freq_para_kHZ_10x;
    diastance_para_M_10x_ctrl = diastance_para_M_10x;
    humidity_para_10x_ctrl = humidity_para_10x;
    }
    if(key_down == 6)
     {
      diastance_show_mode ^=1;
     }

    break;

    //参数
    case 3:
    if(key_down == 4)
      {
        seg_show_mode = 0;
        freq_show_mode = 0;

        freq_para_kHZ_10x = freq_para_kHZ_10x_ctrl;
        diastance_para_M_10x = diastance_para_M_10x_ctrl;
        humidity_para_10x = humidity_para_10x_ctrl;
      }
      if(key_down == 5)
      {
        para_show_mode = (++para_show_mode)%3;
      }
     
    switch(para_show_mode)
   {
    //频率
    case 0:
    if (key_down == 6)
    {
      freq_para_kHZ_10x_ctrl = (freq_para_kHZ_10x_ctrl == 120) ? 10 :
       (freq_para_kHZ_10x_ctrl + 5);
    }
    if (key_down == 7)
    {
      freq_para_kHZ_10x_ctrl = (freq_para_kHZ_10x_ctrl == 10) ? 120 :
       (freq_para_kHZ_10x_ctrl - 5);
    }
    break;

    //湿度
    case 1:
    if (key_down == 6)
    {
      humidity_para_10x_ctrl = (humidity_para_10x_ctrl == 60) ? 10 :
       (humidity_para_10x_ctrl + 10);
    }
    if (key_down == 7)
    {
      humidity_para_10x_ctrl = (humidity_para_10x_ctrl == 10) ? 60 :
       (humidity_para_10x_ctrl - 10);
    }
    break;

    //距离
    case 2:
    if (key_down == 6)
    {
      diastance_para_M_10x_ctrl = (diastance_para_M_10x_ctrl == 12) ? 1 :
       (diastance_para_M_10x_ctrl + 1);
    }
    if (key_down == 7)
    {
      diastance_para_M_10x_ctrl = (diastance_para_M_10x_ctrl == 1) ? 12 :
       (diastance_para_M_10x_ctrl - 1);
    }
    break;
   }

    break;
   }
}

//seg
void seg_proc()
{ 
   switch(seg_show_mode)
   {
    //频率
    case 0:
    seg_buf[0] = 11;
    seg_buf[1] = 10;
    seg_buf[2] = 10;
    if(freq_show_mode == 0)
    {//HZ
      seg_buf[3] = (freq_val/10000 == 0) ? 10 : freq_val/10000;

      seg_buf[4] = ((freq_val/1000%10== 0)&& seg_buf[3] == 10)? 10 :
                    freq_val/1000%10;
      seg_buf[5] = ((freq_val/100%10== 0)&& seg_buf[4] == 10)? 10 :
                    freq_val/100%10;
      seg_buf[6] = ((freq_val/10%10== 0)&& seg_buf[5] == 10)? 10 :
                    freq_val/10%10;
      seg_buf[7] = freq_val%10;
    }else
    {
      seg_buf[3] = 10;
      seg_buf[4] = 10;
      seg_buf[5] = (freq_val/10000 == 0) ? 10 : freq_val/10000;
      seg_buf[6] = freq_val/1000%10 + ',';
      seg_buf[7] = freq_val/100%10;
    }
    break;

    //湿度
    case 1:

    seg_buf[0] = 12;
    seg_buf[1] = 10;
    seg_buf[2] = 10;
    seg_buf[3] = 10;
    seg_buf[4] = 10;
    seg_buf[5] = (humidity_val/100 == 0) ? 10 : humidity_val/100;
    seg_buf[6] = humidity_val/10%10;
    seg_buf[7] = humidity_val%10;

    break;

    //测距
    case 2:
    seg_buf[0] = 13;
    seg_buf[1] = 10;
    seg_buf[2] = 10;
    seg_buf[3] = 10;
    seg_buf[4] = 10;
    if (diastance_show_mode == 0)
    {   //cm
    seg_buf[5] = (diastance_val/100%10 == 0) ? 10 
                  : diastance_val/100%10;
    seg_buf[6] = ((diastance_val/10%10 == 0) && seg_buf[5] == 10) ? 10
                  : diastance_val/10%10;
    seg_buf[7] = diastance_val%10;
    }
    else
    {  //m
    seg_buf[5] = diastance_val/100%10 +',';
    seg_buf[6] = diastance_val/10%10;
    seg_buf[7] = diastance_val%10;
    }
    break;

    //参数
    case 3:
    seg_buf[0] = 14;
    seg_buf[1] = para_show_mode + 1;
    seg_buf[2] = 10;
    seg_buf[3] = 10;
    seg_buf[4] = 10;
    switch(para_show_mode)
   {
    //频率
    case 0:
    //freq_para_kHZ_10x_ctrl
    seg_buf[5] = (freq_para_kHZ_10x_ctrl/100%10 == 0) ? 10
                  : freq_para_kHZ_10x_ctrl/100%10;
    seg_buf[6] = freq_para_kHZ_10x_ctrl/10%10 + ',';
    seg_buf[7] = freq_para_kHZ_10x_ctrl%10;
    break;

    //湿度
    case 1:
    //humidity_para_10x_ctrl
    seg_buf[5] = 10;
    seg_buf[6] = humidity_para_10x_ctrl/10%10;
    seg_buf[7] = humidity_para_10x_ctrl%10;
    break;

    //距离
    case 2:
    seg_buf[5] = 10;
    seg_buf[6] = diastance_para_M_10x_ctrl/10%10 + ',';
    seg_buf[7] = diastance_para_M_10x_ctrl%10;
    break;
   }
    break;

   }
}

//led
void led_proc()
{

  if (seg_show_mode == 3)
  {
    ucLed[0] = led_freq_flight;
    ucLed[1] = led_humidity_flight;
    ucLed[2] = led_diatance_flight;
  }
  else
  {
    ucLed[0] = (seg_show_mode == 0);
    ucLed[1] = (seg_show_mode == 1);
    ucLed[2] = (seg_show_mode == 2);
  }

  ucLed[3] = (freq_val > freq_para_kHZ_10x * 100);
  ucLed[4] = (humidity_val > humidity_para_10x);
  ucLed[5] = (diastance_val > diastance_para_M_10x * 10);


  Led_Disp(ucLed);

  relay_work = (diastance_val > diastance_para_M_10x * 10);
  if (relay_work != relay_old)
  {
    relay_num++;
    Relay(relay_work);
    relay_old = relay_work;
  }
}

//adda
void ad_da()
{
  unsigned char temp_ad;
  unsigned char da_out;
  float da_temp;

  IE2 &= ~0x04;             // 关Timer2中断,保护I2C
  temp_ad = Ad_Read(0x43);
  IE2 |= 0x04;              // 恢复Timer2中断

  humidity_val =100*temp_ad/255;

  if (humidity_val < humidity_para_10x)
    da_out = 51;
  else if(humidity_val > 80)
    da_out = 255;
  else
  {
    da_temp = 1.0 + 4.0*(float)(humidity_val - humidity_para_10x)/(float)(80 - humidity_para_10x);
    da_out = (unsigned char)(da_temp*51);
  }

  IE2 &= ~0x04;             // 关Timer2中断,保护I2C
  Da_Write(da_out);
  IE2 |= 0x04;              // 恢复Timer2中断
}

//
void get_diastance()
{
  unsigned char temp_distance;
  temp_distance = Ut_Wave_Data();
  if (temp_distance != 0)
    diastance_val = Moving_Average_Filter(temp_distance);
}

//eeprom
void eeprom_proc()
{
  if (relay_num != relay_num_pre)
  {
    relay_num_pre = relay_num;
    IE2 &= ~0x04;
    EEPROM_Write(&relay_num, 0, 1);
    IE2 |= 0x04;
  }
}

void Timer0_Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0x7F;			//定时器时钟12T模式
	TMOD &= 0xF0;			//设置定时器模式
  TMOD |= 0X05;
	TL0 = 0;				//设置定时初始值
	TH0 = 0;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
}

void Timer1_Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0xBF;			//定时器时钟12T模式
	TMOD &= 0x0F;			//设置定时器模式
	TL1 = 0x18;				//设置定时初始值
	TH1 = 0xFC;				//设置定时初始值
	TF1 = 0;				//清除TF1标志
	TR1 = 1;				//定时器1开始计时
  ET1 = 1;
  EA = 1;
}


void Timer2_Init(void)		//100微秒@12.000MHz
{
	AUXR &= 0xFB;			//定时器时钟12T模式
	T2L = 0x9C;				//设置定时初始值
	T2H = 0xFF;				//设置定时初始值
	AUXR |= 0x10;			//定时器2开始计时
  IE2 |= 0X04;
  EA = 1;
}


void Timer1Isr(void) interrupt 3
{
  uwtick++;
  seg_pos = (++seg_pos)%8;
  if (seg_buf[seg_pos] > 20){
    Seg_Disp(seg_pos,seg_buf[seg_pos] - ',',1);
  }else{
    Seg_Disp(seg_pos,seg_buf[seg_pos] ,0);
  }
  if (++time_1s_freq == 1000)
  {
    time_1s_freq = 0;
    freq_val = TH0<<8|TL0;
    TH0 = TL0 = 0;
    if(freq_val >freq_para_kHZ_10x*100)
      pwm_compare = 8;
    else
      pwm_compare = 2;
  }
  if (long_press_falg == 1){
    if (++time_1s >= 1000)
      {
        time_1s = 1001;
      }
  }
  else
    time_1s = 0;

  if (seg_show_mode == 3)
  {
    switch(para_show_mode)
    {
      case 0 :
      if (++time_100ms_freq >= 100)
      {
        time_100ms_freq = 0;
        led_freq_flight ^= 1;
        time_100ms_humidity = time_100ms_diatance = 0;
        led_humidity_flight = led_diatance_flight = 0;
      }
      break;

      case 1 :
      if (++time_100ms_humidity >= 100)
      {
        time_100ms_humidity = 0;
        led_humidity_flight ^= 1;
        time_100ms_freq = time_100ms_diatance = 0;
        led_freq_flight = led_diatance_flight = 0;
      }
      break;

      case 2 :
      if (++time_100ms_diatance >= 100)
      {
        time_100ms_diatance = 0;
        led_diatance_flight ^= 1;
        time_100ms_humidity = time_100ms_freq = 0;
        led_humidity_flight = led_freq_flight = 0;
      }
      break;
    }
  }
  else
  {

    time_100ms_humidity = time_100ms_freq = time_100ms_diatance = 0;
    led_humidity_flight = led_freq_flight = led_diatance_flight = 0;
  }
}

void Timer2Isr(void)		interrupt 12
{
  pwm_period = (++pwm_period)%10;
  MOTOR(pwm_period < pwm_compare);
}

typedef struct
{
  void(*task_func)(void);
  unsigned long int rate_t;
  unsigned long int last_t;
}task_t;


task_t schlduler_task[] =
{ 
  {led_proc,1,0},
  {key_proc,10,0},
  {seg_proc,100,0},
  {ad_da,160,0},
  {get_diastance,100,0},
  {eeprom_proc,500,0}
};

idata unsigned char task_num;

void schlduler_init()
{
  task_num = sizeof(schlduler_task)/sizeof(task_t);
}

void schlduler_run()
{
  unsigned char i;
  for (i = 0;i < task_num;++i){
    unsigned long int nowtime = uwtick;
    if (nowtime >= schlduler_task[i].rate_t + schlduler_task[i].last_t){
      schlduler_task[i].last_t = nowtime;
      schlduler_task[i].task_func ();
    }
  }
}

void main()
{
  unsigned char eeprom_temp;
  System_Init();

  //上电恢复EEPROM
  EEPROM_Read(&eeprom_temp, 22, 1);
  if (eeprom_temp == eeprom_lock)
  {
    EEPROM_Read(&relay_num, 0, 1);
    relay_num_pre = relay_num;
  }
  else
  {
    relay_num = 0;
    EEPROM_Write(&eeprom_lock, 22, 1);
  }

  schlduler_init();
  Timer0_Init();
  Timer1_Init();
  Timer2_Init();
  while (1)
  {
    schlduler_run();
  }
}