直接上干货,不兜圈子。这篇写给那些手边同时摆着51开发板和STM32开发板的人,也就是我踩过一遍坑的地方。
先说结论:Keil C51和Keil MDK-ARM不是同一个软件的两个版本,而是两套完全独立的工具链,只是共用了一个名叫µVision5的IDE外壳。理解了这一点,后面所有安装、共存、切换的问题就都通了。
1. 先搞清楚:C51和MDK是两套独立的工具链,不是"高低配"
1.1 为什么Keil要分C51和MDK两个产品线
要说清楚这件事,得从Keil的历史讲起。Keil最早是一家做8051开发工具的德国公司,C51编译器就是那个时代的招牌产品,专门服务Atmel、Intel、STC、Winbond这些8051内核的8位单片机。后来ARM公司收购了Keil,把原本的ARM开发工具整合成MDK-ARM(Microcontroller Development Kit),也就是现在写STM32、NXP、GD32这些Cortex-M芯片用的主工具链。
所以C51和MDK实际上是两家公司、两代产品、两条产品线的产物,而不是同一个软件装了不同插件。
我见过不少新手在网上下了一个"Keil"安装包,装完发现只能建51工程,或者只能建STM32工程,就说这个Keil版本不对。其实不是版本不对,是你只装了其中一条产品线。C51安装包和MDK安装包是两个独立的exe,你甚至可以把它们装在不同的文件夹里当两个独立软件用,但那样用起来很别扭。
1.2 两个平台在编译器层面的本质差异
C51编译的是8051指令集,也就是经典的8位CISC架构,片内RAM通常只有128字节或者256字节,Flash从几KB到64KB不等。STM32是ARM Cortex-M内核,32位RISC,带Thumb-2指令集,存储和外设规模完全不在一个量级。
这两者的编译器完全是两套东西:
| 对比项 | Keil C51 | Keil MDK-ARM |
|---|---|---|
| 目标内核 | 8051、8052等8位MCU | Cortex-M0/M3/M4/M7等 |
| 编译器 | C51编译器 | armcc(AC5)或 armclang(AC6) |
| 汇编器 | A51 | armasm |
| 链接器 | BL51/LX51 | armlink |
| 典型用户 | STC89C52、AT89S52 | STM32F103、GD32、NXP |
| 输出文件 | HEX为主 | HEX/BIN/AXF |
这些工具链文件在安装目录里是分开存放的。你打开Keil的安装目录会看到类似这样的结构:
C:\Keil_v5\ ├─ C51\ <- C51编译器、A51汇编器、头文件库 ├─ ARM\ <- ARM编译器、ARMCLANG、设备PACK ├─ UV4\ <- µVision5 IDE外壳程序 ├─ PACK\ <- 芯片支持包(Device Family Pack) └─ TOOLS.INI <- IDE启动时扫描工具链的配置文件µVision5本身只是一个图形前端,它启动的时候读TOOLS.INI,把C51和ARM两套工具链都加载到界面里。这就像一个写字楼的前台,一楼是律师事务所,二楼是会计事务所,前台长得一样,但楼上干的事完全不一样。
所以共存这事儿,从原理上一点不玄学:只要让这个前台同时登记两家公司就行。
2. 双平台共存的安装路线:统一目录与隔离目录的取舍
2.1 我推荐的方案:装进同一个Keil_v5目录
先下载好两个安装包。C51目前最新的大版本是C51V960.EXE,MDK这边版本号一直在滚,常见的有MDK538A、MDK540之类的。两个安装包都可以从Keil官网下载,C51的下载入口在keil.com/c51,MDK的下载入口在keil.com/download/products。
安装的时候路径统一填C:\Keil_v5,不要改。我习惯先装C51再装MDK,实际上两个顺序无所谓,后面那个安装程序会检测到µVision5外壳已存在,只做升级和补充组件,不会把前一个卸载掉。
具体步骤:
- 双击C51V960.EXE,安装目录默认就是C:\Keil_v5,保持默认一直Next。
- 安装完成后先不要急着打开,继续双击MDK安装包。
- MDK安装过程中同样选择C:\Keil_v5,注意这里如果选了别的目录,就等于把两个Keil拆成两个独立软件了,后面会讲这种情况有什么影响。
- MDK安装时会有选择组件的界面,如果你要跑老工程,建议勾选Legacy Arm Compiler 5,后面切换AC5/AC6会用到。
- 装完后桌面只出现一个µVision5的快捷方式,双击打开就是同时具备C51和ARM两套能力的IDE。
打开之后你可以验证一下:在Project菜单里新建一个工程,设备选择对话框左侧的厂商列表里,既能找到Atmel、STC(如果你添加过)、Nuvoton这一堆51厂商,也能找到STMicroelectronics这一堆Cortex-M厂商。出现这个画面,说明两套工具链已经共存在同一个IDE里了。
2.2 为什么我不建议拆开装到两个目录
网上也有人给出另一种方案:C51装到C:\Keil_C51,MDK装到C:\Keil_MDK。这种隔离安装不是不能用,每次编译51代码就打开C51那个快捷方式,编译STM32代码就打开MDK那个快捷方式,等于给电脑装了两个IDE。
我实际用下来,这个方案有几个麻烦:
- 两套IDE的界面语言、插件配置、代码格式设置不互通,你在这边设置好的编辑器风格,那边还得再来一遍。
- License管理是两套独立的,你注册了C51那边,MDK这边还得单独激活。
- 如果你用STC-ISP给C51目录添加STC型号,那边MDK目录里是看不到STC的。
- 来回切换窗口,总有一种"在用两台电脑"的割裂感。
统一目录的最大好处就是一次配置,两边通用。PACK目录、TOOLS.INI、界面主题、快捷键全部共享。唯一的风险是重装其中一个的时候容易误删对方的东西,但正常使用没有影响。
2.3 许可证(License)必须分别激活,评估版限制要知道
这是很多人装完环境之后最迷惑的一个点:明明装好了两个平台,写51代码编译一切正常,新建STM32工程一编译就报License相关的错误。原因很简单,C51和MDK各用各的许可证,你得在File -> License Management里分别注册。
License Management窗口打开后,左侧会列出当前检测到的产品。装了两个平台后,你应该看到至少两条Product记录,一条C51相关,一条MDK-ARM相关。每一条都需要填入对应的LIC(License ID Code)才能解锁。
如果你没有注册正版授权,两个平台都在评估模式下跑:
- C51评估版限制代码量2KB。超过会在链接时报类似"CODE SIZE LIMIT EXCEEDED"的错误。
- MDK评估版限制代码量32KB,做STM32小Demo够用,一旦工程变复杂几十个源文件编译链接,就很容易超限。
网上搜"2K限制怎么解除"的人特别多。正规解除方法只有一个:购买正版C51许可证并注册。Keil MDK这边除了买授权,还有官方的MDK Community社区版可以申请,免费,但是有规模和使用场景限制,适合学习和非商业用途。C51目前没有类似的社区版,只能用评估版或者买授权。
3. Keil5的芯片支持包(Pack):51芯片和STM32芯片的获取方式完全不同
3.1 Keil5和Keil4最大的架构变化在于Pack机制
如果你是Keil4时代的老用户,对芯片支持的理解可能是"安装完Keil,里面所有芯片型号都有了"。Keil4确实把常见芯片的SFR定义、启动代码、Flash算法都内置到IDE里了,装完就能选。
Keil5则改成了PACK机制:IDE本身只是一个空壳,你要用哪个厂商的芯片,就去装对应的Device Family Pack。比如用STM32F103,需要装Keil.STM32F1xx_DFP;用GD32,需要装GigaDevice的DFP包;用瑞萨RA系列,需要装瑞萨的DFP包。
3.2 51芯片的获取方式:Legacy Device Database和STC特殊通道
这是C51用户最常踩的第一个坑:装完C51,新建工程却发现设备列表里找不到AT89C51。
原因在于Keil5把老设备的型号放在了一个叫Legacy Device Database(经典设备数据库)的地方。你在Select Device对话框的右侧下方,注意看有没有一个"Legacy Device Database"复选框,勾上之后,设备列表会多出一大串老厂商的8051芯片,Atmel的AT89C51、AT89S52,Nuvoton的N76E003,Silicon Labs的C8051F系列都在里面。
如果你用的是STC单片机,就是另一回事了。STC的型号并不在Keil自带的数据库里,需要你自己手动添加。方法是用STC官方的STC-ISP下载工具,里面有一个"Keil仿真设置"标签页,点"添加型号和头文件到Keil中",选择你的Keil安装目录(C:\Keil_v5),它就会把STC系列的头文件拷进C51\INC目录,并把STC的器件信息注册到Keil的设备数据库。
做完这一步,你再新建51工程,厂商列表里会多出一个STC MCU,下面能看到STC89C52RC、STC12C5A60S2这些常见型号。
3.3 STM32芯片支持包的安装:在线还是离线
STM32的DFP包有在线和离线两种装法。
在线安装很简单:打开µVision5,点工具栏上的Pack Installer图标(一个绿色包裹),在Pack标签页左侧找到STMicroelectronics,展开后选STM32F1系列、STM32F4系列等,点右侧的Install按钮。前提是网络能正常访问Keil的服务器。
但国内网络环境下,在线安装经常卡在下载或者更新索引这一步,速度感人。我一般直接用离线包:
- 去Keil的Pack官网页面(keil.arm.com/packs)下载对应芯片系列的.pack文件。
- 比如STM32F103需要的是Keil.STM32F1xx_DFP.2.x.x.pack,STM32F407需要Keil.STM32F4xx_DFP.2.x.x.pack。
- 下载完双击这个.pack文件,它会自动调用µVision5的Pack安装器装进PACK目录。
装完怎么验证?打开Pack Installer,在Devices标签页左侧找到STMicroelectronics,展开能看到STM32F1、STM32F4这些系列,说明包已经装好。或者在新建工程的Select Device对话框里直接搜"STM32F103C8",能搜到型号就是装好了。
4. 环境就绪后的一次完整双平台实测
4.1 新建C51工程:点灯程序和一个Keil老掉牙的提醒
安装验证完,我建议你动手各建一个最小工程跑一遍,而不是急着写业务代码。先用51平台建工程:
- Project -> New µVision Project,起个名字保存。
- 在Select Device窗口勾选Legacy Device Database,然后选择Atmel下的AT89C52,或者你之前添加好的STC89C52RC。
- 弹窗问你是否添加启动文件,选是。C51的启动文件里会初始化栈指针和内存清零,比你自己手写省事。
- 新建一个main.c,写个最简单的点灯:
#include <REG52.H> sbit LED = P1^0; void main(void) { LED = 0; // 低电平点亮,具体取决于开发板硬件 while (1) { // 空循环,点亮后保持 } }- 点编译按钮,能生成HEX文件就算成功。
51程序的烧录方式和STM32完全不同,不需要仿真器,一般是用STC-ISP这个工具通过串口把HEX下载到单片机里。选择串口号、选择芯片型号、加载HEX文件,点下载,然后给开发板上电,注意STC系列是"先点下载再上电"这个顺序。
这里提醒一句:C51评估版只有2KB代码限制,如果你写个稍微复杂点的课程设计就超了。实际应用中2KB其实能装不少东西,但如果你要正经做个项目,还是建议先解决License问题。
4.2 新建STM32工程:寄存器点灯演示完整流程
STM32工程建起来比51复杂一些,主要是启动文件和芯片型号配置的差异。
- Project -> New µVision Project,保存工程。
- Select Device窗口搜STM32F103C8,选中。
- 弹窗问是否添加启动文件,选是。这一步Keil会根据芯片型号自动选择合适的startup文件。
- 在Options for Target -> C/C++里,Define一栏填STM32F10X_MD,这个宏决定了标准外设库编译哪些外设模块。
- 写一段最底层的寄存器操作代码,体会一下和51完全不同的框架:
#include "stm32f10x.h" int main(void) { // 使能GPIOA时钟,APB2外设时钟使能寄存器的第2位对应GPIOA RCC->APB2ENR |= (1UL << 2); // 配置PA1为推挽输出,CRL寄存器控制PIN0~PIN7 // 先把PA1对应的4位清零,再设置为通用推挽输出、50MHz GPIOA->CRL &= ~(0x0FUL << 4); GPIOA->CRL |= (0x02UL << 4); // 用BRR寄存器把PA1拉低,点亮LED GPIOA->BRR = (1UL << 1); while (1) { } }- 在Options for Target -> Debug里选择ST-Link Debugger(如果你用的是J-Link就选J-LINK)。
- 设置Flash Download选项卡里的编程算法,选择STM32F10x Med-density Flash,容量128K。
- 编译下载,LED点亮。
这个寄存器点灯示例的目的不在于教程本身,而是验证环境里启动文件、芯片头文件、编译器、链接脚本这一整条链都通了。如果编译零错误零警告,STM32侧的环境就是健康的。
4.3 两个平台的调试器配置差异
很多51用户第一次接触STM32时会困惑:为什么51用Keil的Simulator就够了,STM32却非要接个仿真器?
因为C51的Simulator做得非常成熟。你在Options for Target -> Debug里选择Use Simulator,不接任何硬件,就能单步执行、查看寄存器、观察内存。我建议学51的新手多习惯用Simulator,很多逻辑错误不用烧片子就能定位。
STM32侧则完全不同。虽然Keil也带Cortex-M的模拟器,但实际开发中几乎没人用,大家都是接ST-Link、J-Link这些调试器在线调试。在Debug选项卡选择对应的调试器,然后点Settings确认它能识别到目标芯片。
调试时有个实用技巧值得记住:在Debug模式下,打开View -> Watch Windows -> Watch 1,在Name列直接输入结构体变量名,回车,它会自动展开所有成员。局部结构体变量需要在函数内打断点停住之后才能查看,直接在全局处输入是看不到的。如果你调试的是外设配置,还可以打开Peripherals菜单,图形化查看GPIO、定时器、串口的寄存器状态,比盯着寄存器数值直观得多。
4.4 老工程在AC6下编译报错的解决办法
STM32工程建立好之后,还有一个绕不开的坑:ARM编译器版本。
MDK在5.37版本之后默认编译器是AC6(armclang),对C99/C11的支持好,编译速度快,但语法检查也更严格,很多老工程从AC5迁过来会报一堆错误,最常见的就是"implicit declaration of function"、变量声明不能放在第一行之后等等。
遇到这种情况,在Options for Target -> Target页面,找到ARM Compiler下拉框,把它切换成"Use default compiler version 5"。前提是安装MDK时勾选了Legacy Arm Compiler 5组件。
切完之后记得Project -> Clean Target,然后重新Build,大概率能解决。
5. 折腾Keil这么久,把这些坑提前替你踩一遍
5.1 装了C51却找不到51芯片:三步排查法
这个问题的排查链路很清晰,按顺序走就好:
第一步,确认你打开的是装了C51的那个µVision5。检查方法:Help -> About,看有没有显示C51相关信息。如果你之前拆开装了MDK目录,打开的是MDK的快捷方式,那肯定看不到51。
第二步,确认Select Device对话框里勾了Legacy Device Database。这是Keil5特有的设计,很多教程没提,导致不少人卡在设备列表里找不到Atmel。
第三步,检查C51安装目录下的Device Database字段是否完整。C:\Keil_v5\C51\DATABASE\下应该有C51的数据库文件,如果这个目录空了或者报错,重装C51组件即可。
5.2 编译报License错误或代码超限
我自己遇到过最典型的情况是:51工程编译好好的,STM32工程一编译就报类似"Product MDK-ARM not found in license"的错误。这种基本就是MDK的License没激活或者激活信息丢了。
打开File -> License Management,检查Product列表里有没有两条记录,对应C51和MDK-ARM。如果有某一条显示为Evaluation或空,就去把对应的License注册上。
代码超限则是另一类提示。C51评估版2K限制,报错信息是CODE SIZE LIMIT EXCEEDED;MDK评估版32K限制,链接时会报Load Region的size超限。如果你出现这两个报错,说明不是代码问题,是License限制问题。
5.3 Pack安装不上的几种情况
情况一:Pack Installer一直转圈加载不出来。多半是网络访问Keil服务器超时,直接关掉提示,用离线.pack包安装。
情况二:双击.pack文件没反应。先确认电脑上已经装了µVision5,然后右键.pack文件,看打开方式,如果默认打开程序不是µVision,就手动用µVision启动它。再不行就以管理员身份运行µVision,然后再双击。
情况三:Pack装上之后新建工程还是搜不到STM32型号。检查Pack的版本和Keil版本是否兼容,太老的Keil版本可能装不了新出的DFP包,反过来太新的Keil版本也可能需要升级。
5.4 STM32调试器连不上:JTAG和SWD引脚的坑
初学STM32的人经常会想把PA13、PA14、PA15、PB3、PB4这几个引脚当普通IO用,因为这些引脚在复位后默认是JTAG调试口。如果你用标准外设库写了类似GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);的代码,把JTAG功能关掉,只保留SWD,那ST-Link还能连上。如果你调用了GPIO_Remap_SWJ_Disable彻底关闭SWJ,那就麻烦了,ST-Link直接识别不到芯片,只能通过ISP串口擦除Flash或者用Boot0拉高恢复。
这个坑的根源在于:代码里把调试口关了,下次就没法在线调试了。我的建议是日常调试阶段不要动SWJ相关的设置,等功能都调通了,最后再把释放PA13~PA15的功能加上。
5.5 目录名含中文和空格引发的玄学问题
最后这条听着像玄学,但我是真实遇到过的。工程保存路径如果带中文字符,比如D:\课程设计\stm32\,经常会出现"cannot open source file"、"unknown type name"这种和代码本身无关的报错。Keil的工具链对中文路径的支持很敷衍,C51侧尤其明显。
所以强烈建议:Keil安装目录、工程文件、源码文件全部放在纯英文、无空格、无特殊符号的路径下。我自己的目录习惯是D:\Work\Projects\STM32F103_Demo这种结构,省得后续一堆麻烦。
最后再分享一个和Keil C51相关的进阶话题,正好也是热搜上有人问的:51的Boot和App中断处理。
8051的中断向量固定在程序存储器起始的0x0003~0x0033区域。做IAP升级时,App代码通常从0x1000这种偏移地址开始跑,但CPU响应中断时依然会去0x0000地址附近取向量。所以Boot区固件里必须放一张中断跳转表,在0x0003、0x000B、0x0013这些位置各放一条LJMP指令,跳转到App对应的中断服务入口。App工程这边,在Options for Target -> BL51 Locate里把Code Range起始地址改成0x1000,确保链接器把代码和中断向量表定位到偏移之后再编译。做完之后用反汇编窗口检查一下向量表位置,确认无误再烧录。这个机制属于C51环境里少数能真正把人绕晕的点,环境搭好之后建议找个小工程亲手试一次。