第十三届省赛 - Led_Proc 刷题总结
一、题目需求拆解
Led_Proc 承载了 3个LED指示 + 继电器控制 共4个子功能:
| 功能 | 触发条件 | 行为 |
|---|---|---|
| L2 工作模式指示 | Work_Mode == 0(温控模式) |
常亮;否则灭 |
| 继电器(温控) | 温控模式下,温度 >= 参数 | 开启继电器 |
| L1 整点指示 | 分==0 且 秒==0 | 亮 5 秒后熄灭 |
| 继电器(整点) | 定时模式(Work_Mode==1)下整点 |
开启 5 秒 |
| L3 继电器状态 | Relay_Enable == 1 |
100ms 翻转闪烁;继电器关则灭 |
二、实现思路逐段分析
2.1 L2 + 温控继电器(互斥两模式)
if(Work_Mode == 0) // 温度控制模式
{
ucLed[1] = 1; // L2 常亮
if(Temperature_10x/10 >= Temperature_Parameter)
Relay_Enable = 1; // 温度 >= 参数 → 开继电器
else
Relay_Enable = 0;
}else // 定时控制模式
{
ucLed[1] = 0; // L2 灭
// 注意:这里不操作 Relay_Enable
// 定时模式下继电器由下方整点逻辑控制
}
要点:
Temperature_10x是温度放大10倍的值(如 25.6°C → 256),除以10得到整数部分与参数比较- 温控模式下继电器由温度实时决定开关
- 定时模式下此处不干预
Relay_Enable,留给整点逻辑处理
2.2 整点检测(触发 5 秒计时)
if((ucRtc[1] == 0) && (ucRtc[2] == 0)) // 分==0 且 秒==0
{
Time5000_Flag = 1; // 置标志,启动5s倒计时
}
要点:
- 仅做置位,不做清零 → 标志一旦置1,由后续超时逻辑自行清除
Led_Proc每1ms执行一次,整点那1秒内会反复进入,但Time5000_Flag已经是1,重复赋值无副作用Timer5000在中断里自增(见 ISR),这里只负责启停
2.3 整点 5 秒动作(L1 + 定时继电器)
if(Time5000_Flag == 1)
{
if(Work_Mode == 1) Relay_Enable = 1; // 定时模式 → 开继电器
ucLed[0] = 1; // L1 亮
if(Timer5000 == 5000) // 5000ms = 5秒到
{
ucLed[0] = 0; // L1 灭
Time5000_Flag = 0; // 清标志
Timer5000 = 0; // 清计数
Relay_Enable = 0; // 关继电器
}
}
要点:
Timer5000由 1ms 中断累加,到 5000 即 5 秒- 超时后一次性清理所有状态:标志、计数器、继电器、LED
- 温控模式下也会亮 L1(
ucLed[0]=1不受Work_Mode限制),但继电器仅在定时模式下由此开启
2.4 L3 继电器闪烁指示
if(Relay_Enable)
{
if(Timer100 == 100) // 100ms 到
{
Timer100 = 0; // 清计数
ucLed[2] ^= 1; // L3 翻转
}
}else ucLed[2] = 0; // 继电器关 → L3 立刻灭
要点:
Timer100同样由 1ms 中断累加,到 100 即 100ms^= 1异或翻转实现闪烁,周期 = 100ms×2 = 200ms- 继电器关闭瞬间 L3 立刻熄灭(不等下一个周期)
三、配合的中断代码
void Timer1_Isr(void) interrupt 3
{
// ... 数码管扫描 ...
if(Time5000_Flag == 1) Timer5000++; // 整点5秒计时
if(Relay_Enable) Timer100++; // 闪烁100ms计时
Relay(Relay_Enable); // 继电器实际输出
Led_Disp(ucLed); // LED实际输出
}
关键设计:
- 计时器(
Timer5000/Timer100)在中断中累加,保证精度 - 比较判断在主循环 Led_Proc 中执行,避免中断处理过长
Relay()和Led_Disp()在中断中调用,实现1ms级刷新
四、编程模式总结
4.1 标志位 + 计时器模式
整点检测 → 置 Flag=1 → 中断累加计时器 → 主循环判超时 → 清 Flag + 清计时器
这是蓝桥杯中延时动作的标准范式,适用于:
- 整点动作(本题)
- 按键长按计时
- 报警持续时间
4.2 异或翻转闪烁
ucLed[x] ^= 1; // 每个周期翻转一次
比用计数器判奇偶更简洁。注意闪烁实际周期 = 定时周期 × 2。
4.3 分层架构
| 层级 | 职责 | 执行环境 |
|---|---|---|
| 决策层 | Led_Proc 设置 ucLed[]、Relay_Enable |
主循环(1ms调度) |
| 驱动层 | Led_Disp()、Relay() 操作硬件 |
定时器中断(1ms) |
| 计时层 | Timer5000++、Timer100++ |
定时器中断(1ms) |
决策和驱动分离,主循环只写数组/标志,中断只读数组/标志并输出硬件,避免竞态。
五、易错点
- 整点重复触发:
Led_Proc每1ms执行一次,整秒内会反复检测到分==0 && 秒==0。靠Time5000_Flag的"已置位则无副作用"来规避,但Timer5000在中断中持续累加,不会被重置(因为清零只在5秒到时)。 - 两个模式对继电器的冲突:温控模式下
Relay_Enable由温度实时控制,但整点超时清零时会强制Relay_Enable = 0。如果此时温度仍高于参数,下一轮Led_Proc会立刻再次置1 → 实际表现为继电器闪断一瞬。 - Timer100 不清零:当
Relay_Enable从0变1时,Timer100可能残留上次的值,导致首次闪烁周期不准。建议在Relay_Enable置1时同时清零Timer100。