简介:本资源是一套基于51单片机的智能垃圾桶嵌入式系统完整开发方案,面向电子类专业初学者、单片机课程设计学生及物联网入门实践者,解决自动感应开盖、垃圾量检测与满溢提醒等典型智能硬件控制问题。压缩包共24个文件,含核心源码文件(.c/.h/.a51)、编译输出文件(.hex/.obj/.lst)、工程配置文件(.uvproj/.uvopt/.lnp)及构建日志(.build_log.htm),覆盖从Keil uVision环境搭建、C语言逻辑编程到烧录验证的全流程,61KB体积轻量实用。已有3744人学习下载,资源结构清晰,保留多版备份(.bak)与用户界面配置(.uvgui_*),便于对比理解工程演化;提供可直接烧录的.hex文件、带注释的delay.c与主控逻辑代码,以及启动文件STARTUP.A51和内存映射说明.m51,显著降低调试门槛,助力快速复现与二次开发。
1. 为什么选51单片机来做智能垃圾桶
1.1 这个设计的真实应用场景
先说说这个东西做出来到底能解决什么问题。宿舍里那种踩踏式垃圾桶,你拎着垃圾袋、端着一碗泡面、或者刚洗完手不想碰盖子的时候,用脚踩不方便,用手掀又嫌脏。做成感应式的之后,人走到垃圾桶正前方约30厘米内,盖子自动弹开,等个几秒再自动合上,全程不用碰一下。实验室里放焊接废料、碎纸屑的垃圾桶也同理,手上都是锡渣或者助焊剂,再去掀盖子,不仅弄脏盖子,还污染手。
这项目在国外创客社区里叫"Smart Trash Can",经典做法是被动红外传感器(PIR)加舵机。国内课程设计里最常见的方案则是51单片机加超声波模块,因为51便宜、教程多、开题报告好写、答辩时也容易讲清楚原理。两者的核心逻辑是一样的:传感器感知人的接近,主控判断后驱动舵机翻盖,再加一个延时自动关闭的功能。
所以这篇博文的定位很明确:如果你是电子信息、自动化、物联网专业的学生,正在做课程设计或者毕业设计,想用51单片机快速做一个能演示、能答辩、成本低的智能垃圾桶项目,那这篇内容基本可以照着抄。如果你是纯粹DIY爱好者,想给自己家里做一个不用手碰的垃圾桶,同样适用,只需要舍弃一些课设味道的部分。
1.2 方案选型:51、STM32、Arduino到底怎么选
先说结论:这个项目用51完全够,甚至是最优解之一。
很多人一上来就纠结要不要上STM32,因为觉得自己用32更高级。但你仔细想想,这个项目到底需要什么算力?超声波测距是一段简单的定时器计程逻辑,舵机转动是固定频率的PWM输出,整个系统连浮点运算都可以通过整数计算规避,不需要操作系统,不需要复杂的外设驱动,存储上几十行C代码不到2KB。这种需求拿STM32属于杀鸡用牛刀,而且会把大量时间花在配置时钟树、学习HAL库上,答辩时老师问一句"你的芯片选型依据是什么"反而不好答。
51的优势在于三点。第一,成本极低,STC89C52最小系统板在电商平台几块钱一片;第二,资料密度全网第一,任何你遇到的问题,基本都有人在CSDN或者论坛写过了;第三,学习曲线平缓,寄存器数量少,手写驱动代码也不复杂,这对课设答辩尤其重要——你不需要解释复杂的库函数内部逻辑,一行一行写出来的代码反而最容易讲。
Arduino虽然开发速度更快,但目标受众是创客而不是课设学生,如果老师要求交代码报告,Arduino那套函数封装会让报告很难写,而且独立芯片搭建电路还需要额外的晶振和复位电路,反而不如51的开发板方便。
1.3 功能需求拆解
把"智能垃圾桶"这个抽象概念拆开,需要的核心功能其实就四项。
第一,人体接近检测。站在垃圾桶前,传感器能识别到有人来了。这里我选用HC-SR04超声波模块做检测,因为它的有效检测距离可以通过程序调节,而且不像红外热释电那样受环境温度影响大、也不像人体红外感应模块那样有较长的恢复时间。
第二,自动开盖执行。检测到人接近后,驱动舵机把盖子打开。舵机选SG90,9克级别的小舵机就够用,扭矩1.8kg·cm,对一个塑料盖子绰绰有余。这里有个容易忽略的点——垃圾桶盖子的阻力矩,如果盖子本身太重或者转轴太涩,SG90会带不动,这个我在后面硬件部分细说。
第三,延时自动关闭。人离开后,盖子保持打开一段时间,然后自动合上。这个延时不能太短也不能太长,3到5秒是黄金区间。太短的话倒垃圾还没走到跟前盖子就关了,太长的话异味散出来,而且还费电。
第四,防夹手保护。这个属于进阶功能,但我觉得很值得加。如果人一直在垃圾桶前不走,盖子反复开合容易夹到手指,所以程序里需要一个状态机,而不是简单的一开一关逻辑。
2. 硬件系统设计与模块选型
2.1 主控与最小系统设计
主控芯片我推荐STC89C52RC,宏晶科技的那颗经典51,3.3V到5.5V宽电压供电,直接串口下载程序,不用额外买编程器。如果用普中、天祥的51开发板,板载了CH340串口芯片,一根USB线就能烧录程序,更省事。
但很多课设要求是"独立设计硬件电路",所以你要理解最小系统的三个组成部分。
晶振电路是核心之一。STC89C52的18脚(XTAL2)和19脚(XTAL1)之间接一个11.0592MHz的晶振,两端各接一个22pF到33pF的瓷片电容到地。为什么是11.0592MHz而不是12MHz?因为11.0592MHz整除9600波特率时误差为零,如果用12MHz晶振,串口通信的波特率会引入约2.1%的误差,短距离可能还能工作,但作为课设,用11.0592MHz是最安全最规范的做法。
复位电路是另一个核心。STC89C52是低电平复位,RST引脚接一个10kΩ下拉电阻到地,再通过一个10μF电解电容接到VCC。上电瞬间电容处于充电状态,等效于短路,RST被拉到高电平,芯片复位;充电完成后电容相当于开路,RST被下拉电阻拉低,芯片开始运行。还有一个按键复位的接法,把按键并联在电容两端,按下去强制RST拉高,这个方便调试用。
P0口上拉电阻是新手最容易漏掉的一个点。51的P0口内部没有上拉电阻,是真正的开漏输出,如果直接驱动LED或继电器,高电平状态是浮空的,输出不稳定。我一般接一排4.7kΩ的排阻到VCC,这样P0口就能正常输出高电平了。如果只用到P1、P2、P3口,P0口悬空也可以,但万一后续要扩展LCD1602,就必须接上拉电阻。
2.2 探测模块:HC-SR04超声波测距
HC-SR04超声波模块是这个项目的"眼睛"。它工作在5V电压下,静态电流小于2mA,测量范围2cm到400cm,精度可以到3mm,对于垃圾桶感应这种场景绰绰有余。
模块的工作原理很有意思。控制端有一个Trig引脚和一个Echo引脚,向Trig引脚发送一个10微秒的高电平脉冲,模块内部的超声波发射器就会发射一串8个周期的40kHz方波,同时Echo引脚会输出一个高电平脉冲,这个高电平持续的时间就是超声波从发射到遇到障碍物返回所用的时间。距离的计算公式是:
距离 = 高电平时间 × 声速 ÷ 2
这里除以2是因为超声波走了一个来回,单程距离才是物体到模块的直线距离。声速在常温下约340m/s,换算成cm/μs就是0.034cm/μs,也就是高电平时间如果是1000μs,距离就是17cm。
实际操作中,我一般不用浮点数计算,因为51单片机没有硬件浮点运算单元,虽然也能算但效率低、代码体积大。我通常用整数运算,距离值等于高电平时间除以58,单位是cm。这个58的来历是340m/s换算后除以2的结果,精度足够用了。
Echo引脚的高电平时间上限,决定了模块的最大测量距离。理论上400cm对应的回波时间是约23.5ms,所以在程序里如果Echo引脚高电平超过25ms还没有变低,就应该判定为超时,否则程序可能死等在那里。这个问题后面软件部分还会重点讲。
2.3 执行机构:SG90舵机与翻盖结构
SG90是一种小型模拟舵机,三根线分别是红色VCC、棕色GND、橙色信号线。它接收50Hz的PWM信号,周期20ms,脉宽0.5ms对应0度,1.5ms对应90度,2.5ms对应180度。
为什么用舵机而不是直流电机?因为垃圾桶盖子需要的是一段往复的角度运动,舵机天生就是干这个的,内置了减速齿轮和位置反馈闭环,给一个特定的PWM脉宽,舵机转轴就会稳定停在对应的角度上。换直流电机的话,你还要额外加限位开关、减速箱、正反转驱动电路,复杂度和故障率都上去了,完全没必要。
SG90的技术参数表里扭矩是1.8kg·cm,工作电压4.8V到6V。这里有个容易踩坑的地方,就是舵机的瞬时电流。SG90空载时电流大约在100mA左右,堵转瞬间电流可以达到700mA甚至更高。如果你的单片机是USB口供电,USB口的输出电流一般只有500mA,舵机一转,电压就会被拉低,单片机瞬间复位,屏幕类设备直接闪一下,整个系统不断重启。所以舵机的电源不能和单片机的逻辑电源混在一起,至少要用带独立稳压的电源,或者在舵机电源线上并联一个470μF的电解电容来吸收瞬时电流尖峰。
翻盖结构是很多人忽略的部分。我最初直接用舵机硬顶着盖子的边缘去推,结果舵机扭矩不够,盖子纹丝不动。后来改成用一根长度为5cm到8cm的金属舵机臂,一端固定在舵机转轴上,另一端用热熔胶粘在盖子内壁靠近转轴的地方,舵臂转动的弧度带动盖子翻起来,杠杆原理下,舵机的有效扭矩被放大了好几倍。另外在盖子的转轴处要保证顺滑,如果是买了现成的垃圾桶,可以在轴心滴几滴机油或者涂一点润滑脂。
2.4 辅助模块:红外避障、蜂鸣器、指示灯
如果想让项目更完整,可以在垃圾桶的内侧加一个红外避障传感器(比如TCRT5000),用来检测垃圾是否满了。TCRT5000是一个反射式红外传感器,发射管和接收管放在同一侧,近距离检测到物体时输出电平变化。放在垃圾桶内部盖沿下方大约2cm的位置,可以检测垃圾有没有堆到盖子附近。
蜂鸣器加不加纯粹看个人需求。我建议加一个,因为在调试阶段非常有用——每次超声波模块检测到有人、舵机开始动作时,蜂鸣器响一声,你不用眼睛盯着垃圾桶,凭声音就能判断传感器是否被触发了。另外答辩时蜂鸣器也是一个演示亮点,老师站在垃圾桶前,蜂鸣器"滴"一声盖子自动打开,感官效果比干巴巴的代码演示强太多了。
指示灯方面,我习惯用一个双色LED或者两个单色LED,红色表示系统待机,绿色表示盖子打开状态。这个纯粹为了视觉效果,不影响核心功能。所有辅助模块加起来,BOM成本不会超过10块钱,但答辩时的完成度会高一个档次。
2.5 引脚分配表与供电方案
拿STC89C52RC举例,我的引脚分配是这样的:
| 模块 | 引脚 | 说明 |
|---|---|---|
| HC-SR04 Trig | P2.0 | 触发信号,输出10μs高电平 |
| HC-SR04 Echo | P2.1 | 回响信号,输入高电平计时 |
| SG90舵机信号线 | P1.0 | PWM输出控制角度 |
| 蜂鸣器 | P1.1 | 低电平触发,预留 |
| 红外避障模块 | P2.2 | 满溢检测输入 |
| 红色LED | P2.3 | 系统待机指示,高电平点亮 |
| 绿色LED | P2.4 | 开盖状态指示,高电平点亮 |
| 按键(可省略) | P3.2 | 手动开盖/参数设置 |
供电方案上,最简单可靠的是用两节18650锂电池串联,经过DC-DC降压模块稳定到5V,然后分两路走。第一路直接供HC-SR04和舵机的VCC;第二路经过AMS1117-3.3稳压到3.3V,供单片机的VCC和逻辑部分。注意STC89C52的VCC范围是3.3V到5.5V,我习惯给它供5V,因为P0口可以直接驱动一些5V设备,省去电平匹配的问题。如果只用USB线供电调试,舵机一动作就是地一次"软复位",所以USB供电只能作为程序下载和逻辑调试的临时手段,不能作为最终系统电源。
3. 软件架构与核心代码实现
3.1 整体程序流程设计
程序流程我建议用"主循环轮询加状态机"的结构,而不是纯中断驱动。整体逻辑是这样的:
- 系统上电后,初始化定时器0为16位计时模式,初始化定时器1为PWM模式或延时基础,初始化各IO口方向
- 主循环里不断调用超声波测距函数,得到当前距离值
- 根据距离值和当前状态机的状态,决定执行开盖、保持、延时、关盖等动作
- 每轮循环之间加一个10ms到50ms的延时,防止超声波模块触发频率太高、回波还没回来
状态机是这个程序的核心,一共四个状态:
- STATE_IDLE:待机状态,盖子关闭,系统持续测距
- STATE_OPENING:开盖过程中,舵机从关闭角度转到打开角度
- STATE_OPEN:开盖完成,保持打开,启动一个延时计数器
- STATE_CLOSING:关盖过程中,舵机从打开角度转回关闭角度
为什么要状态机而不是简单地"感应到人→开盖→延时→关盖"?因为状态机可以处理一个非常常见的情况——人站在垃圾桶前超过5秒还没走。如果是简单的顺序执行逻辑,延时结束后盖子就会强行关闭,可能夹到手指;而状态机在延时结束时,会再次检查当前距离,如果仍然检测到人,就重新回到STATE_OPEN状态,把延时计数器清零,只要人没走就不关盖。这个细节在答辩时讲出来,老师会觉得你考虑了实际使用场景,分数通常不会低。
3.2 超声波测距模块的驱动代码
超声波测距的驱动程序是全网抄得最多的一段代码,但我要说的是其中最关键的细节。
unsigned int get_distance_cm(void) { unsigned int time_us = 0; unsigned int distance = 0; // 1. 触发信号:给Trig引脚一个10us以上的高电平 TRIG = 0; Delay10us(); TRIG = 1; Delay10us(); TRIG = 0; // 2. 等待Echo引脚变为高电平,超时保护200us // 这里要注意,不能一直死等,否则外设异常会导致程序卡死 time_us = 0; while(!ECHO && time_us < 200) { Delay10us(); time_us++; } // 3. ECHO高电平期间,用定时器计时 TR0 = 0; // 停止定时器0 TH0 = 0; // 清零 TL0 = 0; while(ECHO) { // 如果溢出,说明测量超时(超出量程),返回一个无效值 if(TF0 == 1) { TR0 = 0; return 9999; // 返回一个明显无效的距离值 } TR0 = 1; // 启动定时器0 } TR0 = 0; // 4. 计算高电平持续时间对应的距离 // 定时器0为16位模式,最大计时65535us time_us = (TH0 << 8) | TL0; // 距离(cm) = 时间(us) / 58,整数运算省去浮点 distance = time_us / 58; return distance; }这段代码里最容易出问题的是第2步和第3步的"死等"。如果模块的Echo信号线断路、或者模块损坏,Echo引脚可能一直保持低电平或者一直保持高电平,如果没有超时保护,程序就会卡在while循环里,整个系统瘫痪。我见过很多人的板子"莫名其妙跑飞了",排查半天发现是超声波驱动没做超时判断。所以第2步和第3步的溢出判断是必须的,这不是锦上添花,而是程序健壮性的底线。
另一个细节是定时器0的使用。这里我用的是模式1,即16位计时器模式,最大可以计时65535微秒。HC-SR04的量程上限是4米,对应回波时间约23.5毫秒,远超16位定时器的上限。所以在while(ECHO)循环里检测TF0溢出标志,一旦溢出就立刻返回一个无效值,而不是继续死等。
3.3 舵机PWM控制:用定时器中断还是延时函数
舵机的控制有两种常见方案,一种是靠延时函数在循环里不停翻转IO口输出PWM波形,另一种是定时器中断生成周期性的PWM。我强烈推荐用定时器中断的方式,因为主循环还需要同时执行超声波测距、按键检测、状态判断等任务,如果PWM波形由主循环的延时函数产生,CPU会被占死,其他任务就没法做了。
定时器1用于PWM生成,配置为模式2(8位自动重载),每100微秒进一次中断。周期20毫秒对应200个100微秒节拍,PWM脉宽0.5毫秒对应占空比0.5/20,即第5个节拍前输出高电平、之后输出低电平;2.5毫秒对应第25个节拍前输出高电平、之后输出低电平。所以我们在中断里维护一个计数器,当计数器小于某个比较值时输出高,大于比较值时输出低,计数器计到200后清零重新循环。
volatile unsigned char pwm_counter = 0; volatile unsigned char servo_compare = 15; // 默认1.5ms脉宽,对应90度 void Timer1_ISR(void) interrupt 3 { TH1 = 0xFF; // 100us @ 11.0592MHz,重载值按实际计算 TL1 = 0x??; // 需要根据晶振精确计算 pwm_counter++; if(pwm_counter >= 200) // 20ms周期结束 { pwm_counter = 0; } if(pwm_counter < servo_compare) { SERVO = 1; } else { SERVO = 0; } }这里servo_compare的值和实际角度的对应关系是线性变化的。0.5毫秒对应0度,2.5毫秒对应180度,那么每增加1度需要增加约1/180毫秒,也就是大约0.0111毫秒,对应100微秒的0.111个节拍。也就是说,舵机角度以约9度为最小精度步进,想精确控制到1度,就需要把定时器中断周期从100微秒缩小到约11微秒,但那样中断频率太高,CPU开销大。实际上垃圾桶只需要"打开到约120度"和"关闭到约10度"两个精确位置,9度步进完全够用。
在实际调试中,我建议先写一个单独的测试程序,让舵机在0度、90度、180度之间依次转动,确认舵机的脉宽-角度对应关系没有大偏差,再集成到主程序里。很多舵机个体之间存在差异,标称值未必准确,以实际观察为准。
3.4 状态机主循环的实现
void main(void) { unsigned char current_state = STATE_IDLE; unsigned int distance_cm = 0; unsigned int close_delay_count = 0; unsigned int retain_count = 0; // 初始化IO、定时器、中断 System_Init(); while(1) { // 周期测距,间隔10ms以上 distance_cm = get_distance_cm(); switch(current_state) { case STATE_IDLE: // 目标距离小于阈值,比如30cm,认为有人靠近 if(distance_cm < OPEN_DISTANCE_THRESHOLD) { current_state = STATE_OPENING; servo_set_angle(120); // 开盖到120度 close_delay_count = 0; GREEN_LED = 1; BEEP = 0; // 蜂鸣器鸣叫一声 } break; case STATE_OPENING: // 如果舵机转动需要时间,可以加一个延时或等待标志 current_state = STATE_OPEN; break; case STATE_OPEN: // 5秒延时 if(close_delay_count >= 50) // 每100ms计一次,50次=5秒 { // 延时结束后,检查是否还有人 if(distance_cm < OPEN_DISTANCE_THRESHOLD) { // 还有人,重置延时 close_delay_count = 0; } else { // 人离开了,开始关盖 current_state = STATE_CLOSING; servo_set_angle(10); // 关盖到10度 } } else { close_delay_count++; } break; case STATE_CLOSING: // 舵机转动完成后回到待机 current_state = STATE_IDLE; GREEN_LED = 0; break; } // 主循环总体延时约100ms,用于超声波释放间隔和状态计时 DelayMs(100); } }这段代码的框架是完整的,但有两个调试点需要注意。第一,close_delay_count每次在STATE_OPEN状态里加1,加到的上限和主循环的延时有关,如果主循环延时从100ms改成了50ms,那50次就只等于2.5秒了。我习惯把延时计算统一换算成毫秒,在主循环里每执行一次就累加实际经过的时间,这样代码可读性更好。第二,servo_set_angle函数里如果包含了等待舵机转动到位的延时,那整个主循环会被阻塞,超声波测距也会被卡住,所以实际使用中我会把舵机转动和测距分离,或者简单粗暴地接受这段阻塞时间,因为舵机转动一般在300ms以内,影响不大。
4. 调试过程中的常见坑与排查链路
4.1 超声波模块隔三差五误触发:地面反射导致的假信号
我做的第一版测试,放在桌面上调试,垃圾桶放在地上实验的时候,出现了莫名其妙的"无人开盖"。人站在五米开外,盖子自己打开了。一开始我怀疑是HC-SR04模块质量不行,换了一个新的模块,问题依旧。后来才想到,超声波模块发射的波束是有扩散角的,大约15度,不是一束笔直的激光。模块安装在垃圾桶上沿朝斜下方探测人的腿部时,有一部分声波会先打到地面再反射回来,地面到模块的距离一般就在20cm到40cm之间,恰好落在触发阈值内,所以地面上哪怕有个小纸团、一只猫走过,都可能引起误触发。
排查确认的过程很简单:把距离值通过串口打印到电脑上,人站在不同角度观察测距值的变化,发现无论有没有人靠近,距离值都会周期性出现30cm左右的"假目标"。继续拿纸板挡在模块正前方路径的地面位置,发现假目标消失,这就确认了罪魁祸首是地面反射。
解决办法有两个。一是程序过滤,连续两次或三次测距结果都小于阈值,才判定为有效触发;二是调整模块的安装角度,让超声波波束尽可能水平向前,而不是向下倾斜。我最后是程序过滤加上调安装角度同时做,误触发率降到几乎为零。
4.2 舵机一转单片机就重启:电源压降引起的复位
这个坑几乎人人都会遇到,尤其是用USB口给整个系统供电的人。现象是:用手靠近垃圾桶,超声波检测到了,蜂鸣器也响了,但盖子还没完全打开,单片机就重启了。重启之后程序重新初始化,盖子又关回去,然后再次检测到人,再次重启,形成无限循环。
排查链路是这样的。先用万用表直流档测单片机VCC引脚的对地电压,正常是5.0V。传感器触发后,舵机开始转动的瞬间,电压表读数掉到3.3V左右,然后系统重启。这说明舵机启动的瞬间把模块电源电压拖垮了。SG90堵转电流可达700mA,如果单片机VCC和舵机VCC是同一路稳压输出,瞬间的大电流会导致稳压器饱和、输出电压跌落,单片机的工作电压低于下限就自动复位了。
解决方案我试过三种,效果从低到高排列。第一,在舵机VCC和GND之间并联一个470μF电解电容,可以吸收部分瞬时电流,电压跌落幅度明显改善,但治标不治本;第二,给舵机单独用一组电池供电,用共地方式连接,彻底把舵机的瞬态电流和单片机隔离开,效果最好;第三,烧录程序后拔掉USB线,改用锂电池组供电,消除USB口输出电流受限的问题。
4.3 程序烧录失败和"冷启动"下载
STC89C52系列单片机下载程序时,有一个让新手很困惑的坑——点击下载按钮后,必须给芯片重新上电,也就是断电再通电,下载过程才开始。原因是STC单片机的ISP引导程序在上电复位后有一个短暂的等待窗口,在这个窗口期内如果检测到串口有合法的下载指令,就进入烧录模式;如果芯片已经在运行状态,你是没法通过软件让它跳转到ISP引导程序的。
我见过好几个同学折腾一下午,板子始终下载不了程序,最后发现是从来不断电、直接点下载。解决方法是:打开下载软件(STC-ISP),选择好单片机型号、串口号、波特率,点"下载/编程"按钮,等软件提示"正在检测目标单片机"后,立刻给板子断电再上电。如果你用开发板,注意查看板子上有没有电源开关,没有的话就把USB线拔掉再插上。
波特率方面,我习惯用默认的4800或9600,试过更高波特率也没问题,但要确保CH340驱动安装好,设备管理器里能识别到COM口。
4.4 超声波测距偶尔返回9999:抗干扰和稳定化处理
前面代码里我用了超时保护返回9999,但9999在实际逻辑中会导致一个bug——如果直接把9999和阈值比较,它是大于阈值的,也就是说测不到有效值时会误判为"无人靠近"。如果超声波模块正好在有人靠近时坏掉了,盖子就不会打开。
我的做法是在主循环里对连续测距结果做滑动滤波,取最近三次有效距离的中值作为最终判定值。如果返回9999就丢弃,继续等下一个有效值;如果连续5次都超时,才判定模块异常,系统进入故障保护状态,开启蜂鸣器长鸣报警。
另外要注意的是测量间隔至少要10ms以上,因为模块在发完声波后需要时间处理回波,连续快速触发会导致模块内部逻辑混乱。
4.5 手动按键失灵:按键抖动和IO口模式
如果给项目加了手动开盖按键,最常见的问题是按下去没反应,或者触发一次变成触发两次。这是两个完全不同的故障源。
没反应,先查硬件连接,按键两端是否一端接IO口另一端接GND,IO口内部是否配置了上拉。STC89C52的P1和P2口内部有上拉电阻,但P0口没有,所以按键最好接P1或P2口。如果是P3口,也要确认外接上拉或者内部上拉配置正确。
触发变成两次,这是因为按键按下和释放的瞬间,机械触点会产生几十毫秒的抖动,IO口电平在逻辑0和逻辑1之间快速跳变,程序如果检测到低电平就立刻执行动作,一次按压可能会被识别成两次。解决方法是加一个30ms到50ms的延时消抖,检测到低电平后延时20ms再确认一次,确认电平确实变低了才算有效触发。代码很简单:
if(KEY == 0) { DelayMs(20); if(KEY == 0) { // 确实按下了,执行动作 } }5. 成本估算、实物制作与升级扩展
5.1 硬件成本明细
很多人问这个项目做完要花多少钱,我把BOM清单列出来供参考。
| 器件 | 数量 | 单价参考 | 用途 |
|---|---|---|---|
| STC89C52RC最小系统板 | 1 | 8-15元 | 主控 |
| HC-SR04超声波模块 | 1 | 3-6元 | 人体接近检测 |
| SG90舵机 | 1 | 8-12元 | 驱动开盖 |
| 蜂鸣器模块 | 1 | 1-2元 | 状态提示 |
| 双色LED模块 | 1 | 1-2元 | 状态指示 |
| 红外避障模块TCRT5000 | 1 | 3-5元 | 满溢检测 |
| 18650锂电池两节加座 | 1套 | 15-20元 | 供电 |
| DC-DC降压模块(MINI-360) | 1 | 1-2元 | 稳压5V |
| 按键、电阻、电容、杜邦线 | 若干 | 5元以内 | 辅助 |
| 塑料垃圾桶 | 1 | 10-30元 | 外壳 |
总计在70到100元之间。如果学校报销材料费,这个预算很容易过审;如果自费,也完全负担得起。值得说明的是,如果实验室已经有51开发板和USB供电线,前期调试阶段的成本几乎为零,只需要买舵机和超声波模块就行。
5.2 结构安装的几个细节
硬件电路搭好之后,最花时间的是把电路装到垃圾桶上。我的经验是:
超声波模块要安装在垃圾桶正面的上沿偏内位置,开孔后把模块卡进去,发射面和接收面不能被遮挡。模块的波束方向尽量水平偏下15度以内,太高会感应不到站在旁边的人,太低会误触地面。
舵机的固定是整个结构最难的部分。垃圾桶盖子通常是圆形的,转轴在中间或一侧。我的做法是在垃圾桶的塑料沿壁上钻孔,用螺丝或扎带把舵机固定在侧壁,舵机转轴朝向盖子转轴方向。舵机臂用螺丝拧紧在转轴上,另一端用热熔胶粘在盖子边缘。调试时先把舵机甩到中位角,再把盖子盖到关闭位置,最后固定舵机臂与盖子的连接点,这样至少在待机时盖子是完全闭合的。
舵机线走线时要注意避开盖子活动的范围,否则盖子开合时会扯断线。延长线用杜邦线或者直接截一段排线焊接,注意正负极不要接反,共地线务必连到单片机的地。
还有一个实用的建议:把垃圾桶放在墙面附近使用时,超声波模块可能会检测到墙的回波。解决方法是把触发阈值调小,比如从30cm调到20cm,或者调整模块朝向,让波束更集中于垃圾桶正前方区域。
5.3 升级扩展方向:让课设变成作品
如果时间充裕,下面几个扩展方向值得考虑,成本不高但效果提升明显。
OLED显示模块。加一个0.96寸的OLED屏幕,实时显示当前距离值、系统状态和开盖次数。成本10元左右,代码量也不大,I2C接口占两个IO口就能驱动。答辩时这块屏幕一亮,整个项目的科技感立刻拉满。
语音播报模块。用一个便宜的语音播报模块(比如JQ8900),在开盖时播放"您好,垃圾桶已打开",满溢时播放"垃圾桶已满,请清理"。这个功能成本15元左右,但演示效果非常震撼,很多老师看完会直接给高分。
蓝牙手机控制。加一个HC-05蓝牙串口模块,手机通过蓝牙发送指令给单片机,实现远程开盖、调整感应距离、查看垃圾桶状态。HC-05模块15元左右,和单片机串口直连,代码是在原有基础上增加串口中断回调,不复杂。
人体红外热释电替代超声波。如果你觉得超声波波束有盲区、地面误触发难以彻底消除,可以换用HC-SR501人体红外热释电模块。它检测的是人体散发的红外热辐射,不存在地面反射问题,但缺点是只能检测运动的人体,人站着不动几秒后会自动进入"无人"状态。这个模块成本4元左右,适合对误触发率要求高的场合。
低功耗设计。如果在电池供电的场景下使用,可以在待机时让单片机进入掉电模式,用外部中断唤醒。STC89C52支持掉电模式,电流可以降到微安级,但这样需要额外设计唤醒电路,课设阶段如果老师不要求,可以不做。
5.4 结语前的最后一个建议
我的体会是,智能垃圾桶这类项目最大的价值不在于技术难度,而在于把一个"知道原理"变成"做出实物"。超声波测距原理书上一句话,你写代码时遇到超时保护、状态机、电源干扰这些实际问题,才能真正理解这句话背后的工程含义。51单片机确实老,但它把编译器、调试器、硬件接口这些底层逻辑摆在你面前,逼着你一点点啃下来,这种积累在后续学STM32、Linux驱动时会成倍地回报你。
做项目的时候,别急着焊电路,先画一张简单的状态流转图(纸上画画就行),明确"无人待机→检测到人→开盖→延时→无人关盖→回到待机"这条主链路,把边界情况(人一直不走、模块故障、电源抖动)都列出来,再动手写代码、接电路。按照这个顺序走下来,整个调试周期能缩短一大半。
最后分享一个我个人的小技巧:所有阈值参数(感应距离、延时时间、舵机关闭角度)都用宏定义写在程序开头,调试时调整参数只需要改一处、重新烧录,不用到处找魔法数字。等你有余力了,还可以把这些参数存到单片机内部EEPROM里,通过按键免下载调节,那个功能做出来,就是妥妥的"作品级"了。
本文还有配套的精品资源,点击获取