为什么要先认识标准库?
官方标准库根目录怎么看?
首先,你需要从 ST 官方网站下载 STM32F10x 标准外设库(推荐最新稳定版本 V3.6.0):https://www.st.com.cn/zh/embedded-software/stsw-stm32054.html?utm_source
页面说明:下载页面包含版本信息、授权协议、用户手册(.chm)等资源,建议保留手册以便查阅函数说明。

解压后根目录如下:

STM32F10x_StdPeriph_Lib_V3.6.0├─ _htmresc ← HTML 文档资源,可跳过├─ Libraries ← 重点,工程所需库文件├─ Project ← 重点,官方模板和官方例程├─ Utilities ← 官方评估板支持包,普通开发板可跳过├─ Package_license.html ← 授权说明,可跳过├─ Package_license.md ← 授权说明,可跳过├─ Release_Notes.html ← 版本说明,可跳过└─ stm32f10x_stdperiph_lib_um.chm ← 标准库用户手册,建议保留
LibrariesProjectstm32f10x_stdperiph_lib_um.chm
Libraries 是工程真正需要的库文件。Project 是官方模板和官方例程。stm32f10x_stdperiph_lib_um.chm 是标准库用户手册。
至于 _htmresc、授权文件、版本说明,前期知道它们存在即可,不需要重点研究。
Libraries:工程真正需要的库文件

Libraries├─ CMSIS└─ STM32F10x_StdPeriph_Driver
CMSIS → 内核和芯片支持STM32F10x_StdPeriph_Driver → STM32外设标准库驱动
CMSIS:启动、内核、芯片和系统时钟

Libraries└─ CMSIS└─ CM3├─ CoreSupport ← Cortex-M3 内核支持└─ DeviceSupport ← ST 芯片支持

CoreSupport├─ core_cm3.c ← Cortex-M3 内核支持源文件└─ core_cm3.h ← Cortex-M3 内核支持头文件
NVIC;SysTick;SCB;内核寄存器访问;一些内核相关的基础定义。

DeviceSupport└─ ST└─ STM32F10x├─ startup ← 启动文件目录├─ stm32f10x.h ← STM32F10x 芯片总头文件├─ system_stm32f10x.c ← 系统时钟初始化源文件└─ system_stm32f10x.h ← 系统时钟初始化头文件
stm32f10x.h ← 定义寄存器、外设基地址、中断号、芯片宏system_stm32f10x.c ← 提供 SystemInit(),负责系统时钟初始化startup_xx.s ← 复位后最先执行,最后进入 main()
启动文件应该怎么选?


STM32F10x└─ startup└─ arm ← Keil MDK 使用这个目录├─ startup_stm32f10x_ld.s ← 低容量芯片├─ startup_stm32f10x_md.s ← 中容量芯片,STM32F103C8T6 常用├─ startup_stm32f10x_hd.s ← 大容量芯片├─ startup_stm32f10x_xl.s ← 超大容量芯片└─ startup_stm32f10x_cl.s ← 互联型芯片
ld → Low-density,低容量芯片md → Medium-density,中容量芯片hd → High-density,大容量芯片xl → XL-density,超大容量芯片cl → Connectivity line,互联型芯片
标准外设库驱动目录:inc 和 src 分别干什么?

Libraries└─ STM32F10x_StdPeriph_Driver├─ inc → 头文件目录└─ src → 源文件目录


Libraries└─ STM32F10x_StdPeriph_Driver├─ inc ← 外设驱动头文件目录│ ├─ misc.h ← NVIC、SysTick 等辅助函数声明│ ├─ stm32f10x_gpio.h ← GPIO 头文件│ ├─ stm32f10x_rcc.h ← 时钟控制头文件│ ├─ stm32f10x_usart.h ← 串口头文件│ ├─ stm32f10x_tim.h ← 定时器头文件│ ├─ stm32f10x_adc.h ← ADC 头文件│ ├─ stm32f10x_dma.h ← DMA 头文件│ └─ ...│└─ src ← 外设驱动源文件目录├─ misc.c ← NVIC、SysTick 等辅助函数实现├─ stm32f10x_gpio.c ← GPIO 驱动实现├─ stm32f10x_rcc.c ← RCC 驱动实现├─ stm32f10x_usart.c ← USART 驱动实现├─ stm32f10x_tim.c ← TIM 驱动实现├─ stm32f10x_adc.c ← ADC 驱动实现├─ stm32f10x_dma.c ← DMA 驱动实现└─ ...
Project:官方模板和官方例程

Project├─ STM32F10x_StdPeriph_Template ← 官方标准库工程模板└─ STM32F10x_StdPeriph_Examples ← 官方标准库外设例程
官方模板里面有什么?

Project└─ STM32F10x_StdPeriph_Template├─ EWARM ← IAR Embedded Workbench 工程文件目录,使用 IAR 时参考├─ HiTOP ← HiTOP 工具链工程文件目录,普通 Keil 用户可跳过├─ MDK-ARM ← Keil MDK 工程文件目录,使用 Keil 时重点看├─ RIDE ← RIDE 工具链工程文件目录,普通 Keil 用户可跳过├─ TrueSTUDIO ← TrueSTUDIO 工程文件目录,普通 Keil 用户可跳过│├─ LICENSE.txt ← 模板工程授权说明,可了解,创建工程时不是必须文件├─ Release_Notes.html ← 模板工程版本说明,可跳过│├─ main.c ← 主函数模板,程序入口,实际项目通常要改成自己的 main.c├─ stm32f10x_conf.h ← 标准库配置文件,决定包含哪些外设库头文件,重点├─ stm32f10x_it.c ← 中断服务函数源文件,写 SysTick、USART、EXTI 等中断函数├─ stm32f10x_it.h ← 中断服务函数头文件,声明中断函数└─ system_stm32f10x.c ← 系统时钟初始化文件,提供 SystemInit(),重点
STM32F10x_StdPeriph_Template├─ main.c ← 主函数模板├─ stm32f10x_it.c ← 中断服务函数源文件├─ stm32f10x_it.h ← 中断服务函数头文件├─ stm32f10x_conf.h ← 标准库配置文件├─ system_stm32f10x.c ← 系统时钟文件,模板中也提供一份
/********************************************************************************* @file Project/STM32F10x_StdPeriph_Template/stm32f10x_conf.h* @author MCD Application Team* @version V3.6.0* @date 20-September-2021* @brief Library configuration file.******************************************************************************* @attention** Copyright (c) 2011 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************//* Define to prevent recursive inclusion -------------------------------------*/#ifndef __STM32F10x_CONF_H#define __STM32F10x_CONF_H/* Includes ------------------------------------------------------------------*//* Uncomment/Comment the line below to enable/disable peripheral header file inclusion */#include"stm32f10x_adc.h"#include"stm32f10x_bkp.h"#include"stm32f10x_can.h"#include"stm32f10x_cec.h"#include"stm32f10x_crc.h"#include"stm32f10x_dac.h"#include"stm32f10x_dbgmcu.h"#include"stm32f10x_dma.h"#include"stm32f10x_exti.h"#include"stm32f10x_flash.h"#include"stm32f10x_fsmc.h"#include"stm32f10x_gpio.h"#include"stm32f10x_i2c.h"#include"stm32f10x_iwdg.h"#include"stm32f10x_pwr.h"#include"stm32f10x_rcc.h"#include"stm32f10x_rtc.h"#include"stm32f10x_sdio.h"#include"stm32f10x_spi.h"#include"stm32f10x_tim.h"#include"stm32f10x_usart.h"#include"stm32f10x_wwdg.h"#include"misc.h"/* High level functions for NVIC and SysTick (add-on to CMSIS functions) *//* Exported types ------------------------------------------------------------*//* Exported constants --------------------------------------------------------*//* Uncomment the line below to expanse the "assert_param" macro in theStandard Peripheral Library drivers code *//* #define USE_FULL_ASSERT 1 *//* Exported macro ------------------------------------------------------------*/#ifdef USE_FULL_ASSERT/*** @brief The assert_param macro is used for function's parameters check.* @param expr: If expr is false, it calls assert_failed function which reports* the name of the source file and the source line number of the call* that failed. If expr is true, it returns no value.* @retval None*/#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))/* Exported functions ------------------------------------------------------- */voidassert_failed(uint8_t* file, uint32_t line);#else#define assert_param(expr) ((void)0)#endif/* USE_FULL_ASSERT */#endif/* __STM32F10x_CONF_H */
#include"stm32f10x_gpio.h"#include"stm32f10x_rcc.h"#include"stm32f10x_usart.h"
官方例程怎么用?

STM32F10x_StdPeriph_Examples├─ GPIO├─ USART├─ TIM├─ ADC├─ DMA├─ SPI├─ I2C├─ EXTI├─ NVIC├─ RCC├─ SysTick└─ ...
推荐工程文件夹和工程名
STM32F103C8T6 → 目标芯片StdPeriph → 基于标准外设库Template → 工程模板
推荐工程模板目录
STM32F103C8T6_StdPeriph_Template├─ CORE ← 手动创建:启动文件、CMSIS、芯片支持、系统时钟├─ FWLIB ← 手动创建:ST 官方标准外设库(Firmware Library,固件库)│ ├─ inc ← 手动创建/复制:标准库头文件│ └─ src ← 手动创建/复制:标准库源文件├─ USER ← 手动创建:main.c、中断文件、stm32f10x_conf.h├─ HARDWARE ← 手动创建:自己写的板级驱动,如 LED、KEY、USART└─ SYSTEM ← 手动创建:delay、sys 等通用工具
官方 CMSIS\CoreSupport → CORE官方 CMSIS\DeviceSupport\ST\STM32F10x → CORE官方 CMSIS\...\startup\arm → CORE官方 STM32F10x_StdPeriph_Driver\inc/src → FWLIB官方 Template 中的 main.c、it.c、conf.h → USER自己写的 led.c、key.c、usart.c → HARDWARE自己写的 delay.c、sys.c → SYSTEM
然后,使用 Keil 新建工程、编译工程后,会自动生成一些工程文件和输出目录,例如:
STM32F103C8T6_StdPeriph_Template├─ Objects ← Keil 自动生成:编译输出,如 .o、.axf、.hex├─ Listings ← Keil 自动生成:编译列表文件├─ DebugConfig ← Keil 自动生成:调试配置├─ RTE ← Keil 自动生成:运行环境相关文件,部分工程会出现├─ STM32F103C8T6_StdPeriph_Template.uvprojx ← Keil 自动生成:工程文件└─ STM32F103C8T6_StdPeriph_Template.uvoptx ← Keil 自动生成:工程选项文件
如果你在 Keil 里把输出目录改成 OBJ,那也可以是:
OBJ ← Keil 编译输出目录,通常由 Keil 生成或按工程配置生成但对初学者来说,更建议顺着 Keil 默认名字写:
ObjectsListings
STARTUPCMSISFWLIBUSER
创建工程模板的具体步骤

这个名字表示:
STM32F103C8T6 ← 目标芯片StdPeriph ← 使用标准外设库Template ← 工程模板
在模板根目录下,手动创建这些源码组织目录:

STM32F103C8T6_StdPeriph_Template├─ CORE├─ FWLIB│ ├─ inc│ └─ src├─ USER├─ HARDWARE└─ SYSTEM
注意:这里只需要手动创建源码目录。下面这些不要急着手动创建:
ObjectsListingsDebugConfigRTE.uvprojx.uvoptx
这些一般由 Keil 新建工程或编译工程时自动生成。

CORE├─ core_cm3.c├─ core_cm3.h├─ stm32f10x.h├─ system_stm32f10x.c├─ system_stm32f10x.h└─ startup_stm32f10x_md.s


官方 STM32F10x_StdPeriph_Driver\inc → 工程 FWLIB\inc官方 STM32F10x_StdPeriph_Driver\src → 工程 FWLIB\src
FWLIB├─ inc│ ├─ stm32f10x_gpio.h│ ├─ stm32f10x_rcc.h│ ├─ stm32f10x_usart.h│ └─ ...└─ src├─ stm32f10x_gpio.c├─ stm32f10x_rcc.c├─ stm32f10x_usart.c└─ ...

USER├─ main.c├─ stm32f10x_it.c├─ stm32f10x_it.h└─ stm32f10x_conf.h
#include"stm32f10x.h"intmain(void){while (1){}}


这是 Keil 的 Manage Run-Time Environment 窗口,简称 RTE 管理窗口。它的作用是让你选择 Keil Pack 里的组件,比如:
CMSISDeviceFile SystemNetworkUSBBoard Support
也就是说,它主要服务于 Keil Pack / CMSIS-Pack 组件化工程。因为我们现在创建的是传统标准库工程模板,不是用 Keil Pack 自动生成组件的工程,所以直接关闭就可以了。此时可以看到工程根目录下,由 Keil 新建工程或编译工程时自动生成了一些文件。


Project: STM32F10x_StdPeriph_Template└─ Target_1└─ Source Group 1
Target_1├─ CORE├─ FWLIB├─ USER├─ HARDWARE└─ SYSTEM
真实文件夹 CORE ↔ Keil Group CORE真实文件夹 FWLIB ↔ Keil Group FWLIB真实文件夹 USER ↔ Keil Group USER真实文件夹 HARDWARE ↔ Keil Group HARDWARE真实文件夹 SYSTEM ↔ Keil Group SYSTEM
右键 Target_1→ Manage Project Items→ 在 Groups 里删除 Source Group 1 或重命名→ 添加 CORE、FWLIB、USER、HARDWARE、SYSTEM或者右键 Source Group 1,先把它重命名成 CORE,再继续添加其他 Group。


STARTUPCMSISFWLIBUSER
.c 文件.s 文件




CORE├─ startup_stm32f10x_md.s├─ core_cm3.c└─ system_stm32f10x.cFWLIB├─ misc.c├─ stm32f10x_gpio.c├─ stm32f10x_rcc.c├─ stm32f10x_usart.c├─ stm32f10x_tim.c└─ ...USER├─ main.c└─ stm32f10x_it.c
HARDWARE 和 SYSTEM 现在空着没问题,后面你写 led.c、delay.c 等模块时再添加。为什么创建了文件夹,还要导入 Keil?
Include Paths 到底是什么?

#include"stm32f10x.h"#include"led.h"
stm32f10x.hled.h

..\CORE..\USER..\FWLIB\inc
HARDWARE\LED\led.hSYSTEM\delay\delay.h
..\HARDWARE\LED..\SYSTEM\delay
Include Paths 不是添加源文件
#include"stm32f10x_gpio.h"GPIO_Init(GPIOC, &GPIO_InitStructure);
配置 Define 宏

USE_STDPERIPH_DRIVER, STM32F10X_MDUSE_STDPERIPH_DRIVER → 启用标准外设库STM32F10X_MD → 选择 STM32F10x 中容量芯片
STM32F10X_MD 这个宏从哪里来?


STM32F10X_LDSTM32F10X_LD_VLSTM32F10X_MDSTM32F10X_MD_VLSTM32F10X_HDSTM32F10X_HD_VLSTM32F10X_XLSTM32F10X_CL
STM32F103C8T6→ 64KB Flash→ Medium-density→ STM32F10X_MD
为什么推荐在 Keil Define 里写宏?
/* Tip: To avoid modifying this file each time you need to switch between thesedevices, you can define the device in your toolchain compiler preprocessor.- Low-density devices are STM32F101xx, STM32F102xx and STM32F103xx microcontrollerswhere the Flash memory density ranges between 16 and 32 Kbytes.- Low-density value line devices are STM32F100xx microcontrollers where the Flashmemory density ranges between 16 and 32 Kbytes.- Medium-density devices are STM32F101xx, STM32F102xx and STM32F103xx microcontrollerswhere the Flash memory density ranges between 64 and 128 Kbytes.- Medium-density value line devices are STM32F100xx microcontrollers where theFlash memory density ranges between 64 and 128 Kbytes.- High-density devices are STM32F101xx and STM32F103xx microcontrollers wherethe Flash memory density ranges between 256 and 512 Kbytes.- High-density value line devices are STM32F100xx microcontrollers where theFlash memory density ranges between 256 and 512 Kbytes.- XL-density devices are STM32F101xx and STM32F103xx microcontrollers wherethe Flash memory density ranges between 512 and 1024 Kbytes.- Connectivity line devices are STM32F105xx and STM32F107xx microcontrollers.*/
提示:为了避免每次需要在这些不同芯片类型之间切换时都修改这个文件,你可以在工具链编译器的预处理器中定义芯片类型。- 低容量器件是指 STM32F101xx、STM32F102xx 和 STM32F103xx 微控制器,它们的 Flash 存储器容量范围为 16KB 到 32KB。- 低容量 Value Line 器件是指 STM32F100xx 微控制器,它们的 Flash 存储器容量范围为 16KB 到 32KB。- 中容量器件是指 STM32F101xx、STM32F102xx 和 STM32F103xx 微控制器,它们的 Flash 存储器容量范围为 64KB 到 128KB。- 中容量 Value Line 器件是指 STM32F100xx 微控制器,它们的 Flash 存储器容量范围为 64KB 到 128KB。- 高容量器件是指 STM32F101xx 和 STM32F103xx 微控制器,它们的 Flash 存储器容量范围为 256KB 到 512KB。- 高容量 Value Line 器件是指 STM32F100xx 微控制器,它们的 Flash 存储器容量范围为 256KB 到 512KB。- 超大容量器件是指 STM32F101xx 和 STM32F103xx 微控制器,它们的 Flash 存储器容量范围为 512KB 到 1024KB。- 互联型器件是指 STM32F105xx 和 STM32F107xx 微控制器。
这一段是 STM32F10x 的器件密度分类说明,用来指导我们选择芯片型号宏,例如 STM32F10X_MD 表示中容量器件,STM32F10X_HD 表示高容量器件,这些宏属于 target device macro,也就是目标芯片选择宏。
这段 Tip 非常重要,它是在解释芯片型号宏应该怎么选、为什么建议在 Keil Define 里定义。这里的 this file 指的是:stm32f10x.h
toolchain compiler preprocessor 对 Keil 来说就是:Options for Target -> C/C++ -> Define
也就是说,官方建议你不要直接改 stm32f10x.h,而是在 Keil 的 Define 里写:STM32F10X_MD
USE_STDPERIPH_DRIVER 是什么?

#ifdef USE_STDPERIPH_DRIVER#include"stm32f10x_conf.h"#endif
USE_STDPERIPH_DRIVER↓启用 stm32f10x_conf.h↓启用标准外设库头文件体系↓可以正常使用标准库函数
编译器版本要选择 ARM Compiler 5

STM32F10x 标准库比较老,官方模板主要面向早期 MDK-ARM 环境。为了避免兼容性问题,建议在 Keil 中选择:
Options for Target→ Target→ ARM Compiler→ Use default compiler version 5
也就是使用 ARM Compiler 5,如果使用 ARM Compiler 6,可能会在 core_cm3.c 中出现类似错误:

这不是标准库文件漏加,也不是头文件路径错误,而是老版 core_cm3.c 中的 naked 函数和 ARM Compiler 6 不兼容,所以标准库模板建议统一使用:ARM Compiler 5
如果 Keil 里没有 Compiler version 5,需要安装:ARM Compiler 5 Legacy Support
STM32F10x 标准库工程建议使用 ARM Compiler 5;初学者不要一上来用 ARM Compiler 6 适配老标准库,否则容易遇到 core_cm3.c 编译错误。

完成以上配置后,点击 Build 或 Rebuild 编译工程。如果编译结果显示:
0 Error(s), 0 Warning(s)
说明启动文件、CMSIS、标准外设库、用户文件、头文件路径和宏定义都已经配置正确。
至此,一个基于 STM32F10x 标准外设库的 Keil 工程模板就创建完成了。后续新建项目时,可以直接复制这份模板,在此基础上添加 LED、KEY、USART、TIM、ADC 等功能模块,进入正式开发。
STM32 标准库工程的调用流
芯片上电 / 复位↓startup_stm32f10x_md.s↓设置初始栈指针 SP↓建立中断向量表↓进入 Reset_Handler↓调用 SystemInit()↓system_stm32f10x.c 配置系统时钟↓进入 C 运行库初始化↓调用 main()↓main.c 用户程序↓调用标准库函数↓stm32f10x_gpio.c / rcc.c / usart.c 等↓访问 stm32f10x.h 中定义的寄存器↓控制 STM32 硬件外设
中断调用链怎么理解?
外设产生中断↓NVIC 响应中断↓根据启动文件中的中断向量表找到入口↓跳转到 xxx_IRQHandler()↓执行 stm32f10x_it.c 中的中断服务函数↓读取数据 / 清除中断标志↓返回主程序
stm32f10x_it.cstm32f10x_it.h
最后检查清单
STM32F103C8T6 → startup_stm32f10x_md.score_cm3.ccore_cm3.hstm32f10x.hsystem_stm32f10x.csystem_stm32f10x.hstartup_stm32f10x_md.s
FWLIB\incFWLIB\src
main.cstm32f10x_it.cstm32f10x_it.hstm32f10x_conf.h
..\CORE..\USER..\FWLIB\inc
..\HARDWARE\LED..\SYSTEM\delay
startup_stm32f10x_md.score_cm3.csystem_stm32f10x.cmain.cstm32f10x_it.cmisc.cstm32f10x_gpio.cstm32f10x_rcc.c
USE_STDPERIPH_DRIVER, STM32F10X_MD几个常见错误总结
Please select first the target STM32F10x device used in your application
或者定位到 stm32f10x.h 中的这一行:
#error"Please select first the target STM32F10x device use
总结
#ifdef USE_STDPERIPH_DRIVER#include"stm32f10x_conf.h"#endif
夜雨聆风