简介:基于单片机金属探测器设计通常从电磁感应原理出发,涉及传感器选型、信号调理电路与单片机控制多个环节,而 Proteus 仿真能够在实物焊接前验证电路和程序逻辑,有效减少重复打板成本。资源包内含完整的 Proteus 仿真工程(DSN)、C 语言源码 main.c、Hex 烧录文件、RZ-51V2.8 开发板原理图 PDF 与实际运行截图,共 17 个文件,压缩包约 173KB;同时保留了 Keil 工程文件、编译中间文件(obj、lst、m51)和 bak 备份,方便直接打开工程查看电路连接、传感器接口与代码结构。对电子类课程设计、毕业设计或单片机入门者,可借助仿真观察金属靠近时输出指示与报警的响应过程,理解灵敏度调节、滤波处理和抗干扰设计的具体实现。设计资料将硬件原理图、程序源码和仿真验证集中在一起,既适合快速学习,也可作为二次开发的参考模板。目前已有 171 人学习下载,体积小巧,便于随时获取并快速复现。
1. 基于单片机金属探测器Proteus仿真+程序到底在解决什么
拿到“基于单片机金属探测器Proteus仿真+程序”这个标题,先别急着找能吸起硬币的电磁铁。工程上的金属探测器往往在低功耗下工作,探头只是一个空心线圈,金属靠近时线圈的电感量或高频损耗发生变化,单片机通过检测这个变化来判断有没有金属。你没有硬件探头时,最难的不是写代码,而是怎么在Proteus里把“金属靠近”这件事重演出来。最常见的做法是:用NE555搭一个可调频率发生器来代替真实LC振荡器,运行时转动电位器模拟金属接近,再用51单片机的定时器测频率、做报警和显示。这个标题没有限定单片机型号,但最容易在Proteus 8 Professional里完整复现的是AT89C51,下面按这个组合展开。
2. 金属探测器检测原理与单片机接口选型
2.1 频率式与幅度式:为什么选测频率
金属探测器的前端电路可以输出两类可判读信号:一类是振荡频率变化,另一类是振荡幅度变化。频率式把探头线圈放进LC振荡回路,金属靠近时等效电感变小、频率升高;幅度式则利用金属在交变磁场中产生涡流,消耗能量,让振荡幅度回落。两种方式都能用,但51单片机做频率式最顺:定时器本身就是数字计数硬件,把正弦波整形后方波接进T0即可,不需要额外ADC芯片。
为什么不用ADC检测幅度
幅度式需要峰值检波、RC滤波和运放,在Proteus里仿真这些模拟环节很容易因时间常数设置不当而出现纹波。更麻烦的是AT89C51没有内置ADC,加ADC0832又要多画几根接线、多写一套SPI时序。频率式只关心方波的跳变沿,计数结果稳定,程序判据也干净。因此在课程设计里,绝大多数Proteus金属探测器工程都采用“LC振荡器 + LM393整形 + 单片机测频”的结构。本文后面统一按频率式处理。
2.2 从LC振荡到单片机可读的方波
LC振荡器输出的通常是正弦波,幅度只有几百毫伏到几伏,不能直接接51的I/O口。中间必须加一级比较器,常见用LM393:同相输入端接探头信号,反相输入端接VCC/2参考电压,输出端加上拉电阻到VCC。这样LM393输出就是与振荡信号同频的0~5V方波,可以直接驱动P3.4引脚。实际硬件里,比较器参考电压的高低会影响方波占空比,但单片机外部计数只认下降沿,占空比稍微偏离50%不会影响计数结果。
Proteus里能不能直接画一个LC振荡器来当探头?能,但运行后你无法在屏幕上“放一块金属”让它自动反应。Proteus的SPICE模型不会把屏幕上的金属物体耦合到线圈里。这里我一般用NE555搭一个张弛振荡器替代探头:频率公式为f=1.44/((RA+2RB)C),把RB的一部分换成电位器POT,运行时用鼠标滚轮调节电位器阻值,频率就会连续变化。这个频率变化的方向和幅度与实际金属靠近时的表现一致,正好用来验证单片机程序。
2.3 定时器分配与测频精度边界
测频需要两个定时器配合:T0作为外部计数器,接收P3.4上的脉冲;T1作为定时器,产生50ms基准闸门。每50ms读一次T0计数寄存器,20次累加后得到1秒内的总脉冲数,也就是频率值,分辨率1Hz。TMOD设置为0x15,含义是T0方式1计数、T1方式1定时,两者互不干扰。
| 参数 | 数值 | 说明 |
|---|---|---|
| 晶振 | 12MHz | T1定时基准 |
| TMOD | 0x15 | T0计数+T1定时,均方式1 |
| T1初值 | 0x3CB0 | 对应50ms中断 |
| 闸门周期 | 50ms | 中断内读T0增量 |
| 软件闸门 | 20次 | 累计1秒得到频率 |
| 频率上限 | 约1.3MHz | 50ms内T0最大65535个脉冲 |
| 报警阈值 | 1000~5000Hz | 按静态频率1%~2%选择 |
这个方案的好处是T0计数时可以完全不用中断,只在T1中断里读TH0和TL0,配合“差分计数”写法,即使T0发生溢出,用无符号减法自动回绕也能得到正确结果。需要优先保障的信号是P3.4的方波质量,NE555输出本身是推挽结构,不需要再整形。
2.4 为什么Proteus适合先验证这套链路
Proteus的优势在于模拟波形和数字逻辑在同一工程里实时可见。把虚拟示波器挂在NE555输出脚,把频率计挂在P3.4,LCD和蜂鸣器的动作随时同步。这样调试时能分清问题出在“前端没有起振”还是“后端计数程序没跑到”。另外,程序在Keil中编译生成Hex,Proteus直接加载后就能跑,不用买开发板,也不用反复烧Flash。这套验证流程对一个需要提交仿真工程或做实物前的风险评估来说,都是低成本高反馈的路径。
3. 在Proteus 8 Professional里搭建金属探测器仿真电路
3.1 元件清单与参数表
新建工程时模板选择Schematic Design,单片机选AT89C51。元件选好后按下面表逐一放置,注意NE555和POT的库名称在Proteus中可能有多个变体,用POT-LIN最稳妥。
| Proteus库名称 | 参数 | 数量 | 作用 |
|---|---|---|---|
| AT89C51 | 12MHz晶振 | 1 | 主控 |
| NE555 | - | 1 | 替代探头产生可调频率 |
| RES | 1kΩ | 2 | NE555振荡电阻 |
| POT-LIN | 10kΩ | 1 | 调节频率模拟金属靠近 |
| CAP | 1nF | 1 | 振荡电容 |
| CAP | 0.01μF | 1 | NE555控制端滤波 |
| RESPACK-8 | 上拉排阻 | 1 | P0口上拉 |
| LCD1602 | 字符型 | 1 | 显示频率与阈值 |
| BUTTON | 轻触按键 | 2 | 基准频率、灵敏度设置 |
| LED-RED | - | 1 | 报警指示 |
| BUZZER | 直流蜂鸣器 | 1 | 报警输出 |
放置LCD1602后,把数据口D0~D7接到P0.0~P0.7,控制口RS、RW、EN依次接P2.5、P2.6、P2.7。P0是开漏引脚,不接上拉排阻会白屏或乱码,这是仿真中最容易出现的问题。
3.2 NE555振荡器连接与频率范围计算
NE555按无稳态多谐振荡器接法:8脚和4脚接VCC,1脚接地,5脚经0.01μF电容接地。7脚与VCC之间接1kΩ电阻RA;7脚再串1kΩ固定电阻和10kΩ电位器,接到2脚和6脚;2脚与6脚短接,对地接1nF电容。3脚是输出端,直接接到AT89C51的P3.4。
频率范围计算:RA=1kΩ,RB=1kΩ+0~10kΩ,C=1nF。电位器阻值从最大调到最小时,频率从约62.6kHz升到约480kHz。这个范围在T0的50ms计数上限之内,又远高于50Hz工频干扰,适合观察。实际运行时,把鼠标移到POT上方,按键盘上下箭头或滚动滚轮,阻值变化后频率会立即改变。如果调了很久频率没反应,检查电位器是否串在了7脚到2/6脚的充电通路上;很多工程把POT滑动端悬空,只焊了两个固定端,导致RB恒为0。
3.2.1 用虚拟示波器确认前端工作正常
在主界面点击Virtual Instruments,选择Oscilloscope,把通道A探头接到NE555的3脚。运行仿真后应看到清晰的方波,频率稳定在几十到几百kHz。若波形频率为0,多半是NE555的5脚没有接地电容,或电位器接线错误。虚拟示波器只是验证仪器,不需要接入单片机电路,不影响信号逻辑。
3.3 单片机侧接线映射与调试
连完振荡源后,把所有外围器件接到单片机,映射关系见下表:
| 信号源 | 单片机引脚 | 说明 |
|---|---|---|
| NE555 3脚 | P3.4/T0 | 方波计数输入 |
| 按键1 | P1.0 到GND | 按下记录当前频率为基准 |
| 按键2 | P1.1 到GND | 循环调节灵敏度阈值 |
| LED | P2.0 通过1kΩ电阻到GND | 报警指示 |
| BUZZER | P2.1 到GND | 报警输出 |
| LCD RS | P2.5 | 寄存器选择 |
| LCD RW | P2.6 | 读写选择,接地只写 |
| LCD EN | P2.7 | 使能信号 |
| 上拉排阻 | P0到VCC | 必须接,否则LCD异常 |
LCD的RW可以直接接地,不用接P2.6,但为了保持代码通用性,这里仍用P2.6控制。蜂鸣器使用Proteus里的BUZZER元件,不要用SOUNDER,因为SOUNDER需要交流驱动信号,直流电平不会响。
3.4 加载Hex并运行仿真
在Keil中编译上一篇代码生成Hex文件,然后在Proteus里双击AT89C51,在Program File属性中浏览选择该Hex,Crystal Frequency改成12MHz。回到原理图点击左下角运行按钮,LCD应显示NE555当前频率。把电位器从中间位置向两端调节,频率偏离基准值超过阈值后LED亮起、蜂鸣器鸣叫。若LCD显示空白,先按暂停,双击LCD1602确认它连接到了VCC和GND,再检查P0口上拉排阻是否放置。整个过程不需要额外下载线,Proteus会在每次运行前重新加载Hex文件。
4. Keil C51程序实现:定时器测频与阈值报警
4.1 TMOD与定时器初值计算
测频的核心是把T0配成外部计数模式,T1配成定时中断。TMOD=0x15,二进制为0001 0101,其中低四位0x01设置T0为方式1计数,高四位0x10设置T1为方式1定时。T1在12MHz晶振下,一个机器周期1μs,要产生50ms中断,初值为65536-50000=15536,即0x3CB0。
每次T1中断读取T0的16位计数值,与上次读取值做差,得到一个50ms内的脉冲增量。用无符号减法做差分,即使T0溢出回绕,差值依然正确。累积20次增量后乘以20,得到1秒频率值。频率计算式不是“读两次相减”这么简单,要点在于把读值动作放在固定中断里,而不是用while循环去等电平。
4.2 完整C51代码(LCD1602显示+测频+报警)
#include <reg52.h> #include <stdio.h> sbit RS = P2^5; sbit RW = P2^6; sbit EN = P2^7; sbit LED = P2^0; sbit BEEP = P2^1; sbit KEY_F0 = P1^0; sbit KEY_BIAS = P1^1; unsigned long freq = 0; unsigned long base_freq = 0; unsigned int bias = 2000; bit alarm = 0; unsigned int last_count = 0; unsigned long total = 0; unsigned char gate = 0; void delay_ms(unsigned int ms) { unsigned int i, j; for(i = 0; i < ms; i++) for(j = 0; j < 120; j++); } void lcd_cmd(unsigned char c) { P0 = c; RS = 0; RW = 0; EN = 1; delay_ms(1); EN = 0; } void lcd_data(unsigned char c) { P0 = c; RS = 1; RW = 0; EN = 1; delay_ms(1); EN = 0; } void lcd_str(char *s) { while(*s) lcd_data(*s++); } void lcd_init(void) { delay_ms(15); lcd_cmd(0x38); /* 8位数据,双行显示,5x7点阵 */ lcd_cmd(0x0C); /* 显示开,光标关 */ lcd_cmd(0x01); /* 清屏 */ lcd_cmd(0x06); /* 光标右移,地址自动加1 */ } void timer1_isr(void) interrupt 3 { unsigned int now; TH1 = 0x3C; TL1 = 0xB0; now = (TH0 << 8) | TL0; /* 读当前T0计数值 */ total += (unsigned long)(now - last_count); last_count = now; gate++; if(gate >= 20) /* 软件闸门到1秒 */ { gate = 0; freq = total * 20UL; total = 0; last_count = 0; } } unsigned long abs_diff(unsigned long a, unsigned long b) { return (a >= b) ? (a - b) : (b - a); } void calc_detect(void) { unsigned long diff = abs_diff(freq, base_freq); if(alarm == 0) { if(diff > bias) /* 超阈值进入报警 */ { alarm = 1; LED = 0; BEEP = 0; } } else { if(diff < (bias > 300 ? bias - 300 : 0)) /* 回差300Hz */ { alarm = 0; LED = 1; BEEP = 1; } } } void display(void) { char line[17]; lcd_cmd(0x80); sprintf(line, "F:%luHz", freq); lcd_str(line); lcd_cmd(0xC0); sprintf(line, "B:%lu S:%u", base_freq, bias); lcd_str(line); } void main(void) { TMOD = 0x15; /* T1定时,T0外部计数,均方式1 */ TH0 = 0; TL0 = 0; TH1 = 0x3C; TL1 = 0xB0; TR0 = 1; TR1 = 1; ET1 = 1; EA = 1; LED = 1; BEEP = 1; P1 = 0xFF; lcd_init(); while(1) { if(KEY_F0 == 0) /* 按下按键1记录基准频率 */ { delay_ms(20); if(KEY_F0 == 0) { base_freq = freq; while(KEY_F0 == 0); } } if(KEY_BIAS == 0) /* 按下按键2切换灵敏度 */ { delay_ms(20); if(KEY_BIAS == 0) { bias += 500; if(bias > 5000) bias = 500; while(KEY_BIAS == 0); } } calc_detect(); display(); } }这段代码可以直接在Keil中新建工程编译,Target Device选择Atmel AT89C51,Options for Target里的晶振频率填12MHz,并勾选Create HEX File。P1口在上电后默认是高电平,按键接低电平触发,按下时引脚被拉低。base_freq初始为0,因此上电后第一次看到报警属于正常现象,需要先按一次KEY_F0记录当前静态频率。
4.3 阈值判定中的回差处理
金属探测器最怕临界状态抖动:金属靠近边缘时频率差在阈值附近反复穿越,LED一闪一闪、蜂鸣器忽响忽停。这里在报警解除条件上引入了300Hz回差。比如bias设为2000,进入报警条件是偏差超过2000Hz,解除条件是偏差低于1700Hz。这样中间留出300Hz缓冲,避免因为频率轻微波动导致状态频繁翻转。
abs_diff函数用无符号比较后相减,避免了有符号数减出负数的问题。freq和base_freq都是unsigned long,基频可能高于当前频率也可能低于当前频率,探测效果只关心差值绝对值,不关心方向。若你的探头是“金属靠近导致频率降低”类型,程序同样适用,因为detect只判断偏差大小。
5. 用虚拟频率计验证探头基准与灵敏度标定
5.1 三分钟校准流程
仿真连好后不要急着转电位器,先执行一轮校准。第一步,把电位器POT调到中间位置,LCD上的F值稳定后按下KEY_F0,此时base_freq等于当前静态频率。第二步,按下KEY_BIAS,bias从2000以500为单位递增到5000再回退到500,选择合适的灵敏度。一般建议取静态频率的1%~2%,例如静态频率约250kHz时bias取2000~5000Hz,太大会漏检,太小会误报。第三步,滚动电位器让频率偏离基频,观察报警指示灯是否在阈值处触发。
5.2 虚拟频率计和示波器双重验证
在Proteus的Virtual Instruments里放一个COUNTER TIMER,把输入端接到NE555的3脚,它会直接显示信号频率。把它的读数和LCD上F值对比,误差应小于2Hz。如果差距很大,先检查TMOD是否0x15、T1初值是否0x3CB0,再看P3.4是否被其他网络占用。虚拟示波器主要用来看波形形状,NE555输出应该是上升沿陡峭的方波;如果看到缓慢的三角波,说明电容充电回路参数不对,需要减小C值。
5.3 从NE555替换到真实探头的接口变化
调试通过后,把NE555及其外围电阻、电位器从原理图中删除,换上真实的LC谐振探头和LM393整形电路。LM393的输出端接P3.4,其他单片机程序不用改动。真实探头静态频率由线圈电感和并联电容决定,可能不在62.6kHz~480kHz范围内,因此硬件的径向尺寸改变后需要重新按一次KEY_F0标定。如果静态频率超过1.3MHz,需要把T1中断周期缩短到20ms,软件闸门次数改成50次,初值和计数器上限随之调整,测频公式不变。将NE555输出断开,把LM393整形后的方波接回P3.4,重新运行仿真即可验证真实前端逻辑。
本文还有配套的精品资源,点击获取