在Proteus中“骗过”MCU:用虚拟雨滴点亮真实逻辑 💧💡

你有没有遇到过这种情况——项目急着要验证雨滴传感器的采集代码,结果快递还在路上,板子没到手,进度卡死?或者学生做课程设计,实验室器材有限,几个人抢一个黄山派开发板?

别慌。我们完全可以 在没有一块真实硬件的情况下,把整个传感-控制闭环跑通 。

今天,我就带你用 Proteus + GD32(或STM32) 搞一场“虚拟降雨”,让仿真里的LED随着你滑动鼠标而亮灭——就像真的下雨了一样。🌧️➡️🟢

这不是简单的“点灯教程”,而是一次 从物理原理建模到ADC采样、再到状态判断与输出响应的全链路打通 。你会发现:原来仿真不仅能“看起来像”,还能“逻辑上真”。


雨滴传感器的本质是什么?别被名字唬住了 🤔

先来拆个谜题:“黄山派雨滴传感器”听起来很玄乎,但它干的事其实特别朴素。

它本质上就是一个 湿敏电阻 + 信号调理电路 。当水落在那块裸露的PCB叉指电极上时,水分子把两个原本绝缘的金属条连了起来,相当于并联了一个可变电阻。雨水越多,导电越好,阻值越低。

这个变化的电阻,通常会被接成一个 分压电路 :

VCC (3.3V)
   |
  [R_fixed] 上拉电阻(比如10kΩ)
   |
   +-----> AO 输出 → 连接到MCU的ADC引脚
   |
  [R_wet] 湿敏部分(受雨水影响)
   |
  GND

所以,当没雨时, R_wet 很大(可能几MΩ),AO接近GND(0V);
当下小雨时, R_wet 下降到几十kΩ,AO电压上升;
暴雨时, R_wet 可能只有几kΩ,AO接近VCC。

看到没?这不就是个 随环境变化的电位器 吗?

所以在Proteus里,我们根本不需要什么“黄山派原厂模型”——只要能模拟出这样一个 可调电压输出 ,就能完美替代!


如何在Proteus里造一场“人工雨”?🌧️⚡

打开Proteus ISIS,你会发现库里面压根搜不到“黄山派”。很正常,这种国产教学模块基本不会进官方元件库。但我们有三种方式“无中生有”。

方式一:手动电压源 —— 快速静态测试首选

最简单粗暴的方法:直接拖一个 DC_VOLTAGE_SOURCE ,设成0~5V之间某个值,接到MCU的PA0。

比如:
- 设为0.5V → 相当于晴天干燥状态
- 设为2.8V → 表示中到大雨

优点是 配置极简 ,适合调试阈值判断逻辑。缺点也很明显:不能动态变,每次想换“雨量”都得暂停仿真、改参数、重启——太麻烦。

小技巧:你可以多放几个不同电压的source,用开关切换,模拟“突降暴雨”的场景。

方式二:电位器分压电路 —— 教学演示神器 🎯

这才是我最推荐的方式,尤其适合课堂讲解或视频录制。

构建如下电路:

+5V
 |
[10KΩ 固定电阻]
 |
 +-----> 连接到 MCU 的 PA0 (ADC输入)
 |
[POT-HG 可调电阻 10KΩ] ← 在Proteus里叫这个名字
 |
GND

在运行仿真时,点击这个电位器,会出现一个滑块条!你可以用鼠标实时调节它的阻值比例,从而改变中间节点的电压。

想象一下这个画面:你在讲课,一边说“现在开始下雨了”,一边慢慢向右拖动滑块,台下的学生看着屏幕上的LED从灭变亮——那种“哇”的瞬间就来了。😎

而且这种方式更贴近实际硬件行为,连非线性特性都能体现出来(毕竟真实传感器也不是完全线性的)。

方式三:PWM+RC滤波 —— 动态模拟高手玩法 🔁

如果你想要更高级的效果,比如模拟“雨势渐强→骤停→再下”的过程,可以用另一个单片机输出PWM波,经过RC低通滤波后生成类模拟电压。

例如:
- PWM占空比0% → 输出0V(晴天)
- 占空比50% → 输出约1.65V(小雨)
- 占空比90% → 输出约4.5V(暴雨)

然后把这个电压送给主控MCU的ADC口。

虽然多了一个MCU,但好处是可以编程控制“降雨曲线”,甚至加入随机扰动模拟噪声,测试系统的鲁棒性。

不过对于初学者来说,这有点杀鸡用牛刀了。先掌握前两种就够了。


ADC采集不是“读个数”那么简单 ⚖️

很多人以为ADC就是 read_adc() 返回一个数字完事。但如果你不去理解背后的机制,很容易踩坑。

我们以GD32F303为例(和STM32几乎一模一样),来看看关键点。

分辨率和参考电压决定精度

GD32的ADC一般是12位,意味着它可以将0~Vref之间的电压分成4096份。

假设供电是3.3V,那么每一份就是:

$$
\frac{3.3}{4096} \approx 0.805\text{mV}
$$

也就是说,理论上你能分辨出0.8毫伏的变化。听起来很牛?但前提是:
- Vref稳定
- 前端信号干净
- 采样时间足够长

否则,读出来的数据跳来跳去,根本没法用。

实际接线要注意这些细节 🔍

我在仿真中经常看到学生犯这几个错误:
- 把PA0接成了GPIO推挽输出(应该设为AIN模拟输入)
- 忘记开启ADC时钟(RCU enable)
- 使用了错误的通道编号(ADC_CHANNEL_0 ≠ PA0?其实是对的,但得确认映射关系)

还有电源去耦问题:虽然仿真里看不出噪声,但在真实世界中,一定要在VDDA和VSSA之间加一个0.1μF陶瓷电容,否则ADC结果会飘。

💡 经验分享:如果你发现ADC值波动大,优先检查三点:接地是否良好、电源是否干净、采样时间是否够长。


写代码之前,先想清楚你要判断什么 🧠

回到最初的问题:我们为什么要检测雨滴?

是为了关窗?晾衣架收回?还是温室大棚遮雨?

不同的应用场景,决定了你的 判断策略完全不同 。

简单阈值法:够用但容易误判

最常见的做法就是设个固定阈值:

#define RAIN_THRESHOLD 2048  // 对应约1.65V

然后:

if (adc_value > RAIN_THRESHOLD) {
    gpio_bit_set(GPIOB, GPIO_PIN_0);  // 开灯
} else {
    gpio_bit_reset(GPIOB, GPIO_PIN_0); // 关灯
}

这当然可以工作。但现实中的问题是:
- 刚下完雨,板子还没干,电阻恢复慢;
- 空气湿度高,露水凝结导致误触发;
- 电压轻微波动就来回闪灯……

这就引出了一个重要概念: 软件滤波 。

加一层滤波,系统立马稳如老狗 🐶

最简单的改进是加个“迟滞比较器”思维:

#define RAIN_START    2200  // 大于才认为开始下雨
#define RAIN_STOP     2000  // 小于才认为雨停

这样就不会因为一点点波动反复开关。

还可以做移动平均:

#define SAMPLES 5
uint16_t buffer[SAMPLES];
int index = 0;

uint16_t get_filtered_adc() {
    uint32_t sum = 0;
    buffer[index++] = read_rain_sensor();
    if (index >= SAMPLES) index = 0;

    for (int i = 0; i < SAMPLES; i++) {
        sum += buffer[i];
    }
    return sum / SAMPLES;
}

或者更进一步,用中值滤波抗突发干扰。

🛠️ 实战建议:在仿真阶段就把滤波逻辑写进去!不要等到实物才发现“怎么老是乱报警”。


完整工程结构长什么样?📦

下面是我常用的Proteus仿真模板结构:

Project/
│
├── rain_sensor_sim.pdsprj      ← Proteus主文件
├── Source/
│   └── main.c                  ← C源码
├── Output/
│   └── firmware.hex            ← 编译生成的HEX文件
└── Docs/
    └── schematic.pdf           ← 电路图打印版

在Keil MDK中编译好代码后,右键MCU元件 → Edit Properties → Program File,选择 .hex 文件加载进去。

推荐使用的MCU模型:
- GD32F303CCT6
- STM32F103C8T6

两者在Proteus中支持良好,外设齐全,且都有VSM模型可供仿真。


让它不只是“亮灯”:扩展更多功能 💡🔊

你以为只能点个LED?Too young.

接个蜂鸣器,搞个声光报警 🚨

很简单,在PB1上接个有源蜂鸣器(通过NPN三极管驱动更好),然后:

if (adc_value > RAIN_THRESHOLD) {
    gpio_bit_set(GPIOB, GPIO_PIN_0);  // LED亮
    gpio_bit_set(GPIOB, GPIO_PIN_1);  // 蜂鸣器响
} else {
    gpio_bit_reset(GPIOB, GPIO_PIN_0);
    gpio_bit_reset(GPIOB, GPIO_PIN_1);
}

瞬间就有了“气象站”的感觉。

数字输出DO也能用:中断唤醒模式 ⚡

别忘了,黄山派模块还有一个DO引脚,可以直接输出高低电平。

在Proteus里你可以额外加一个 COMPARATOR ,比如LM393,把AO输入和一个参考电压比较,生成数字信号。

然后把这个DO接到MCU的外部中断引脚(比如PA4),设置上升沿触发:

// 初始化EXTI
exti_init(EXTI_4, EXTI_INTERRUPT, EXTI_TRIG_RISING);
nvic_irq_enable(EXTI4_IRQn, 0, 0);

这样主循环可以休眠,靠中断唤醒,省电又高效。

✅ 特别适合电池供电的户外监测设备。


学生党福利:这是我给学生的标准作业模板 📄

每年带课设,我都发这么一套资料包:

✅ Proteus电路图
- 包含GD32最小系统(晶振、复位、BOOT)
- 电位器模拟雨滴传感器
- LED、蜂鸣器、串口调试接口预留

✅ Keil工程模板
- 已配置好启动文件、链接脚本、头文件路径
- 提供 delay.h 、 adc.h 、 gpio.h 基础封装

✅ 任务清单
1. 实现ADC轮询采集
2. 添加移动平均滤波
3. 设置合理阈值控制LED
4. (加分项)通过串口打印当前ADC值
5. (挑战项)实现雨强分级(小雨/中雨/大雨)

效果立竿见影:以前学生花三天搭环境调硬件,现在第一天就能跑通逻辑,剩下两天优化算法。


为什么我说这是“国产MCU教育的好帮手”?🇨🇳

“黄山派”这类基于GD32的开源平台,最大的优势是什么?

不是性能多强,也不是价格多低,而是 生态友好 + 教学适配度高 。

  • 引脚兼容STM32,学习成本低
  • 社区教程丰富,百度能搜到答案
  • 配合Proteus仿真,实现“零硬件启动”

更重要的是,它让我们有机会在教学中强调一点:

技术的本质不是依赖特定工具,而是解决问题的能力。

你没有黄山派?没关系,用STM32也行。
你没有雨滴传感器?没关系,用电位器代替。
你没有示波器?没关系,用串口打印观察波形。

这才是工程师该有的思维方式。


最后一个小彩蛋:如何假装自己有个“智能雨量计”?🎉

想不想让你的仿真看起来更专业一点?

试试这样做:

  1. 在Proteus里添加一个 VIRTUAL TERMINAL (虚拟终端)
  2. 通过USART1连接到MCU的TX引脚
  3. 在代码里加上串口初始化,并定期发送ADC值:
printf("Rain ADC: %d (%.2fV)\r\n", adc_value, adc_value * 3.3 / 4096);
  1. 启动仿真,打开虚拟终端窗口

你会看到一行行数据刷出来:

Rain ADC: 1024 (0.82V)
Rain ADC: 1031 (0.83V)
Rain ADC: 2056 (1.65V) ← 开始下雨!
Rain ADC: 3120 (2.50V)

配上旁边慢慢变亮的LED,谁看了不说一句:“这系统真灵敏!” 😎


结束语?

不,这只是开始。

当你能在仿真中搞定雨滴检测,下一步就可以尝试:
- 温湿度传感器(DHT11/SHT30)仿真
- 光照强度(BH1750)I2C通信模拟
- OLED显示驱动
- 甚至WiFi模块(ESP8266)串口透传

最终拼成一个完整的“智慧农业监测站”仿真系统。

而这一切,只需要一台电脑 + 一份好奇心。

所以,还等什么?赶紧打开Proteus,拉个电位器,给自己下场雨吧。☔💻

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐