
简介这是一份基于STM32的宠物智能饲养盒Proteus仿真设计资源适合单片机初学者、电子设计竞赛备赛者以及嵌入式系统课程设计人员使用。资源围绕智能喂食与温湿度监控两大核心功能展开利用定时器完成定时投喂、LCD1602实时显示相关状态、继电器控制投食机构动作并集成温湿度采集与智能调节逻辑可帮助读者完整理解软硬件协同实现流程。压缩包共223个文件包含C语言源代码、H头文件、Proteus工程文件、Keil工程文件、编译生成的HEX固件等另有AVI格式讲解演示视频和PNG仿真效果图包体约32.88MB。目前已有184人学习资料结构清晰既可通过仿真图直观观察运行效果也可对照源码和视频逐步复现适合作为课程设计或毕业设计的参考资料。1. 为什么拿 stm32 做宠物智能饲养盒却要在 Proteus 里先跑通很多人做宠物智能饲养盒习惯先把开发板、舵机、DHT11、OLED 买齐然后照着网线教程接线。结果往往是第一步上电就把 GPIO 烧了或者 DHT11 永远读回 255最后只能拍照求助。回头复盘会发现真正的问题不在焊接而在没有先把电路和固件边界梳理清楚。标题里的“基于 stm32 设计的宠物智能饲养盒 Proteus 仿真设计包含仿真图、源代码和讲解演示视频”本质是一套交付物方案仿真图交代硬件连接源代码交代控制逻辑演示视频证明这套逻辑能跑通。它同时满足课程设计和毕业设计里最常见的“有电路、有代码、有演示”评审要求也适合在职工程师在上板之前先验证时序和外设。这篇文章从 Proteus 建模、STM32F103 最小系统、DHT11 采集到喂食状态机把完整路径走一遍。2. 基于 Proteus 的宠物饲养盒硬件选型与 STM32F103 最小系统设计2.1 芯片选型为什么是 STM32F103C8 而不是更高配Proteus 元件库中搜索 STM32 时最常见到的是 STM32F103C8 和 STM32F103R6 这类 Cortex-M3 模型。C8 有 64KB Flash、20KB SRAM跑一个包含 OLED 显示、DHT11 采集、舵机 PWM 和状态机的固件非常宽裕。宠物饲养盒的外设需求并不高一个定时器输出舵机 PWM、一个 ADC 通道读水位、一个单总线接温湿度、一组 I2C 接屏幕再加两三个按键。这些在 F103C8 上不需要做任何引脚复用冲突处理选型上的成本也最低。有一点容易被复制工程的人忽略如果从网上下载的模板是 STM32F103C6 或 R6直接拿到 C8 工程里编译启动文件startup_stm32f103xb.s和链接脚本里的 Flash 容量可能不匹配。Proteus 加载 hex 后短时间看不出问题一旦固件里的常量池超过实际 Flash 就会跑飞。换芯片型号后第一步永远是核对启动文件和 scatter 文件而不是急着改外设代码。# 晶振负载电容估算CL C1*C2/(C1C2) Cstray # 当 C1C2C 时CL 约等于 C/2 Cstray for c in (12, 15, 18, 22): cl c / 2 4 # Cstray 经验值取 4pF print(fC{c}pF - 估算CL{cl:.1f}pF)这段脚本用于快速估算 8MHz 晶振的匹配电容。典型 8MHz 晶振负载电容 CL 为 12pF减去 4pF 走线和引脚寄生电容得到单边电容约 16pF实际取 15pF 或 22pF 都能稳定起振。Proteus 仿真里晶振电容的影响被模型弱化但制板和答辩画原理图时评审会看这个计算过程不能只写“接了 22pF 就完事”。2.2 最小系统四要素晶振、复位、BOOT 和电源网络STM32F103C8 的最小系统在 Proteus 里需要画清四组电路。第一是主晶振8MHz 晶振接 PD0/PD1也就是 OSC_IN/OSC_OUT两个负载电容分别到地。很多新手把 PD0/PD1 当成普通 IO 去接 LED程序里又把这两个脚配置成 GPIO结果是仿真怎么跑都不对。第二是复位NRST 引脚接 10kΩ 上拉到 3.3V再接 100nF 电容到地可以并联一个按键做手动复位演示。第三是启动配置BOOT0 内部有下拉悬空就能从 Flash 启动量产设计建议保留外部 10kΩ 下拉电阻以保证确定性BOOT1 悬空即可。第四是供电Proteus 里用 5V 仿真源经过 AMS1117-3.3 输出 3.3V输出侧加 10μF 和 100nF 滤波电容。Proteus 的 STM32 模型把 VDD/VSS 作为隐藏电源引脚处理软件按网络名自动连接。这意味着原理图里必须存在名为3.3V和GND的电源端子。如果图里既有3.3V又有VCC或者 AMS1117 输出端直接画网络标号VCCMCU 实际供电网络可能是 5V仿真时就会出现 GPIO 电平异常、I2C 时序错乱这类“玄学”问题。排查顺序先看电源网络命名再看隐藏引脚最后才动代码。2.3 传感器与执行器的 Proteus 选型清单功能模块实物器件Proteus 中的建模方式连接要点温湿度采集DHT11DHT11 模型单总线数据线加 4.7kΩ 上拉水位检测电位器模拟 0~3.3VPOT-HG输出电压接 ADC 输入引脚喂食执行器SG90 舵机SERVO/PWM 电机模型TIM2_CH2 输出 50Hz PWM状态显示0.96 寸 OLED SSD1306SSD1306 I2C 模型PB6/PB77 位地址 0x3C报警提示有源蜂鸣器BUZZER三极管驱动或仿真直接 GPIO手动喂食/模式切换轻触按键BUTTON内部上拉输入低电平有效水位检测在 Proteus 里不建议用浮球开关模型因为浮球只有通断两态演示不出“水位逐渐下降”的过程。常见做法是用一个电位器输出连续电压接 ADC主控里设置两个阈值高于 2.5V 为正常1.2V 到 2.5V 为偏低低于 1.2V 触发缺水报警。这样仿真运行时拧电位器OLED 和蜂鸣器会跟着变化演示效果比一个开关直观得多。舵机模型在不同 Proteus 版本里命名不完全一致有的叫MOTOR-PWMSERVO有的直接叫SERVO。如果当前版本搜不到不要硬接一个直流电机模型正确做法是保留舵机模块的电气接口同时在原理图上放一个虚拟示波器接 PWM 输出脚透过占空比和频率确认控制信号正确。舵机的机械转角在仿真里本身看不出来验证重点是“占空比映射逻辑是否进入了正确分支”。3. Proteus 8 Professional 仿真电路搭建与 Keil5 hex 烧录流程3.1 芯片包安装、元件搜索与工程目录组织Proteus 8 Professional 在完整安装后自带 ARM Cortex-M3 模型库直接搜索STM32F103C8就能找到芯片。如果搜索不到先确认安装时是否勾选了 ARM 相关模型包在 Library Manager 里检查是否有ARM7、ARM-Cortex类目。老版本 Proteus 对 STM32 模型的支持不完整建议直接用 8.9 及以上版本。不要用覆盖库文件的方式去“补丁”那样会把原有元件库弄坏工程换台电脑打不开更麻烦。工程目录组织上我一般会把.pdsprj仿真文件、Keil 工程、输出 hex、以及最后导出的仿真截图放在同一个项目根目录下。比如PetFeeder/ ├── PetFeeder.pdsprj ├── PetFeeder.uvprojx ├── Objects/PetFeeder.hex ├── Docs/ └── README.mdREADME 里写明 Proteus 版本、Keil 版本、接线表。这个习惯在答辩和交接时非常省事评审问“这板子供电怎么设计的”“DHT11 接哪个脚”直接指 README 和仿真图比现场翻代码快得多。3.2 原理图连线顺序与网络标号使用规范连线顺序决定排查效率。我一般的操作顺序是先放 STM32F103C8再放 8MHz 晶振和两个 22pF 电容接 PD0/PD1接着放复位电路和电源电路最后放外设。布局上遵守“左电源、中主控、右外设”的规则AMS1117 放最左边传感器和执行器放右边按键放下方。这样截图和演示视频里视觉路径和信号流向一致。Proteus 里拖动元器件后原有的连线容易变成“看似相连实则悬空”的状态这是仿真图最常见的隐性错误。推荐的原则是跨越较远的信号用网络标号而不是长连线。比如 DHT11 的数据线叫DHT11_DATAOLED 的 SDA/SCL 叫I2C_SDA、I2C_SCL电位器输出端叫WATER_LEVEL。网络标号在元件移动后仍然保持电气连接长线拖拽却可能断成两截。3.3 从 Keil5 编译到 Proteus 加载 hex 的完整链路Keil5 里生成 hex 只需要在 Options for Target 的 Output 页勾选 Create HEX File。要注意的是如果你电脑上同时装了 Keil C51 和 MDK编译前先确认 Tools 里选的是 ARM 编译器 AC5 或 AC6而不是 C51。C51 编出来的 A51 文件加载到 Proteus 里不会有任何反应这是两类工程混装时最常见的低级错误。编译完成后可以先在命令行确认 hex 是否真的更新了dir .\Objects\PetFeeder.hex看到文件时间戳与当前编译时间一致再双击 Proteus 原理图里的 STM32 芯片在 Edit Component 对话框的 Program File 里选择这个 hex 文件。Crystal Frequency 填8000000不是72M。仿真器的时钟源是外部 8MHz 晶振PLL 倍频到 72MHz 是固件内部的事情填 72M 会让定时器、串口波特率全部偏离。加载完成后点左下角运行按钮。如果程序没跑按 F12 打开调试模式单步看 PC 指针是否停留在Reset_Handler这样能快速区分是 hex 没加载成功还是固件初始化卡死。现象大概率原因处理方式点击运行后无任何反应hex 未加载或加载的是旧文件重新选择 hex确认时间戳程序停死在启动文件时钟配置开错了 HSE/PLL检查 CubeMX 时钟树确认外部晶振 8MHz串口输出乱码Crystal Frequency 与波特率计算不一致统一为 8000000Proteus 卡死或闪退后台 PDS.EXE 被安全软件拦截结束进程重开关闭实时监控4. 宠物喂食控制、DHT11 温湿度采集与 OLED 显示的代码实现4.1 初始化骨架GPIO、定时器 PWM 与执行器驱动固件按模块划分文件main.c管初始化dht11.c管温湿度feeder.c管喂食状态机oled.c管显示adc.c管水位。这样划分的好处是演示视频里可以指着文件讲职责排错时也能快速定位。舵机 PWM 用 TIM2_CH2对应 PA1。初始化代码/* TIM2 PWM 初始化72MHz 经 72 分频得到 1MHz 计数频率 */ void PWM_Init(void) { TIM_HandleTypeDef htim2; TIM_OC_InitTypeDef oc; __HAL_RCC_TIM2_CLK_ENABLE(); htim2.Instance TIM2; htim2.Init.Period 19999; /* ARR19999周期 20ms */ htim2.Init.Prescaler 71; /* 分频后 1MHz */ htim2.Init.CounterMode TIM_COUNTERMODE_UP; HAL_TIM_PWM_Init(htim2); oc.OCMode TIM_OCMODE_PWM1; oc.Pulse 500; /* 初始 0 度占空比 2.5% */ oc.OCPolarity TIM_OCPOLARITY_HIGH; HAL_TIM_PWM_ConfigChannel(htim2, oc, TIM_CHANNEL_2); HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_2); }Period19999加上 ARR 从 0 计数得到 20000 个计数单位也就是 20ms 周期。Prescaler71把 72MHz 时钟分成 1MHz每个计数单位正好 1μs。Pulse500 对应高电平 500μs占空比 2.5%这是 SG90 舵机的 0 度位置。角度与 Pulse 的映射关系放到下一节这里只需要保证 PWM 波形频率严格是 50Hz。4.2 DHT11 单总线协议的仿真适配与读取代码DHT11 是单总线器件数据线既要输出又要输入Proteus 仿真里最常见的失败是读回 0xFF。先看原理图上有没有给数据线加 4.7kΩ 上拉再看Delay_us是不是真实微秒级延时。HAL 库自带的HAL_Delay是毫秒级不能拿来凑微秒。/* DHT11 起始信号主机拉低 18ms 再释放 */ void DHT11_Start(void) { GPIO_InitTypeDef gpio {0}; gpio.Mode GPIO_MODE_OUTPUT_OD; /* 开漏输出配合外部上拉 */ gpio.Pull GPIO_PULLUP; gpio.Pin DHT11_PIN; HAL_GPIO_Init(DHT11_PORT, gpio); HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET); Delay_us(18000); HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET); gpio.Mode GPIO_MODE_INPUT; /* 释放总线切换输入 */ gpio.Pull GPIO_PULLUP; HAL_GPIO_Init(DHT11_PORT, gpio); }开漏输出的原因很简单单总线是双向半双工开漏配合外部上拉可以做到“谁拉低谁说话”不需要在两个模式之间频繁切换复用功能。DHT11 返回的 40 位数据格式是湿度整数、湿度小数、温度整数、温度小数、校验和。校验和必须等于前四个字节相加的低 8 位否则这帧数据丢弃重读。读一位数据的核心代码要严格控制采样点uint8_t DHT11_Read_Bit(void) { uint32_t timeout 0; while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_RESET) { if (timeout 120) return 0xFF; /* 超时保护防止死循环 */ Delay_us(2); } Delay_us(30); /* 高电平中心采样 */ return HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) ? 1 : 0; }DHT11 数据位用高电平宽度区分26~28μs 表示 070μs 表示 1。低电平后的 30μs 处正好落在两种高电平宽度之间这个采样点能同时兼顾两种位值。仿真时如果读出的温湿度偶尔跳变优先检查Delay_us是否被 Keil 优化掉优化等级建议先设为 O0跑通后再开更高的优化。4.3 状态机手动喂食、自动喂食与时间基准喂食逻辑不展开成串行流程而是用一个简单的状态机。状态有三类手动开盖、自动定时开盖、关盖等待。/* 角度到比较寄存器值的映射0 度500us180 度2500us */ uint32_t Angle_To_CCR(uint8_t angle) { return 500 (uint32_t)angle * 2000 / 180; } void Feeding_Task(void) { static uint32_t last_feeding_ms; static uint8_t feeding_active 0; uint32_t now HAL_GetTick(); if (manual_trigger || (mode MODE_AUTO now - last_feeding_ms FEED_INTERVAL_MS)) { TIM2-CCR2 Angle_To_CCR(180); /* 开盖下料 */ feeding_active 1; last_feeding_ms now; manual_trigger 0; } if (feeding_active now - last_feeding_ms 500) { TIM2-CCR2 Angle_To_CCR(0); /* 0.5 秒后关盖回位 */ feeding_active 0; } }这里直接改写TIM2-CCR2而不是重新调用HAL_TIM_PWM_Start。重复调用 PWM Start 会不停重置计数器仿真示波器上看到的就是占空比抖动。时间基准用HAL_GetTick()做非阻塞延时整个喂食过程不会卡住 OLED 刷新和按键扫描。4.4 OLED 的 I2C 地址与 ADC 水位检测的阈值设计OLED 使用 I2C1SCL 在 PB6SDA 在 PB7CubeMX 里直接选择 I2C1 即可。0.96 寸 SSD1306 模块的 7 位从机地址大多是 0x3C但在 HAL 库的HAL_I2C_Mem_Write里DevAddress 需要传入左移一位后的 8 位地址也就是0x3C 1等于 0x78。写 0x3C 会导致所有 OLED 写操作返回 HAL_ERROR屏幕空白。这是移植现成例程时最高频的坑。水位检测用 ADC 加电位器。电位器输出电压接 PA4转换成数字量后按 3.3V 参考电压换回电压值。缺水下限设在 1.2V对应约 36% 电压低于这个值蜂鸣器以 1Hz 频率鸣叫OLED 第三行固定显示WATER LOW!。阈值不要设得太接近满量程否则仿真时电位器稍微一抖就误触发。void OLED_ShowStatus(uint8_t temp, uint8_t humi, uint8_t water_low) { char line[20]; OLED_Clear(); sprintf(line, T:%dC H:%d%%, temp, humi); OLED_ShowString(0, 0, line); if (water_low) { OLED_ShowString(0, 3, WATER LOW!); } }刷新策略是每秒刷一次不要每次 ADC 转换完都刷屏。SSD1306 是整屏刷新频繁写 I2C 会占用总线影响 DHT11 的时序判断。ADC 转换和 OLED 刷新放在主循环的不同时间片里各干各的。5. Proteus 与 Keil 联调PDS.EXE 进程、时序基准确认与复位陷阱5.1 联调入口PDS.EXE、Virtual Terminal 与虚拟示波器Proteus 8 的仿真核心进程是 PDS.EXE。启动仿真后任务管理器里能看到这个进程占用 CPU这在软件仿真里是正常的因为要实时模拟器件电气特性。如果仿真中途界面无响应先结束 PDS.EXE 再重新打开工程而不是直接关掉 Proteus 主程序。联调时我会在原理图上挂两个调试工具一个 Virtual Terminal 接 USART1用于输出日志一个虚拟示波器接 PA1 和 PB0同时观察舵机 PWM 与 DHT11 单总线波形。示波器的接法是 A 通道接 PA1B 通道接 PB0地线接 GND。这样不用切换画面一眼就能看出 PWM 频率是不是 20ms 周期DHT11 的应答时序有没有对齐。串口日志用标准库 printf 重定向到 USART1注意 Keil 里勾选 MicroLIB否则 printf 会占用大量 Flash仿真速度也会变慢。Virtual Terminal 上看到乱码时先查波特率再看 Proteus 芯片属性里的 Crystal Frequency 是否和 Keil 时钟树一致两边差一点波特率就偏了。5.2 从“仿真能跑”到“仿真和实物一致”的校验表故障现象直接原因验证与处理DHT11 读回全 0xFF数据线缺少 4.7k 上拉或微秒延时不准示波器看单总线波形检查上拉电阻舵机不动作PWM 频率不是 50Hz或 CCR 映射错误示波器测 PA1确认周期 20msOLED 花屏或白屏I2C 速率超过模型能力地址写错降到 100kHz核对 0x78按键一次触发多次Proteus 按键模型存在抖动软件消抖 50ms程序运行几秒后复位IWDG 看门狗没有喂狗检查是否有 MX_IWDG_Init仿真里直接禁用变量值仿真时被清空Keil 优化把变量优化掉了关键状态量加 volatile先用 O0仿真里最隐蔽的问题是看门狗。如果工程是从别的项目复制的CubeMX 里可能默认初始化了 IWDG仿真运行速度比真实芯片慢喂狗不及时就会触发复位。现象就是“跑几秒重新从头开始”不加日志很难定位。排查时先确认工程里有没有 IWDG 初始化代码有就注释掉仿真阶段不需要验证看门狗功能。5.3 微秒延时的可靠性DWT 实现与中断里的阻塞陷阱微秒延时不能用简单空循环因为 Keil 优化等级一变循环次数和实际时间就对不上。推荐用 DWT 计数器做微秒延时精度和稳定性都比空循环好void Delay_us(uint32_t us) { DWT-CYCCNT 0; DWT-CTRL | DWT_CTRL_CYCCNTENA_Msk; while (DWT-CYCCNT (SystemCoreClock / 1000000) * us); }这段代码利用 Cortex-M3 的 DWT 周期计数器以 72MHz 时钟为基准。调用前不需要额外初始化只要芯片内核时钟正常就能用。它在仿真里的行为比软件循环更贴近真实硬件因为 Proteus 模拟的是指令执行和时钟计数而不是编译后空指令的数量。在定时器中断里禁掉任何 HAL_Delay 调用。HAL_Delay依赖 SysTick 中断递增uwTick如果当前中断优先级比 SysTick 高uwTick不再更新HAL_Delay会永远等下去。仿真里表现为主循环卡死按键无响应点暂停后调试器停在延时函数里。正确做法是中断只置标志位喂食、显示这些耗时的动作全部放主循环状态机里处理。6. 仿真图、源代码和演示视频对齐的验收清单与录屏技巧6.1 仿真自检清单逐步确认每一层信号仿真图交付之前按下面这张表逐项检查每一项都要有截图证据。截图里保留 Proteus 状态栏的时间信息能证明是运行态而不是摆拍。检查项操作方式预期结果供电放电压探针到 AMS1117 输出稳定显示 3.3V时钟示波器接 PD18MHz 波形稳定手动喂食按手动按键舵机 CCR 从 500 到 2500 再回 500自动喂食缩短喂食间隔验证到达时间点自动执行一次温湿度显示观察 OLED 和串口两者数据一致缺水提示拧电位器到 1.2V 以下蜂鸣器响OLED 显示 WATER LOW仿真图至少导出三张原理图全貌、外设局部放大、运行状态截图。原理图全貌用于讲硬件架构局部放大用于说 DHT11 和舵机接法运行截图配合代码一起讲。导出的图片放进论文附录时注意把网络标号截清楚评审最常问的就是“这个 DHT11 接在哪个引脚”。6.2 演示视频的五分钟脚本与录制参数演示视频不要边操作边想词先录运行素材再补讲解音轨。推荐顺序前 20 秒展示仿真图全貌和主要模块分布接下来 40 秒讲最小系统包括晶振、复位、BOOT 和电源网络再花 60 秒对照源码讲状态机和 DHT11 时序然后进入运行演示环节展示按键喂食、温湿度变化、缺水报警三个场景最后用 30 秒展示文件目录结构和 hex 时间戳。全片控制在五分钟以内。脚本末尾放一个刻意设计的技巧把 Keil 编译生成的 hex 文件时间戳与 Proteus 仿真运行的时间戳录进同一个画面。具体操作是编译完成后不要立刻打开 Proteus而是把 Windows 资源管理器里 hex 文件属性窗口和 Proteus 运行画面分屏录一两秒。这个细节让评审能直接确认“源码里编译出来的固件就是仿真里正在跑的固件”比口头说“我测试过了”可信得多。录制参数上OBS 里设为 1920×1080、30fps码率 8000Kbps。仿真画面和代码窗口分屏时把 Keil 的代码字体放大到 14 号以上保证手机端能看清。讲解音频单独录一轨讲错的地方直接重录这一句不要整段推倒最后在剪辑软件里对齐画面和音轨。本文还有配套的精品资源点击获取