简介:这份Protues仿真实例围绕51单片机电流检测设计,适合自动化、电子类学生及初学者用来理解电流采样与单片机控制结合的完整流程。资源内含完整的DSN仿真电路、KEIL工程及C51源程序,并附带编译后的HEX文件,可直接加载到Protues中运行观察。包体共12个文件,包含Uv2工程文件、C源程序、DSN仿真图、LST/OBJ中间文件及HEX烧录文件等,压缩包仅30KB,轻量便捷。目前已有578人学习使用。实例展示了电流传感器输出信号经A/D转换后由51单片机读取、计算并实现阈值报警的方法,通过调整输入可观察电流变化对系统输出的影响,从而快速掌握Protues与KEIL联调的要点,为课程设计或实际项目打下基础。
1. 为什么我建议大家先用 Proteus 把电流检测跑通
做 51 单片机项目时,电流检测看起来简单,真正上手却容易翻车。直流电机堵转瞬间电流能冲到额定值的好几倍,锂电池放电回路过流轻则保护板误动作,重则烧采样电阻。用万用表去量是一回事,在电路里做实时监测是另一回事。这个 Protues 仿真实例(8051)压缩包我解压后看过,里面有完整的 .DSN 原理图、1.c 源码、Keil 工程和编译好的 11.hex,属于拿到手就能直接开仿真跑的那种。对于正在做课程设计、或者想在自己板子上加过流保护的工程师来说,用这套现成工程先摸清"电流怎么变成电压、单片机怎么读、阈值怎么设",比自己从零搭要快得多。
这份资源解决的并不是"怎么点亮一个 LED"这类入门问题,而是把一条完整的信号链路摆在你面前:采样电阻或传感器把电流变成电压,A/D 转换器把模拟量变成数字量,51 单片机读回来换算成真实电流值,再根据阈值做报警或切断控制。下面我从原理选型开始,逐步拆解这个仿真工程的每个环节,并附上可以直接抄的代码和参数说明。
2. 电流检测的原理与仿真方案选型
2.1 分流电阻法:低端采样与高端采样的区别
电流检测最基础的手段是在回路里串一只低阻值采样电阻。电流流过它时会产生压降,根据欧姆定律 U = I x R,测出 U 就知道 I。这里有个容易忽略的问题:采样电阻放在负载和地之间叫"低端采样",放在电源和负载之间叫"高端采样"。低端采样共模电压低,运放好选,但会切断负载的接地回路,在某些系统里会引入地弹噪声;高端采样不会扰动负载地,但共模电压接近电源电压,对运放的共模抑制比要求高,电路成本随之上升。
Protues 仿真时考虑的重点和实物不太一样。仿真里电源模型是理想的,地弹不真实,所以很多现成仿真实例都采用低端采样,这个 .DSN 文件也不例外。我拿到电路图第一件事就是查找采样电阻的位置和阻值,确认它是接在负载下端到地之间。常见值为 0.1Ω 到 0.5Ω,阻值越大,同样电流下压降越大,分辨率越高,但功耗也会按 I²R 上升,所以实物的采样电阻需要算功率,0.1Ω/1W 的贴片合金电阻是常见选型。
2.2 没有片上 ADC 的 51 单片机该怎么读模拟量
教材里学的 8051 内核单片机,大部分型号是不带片上 ADC 的。AT89C51、AT89C52 这类经典芯片数据手册里根本没有 ADC 寄存器,所以仿真工程里必然有一个外扩的 A/D 转换环节。常见的搭配是 ADC0808/ADC0809 这类 8 位逐次逼近型 ADC,8 个模拟输入通道,三总线接口和单片机连接,转换结果通过 P1 口或 P0 口读回。
我在这个工程里看到的是 ADC0808 和 51 单片机的经典接法,读取流程大致分三步:先给地址锁存信号,选择要采样的通道;然后给启动转换信号;最后等待转换结束信号变低,把数据口的数据读回来。之所以常见,是因为这类 ADC 在 Proteus 里有现成仿真模型,逻辑时序和真实芯片一致,换到实物上也只需要改引脚接线,代码框架不用重写。如果换用 STC 系列这种内置 ADC 的增强型 51,程序会简化很多,直接读 ADC 寄存器就行,这可以作为后期优化方向。
2.3 霍尔传感器方案:为什么仿真里反而少见
工程领域常用的 ACS712 这类霍尔电流传感器,内部已经把电流信号转换成电压输出,灵敏度固定为 185mV/A 或 100mV/A 等规格。单片机和它对接时不需要额外的差分放大电路,ADC 直接采样输出脚即可。但这类器件在 Protues 标准库里的模型不常见,很多仿真环境里根本找不到 ACS712 原件,所以做仿真的同学一般不会选这个方案。
从软件架构上看,霍尔方案和外扩 ADC 方案的差别只在数据采集层。霍尔输出电压直接对应电流,不需要计算采样电阻压降;而在 ADC 采样前会经过信号调理电路。后面的数字滤波、标度和阈值判断逻辑完全一致。所以我给出的建议是:仿真阶段用分流电阻加 ADC0808 把流程跑通,真正做产品原型时,如果不想碰运放,可以把前端换成 ACS712,主程序只改一个换算系数即可。
下表列出了几种方案的适用场景,方便在做 Proteus 仿真时选择:
| 方案 | 典型器件 | 仿真模型可用性 | 输出形式 | 实物成本 |
|---|---|---|---|---|
| 低端分流采样 | 0.1Ω/0.5Ω 电阻 + 运放 | 高 | 电压,需 ADC | 低 |
| 高端分流采样 | 精密运放 + 仪表放大器 | 中 | 电压,共模干扰敏感 | 中 |
| 霍尔传感器 | ACS712 / WCS1800 | 低 | 电压,线性 | 中 |
| 专用电量计芯片 | INA219、INA226 | 中 | I2C 数字量 | 中偏高 |
3. 工程文件解析与核心代码实现
3.1 拿到压缩包后如何快速定位文件结构
解开压缩包后,你看到的文件远不止 .DSN 和 .hex,还包括一堆 Keil 编译中间产物。我做工程时有个固定流程:先按时间戳把过期文件清理掉,避免下次误打开旧文件产生困惑。下面这份文件清单可以让你很快找到关键入口:
- 5.DSN:Proteus 原理图文件,双击直接打开
- 1.c:51 单片机 C 语言源程序,用 Keil 打开或文本编辑器查看
- 11.Uv2:Keil 工程文件,需要修改程序时从它进入
- 11.hex:编译生成的十六进制固件,Proteus 加载这个文件运行
- 11.M51 / 11.Opt / 11.plg:Keil 编译信息和内存映射文件,报错排查时有用
- 1.LST / 1.OBJ:编译器生成的列表文件和目标文件,一般不直接使用
在 Protues 中加载程序的方式是这样的:先在原理图上双击 51 单片机芯片,弹出属性对话框,在 Program File 一栏选中 11.hex。这个操作在每次重新编译后都要做一确认,否则仿真加载的还是旧的 hex 文件。常见的一个坑是:Keil 编译时提示错误,但 Protues 依然运行上一次留下的旧 hex,导致程序看起来"改了没生效"。
3.2 主程序逻辑拆解:ADC 采集、数字滤波与电流换算
工程里的 1.c 是整套逻辑的核心。我阅读文件内容并结合 51 单片机的常见写法,把它的核心流程提炼如下,这是这个实例中 ADC0808 驱动的常规实现方式:
#include <reg51.h> // 51 单片机寄存器定义 #include <intrins.h> // 内部函数库,包含 _nop_() 延时 sbit ALE = P3^0; // ADC0808 地址锁存信号 sbit START = P3^1; // 启动 A/D 转换信号 sbit EOC = P3^2; // 转换结束信号,低电平表示正在转换 sbit OE = P3^3; // 输出使能信号 sbit BEEP = P3^7; // 蜂鸣器报警引脚,低电平驱动 sbit LED = P2^0; // 报警指示灯 #define ADC_DATA P1 // ADC 数据口接 P1 #define CURRENT_THRESHOLD 5.0 // 报警阈值,单位安培 #define SAMPLE_COUNT 10 // 数字滤波采样次数 unsigned char adc_value; float current_value; // 从 ADC0808 读取一次转换结果 unsigned char read_adc(unsigned char channel) { P1 = 0xFF; // 读数据前先写 1,避免总线冲突 ALE = 1; // 锁存通道地址 channel = channel & 0x07; // 只取低 3 位,映射到 IN0-7 P1 = channel; // 通道地址从数据口输出 ALE = 0; // 地址锁存完成 START = 1; // 启动转换 START = 0; _nop_(); // 等待短暂时间 while(EOC == 0); // EOC 变高表示转换完成 OE = 1; // 打开输出使能 adc_value = ADC_DATA; // 读取 8 位转换结果 OE = 0; // 关闭输出使能 return adc_value; } // 滑动平均滤波,抑制采样抖动 unsigned int filter_read(unsigned char channel) { unsigned int sum = 0; unsigned char i; for(i = 0; i < SAMPLE_COUNT; i++) { sum += read_adc(channel); } return sum / SAMPLE_COUNT; // 返回平均值 } void main(void) { while(1) { // 采样 IN0 通道,换算成实际电流值 unsigned int adc = filter_read(0); // 假设 5V 对应满量程 10A,基准电压 5V,8 位 ADC 满量程 255 current_value = (adc * 5.0 / 255.0) / 0.5; // 换算逻辑:先算采样电压,再除以传感器灵敏度 0.5V/A if(current_value > CURRENT_THRESHOLD) { BEEP = 0; // 拉低引脚,蜂鸣器报警 LED = 0; // 指示灯点亮 } else { BEEP = 1; // 关闭蜂鸣器 LED = 1; // 熄灭指示灯 } } }这段代码有四个关键参数要说明:
- 采样电阻或传感器灵敏度 0.5V/A 是一个假设值,实际使用时如果换用了不同的传感器,需要修改这一项,这也是最容易出错的点。
- ALE、START、EOC 这三根控制线的时序关系是所有 ADC0808 驱动程序的基础,如果仿真时读数不稳定,可以先检查 EOC 是否被误接到别的引脚上。
- P1 口在 ADC0808 方案中既是数据口也是通道地址口,所以读数据前先给 P1 写 0xFF,是为了防止总线冲突导致读数异常,这个细节在实物调试中很重要。
SAMPLE_COUNT设为 10,作用是滑动平均滤波。实际工程中我会根据信号的波动程度把它设在 8 到 32 之间,数值太大会导致响应变慢,单片机检测到过流时可能已经造成实际损害。
3.3 Keil 工程编译与重新生成 hex 文件
如果你想修改这个工程,比如把报警阈值从 5A 改成 2A,或者换一个通道采样,需要按下述流程操作:双击 11.Uv2 打开 Keil 工程,修改 1.c 后按 F7 编译。编译成功后会生成新的 11.hex。然后把 Protues 中的 hex 文件路径刷新一下,重新运行仿真。
编译时 M51 文件可以帮到你:如果提示"OVERFLOW"之类的问题,表示你的代码超出 51 单片机的 RAM 或 ROM 限制,优先考虑减小数组长度或把大数组定义到 code 段。用 AT89C51 时最常见的问题是代码量超过 4KB,地址空间不够,这时候可以考虑给工程切换更高容量的 AT89C52 芯片,两者的引脚是兼容的,在 Protues 里直接替换即可。
4. 阈值标定、参数修改与仿真排错
4.1 如何把 ADC 读数换算成真实电流。
ADC0808 的分辨率是 8 位,满量程 255。假设基准电压是 5V,那么每个 LSB 对应的电压为 5/255 ≈ 19.6mV。如果前端灵敏度是 0.5V/A,也就是每安培电流对应的传感器输出电压为 0.5V,那么每个 LSB 对应的电流是 39.2mA。换算公式为:实际电流 = ADC值 x 0.0196 / 0.5。这就是代码中(adc * 5.0 / 255.0) / 0.5这一行的含义。
我在实际调试中一般不会只靠理论计算,因为采样电阻有误差。常见的标定方法是:给电路施加一个已知电流,记录下来 ADC 读数,算出实际灵敏度,然后把修正后的系数写进代码。在 Protues 仿真中做这一步更简单,用一个可变电阻模拟采样电阻的压降变化,看程序的输出值是否跟随变化。
4.2 修改报警阈值和量程
如果当前仿真量程是 10A,想改成 20A,需要同时修改两个地方的参数。一个是 ADC 换算的灵敏度系数,从 0.5 改成 0.25,这样同样的 ADC 读数对应两倍的电流;另一个是CURRENT_THRESHOLD常量,改成你需要的报警点,比如 15A。这两处不匹配时,仪表上显示的电流值会和实际电流不一致,所以改量程后必须同步校准换算系数。
这些参数在 Keil 里改完后重新编译,再回到 Protues 重新加载 hex 文件的过程,我在 3.3 节已经讲过。这里再强调一点:Protues 在每次仿真启动时才会重新加载 hex 文件,如果仿真已经在运行,光在 Keil 里编译不会自动生效,需要手动停止仿真再重启。
4.3 仿真运行时的常见故障对照表
仿真环境虽然比实物稳定,还是会遇到一些典型的"看起来正常但结果不对"的情况。下面这张表是我总结的排查方向,覆盖了大多数会导致结果异常的情况:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 电流值固定不变 | ADC 通道地址没切换,一直采样同一通道 | 检查 ALE 锁存时序,确认通道地址已置位 |
| 电流值跳变很大 | 未做数字滤波或滤波次数太少 | 增大SAMPLE_COUNT,观察读数变化 |
| 无论电流多大都不报警 | 阈值设置过大或蜂鸣器引脚接反 | 查看CURRENT_THRESHOLD的值,确认 BEEP 引脚的极性 |
| 采样值始终为 0 | EOC 引脚接线错误或悬空 | 检查 EOC 是否连接在高低电平正常变化的引脚上 |
| 修改程序后无变化 | Protues 加载了旧 hex 文件 | 在仿真启动前重新选中 11.hex |
| 电压表输出异常 | 参考电压不是 5V | 明确 ADC 的基准电压再接换算系数 |
提示:在 Protues 中排查这类问题比实物方便得多,你可以直接放一个电压表探针在采样电阻两端,看电压是否跟随电流变化。这样能快速定位是前端硬件链路的问题,还是软件换算的问题。
4.4 仿真与真实硬件的关键差异
Protues 仿真的电压源是理想的,不会真的出现压降和噪声,但实际中采样电阻输出的电压信号往往带有纹波,尤其是电机等感性负载启动时,干扰会通过地线耦合进模拟信号。仿真中不存在的这些问题,实物调试时要格外注意。
常见做法是在 ADC 输入端并联一个 0.1μF 的高频去耦电容,必要时再加一级 RC 低通滤波。这个工程代码里的数字滤波解决的是随机抖动问题,但解决不了真实的工频干扰,所以实物场景下运算放大器前端要先做模拟滤波,数字滤波作为第二道防线。经验是先让仿真工程把算法框架跑通,然后把采样前端替换成实物的真实电路,分两步完成移植。
5. 把这套仿真改造成自己的过流保护模块
这个实例的真正价值不在于跑通,而在于可以把它改造成一个通用的电流监测框架。我在实际工作中做过几个改造,都是在这个基础上改出来的:一是增加超过持续时间的判定,避免短时浪涌电流误触发报警;二是增加串口输出,把电流值实时发送给上位机;三是加入闭环控制,把电流限制在设定值以内。
先从工程实践中最实用的一个改进说起:过流持续时间判定。电动机启动瞬间电流往往是正常运行时的几倍,持续时间只有几十毫秒,如果只做瞬时值超过阈值的判断,设备一启动就会误报警。改进方法是在报警触发条件里加一个连续计数:只有连续 N 次检测到超过阈值时,才真的触发保护动作。这个功能只需要修改主循环中的逻辑代码即可实现,核心思路是使用一个计数变量记录连续超限次数。
#define REQUIRE_COUNT 5 // 连续 5 次超限才报警,约 50ms int over_count = 0; // 在主循环中替换原来的简单判断 if(current_value > CURRENT_THRESHOLD) { over_count++; if(over_count >= REQUIRE_COUNT) { BEEP = 0; // 触发报警 LED = 0; } } else { over_count = 0; // 电流恢复正常,计数器清零 BEEP = 1; LED = 1; }这个改动的核心参数是REQUIRE_COUNT,它的取值和采样周期直接相关。如果主循环每次采样间隔约 10ms,REQUIRE_COUNT 设为 5 就相当于要求连续 50ms 超限才动作,能避开大部分电机启动瞬间的误报。把整个延时和滑差的关系列出来对照,可以帮助你根据自己的采样周期调整这个参数。
另一个常用的改造是把电流值通过串口发送到上位机,方便在调试中实时观察电流波形。51 单片机用 UART 输出字符串,通过 USB-TTL 转接线连接到电脑的串口终端。在代码中加上串口初始化函数,在主循环里隔一段时间调用一次串口发送函数,把计算得到的电流值格式化后发送出去。上位机可以用串口助手直接观察数据,也可以用 Python 的 pyserial 库做实时曲线绘制。
最后一个实用技巧是:当用 Protues 验证完算法后,你可以把采样前端换成 INA219 这类数字输出型芯片。这类芯片自带 12 位 ADC 和 I2C 接口,不需要外接 ADC0808,改写的只是驱动层,把读 ADC0808 的代码替换成读 I2C 寄存器的代码即可,主程序中的换算、滤波和报警逻辑完全复用。整个改造路径从外部分立 ADC 到集成数字传感器,本质是驱动层替换,我在项目里这么做过多次,能明显缩短开发周期。
本文还有配套的精品资源,点击获取