简介:面向嵌入式初学者与STM32开发者的Proteus仿真工程包,聚焦在无实物硬件条件下完成单片机软硬件协同验证。资源以常用STM32型号为对象,整合Proteus原理图与Keil工程,可用于GPIO、ADC/DAC、UART、SPI等外设的电路搭建、程序导入、波形观测与断点调试,帮助读者理解从代码编译到虚拟硬件运行的完整链路。压缩包共433个文件、约9.04MB,其中290个C源文件与43个头文件构成主要程序主体,并附d、crf、o等编译中间文件、pdsprj仿真工程、uvprojx工程配置及hex烧录文件,目录保留了较完整的开发过程痕迹。已有1128人学习下载,适合课程设计、毕业设计前期验证与STM32入门练习时对照参考,也可作为排查外设初始化、时钟配置与中断逻辑问题的工程样例。
1. 装 Proteus 仿真 STM32,先认清这套环境的三个前提
第一次在一台干净机器上做 Proteus 的 STM32 仿真,出问题的往往不是软件装不上去,而是装完之后打开工程、点下运行,LED 一动不动,示波器拉出一条直线。我遇到过一次查了半宿:Keil 里选的是 48 脚的 C8,Proteus 原理图上摆的是 64 脚的 R6,引脚编号整体错位,串口 TX 接到了晶振脚上,程序本身没有任何问题。
Proteus 仿真 STM32 这套环境看着简单,一个仿真软件加一个编译器,实际上版本、器件库、时钟频率、引脚封装这四件事必须同时对上,错一个就是「能编译、能加载、就是不跑」。它适合手头没板子但要练外设的学生,也适合要给学生演示时序和中断的人。下面按装什么、怎么装、装坏了怎么救的顺序走一遍。
2. 版本对不上,后面全白搭:Proteus 8.15 配 Keil MDK 5.38
在装 Proteus 仿真 STM32 这套环境之前,我得先把版本定死。这一章说的版本号以你手上的授权包为准,道理是通用的:Proteus 的小版本决定器件库里有没有你要的 STM32 型号,Keil 那边的编译器版本决定一份老工程能不能直接编译过。两边各自有坑,合起来还有对齐问题。我的习惯是先定芯片型号,再倒推两个软件的版本,而不是先下软件再想跑什么芯片。
2.1 Proteus 侧:8.13 和 8.15 在 STM32 模型上的差别
Proteus 8.x 系列里,STM32 的 VSM 模型是随器件库一起更新的。8.13 是很多人手里的稳定版本,常用的 STM32F103R6、STM32F103C8 这些型号都能搜到;8.15 的器件库更全一些,对新一些的外设模型支持更完整,比如某些定时器和 ADC 的行为差异。如果你只是点灯、串口、定时器中断这类基础验证,8.13 完全够用;如果要跑 SPI 从机、DMA 搬运这类时序敏感的仿真,8.15 会少一些奇怪现象。
要避开的反而是更老的版本。8.6、8.7 时代的器件库里,Cortex-M3 的模型不完整,STM32 系列的引脚电源模型经常是缺失的,你会遇到「程序在跑但外设不响应」这种很难定位的问题。判断办法很直接:在 Pick Devices 里搜到芯片后,看它右侧 Description 里有没有 VSM 字样,以及 Model Type 是不是 VSM。
提示:Proteus 的版本号和器件库版本号是两回事。装完主程序后,器件库要单独确认一遍,别默认它一定装全了。
我一般把版本定在 8.15 做主用,8.13 留一份装在另一台机器上做交叉验证。因为仿真出问题的时候,你永远不知道是代码错了还是模型不对,换一台机器跑一遍,能立刻把范围缩小一半。
2.2 Keil 侧:MDK 5.38 加编译器 v5 还是 v6
Keil 这边有两个版本要定:IDE 本身的版本,和编译器(AC5 / AC6)的版本。MDK 5.38 是目前比较省心的一个选择,它默认带编译器 v6,同时可以通过 Pack 补装 v5。这两个编译器对代码的要求不一样:v6 对语法更严格,老代码里的一些隐式类型转换、内联汇编写法会直接报错;v5 宽松,标准外设库时代的工程基本能直接编过。
用寄存器写验证代码的话,两个都能过。用标准外设库(StdPeriph)写的话,我建议在工程里显式指定编译器 v5,在 Options for Target 的 Target 页把 ARM Compiler 选成 Use default compiler version 5。原因很实际:那套库的启动文件是给 v5 写的,换成 v6 之后经常冒出__main相关的链接错误,新手在这一步很容易卡住。
编译器切换之后,一定要做一次 Rebuild 而不是 Build。混编过之后的目标文件缓存会让错误看起来时有时无,这是我见过最费时间的一类假故障。
2.3 安装路径和目录规划:中文和空格是两个隐形雷
两个软件都别装在带中文或空格的路径下。这不是迷信,是编译器和仿真器在解析路径时的老毛病。我一般这样规划:
| 软件 | 安装路径 | 说明 |
|---|---|---|
| Proteus | D:\Proteus8 | 不放 C 盘,避免权限问题 |
| Keil MDK | D:\Keil_v5 | 与 Pack 目录分开 |
| 工程目录 | D:\mcu_work\stm32_sim | 全英文,无空格 |
Keil 的工程路径里如果有空格,某些版本的 fromelf 转换工具会静默失败,最后的表现是「编译成功但没生成 hex」,非常难查。工程目录也不要放在桌面或者「我的文档」下面,因为这些路径会展开成带中文的用户目录。
2.4 一张对齐表:芯片型号、封装、Flash 要对上
真正决定成败的是这张表。Proteus 里摆哪个型号,Keil 里就必须选同一颗,封装和 Flash 容量都要一致。
| 项目 | Proteus 器件库 | Keil Device | 封装 | Flash |
|---|---|---|---|---|
| 型号 A | STM32F103R6 | STM32F103R6 | LQFP64 | 32 KB |
| 型号 B | STM32F103C8 | STM32F103C8 | LQFP48 | 64 KB |
R6 和 C8 差了 16 个引脚,PA8 之后的引脚编号完全不同。我在第 1 章说的那个坑就是这么来的:Keil 里选 C8,Proteus 里摆 R6,程序里操作 PA13、PA14 的时候,原理图上对应的却是别的脚。
选型号的时候记住一条:Proteus 的 STM32 模型以 R6 为主,很多示例工程都是按 R6 画的。你手上这份仿真资源如果不是自己画的原理图,进 Keil 之后第一件事就是打开 Options for Target,确认 Device 那一栏的型号和原理图上的丝印一致,不一致就改 Keil 这边,别改原理图。
3. 安装 Proteus:主程序、授权、器件库三段落位
Proteus 的安装过程本身不算复杂,麻烦的是装完之后器件库里没有 STM32,或者有型号但模型是空的。这一章按三段来:主程序落到盘上、授权导入生效、器件库和模型文件确认到位。三段都过了,才算装完。
3.1 主程序安装:路径、组件与首次启动
从压缩包解出来的目录里找安装程序,右键用管理员身份运行。安装向导里有两个地方要留意:一是安装路径,按上一章的规划填D:\Proteus8;二是组件选择页,把 VSM Simulator、Library、Sample Projects 这三项都勾上,尤其是 Library,某些定制安装包里它默认是不勾的。
装完之后不要急着打开工程,先做一件事:确认安装目录下这几个子目录存在。
# 在安装目录下确认关键子目录,缺哪个补哪个 cd /d D:\Proteus8 dir /b # 期望看到 BIN、LIBRARY、MODELS、SAMPLES、TEMPLATES 这几个目录BIN是可执行文件目录,LIBRARY放器件库,MODELS放 VSM 模型(DLL 形式),SAMPLES是示例工程。如果LIBRARY是空的,说明组件没勾全,重新跑一遍安装程序选 Modify 补上,别想着从别处拷一份凑合,版本对不上的库会让仿真行为变得不可预测。
8.x 的某些小版本会把器件库放到系统盘的公共程序数据目录下,而不是安装目录。装完在两边都看一眼,实际路径以安装日志为准。
首次启动建议用管理员身份运行一次,让它把配置文件写到用户目录下,之后再普通权限启动就不会反复弹配置向导了。
3.2 授权导入:许可证文件放在哪、导入失败怎么回退
启动后会提示激活。正版授权一般是一个许可证文件,在软件里打开 License Manager,选择导入,指向那个文件,重启软件生效。导入失败最常见的原因有两个:一是文件被浏览器或解压工具改过扩展名,变成了.txt或者.lxk.txt;二是导入的时候软件没有管理员权限,写不进授权存储位置。
判断是否成功很简单:重启后在 Help 菜单里看授权状态,能看到有效期和功能列表就是成功了。如果显示的是试用模式,说明文件没被识别。
注意:授权导入之后先别改系统时间做测试,某些授权机制对时间回拨敏感,会导致授权状态异常,恢复起来要去删配置文件。
出问题想回退的话,把用户目录下的 Proteus 配置文件夹整个改名(比如加个.bak后缀),再重新启动,软件会重建一份干净的配置。这一步比重装软件快得多。
3.3 器件库和 VSM 模型:搜不到 STM32 就差这一步
器件库和模型是两套东西。器件库(LIBRARY 目录下的.LIB文件)决定你在 Pick Devices 里能不能搜到这颗芯片,模型(MODELS 目录下的.DLL)决定它能不能被仿真。两个都在,才是完整可用。
装完之后打开软件,新建一个空工程,按 P 打开 Pick Devices,搜索框里输入STM32F103,看结果列表。正常情况下能看到 R6、C8、RB 等几个型号。点中 R6,右侧会显示它的描述信息和模型类型。如果列表是空的,说明库没装进来;如果列表有型号但模型类型显示的是 None 或者 SPICE,那这颗芯片只能画图不能仿真。
模型缺失的补救办法:安装包里通常有一个单独的 Library 或 Update 目录,里面是器件库和模型的安装程序,单独跑一遍。这一步做完还不行,就换一个版本的安装包,不要在不同版本之间互相拷文件。
3.4 装完立刻做的三条自检
装完别急着画图,花五分钟做这三条,能省掉后面几小时的排查。
第一条,Pick Devices 搜STM32F103R6能搜到,且模型类型是 VSM。第二条,随便摆一颗电阻和一个 LED,接上电源和地,点运行,LED 能亮。这一步验证的是基础仿真引擎,跟 STM32 无关。第三条,在 System 菜单里看 Simulation 相关的设置项是否可点,如果整个菜单是灰的,说明授权没生效。
三条都过了,Proteus 这一侧就算落地了。接下来是 Keil 那半边。
4. 打通工具链:Keil 编出 HEX,Proteus 吃得下
两边软件都装好了,中间还差一根管子:Keil 编译产出的 HEX 文件,要能被 Proteus 的 STM32 模型正确读取和执行。这一章把这根管子接上,顺带把原理图上必须接的几处电源和配置脚说清楚,很多人到这里就卡在「程序加载了但不运行」。
4.1 Keil 新建工程:器件型号必须和原理图同封装
新建工程,选 Device 的时候按第 2 章那张表来,原理图上是 R6 就选 R6。选完之后会弹一个运行环境管理窗口,用寄存器写代码的话,里面什么都不用勾,直接关掉;用标准库的话,把 CMSIS 的 Core 和 Device 的 Startup 勾上。
工程建好之后,先加一个最小的 main 文件,把编译链跑通,别一上来就堆几百行外设代码。最小验证代码用寄存器写,不依赖任何库,最省事:
#include <stdint.h> /* STM32F103 寄存器地址,只列验证代码用到的几个 */ #define RCC_APB2ENR (*(volatile uint32_t *)0x40021018U) #define GPIOA_CRL (*(volatile uint32_t *)0x40010800U) #define GPIOA_ODR (*(volatile uint32_t *)0x4001080CU) #define USART1_SR (*(volatile uint32_t *)0x40013800U) #define USART1_DR (*(volatile uint32_t *)0x40013804U) #define USART1_BRR (*(volatile uint32_t *)0x40013808U) #define USART1_CR1 (*(volatile uint32_t *)0x4001380CU) static void delay(volatile uint32_t count) { while (count--) { __asm volatile ("nop"); /* 空转,只保证肉眼能看出闪烁节奏 */ } } static void uart1_send(char c) { while (!(USART1_SR & (1U << 7))) { } /* 等 TXE 置位,发送寄存器空 */ USART1_DR = (uint32_t)c; } int main(void) { uint32_t tick = 0; RCC_APB2ENR |= (1U << 2) | (1U << 14); /* 开 GPIOA、USART1 时钟 */ GPIOA_CRL &= ~(0xFU << 0); /* PA0 推挽输出,驱动 LED */ GPIOA_CRL |= (0x2U << 0); GPIOA_CRL &= ~((0xFU << 8) | (0xFU << 12)); /* PA2 复用推挽 TX,PA3 浮空输入 RX */ GPIOA_CRL |= ((0xBU << 8) | (0x4U << 12)); USART1_BRR = 0x341; /* 8MHz 时钟下 9600 波特率 */ USART1_CR1 = (1U << 13) | (1U << 3) | (1U << 2); /* 使能、发送、接收 */ for (;;) { GPIOA_ODR ^= (1U << 0); /* 翻转 PA0 */ uart1_send('A' + (tick++ % 26)); /* 循环发 A~Z */ delay(200000); } }代码里每个寄存器地址对应 STM32F103 的参考手册,RCC_APB2ENR的 bit2 是 GPIOA 时钟,bit14 是 USART1 时钟。GPIOA_CRL里每 4 位配置一个引脚,PA0 写0x2是 2MHz 推挽输出,PA2 写0xB是 50MHz 复用推挽,PA3 写0x4是浮空输入。USART1_BRR的 0x341 是按 8MHz 的 PCLK2 算出来的:8 000 000 ÷ 9600 ≈ 833,也就是 0x341。这个值在时钟改了之后必须重算,否则串口出来的就是乱码,这一点在第 5 章还会说。
4.2 三个开关让 HEX 落到 Objects 目录
代码编过之后,HEX 不一定生成了。打开 Options for Target 的 Output 页,确认三件事:Create HEX File 勾上、输出目录是.\Objects\、Browse Information 勾不勾都行。勾上 Create HEX File 之后做一次 Rebuild,然后去工程目录的 Objects 下看,应该有一个和工程同名的.hex。
如果编译显示 0 Error 但 Objects 里没有 hex,先看 Output 页那个勾是不是被别的配置覆盖了,再看工程路径有没有空格。Keil 的工程文件里存的是相对路径,路径里有空格的时候转换工具会失败,错误信息往往被吞掉。
提示:改完 Output 设置之后要做 Rebuild,Build 有时候不会重新生成目标文件。
4.3 原理图上必须接的三处:电源、BOOT0、复位
这一步是「加载了 HEX 但不跑」的头号原因。Proteus 的 STM32 模型不会自动补电源,原理图上必须显式连接:
| 引脚 | 接法 | 不接的后果 |
|---|---|---|
| VDD(各电源脚) | 接 VCC,并加 100nF 去耦 | 程序不运行,无任何输出 |
| VDDA | 接 VCC | ADC 相关全为 0,其他功能可能正常 |
| BOOT0 | 接 GND(配 10k 下拉) | 从错误区域启动,程序不执行 |
| NRST | 接 10k 上拉到 VCC,加按键到地 | 上电复位不确定,偶发不跑 |
电源脚在 64 脚封装上不止一个,VDD 有多个,VSS 也有多个,都要接。画图的时候用总线或者复制粘贴,漏一个都会出现「有时能跑有时不跑」这种玄学现象。
BOOT0 是最容易漏的。它悬空的时候电平不确定,芯片可能进了系统存储器启动模式,表现就是加载了 hex 但 PC 从来没跳到你的 main。
4.4 Proteus 侧加载 HEX 和时钟频率对齐
双击原理图上的 STM32 芯片,打开属性窗口。Program File 那一栏指向刚才 Keil 生成的 hex 文件,路径用绝对路径最稳。Clock Frequency 那一栏,默认是 8M,这个值必须和代码里算 BRR 用的频率一致。
如果你的代码没有配置时钟树,芯片就跑 HSI 的 8MHz,那么 Clock Frequency 保持 8M,BRR 写 0x341。如果你在代码里把时钟切到了 72MHz,那么 Clock Frequency 要改成 72M,BRR 也要重算成 0x1D4C。两边不一致的后果,就是串口波特率偏移,输出乱码。
属性里还有一个 Advanced Properties 折叠区,里面的东西一般不用动,除非你要调仿真精度。加载完 hex 之后,最好点一次属性窗口的确定,让它重新读一遍文件,有时候改了文件不刷新,跑的还是旧的。
4.5 虚拟终端和示波器:把串口和电平接出来看
串口验证不需要外部器件,在 Proteus 的 Instruments 里拖一个 Virtual Terminal 出来,它的 RXD 接单片机的 PA2(TX),TXD 接 PA3(RX),然后双击终端把波特率设成 9600,和数据位、停止位对上。运行之后终端窗口里应该滚动出现 A 到 Z。
LED 那一路,PA0 串一个 330 欧电阻到 LED 再到地。想看得更清楚,可以用电压探针或者示波器接 PA0,观察方波周期,比肉眼看闪烁可靠得多。
虚拟终端和示波器都属于虚拟仪器,不吃器件库,但吃仿真速度。仪器开太多会让仿真变慢,这点在第 5 章讲。
5. 排查记录:五个让我返工的坑
下面这五条都是我自己或者身边的人真实返工过的,按现象、原因、解决的顺序写。每一条都不是理论推演,是同一类问题反复出现之后总结出来的判断顺序。
5.1 编译零错误,Proteus 里一点反应都没有
现象:Keil 显示 0 Error 0 Warning,Proteus 加载了所谓的 hex,点运行,LED 不闪,终端无输出,示波器一条直线。
原因:九成是 hex 根本没生成。Output 页的 Create HEX File 没勾,或者勾了但工程路径带空格导致转换工具静默失败。还有一种情况是加载的时候指错了文件,指向了上一次编译留下的旧 hex。
解决:先进 Objects 目录看文件的时间戳,是不是本次编译时间。路径里有空格就整个工程挪到D:\mcu_work下面重新编译。加载的时候用绝对路径,别用相对路径。
5.2 HEX 加载正确,程序就是不跑
现象:文件确实是最新的,时钟也设了,运行之后毫无反应,连复位瞬间的引脚电平变化都没有。
原因:电源脚或者 BOOT0 悬空。Proteus 的模型不会替你上电,原理图上少接一个 VDD,整个芯片就是死的。
解决:把芯片所有 VDD、VSS 脚逐个检查一遍,VDDA 单独接。BOOT0 拉低,NRST 加上拉。改完重新运行。
5.3 串口出来是乱码或者只有空白
现象:LED 正常闪,说明程序在跑,但虚拟终端里是乱码,或者干脆什么都没有。
原因:BRR 寄存器的值和芯片实际时钟不一致。代码按 8MHz 算的 BRR,属性里 Clock Frequency 却填了 72M;或者反过来。另一种是虚拟终端自己的波特率没设,默认值不是 9600。
解决:先把属性里的时钟频率和代码注释里的假设对齐,再算一遍 BRR。然后双击终端确认波特率。两边都对齐之后还乱,就看引脚是不是接反了,TX 要接终端的 RXD。
注意:终端接反不会报错,只会显示空白,很容易被当成程序问题。
5.4 器件库搜不到 STM32F103R6
现象:Pick Devices 里搜STM32什么都没有,或者只有几个不相关的型号。
原因:安装时 Library 组件没勾,或者器件库被放到了公共程序数据目录而软件读的是安装目录。也有可能是权限问题,普通用户读不到那个目录。
解决:先用管理员身份启动一次,再搜。还不行就重跑安装程序选 Modify,把 Library 补上。补完之后在安装目录和公共数据目录两边都确认一下 LIBRARY 文件夹里有内容。
5.5 仿真慢到没法看,动一下就卡几秒
现象:程序本身很简单,但仿真跑起来帧率极低,LED 闪烁变成几秒一次。
原因:虚拟仪器开太多,或者仿真精度设得太高。Proteus 默认会对模拟量做精细计算,纯数字电路被当成模拟电路算,速度就下来了。
解决:System 菜单里找仿真设置,把动画相关的选项调低,数字电路的精度档位放宽。只保留当前验证必需的仪器,示波器看完就关掉。改完重启仿真,速度差别很明显。
6. 跑通验证:一个 LED 加一路串口,十分钟判断环境是否真的通了
环境装完,怎么算通?我的标准很低但很硬:同一个工程里,LED 在闪,串口在出字符,两个现象同时存在。只亮灯不算通,因为灯亮可能只是电源接对了而程序压根没跑;只出串口也不算,因为那可能只是模型自己在输出。
具体做法就是第 4 章那段代码,一次验证四件事:时钟使能对不对、GPIO 配置对不对、串口寄存器配置对不对、时钟频率假设对不对。运行之后对照下面这张表逐项确认。
| 观察点 | 正常表现 | 异常时的第一怀疑对象 |
|---|---|---|
| PA0 电压 | 周期性高低翻转 | GPIOA_CRL 配置位 |
| 虚拟终端 | 循环输出 A~Z | BRR 值、终端波特率 |
| 芯片供电脚 | 全部为高电平 | 原理图漏接 |
| 复位后首帧 | 一秒内出现 | BOOT0 电平 |
判断顺序我一般是从后往前:先确认电源,再看复位,再看时钟,最后才看代码逻辑。因为前三个错了,代码逻辑根本没机会执行。
还有一个技巧,把仿真速度调慢。System 里的动画设置调低之后,仿真相当于单步慢放,你能清楚看到 PA0 的电平在一个周期内怎么变化,比看示波器波形更直观,尤其适合排查「翻转过快看起来像常亮」这种情况。
反过来,如果要看串口一帧数据的完整时序,就得把速度调快,让采样点密度上来,否则终端里会出现丢字符的假象。这个快慢切换我一般在验证阶段来回调两三次,确认现象稳定之后再固定下来。
真正让我改掉习惯的是一次给外设做演示,现场打开工程,灯亮着,串口也是通的,我就直接开始讲了,讲到一半发现定时器中断根本没进,原因是那把工程里的启动文件被换过,中断向量表偏了。当着人的面翻车,比自己在实验室里翻车难受十倍。从那以后我每次装完这套环境,都强制走一遍「灯加串口」的最小验证,两项都过才碰业务代码,绝不省这一步。
这份仿真资源里带的工程可以按上面的顺序对照着装一遍,装完用最小验证过一遍,再打开示例工程,出问题的时候你至少知道该从哪一层开始查。希望帮到你。
本文还有配套的精品资源,点击获取