Keil MDK-ARM 5 嵌入式开发环境搭建与配置实战指南
1. 嵌入式开发环境搭建的起点
搞嵌入式开发,绕不开的第一个坎就是装工具链。不管你后面是要点灯、调串口、跑RTOS还是搞USB协议栈,第一步永远是让编译器和调试器在电脑上跑起来。MDK-ARM 5(大家更习惯叫它Keil 5)就是ARM Cortex-M系列MCU开发里用得最广的一个集成开发环境,没有之一。它把编辑器、编译器、链接器、调试器、烧录工具全部打包在一起,装完之后基本可以做到“开箱即用”,不需要你再去手动拼凑GCC、OpenOCD、GDB那一套。
这篇内容适合谁看?如果你是刚入行的嵌入式新手,手里拿着一块STM32或者GD32的开发板,完全不知道从哪下手,那这篇就是写给你的。如果你是从IAR或者老版本Keil 4迁移过来的老手,也可以看看里面关于Pack Installer和器件支持包的细节,能帮你少走弯路。我会从下载、安装、激活、器件包管理、工程创建、编译配置、调试器设置这几个环节一步步拆开讲,把每个步骤背后的原因也一并说清楚,让你不只是“照着点”,而是真正理解这套工具链是怎么运转的。
需要提前说明的是,Keil 5和Keil 4最大的区别在于器件支持包(Device Family Pack,简称DFP)的引入。Keil 4时代,你装一个版本就固定支持那几家的芯片,想换芯片型号往往要重装或者打补丁。Keil 5把内核、编译器、器件支持彻底解耦了,IDE本体只负责框架,具体芯片的支持通过Pack Installer在线下载。这个设计思路很像现代浏览器的插件机制,好处是灵活,坏处是新手第一次装完之后发现“怎么找不到我的芯片”,其实只是包没装而已。
2. 安装前的准备工作与版本选择
2.1 安装包从哪里获取
Keil MDK-ARM 5的官方安装包只有一个来源,就是Keil官网(现在归属Arm旗下)。搜索“MDK-ARM”就能找到下载页面,填写一个简单的表单(姓名、邮箱、公司、国家)之后就能拿到下载链接。安装包是一个大概800MB到1GB左右的exe文件,版本号格式类似MDK537.exe这种,前面的5代表Keil 5,后面的数字是具体版本。
这里有个经验:不建议盲目追最新版本。我踩过的坑是,某些新版本刚发布时对老器件的支持包兼容性会有问题,尤其是那些已经停产的Cortex-M0/M3老芯片。如果你做的是新项目,用最新版没问题;如果是维护老项目,建议用比项目立项时间晚半年到一年的版本,稳定性最好。比如项目是2020年做的,用MDK 5.30到5.33这个区间通常比较稳。
另外要注意,官网下载的是MDK-ARM完整版,不是那个免费的MDK-Lite。完整版安装后如果不激活,会以Lite模式运行,代码大小限制在32KB。对于学习点灯、串口收发这种小工程完全够用,但一旦你的工程超过32KB,编译就会报错。所以后面激活这一步是必须做的。
2.2 系统环境要求
Keil 5对系统要求不算高,但也有几个硬性条件:
| 项目 | 最低要求 | 推荐配置 |
|---|---|---|
| 操作系统 | Windows 7 SP1 | Windows 10/11 64位 |
| 处理器 | 1GHz | 2GHz以上多核 |
| 内存 | 2GB | 8GB以上 |
| 硬盘空间 | 2GB可用 | 10GB以上(Pack包很占地方) |
| 显示器 | 1024x768 | 1920x1080 |
特别提醒两点。第一,安装路径绝对不要带中文和空格。默认路径是C:\Keil_v5,这个就很好,别手贱改成D:\我的软件\Keil 5,后面编译时各种莫名其妙的路径错误能把你逼疯。第二,如果你电脑上已经装了Keil 4或者更早的版本,建议先卸载干净再装Keil 5,两个版本共存虽然理论上可以,但注册表里的编译器路径会打架,导致编译时调用了错误的armcc。
2.3 安装过程中的关键选项
双击安装包之后,前面几步都是常规的下一步,到了选择组件的界面要留意一下。安装程序会列出几个可选项:
- Core:核心组件,必装,包含IDE本体和ARM编译器。
- Device Specific:器件相关的示例和库,可以不装,后面用Pack Installer按需下载。
- Pack Installer:器件包管理器,必装,这是Keil 5的灵魂。
- CMSIS:ARM官方的一套软件接口标准,建议装,很多库依赖它。
我的建议是Core、Pack Installer、CMSIS三个勾上,Device Specific不勾。原因很简单,Device Specific里预置的那些示例工程往往版本很老,你后面从Pack Installer下载的才是最新的,装了反而占地方还容易混淆。
安装过程大概需要5到10分钟,取决于硬盘速度。装完之后第一次启动,IDE会提示你安装Pack,这时候先别急着装,把激活搞定再说。
3. 激活与License管理
3.1 为什么必须激活
前面说了,不激活就是Lite模式,32KB代码限制。这个限制在什么概念呢?一个简单的HAL库点灯工程,编译出来大概6到8KB;加上串口打印、定时器、中断,可能到15KB;一旦你上了RTOS或者USB协议栈,轻松突破32KB。所以只要你不是纯粹玩玩,激活是必须的。
激活需要申请一个License。Keil提供几种License类型,个人学习可以申请免费的社区版(Community Edition),商业用途需要购买。社区版的申请流程很简单,在Keil官网注册账号,填写用途说明,一般几个工作日内会收到License文件。
3.2 激活操作步骤
拿到License之后,打开Keil 5,点击菜单栏的File->License Management,会弹出一个对话框。对话框里有一个CID(Customer ID)字段,这是一串基于你电脑硬件生成的唯一标识。把CID复制出来,粘贴到Keil官网的License申请页面,提交后会生成一个License字符串,再把它粘贴回对话框的New License ID Code输入框,点击Add LIC就完成了。
注意:CID是和电脑硬件绑定的,换主板或者重装系统后CID会变,需要重新申请License。所以建议把License文件备份好,换系统后直接重新添加即可。
激活成功后,对话框上方会显示License的过期日期和代码大小限制,看到Unlimited就说明搞定了。
3.3 常见激活问题
有时候添加License会提示“Invalid License”,大概率是CID复制时带了空格或者换行。解决方法是把CID粘贴到记事本里,确认是一串连续的字符,再复制过去。还有一种情况是License已经绑定到其他电脑了,社区版一般允许绑定两台设备,如果超了需要先在官网解绑旧设备。
4. Pack Installer与器件支持包管理
4.1 Pack Installer是什么
Pack Installer是Keil 5区别于Keil 4的核心组件。你可以把它理解成一个应用商店,里面按芯片厂商分类,列出了各家MCU的支持包。每个支持包(DFP)里包含:
- 该系列芯片的启动文件(startup_xxx.s)
- 外设寄存器定义头文件
- 系统初始化代码(system_xxx.c)
- Flash烧录算法
- 调试配置文件
- 示例工程
没有装对应的DFP,你在新建工程时就找不到那颗芯片,编译也会报错说找不到器件。
4.2 如何安装器件包
打开Pack Installer(工具栏上那个绿色小盒子图标),左侧是厂商列表,右侧是包列表。找到你用的芯片厂商,比如STMicroelectronics,展开后找到对应的系列,比如STM32F1 Series,再找到具体型号的DFP,点击Install。
这里有个坑:Pack Installer的下载服务器在国外,国内下载速度可能很慢甚至超时。解决办法是使用离线包。各家厂商的DFP也可以从官网单独下载,下载下来是一个.pack文件,双击就能安装,不需要通过Pack Installer在线下载。我一般会提前把常用的几个系列的pack文件都下载好放在本地,换电脑时直接双击安装,比在线等快得多。
4.3 器件包版本选择
同一个系列可能有多个版本的DFP,比如STM32F1系列有2.3.0、2.4.0等。选哪个?原则是选新不选旧,但别选太新。新版本通常修复了bug、增加了新器件支持,但偶尔会引入兼容性问题。如果你用的是比较成熟的芯片型号,用比最新版早一两个小版本的DFP通常最稳。
安装完DFP之后,新建工程时在Device选择框里就能搜到你的芯片了。如果搜不到,检查一下是不是Pack Installer里没装对应的包,或者装完之后没有重启IDE。
5. 新建工程与编译配置
5.1 从零创建一个工程
点击Project->New uVision Project,选择一个空文件夹作为工程目录。注意,工程路径同样不能有中文和空格。然后会弹出器件选择窗口,搜索你的芯片型号,选中后点击OK。
接下来会弹出一个“Manage Run-Time Environment”窗口,这是Keil 5的另一个特色功能,叫RTE(Run-Time Environment)。它允许你直接勾选需要的软件组件,比如CMSIS Core、Device Startup、HAL库等,IDE会自动把对应的源文件和头文件路径添加到工程里。对于新手来说,这个功能非常方便,省去了手动添加文件的过程。
不过我的习惯是不用RTE,手动管理文件。原因有两个:一是RTE自动添加的文件路径很深,工程迁移时容易出问题;二是手动添加能让你清楚知道每个文件是干什么的,对理解工程结构有帮助。当然,如果你只是想快速跑个Demo,用RTE也没问题。
5.2 手动添加启动文件和库文件
一个最简的STM32工程需要哪些文件?
- 启动文件:
startup_stm32f103xb.s(根据具体型号选) - 系统文件:
system_stm32f1xx.c - 链接脚本:
STM32F103xB_FLASH.ld(Keil用的是.sct分散加载文件,在Options里配置) - 库文件:标准外设库或者HAL库的.c文件
- 主文件:
main.c
把这些文件添加到工程的不同Group里,比如Startup、CMSIS、Library、User,结构清晰,后面维护方便。
5.3 编译选项配置
点击Options for Target(魔术棒图标),有几个关键配置:
Target标签页:设置晶振频率(Xtal),这个值影响调试时的时序计算,填你板子上的实际晶振频率,比如8MHz或者12MHz。
Output标签页:勾选Create HEX File,这样编译后会生成hex文件,方便用其他工具烧录。Debug Information也勾上,调试时需要。
C/C++标签页:在Define里填入宏定义,比如STM32F103xB, USE_HAL_DRIVER。在Include Paths里添加所有头文件所在目录。这里有个技巧:头文件路径要加到最底层的公共目录,而不是每个子目录都加,否则路径列表会非常长。
Debug标签页:选择调试器,比如ST-Link Debugger或者J-LINK。旁边的Settings里可以配置烧录算法和调试接口(SWD还是JTAG)。
5.4 编译与常见报错
配置好之后按F7编译。常见的报错和解决方法:
| 报错信息 | 原因 | 解决方法 |
|---|---|---|
| cannot open source input file "xxx.h" | 头文件路径没加 | 在Include Paths里添加对应目录 |
| undefined symbol xxx | 源文件没加到工程 | 把对应的.c文件添加进来 |
| region RAM overflowed | 内存不够 | 优化代码或换更大内存的芯片 |
| L6218E: Undefined symbol | 函数声明了但没实现 | 检查是否漏了.c文件 |
| Error: Flash Download failed | 烧录算法没选对 | 在Debug Settings里选正确的Flash算法 |
编译通过后,Output窗口会显示0 Error(s), 0 Warning(s),并给出代码大小信息。如果Warning很多,别忽视,很多运行时的问题都是Warning积累出来的。
6. 调试器配置与程序烧录
6.1 调试器选型
常见的调试器有ST-Link、J-Link、DAPLink等。ST-Link是ST官方出的,便宜好用,支持SWD接口,配合STM32芯片很稳。J-Link性能更强,支持更多芯片,但价格贵。DAPLink是开源的,性价比高,适合DIY。
在Keil里配置调试器:Options for Target->Debug,下拉框里选对应的调试器。然后点Settings,在Debug标签页里确认能识别到芯片ID。如果识别不到,检查接线:SWD需要接SWCLK、SWDIO、GND、VCC四根线,有时候还需要接RESET。
6.2 Flash烧录算法配置
在Settings的Flash Download标签页里,需要添加对应的Flash算法。比如STM32F103xB的算法是STM32F10x High-density Flash。如果列表里没有,说明DFP没装好或者版本不对。选好算法后,下面的地址范围会自动填充,确认起始地址是0x08000000(STM32的Flash起始地址)。
提示:如果烧录时提示“Flash Download failed - Target DLL has been cancelled”,大概率是Flash算法选错了,或者芯片处于读保护状态。前者换算法,后者需要用工具解除读保护。
6.3 在线调试技巧
配置好调试器后,按Ctrl+F5进入调试模式。常用的调试操作:
- 断点:在代码行号旁边点击,出现红点就是断点。程序运行到断点会暂停。
- 单步执行:F11是Step Into(进入函数),F10是Step Over(跳过函数)。
- 查看变量:把鼠标悬停在变量上,或者把变量拖到Watch窗口。
- 查看寄存器:在View菜单里打开Registers窗口,可以看R0-R15、SP、PC等。
- 查看内存:View -> Memory Windows,输入地址可以查看内存内容。
我个人的经验是,调试时优先看外设寄存器的值,比看变量更直接。比如串口没输出,先看USART的CR1寄存器里UE位和TE位有没有置1,再看BRR寄存器的分频值对不对,基本就能定位问题。
7. 实操心得与避坑指南
7.1 工程目录结构规范
我见过太多新手把所有的.c和.h文件堆在一个文件夹里,工程大了之后根本没法维护。建议的目录结构:
Project/ ├── CMSIS/ # 内核相关文件 ├── Drivers/ │ ├── STM32F1xx_HAL_Driver/ # HAL库 │ └── CMSIS/ # 器件头文件 ├── Inc/ # 用户头文件 ├── Src/ # 用户源文件 ├── Startup/ # 启动文件 └── MDK-ARM/ # Keil工程文件这样分的好处是,换IDE或者迁移工程时,只需要改MDK-ARM目录,其他源码目录可以原封不动。
7.2 编译速度优化
Keil默认是单线程编译,工程大了之后编译一次要几分钟。可以在Options for Target->Output里勾选Browse Information关掉,能省不少时间。另外,C/C++标签页里的Optimization级别,调试阶段用Level 0(不优化),方便单步跟踪;发布阶段用Level 3(最高优化),代码体积和速度都更好。
7.3 常见问题速查
问题一:编译提示“cannot open source input file 'core_cm3.h'”这是CMSIS核心头文件找不到。解决方法是确认DFP装好了,并且在Include Paths里添加了CMSIS/Include目录。
问题二:烧录后程序不运行先检查启动文件里的堆栈大小是否够用,再检查中断向量表是否偏移正确。如果是Bootloader+App的结构,App的向量表需要重映射。
问题三:调试时进不了main函数大概率是启动文件里的Reset_Handler有问题,或者时钟初始化卡住了。可以在SystemInit函数里加个断点,看能不能走到。
问题四:Pack Installer下载速度极慢用离线pack文件,前面说过了。另外,Pack Installer的缓存目录在C:\Users\用户名\AppData\Local\Arm\Packs,可以把下载好的pack文件直接拷进去,重启IDE就能识别。
7.4 版本迁移注意事项
从Keil 4迁移到Keil 5,最大的变化是器件包管理方式。Keil 4的工程文件(.uvproj)不能直接在Keil 5里打开,需要先转换成.uvprojx格式。转换过程中,器件选择、头文件路径、库文件引用都可能需要手动调整。我的建议是不要直接转换,而是新建工程,把源码文件重新添加一遍,这样能顺便清理掉一些历史遗留的冗余配置。
从标准外设库迁移到HAL库也是类似的情况。HAL库的API和标准库完全不同,函数名、参数、返回值都不一样,直接替换文件是编译不过的。需要逐个外设重写初始化代码。这个过程很痛苦,但HAL库的跨系列兼容性更好,长期来看是值得的。
8. 工具链的延伸与扩展
Keil 5装好之后,其实还有很多可以折腾的地方。比如可以配置外部编辑器(VS Code、Sublime Text)来写代码,Keil只负责编译和调试。方法是在Options for Target->Output里勾选Browse Information,然后在VS Code里装Keil Assistant插件,就能在VS Code里直接调用Keil的编译命令。
还可以配置版本控制,把工程里的.uvprojx、.uvoptx、源码文件纳入Git管理,但Objects、Listings、DebugConfig这些编译产物目录要加到.gitignore里。这样多人协作时,每个人拉下来代码就能直接编译,不会因为路径问题打架。
另外,Keil 5支持命令行编译,用UV4.exe -b project.uvprojx -o build.log就能在CI流水线里自动编译。这个对于需要持续集成的项目很有用,每次提交代码后自动编译一遍,能及早发现编译错误。
最后分享一个我个人的习惯:每装好一个环境,先跑一个最小系统验证。不要急着上复杂功能,先写一个空的main函数,编译烧录,确认能进调试、能看变量、能单步执行。这个最小系统跑通了,说明工具链没问题,后面再出问题就是代码问题,排查范围小很多。这个习惯帮我省了无数次“到底是环境问题还是代码问题”的纠结。