☰
Keil5新建STM32标准库工程:从零配置到编译下载避坑指南
2026/10/2 9:38:11 网站建设 项目流程

写这篇教程之前,我犹豫了一下要不要写。网上搜"Keil5新建工程",确实有铺天盖地的图文教程,但大多数都停留在"点Next、点Finish"的层面,完全没讲清楚为什么要这样配置。结果就是很多新手照着教程建好工程,一编译几十个报错,也不知道去哪里排查,回头又得重新搜一遍。之所以还要再写这篇,是因为我在带新人、逛论坛的过程里发现,真正把Keil5 MDK新建工程这件事讲透的内容并不多,尤其是标准外设库(Standard Peripheral Library)工程的完整搭建流程、编译下载时那些看起来莫名其妙的问题该怎么定位,很少有教程系统地讲。

这篇教程以STM32F103C8T6为基础,使用Keil MDK 5.37版本,配合标准外设库V3.5.0,从准备工作开始,到新建工程、配置魔术棒(Options for Target)、添加启动文件、编译下载,全程手把手。你不需要有任何Keil的使用经验,但需要已经装好了Keil MDK和对应的器件支持包(Device Family Pack,简称DFP)。如果连Keil都还没装,建议先去ST官网把STM32F1系列的DFP下载好,再搜一篇安装教程按照步骤装完,再回来看这篇,否则下一步会卡住。

1. 新建工程前,先搞清楚几个"为什么"

新手最容易犯的错误是上来就开始点点点,建到一半才突然发现,自己连到底要用标准库还是HAL库都没想明白,工程建好了,代码结构却乱成一锅粥。这里花几分钟把几件影响后面所有操作的事说清楚。

1.1 标准库和HAL库的区别:你的选择决定了工程结构

STM32的开发库目前主流就两个:标准外设库(Standard Peripheral Library,简称标准库)和HAL库。标准库偏向寄存器操作,代码直接、简洁,执行效率高,但需要手动处理的细节多,适合学习原理、做小型项目。HAL库封装层次更高,代码量更大,但跨芯片平台的移植性更好,配合STM32CubeMX图形化配置工具,开发速度更快,适合做产品原型和项目交付。

对于学STM32的新手,或者只是想在Keil里跑通一个简单的点灯、串口实验,我非常推荐标准库。原因很简单:标准库里每个外设的初始化过程、寄存器配置逻辑都是显式暴露的,你在阅读代码的过程里能清晰地看到"这个寄存器写这个值有什么作用",对理解芯片工作方式帮助极大。HAL库虽然也能完成同样的功能,但一层套一层的封装会让初学者很难建立底层概念。

这篇文章以标准库V3.5.0为例,如果你用的是HAL库,工程搭建流程大方向一致,只是文件添加、宏定义、启动文件这些地方略有区别,后面我会专门提一句HAL库版本有哪些不同。

1.2 STM32F103C8T6的启动文件该选哪个:HD还是MD

STM32启动文件(startup_stm32f10x_hd.s等)是根据芯片Flash容量来区分的。STM32F103C8T6的Flash容量是64KB,按官方分类属于中等容量(Medium-density),应该用 start_stm32f10x_md.s。这是新手最容易踩的坑之一,很多人都听说过要选hd,但那是针对STM32F103RCT6(256KB Flash)这类高容量芯片的。

如果你选错启动文件,工程也能编译通过,但下载到板子上大概率跑不起来,或者调试时发现在启动阶段就异常跳飞了。这个坑在后面的避坑章节我会再展开一次。

1.3 Keil5 MDK、Keil uVision、MDK-ARM到底什么关系

很多新手下载软件的时候会被这几个词搞晕:Keil5、MDK、MDK-ARM、uVision5。这里统一解释一下:Keil是公司名字,MDK(Microcontroller Development Kit)是Keil针对ARM系列芯片推出的开发套件,uVision是它的集成开发环境界面程序,MDK5.37里自带的版本是uVision5。所以你打开软件,看到窗口标题写着"uVision5",这跟"Keil5 MDK"就是一回事,不存在什么"Keil5和Keil5 uVision5是两款软件"的区分。

有一个点需要特别注意:MDK-ARM只支持ARM架构的芯片,不支持8051等51单片机。如果你同时玩51和STM32,需要在电脑上分别安装Keil C51和MDK-ARM两个版本,并且安装路径要分开,界面看起来一样,但底层编译工具链完全不同。这也是热词里有"keil5mdk和keil5uvision有什么区别"这个问题的原因。

2. 动手新建工程的完整过程

前面的理论问题梳理清楚了,现在正式进入实际操作。整个过程我按顺序拆成六个步骤,每一步都写得比较细,照着做基本不会漏东西。

2.1 第1步:规划你的工程目录结构

建工程之前,先在你的工作目录下建一个用来放工程的文件夹,名字随你喜欢,但建议不要带中文和空格,否则Keil在编译时可能因为路径解析问题报奇怪错误。我习惯用类似"STM32_Project"这样的目录规划:

  • 一级目录:工程名(如LED_Demo)
  • 二级目录下面按照功能拆分子文件:
    • Doc(存放文档说明)
    • Libraries(核心库文件)
    • Project(存放Keil工程文件)
    • User(存放main.c等用户代码)
    • Hardware(存放自己写的外设驱动代码)

Libraries里面再分CMSIS和STM32F10x_StdPeriph_Driver两个文件夹。CMSIS放内核相关的头文件、系统初始化文件,标准外设驱动库的inc和src则放在STM32F10x_StdPeriph_Driver里。用户自己写的程序放在User和Hardware里。

2.2 第2步:准备标准库里那堆文件

如果你下载的是标准外设库V3.5.0的压缩包,解压之后可以找到Libraries和Project两个文件夹。打开Libraries,里面有CMSIS和STM32F10x_StdPeriph_Driver,这两个文件夹直接复制到你刚才规划的工程目录的Libraries里即可。

你需要的核心启动文件在Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm路径下。标准库的启动文件只分了ld/ md/ hd/ xl这几档,按芯片容量选就行。我用STM32F103C8T6就选startup_stm32f10x_md.s。

还需要在CMSIS里找到两个核心文件:

  • core_cm3.c和core_cm3.h:ARM Cortex-M3内核相关定义
  • system_stm32f10x.c和system_stm32f10x.h:系统时钟初始化

2.3 第3步:打开Keil,新建空白工程

启动MDK5后,在菜单栏选择Project | New uVision Project,然后选到你规划的Project文件夹里,输入工程名字点保存。此时弹出一个对话框,让你选择设备型号。如果你之前已经装好了STM32F1系列的DFP包,在这个界面左侧的搜索栏直接输入STM32F103C8,下方会列出对应芯片,选中后点OK。

如果这个对话框里什么都搜不到,说明DFP包没装或者装错了版本。解决办法是在Keil里打开Pack Installer,找到STMicroelectronics目录,展开后找到STM32F1系列DFP,点Install等待下载完成,再重新新建工程。

选完芯片后会弹出一个问你是否要复制启动文件到工程文件夹的对话框,建议直接选No,后面手动控制启动文件。因为自动复制的启动文件不一定会根据你的芯片容量正确选型,而且文件位置可能和你自己的工程目录结构冲突,不如自己管理来得清晰。

2.4 第4步:添加文件到工程

在工程管理界面左侧的Project栏,右键点击Target 1,选择Manage Project Items。在这里可以自由组织工程分组。我习惯这样建:

  • User:放main.c、stm32f10x_it.c(中断服务函数文件)等
  • CMSIS:放core_cm3.c、system_stm32f10x.c
  • Startup:放启动文件startup_stm32f10x_md.s
  • FWLIB:放标准库的src目录下的所有外设驱动源文件
  • Hardware:放自己写的外设驱动

添加完第一层分组后,还需要把物理路径和分组对应起来。先关闭Manage Project Items对话框,回到主界面。选中一个分组,右键点击Manage Project Items,在每个分组下点Add Files,把对应文件加进来。注意文件类型过滤器,启动文件、源文件、头文件的扩展名不同,需要在文件类型下拉框里切换。

标准库src下有很多外设驱动源文件,不需要全加进来,用哪个外设加哪个。比如只用GPIO和RCC,就加stm32f10x_gpio.c和stm32f10x_rcc.c。懒一点的全加进去也行,就是编译时间长一些,工程冗余一些。新手建议即使暂时用不到,也把常用外设加上省得后面反复操作。

2.5 第5步:指定头文件路径(Include Path)

添加源文件只是第一步,编译时Keil要能找到对应的.h头文件,否则会报fatal error: stm32f10x.h: No such file or directory。解决办法是进入魔术棒Options for Target,在C/C++选项卡里,把Include Paths那栏填上所有头文件所在的路径。

需要填的路径至少包括:

  • 工程目录里的User文件夹
  • Libraries\CMSIS\CM3\CoreSupport
  • Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x
  • Libraries\STM32F10x_StdPeriph_Driver\inc
  • 你新建的Hardware文件夹

路径用分号分隔。在输入框右侧有三个点的按钮,点开可以像资源管理器一样逐层选择文件夹,逐个加进去,比手输路径更不容易出错。

2.6 第6步:配置宏定义(Define)

在C/C++选项卡里还有一个Define输入框,标准库工程里通常要填USE_STDPERIPH_DEVICE,有些教程还会加上STM32F10X_MD。前者是标准库编译时用来使能外设驱动的宏,后者是配合条件编译区分芯片容量的宏。STM32F103C8T6就填STM32F10X_MD,符合中等容量芯片的定位。有些HAL库工程还会填USE_HAL_DRIVER,但标准库不需要。

这些宏定义看起来简单,少了任何一项,编译时都会报一堆莫名其妙的未定义错误。新手经常在这里看漏,看到几千行报错直接懵了。

3. 魔术棒Options for Target:少配一项都会翻车

工程文件添加完毕,头文件路径和宏定义也填好了,但还有几个关键配置没过。很多人东拼西凑建的工程之所以下载不了程序、调试失败,绝大多数情况都是这节内容出了问题。

3.1 Target选项卡:芯片型号和浮点单元

魔术棒的第一个Tab是Device,显示当前选的芯片型号,一般不需要动。第二个Tab是Target,这里有几个注意事项:

  • ARM Compiler版本选择:在新版MDK5里默认用的编译器是Arm Compiler 6(AC6),老教程大多是针对AC5写的。标准库的代码是以AC5的语法风格写的,直接拿到AC6下编译偶尔会遇到语法警告甚至报错。解决办法是在这个界面把编译器版本切换成AC5(如果安装了对应版本),或者保留AC6,把有兼容性问题的代码改用__STATIC_INLINE等新写法。新手建议直接用AC5比较稳妥。
  • 不要勾选Use MicroLIB:这个勾选选项只在你使用标准库的printf重定向到串口时需要用。它会把标准C库精简掉一部分,减小Flash占用,代价是部分C库函数行为不一样。刚建工程用不上,可以先不勾。
  • Floating Point Hardware:STM32F103没有FPU,就选Not Used,不用管。

3.2 Output选项卡:生成Hex文件

点Output选项卡,勾选Create HEX File。这一步很多人漏掉,结果程序编译通过、下载也没问题,但量产烧录的时候发现根本没有.hex文件能用。它的作用是在编译完成后额外生成一个HEX格式的机器码文件,这个文件可以直接用ST-LINK的烧录工具或串口ISP方式烧进芯片,不需要依赖Keil环境。日常学习点灯可以不在意,但如果你后面要做离线烧录、给板卡批量写程序,这个文件是刚需。

3.3 Debug选项卡:ST-Link还是J-Link

Debug选项卡用于选择调试器和下载器。左边Use那栏默认可能是Use Simulator,要改成你实际使用的调试器。我用的是ST-Link,就在下拉框里选ST-Link Debugger。选完右边还有一行Settings按钮,点进去可以看到调试器是否识别到目标板芯片信息,如果这里读不到芯片,程序下载基本不可能成功。

我用的ST-Link在调试打印机被识别成CMSIS-DAP或ST-Link V2,新版Keil都能兼容。J-Link用户就选J-LINK / J-TRACE Cortex。

3.4 Utilities选项卡:Flash Download设置必看

Utilities选项卡对于新手来说最容易被忽略,但下载失败大多集中在两个地方:一是Flash Download里的编程算法(Programming Algorithm)没有添加,二是Reset and Run没有勾选。

点Utilities选项卡,在Flash Download那一栏点右边的Settings,弹出来的对话框里查看Programming Algorithm列表是否为空。为空的话点Add,选择对应芯片的烧写算法。STM32F103C8T6是512KB Flash的型号,算法文件选择STM32F10x Med-density Flash,不要选成Low-density或High-density,否则下载时地址空间对不上会报错。

对话框下方有个Reset and Run复选框,勾选后程序下载完自动复位运行,不然每次下载完你都要手动按一下板子上的复位键才能跑程序。这个小细节对调试体验影响很大,建议一开始就勾上。

3.5 C/C++选项卡的其他小项

C/C++选项卡里除了前面提到的Include Paths和Define,还有个Optimization选项。新手调试阶段建议选Level0(-O0),也就是关闭优化。优化级别越高,调试时看到的变量实时值越可能"不真实",断点也可能因为代码被重排而跳来跳去,增加定位问题的难度。运行稳定后再切换成Level2或Level3来减小代码体积和提升运行速度。

4. 编译、下载和第一个点灯程序

所有配置都完成之后,现在写一段最基础的程序验证整个工程是否构建成功。

4.1 编写main.c和系统初始化

在User分组下的main.c中写入最精简的模板。标准库的应用代码需要包含stm32f10x.h头文件,并在main函数里调用SystemInit(如果是标准库新版本的启动文件中已经调用过,则可以省略),再初始化外设。用GPIO点亮PC13上的LED,完整代码如下:

#include "stm32f10x.h" void delay(void) { unsigned int i; for (i = 0; i < 500000; i++); } int main(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); while (1) { GPIO_SetBits(GPIOC, GPIO_Pin_13); delay(); GPIO_ResetBits(GPIOC, GPIO_Pin_13); delay(); } }

同时建议在User分组中添加stm32f10x_it.c,这个文件负责存放中断服务函数。标准库模板里会有一个空实现的版本,你可以从标准库的Project例程中复制过来,避免后面要用中断时出现硬错误。

4.2 编译时遇到的那些报错

写完代码点Build按钮(或者按F7),正常情况下底部编译输出栏会显示0 Error(s),0 Warning(s)。如果出现两个Warnings,通常来自标准库里的某些宏未被使用,不影响运行,暂可忽略。

如果编译直接报错,常见的有:

  • error: #5: cannot open source input file "core_cm3.h":Include Paths没配对,回去把CMSIS那两个路径加上
  • error: #20: identifier "GPIO_Pin_13" is undefined:头文件没包含,或者宏定义STM32F10X_MD没填
  • Error: L6218E: Undefined symbol SystemInit (referred from startup_stm32f10x_md.o):system_stm32f10x.c文件没有添加到工程,或者分组文件里只有启动文件缺失源文件

这些报错看起来吓人,但把每个报错的信息和文件路径逐行翻译成中文,基本上就是"找不到XX文件"或者"XX符号没定义"两类,顺着上面配置项排查就能解决。最忌讳的是看到报错就重新建工程,反而找不到真正的问题。

4.3 下载和运行

编译通过后,接好ST-Link,点击Download按钮下载。下载成功的标志是底部信息栏出现Flash Load finished at ...,同时板子上的LED开始按照你写的延时节奏闪烁。如果这一步出问题,留意以下三种情况:

  • Error: Flash Download failed - "Cortex-M3":通常是因为芯片进入休眠或者没有正确复位,可以把板子断电重插,或者按住复位键再点下载,下载瞬间松开复位键
  • Error: No ST-LINK detected:驱动或接线问题,去设备管理器查看ST-Link是否被正确识别,识别不到就重新安装ST-Link驱动
  • 下载成功但程序不运行:检查Utilities里的Reset and Run有没有勾选,没勾就手动按一下板子上的复位键

5. 我实际使用中一定要再重复的几件事

工程搭建的流程走完一遍之后,最后再分享几次实操下来比较痛的经验,每一条都是我自己踩过并花时间排查过的,希望对你有帮助。

5.1 路径中永远不要带中文

这一点老生常谈但年年有人踩。Keil5的编译器对中文路径的支持一直不太好,就算当前版本能编译通过,后面换台电脑、换个编译器版本就可能崩。工程文件夹和安装路径统一使用英文或拼音,别在"新建文件夹(3)"里建工程。我见过一个朋友把工程放在桌面上一个叫"毕业设计最终版"的文件夹里,结果AC6编译器直接报无法打开中间文件,换AC5倒是编译过了,但每次构建都要三十秒以上,极其难受。

5.2 启动文件和芯片容量不匹配时,现象会很诡异

如果你选错了启动文件,程序编译正常,下载正常,但芯片运行起来完全不符合预期:点灯不亮、串口乱码、调试时PC指针跑飞。排查这种问题非常耗费时间。遇到此类现象,务必回来检查startup文件是否是与你芯片Flash容量对应的版本。STM32F103C8T6是64KB Flash,用md版本,不要用hd版本。

5.3 标准库和HAL库的工程不能混用

很多人在中途从标准库迁移到HAL库,或者反过来,把两个库的源文件都加进同一个工程。两个库对寄存器操作、时钟配置的实现完全不同,混在一起编译十有八九会出现重复定义、头文件冲突甚至编译直接崩溃。实际项目中确定了一种库,就坚持用到项目结束,不要中途切换,否则改代码的成本远大于你的预期。

5.4 用CubeMX生成工程也需要做同样的事

热词里有cubemx新建工程,如果你用STM32CubeMX生成工程,那么恭喜你,前面提到的启动文件、芯片选型、头文件路径这些事CubeMX都会自动生成好,不用手动配置。但CubeMX默认生成的是HAL库工程,而且生成时选择的IDE要选MDK-ARM V5,生成后用Keil打开还是建议在魔术棒里把Include Paths和Define检查一遍。CubeMX不是万能药,生成后的工程依然可能因为编译器版本不匹配(AC5/AC6)出现标准库风格的报错。

5.5 最后补充一个调试效率小技巧

如果你用的是ST-Link,并且Keil版本比较新,可以在Debug选项卡里把ST-Link的SWO跟踪使能打开,配合ITM_SendChar函数实现printf调试输出。串口调试要接线、要配置波特率,SWD调试只要一根线,查看变量、打印日志都方便很多。具体操作是:在main.c中包含stdio.h,然后用fputc重定向ITM端口,在Debug(printf)Viewer窗口就能看到打印信息。这一段细节偏进阶,但学会了能明显提升调试效率。

建工程这件事,本质上就是一个模板化的流程。第一次跟着教程完整走一遍,后面再建新项目就是十分钟的事。我自己的习惯是在本地保留一份建好的标准库工程模板,新建项目时直接复制一份改改名字就开工,比每次从头配置省事很多。如果你也打算长期用Keil开发STM32,建议花点时间整理自己的模板库,把常用外设的裸机驱动都封装好放进去,后续项目开发效率会有质的提升。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询