共计 2026 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点:为什么需要优化检测方式?
刚开始玩 51 单片机做目标检测时,最直接的方法就是在 main 函数里写个死循环,不断读取传感器状态。但实际用起来会发现两个致命问题:

- 实时性差:如果循环里还有其他任务(比如显示刷新),检测响应可能延迟几十毫秒
- 资源浪费:CPU 总是在满负荷运行,哪怕没有检测到任何变化
我最早做的红外计数器就遇到过这种情况——人快速通过时居然会漏计数,后来用示波器抓波形才发现检测响应延迟了 15ms。
硬件方案选择:中断 or 定时器?
外部中断方案
void INT0_ISR() interrupt 0 {if(++count > 100) count = 0;
}
优点:
– 响应速度极快(微秒级)
– 不占用 CPU 时间
缺点:
– 需要传感器支持硬件中断
– 容易受噪声干扰(必须加硬件滤波)
定时器扫描方案(推荐)
void Timer0_ISR() interrupt 1 {
static uint8_t state;
TH0 = 0xFC; // 1ms@11.0592MHz
switch(state) {
case 0: /* 检测逻辑 */ break;
case 1: /* 消抖处理 */ break;
}
}
优势对比:
– 检测周期稳定可控(我通常设为 1 -5ms)
– 可软件滤波消除抖动
– 兼容各类数字 / 模拟传感器
状态机设计:让检测更可靠
以红外避障模块为例,典型的状态转换流程:
stateDiagram
[*] --> IDLE
IDLE --> DETECTED: 输入变低
DETECTED --> CONFIRM: 持续 10ms 低电平
CONFIRM --> TRIGGER: 达到阈值
TRIGGER --> IDLE: 复位条件
实际代码实现时要注意三点:
- 状态变量用
static修饰防止被意外修改 - 迁移条件要包含超时处理(防止卡死)
- 关键状态变化点可以加 LED 指示
完整代码示例(Keil C51)
定时器配置
void Timer0_Init() {
TMOD &= 0xF0; // 清除 T0 配置位
TMOD |= 0x01; // 16 位定时模式
TH0 = 0xFC; // 1ms 定时初值
TL0 = 0x18;
ET0 = 1; // 使能中断
TR0 = 1; // 启动定时器
}
消抖算法核心
#define DEBOUNCE_TH 3 // 连续 3 次确认才触发
uint8_t Debounce_Check(uint8_t current) {
static uint8_t history;
history = (history << 1) | (current & 0x01);
return (history == 0x00) ? 0 :
(history == 0xFF) ? 1 : 2; // 2 表示状态未稳定
}
状态机实现
void Detection_FSM() {static enum { IDLE, CHECK, ACTION} state = IDLE;
static uint8_t stable_cnt;
switch(state) {
case IDLE:
if(P3_4 == 0) { // 检测到低电平
state = CHECK;
stable_cnt = 0;
}
break;
case CHECK:
if(Debounce_Check(P3_4) == 0) {if(++stable_cnt >= DEBOUNCE_TH) {
state = ACTION;
Beep_Alert(); // 触发动作}
} else {state = IDLE;}
break;
case ACTION:
if(P3_4 == 1) state = IDLE; // 等待释放
break;
}
}
避坑经验分享
电源噪声处理
遇到误触发时,可以尝试以下方法:
- 在传感器 VCC 引脚加 0.1μF 陶瓷电容
- 信号线串联 100Ω 电阻
- 软件上增加采样间隔(比如隔次检测)
多任务优先级
如果系统还有显示、通信等功能,建议:
- 检测中断设为高优先级
- 耗时操作分步执行
- 关键状态变量加
volatile修饰
volatile uint8_t g_detected_flag = 0; // 跨中断使用的标志
性能验证方法
逻辑分析仪抓波形
连接步骤:
- 通道 1 接传感器输出
- 通道 2 接 MCU 响应引脚
- 设置触发模式为下降沿
正常波形应显示响应延迟 <2ms(1ms 定时情况下)
CPU 占用率测试
在 main 循环添加计数器:
while(1) {idle_counter++; // 用示波器测该引脚方波周期}
对比不同检测间隔下的空占比:
| 间隔(ms) | 占用率(%) |
|---|---|
| 1 | 8.2 |
| 5 | 1.5 |
| 10 | 0.7 |
进阶思考方向
-
移植到 STM8:主要修改定时器配置部分,STM8 的 TIM1 配置示例:
TIM1_PSCRH = 0; // 预分频 16MHz/1 TIM1_ARRH = 0x3E; // 1ms 重装载值 TIM1_IER = TIM1_IER_UIE; // 使能中断 -
ADC 检测优化:对于模拟量传感器(如光敏电阻):
- 采用滑动窗口滤波
- 设置动态阈值(当前值的±10%)
-
开启 ADC 连续转换模式
-
低功耗改进:
- 检测间隔动态调整(有变化时加快检测)
- 空闲时进入掉电模式
这套方案在我参加的智能车竞赛中实际测试,在 72 小时连续运行中误报次数 <3 次,CPU 平均占用率不到 5%。建议初学者先用面包板搭建测试电路,逐步调整检测参数,一定会收获稳定的检测效果!
正文完
