☰
【江科大STM32学习笔记-02】标准固件库工程模板创建寄存器/库函数点亮LED灯
2026/10/6 22:20:00 网站建设 项目流程

目录

1. STM32F103C8T6最小系统板

1.1 板载硬件资源

1.2 最小系统板引脚图

1.3 STM32 最小系统电路

1.3.1 晶振电路

1.1.2 复位电路

1.1.3 电源电路

2. STM32 启动配置(BOOT 启动模式)

2.1 各启动模式的含义与工程用途

2.1.1 主 Flash 启动(BOOT0 = 0,最常用)

2.1.2 系统存储器启动(BOOT0 = 1,BOOT1 = 0)

2.1.3 SRAM 启动(BOOT0 = 1,BOOT1 = 1)

3. ST-LINK-V2简介

3.1 下载ST-Link驱动

3.2 ST-LINK和开发板硬件接线图

3.3 BOOT 启动模式配置

3.3.1 程序下载的位置(Flash 写入位置)

3.3.2 程序是否执行,取决于 BOOT 启动配置

3.3.3 推荐的 BOOT 引脚硬件配置(工程实践)

3.4 KeilMDK设置及下载流程

4. 标准固件库工程模板创建

4.1 器件包的安装

4.1.1 在线安装

4.1.2 离线安装

4.2 标准固件库获取

4.3 标准固件库目录介绍

4.3.1 文件夹介绍

4.3.1.1 Project文件夹

4.3.1.2 Libraries文件夹

4.3.1.3 Utilities文件夹

4.4 创建标准固件库工程模板

4.4.1 前提条件

4.4.2 新建工程目录

4.4.3 拷贝工程文件

4.4.4 新建Keil工程

4.4.5 创建分组并添加源文件

4.4.6 添加头文件路径

4.4.7 配置工程

4.4.8 编译测试

5. 寄存器点亮LED灯

5.1 LED灯原理介绍

5.1.1 LED灯基础知识

5.1.1.1 LED灯结构组成

5.1.1.2 LED灯发光原理

5.1.1.3 LED灯驱动原理

5.1.2 LED灯原理图

5.1.3 LED灯驱动流程

5.2 配置流程

5.2.1 开启GPIO的端口时钟

5.2.2 配置GPIO模式&输出

5.3 配置GPIO输出低电平

5.4 实验现象

6. 基于标准外设库点亮 LED

6.1 STM32 标准外设库概述

6.1.1 为什么要使用库函数

6.1.2 标准外设库的基本结构

6.2 寄存器操作与库函数的区别

6.3 GPIO 配置的一般流程(基于库函数)

6.3.1 使能 GPIO 外设时钟

6.3.2 配置GPIO的输出参数

6.4 控制 GPIO 输出电平(点亮 LED)

6.4.1 GPIO_SetBits

6.4.2 GPIO_ResetBits

6.4.3 GPIO_WriteBit

6.4.4 GPIO_Write

6.5 实验现象


1. STM32F103C8T6最小系统板

1.1 板载硬件资源

1.2 最小系统板引脚图

注意:不同商家贩卖的板子的设计可能会有所差别,比如BOOT引脚的位置,3V3和GND引脚的位置可能正好相反,所以需以自己购买商家的商品介绍为准,否则BOOT配置不当,导致下载程序之后,新程序不会运行。

1.3 STM32 最小系统电路

要使 STM32 单片机能够稳定、正常运行,必须保证其最小系统正确设计并可靠工作。所谓最小系统,是指在不连接任何外部功能模块的情况下,仍能保证 MCU 上电、复位并执行程序的基础电路集合。

STM32 最小系统通常包括以下几部分:

  • 晶振电路:为 STM32 提供系统时钟,是 MCU 正常运行的基础。
  • 复位电路:用于在上电或系统异常时,使 MCU 回到初始状态,重新开始执行程序。
  • 电源电路:为 MCU 及外围电路提供稳定、符合规格的工作电压,是系统可靠性的关键因素。

下面分别对各部分进行说明。


1.3.1 晶振电路

STM32 正常工作需要稳定的时钟源。虽然 STM32 内部集成了高速和低速 RC 振荡器,但在大多数应用中,仍然采用外部高速晶振作为系统主时钟,以获得更高的精度和稳定性。以 STM32F103ZET6 为例:

  • 主晶振引脚为 OSC_IN / OSC_OUT;
  • 芯片主频范围为 0~72 MHz;
  • 工程中常选用 8 MHz 晶振,便于系统倍频及外设时钟配置;

在实际电路中,晶振不能直接裸接到 MCU 引脚上,否则容易导致起振不稳定。通常需要在晶振两端各并联一个无极性负载电容,电容另一端接地,电容值一般选在 10~33 pF 之间。这样即可构成稳定的外部高速晶振电路,确保 MCU 正常运行。

说明:STM32 还支持 RTC 使用的 32.768 kHz 低速晶振,该部分与主系统运行无直接关系,后续再介绍。


1.1.2 复位电路

即使系统时钟正常工作,MCU 在运行过程中仍可能因程序异常、电磁干扰等原因进入不可预期状态,因此必须设计可靠的复位电路。

STM32 配备专用的 NRST 复位引脚,采用低电平有效复位机制,只需在 NRST 引脚维持足够时长的低电平信号,即可触发系统复位。常见的复位方式包括:

  • 手动复位:通过按键将 NRST 短暂拉低;
  • 上电复位:利用 RC 充放电特性,在上电瞬间产生低电平复位;

在开发板和调试场景中,通常采用手动复位电路,该方式结构简单、可靠,便于人工控制复位时机:

  • 按键按下:NRST 直接接地,触发复位;
  • 按键释放:NRST 通过上拉电阻恢复为高电平,系统重新运行;

1.1.3 电源电路

电源电路是最小系统中极其关键的一部分。STM32 对供电电压有严格要求:

  • 工作电压范围:1.8 V ~ 3.3 V
  • 实际工程中通常采用 3.3 V 供电

电源电路的需要提供稳定、低噪声的 3.3 V 电压,以满足 MCU 及外设的电流需求,同时确保合理的稳压与滤波设计。在典型开发板中:

  • 外部可通过 DC 接口或 USB 接口等输入 5 V 或其他电源;
  • 经DCDC和LDO芯片转换为适合的电压,为 STM32 及外围模块统一供电;
  • 通常还会预留 5 V / 3.3 V 电源引出端口,便于外接模块使用;


2. STM32 启动配置(BOOT 启动模式)

在 STM32F10xxx 系列单片机中,芯片支持通过 BOOT0 和 BOOT1 引脚在复位后选择不同的启动模式,从而决定 CPU 在复位后从哪一块存储区开始取指并执行程序。

在一次系统复位过程中,STM32 会在 SYSCLK 的第 4 个上升沿对 BOOT 引脚的电平状态进行采样,并将采样结果锁存。随后,芯片根据 BOOT0 / BOOT1 的组合值,将对应的存储区域映射到启动空间(地址 0x0000 0000),并从该地址开始执行代码。

BOOT1BOOT0启动模式说明
X0从主闪存存储器Flash(原地址: 0x0800 0000)启动主 Flash 被映射到 0x00000000,执行用户程序
01从系统存储器(原地址:0x1FFF F000)启动

启动 ST 官方 System Bootloader(ISP)

11从内部SRAM启动(原地址:0x2000 0000)

内部 SRAM被映射到 0x00000000

说明:

  • X 表示 BOOT1 的状态对该启动模式无影响
  • 在大多数 STM32F10xxx 封装中,BOOT1 为普通 GPIO(部分型号内部带上拉或下拉)

2.1 各启动模式的含义与工程用途

2.1.1 主 Flash 启动(BOOT0 = 0,最常用)

主 Flash 启动模式是 STM32 的正常工作模式,也是绝大多数应用在量产和日常运行时采用的默认配置。在该模式下:

  • CPU 从主 Flash 存储器中取指执行
  • 主 Flash 被重映射到 0x0000 0000,作为程序的启动地址
  • 用户通过 ST-LINK / J-LINK 下载的应用程序必须存放在主 Flash 中

工程实践中通常采用如下配置:

  • BOOT0 固定接 GND(低电平)
  • 确保上电或复位后,芯片始终从 Flash 中执行用户程序

2.1.2 系统存储器启动(BOOT0 = 1,BOOT1 = 0)

在系统存储器启动模式下,STM32 不会执行主 Flash 中的用户程序,而是启动芯片内部固化的 System Bootloader。该模式下的行为特征为:

  • CPU 从 系统存储器(System Memory) 中取指执行
  • 启动 ST 官方提供的 Bootloader 程序
  • 主 Flash 中已存在的用户程序不会被执行

System Bootloader 主要用于ISP(In-System Programming),通常支持以下接口(具体取决于芯片型号):

  • USART(如 USART1)
  • USB(DFU 模式)
  • CAN 等

该启动模式常用于以下场景:

  • 芯片刚出厂,Flash 中尚未烧写用户程序
  • SWD / JTAG 被禁用后的应急恢复(“救砖”)
  • 通过串口或 USB DFU 对 Flash 进行重新烧录

2.1.3 SRAM 启动(BOOT0 = 1,BOOT1 = 1)

在 SRAM 启动模式下:

  • 内部 SRAM 被映射到 0x0000 0000
  • CPU 从 SRAM 中开始取指执行

该模式通常不用于常规应用,而主要用于以下特殊场景:

  • 调试阶段,在 RAM 中直接运行测试代码
  • 验证启动流程或特殊加载机制
  • 某些底层软件或安全相关实验

由于 SRAM 掉电即丢失,且不具备非易失性,该模式在实际产品中极少作为长期运行方式。


3. ST-LINK-V2简介

3.1 下载ST-Link驱动

Keil 安装目录下就有ST-Link驱动,如图所示,双击运行即可:

也可以选择官网下载,官网下载地址:https://www.st.com/zh/development-tools/stsw-link009.html#get-software

将文件解压到文件夹中:

然后双击运行

之后一步步运行就可以了

3.2 ST-LINK和开发板硬件接线图

3.3BOOT 启动模式配置

在使用ST-LINK或J-LINK对 STM32F10xxx 系列单片机进行程序下载与调试时,需要明确区分两个容易混淆的概念:

  • 程序下载行为:通过调试接口将代码写入指定的存储区域;
  • 程序启动行为:芯片复位后,从哪个存储区域开始取指并执行代码;

二者由不同机制控制,分别对应调试接口(SWD/JTAG)和BOOT 启动模式选择逻辑。

3.3.1 程序下载的位置(Flash 写入位置)

无论使用 ST-LINK 还是 J-LINK 作为下载器,新烧写的用户程序始终被写入主 Flash 存储器,默认起始地址为 0x0800 0000。下载过程通过 SWD / JTAG 调试接口完成。

需要特别说明的是:

  • 下载器并不会参与“启动模式”的选择;
  • BOOT0 / BOOT1 的电平状态不会影响 Flash 的写入行为;

换言之,下载过程与 BOOT0 / BOOT1 当前状态无直接关系。ST-LINK / J-LINK 的职责仅限于:通过调试接口,将“新编译生成的用户程序”写入主 Flash 的指定地址空间。

3.3.2 程序是否执行,取决于 BOOT 启动配置

程序是否在下载完成后、复位或上电时自动运行,并不取决于下载器,而是完全由 BOOT 引脚的配置决定。

在系统复位后,STM32 会根据BOOT0 / BOOT1 的组合状态,选择一个存储区域并将其映射到地址0x0000 0000,随后从该区域开始取指执行。

典型情况如下:

  • 若 BOOT0 = 0:主 Flash 被映射到 0x0000 0000,CPU 从主 Flash 中执行刚刚烧写的用户程序,属于正常运行状态。
  • 若 BOOT0 = 1:系统存储器被映射到 0x0000 0000,CPU 启动 ST 内置的 System Bootloader,主 Flash 中的用户程序不会被执行。

3.3.3 推荐的 BOOT 引脚硬件配置(工程实践)

为保证系统在正常上电或复位后稳定、可预期地运行用户程序,推荐采用如下 BOOT 引脚配置方式:

  • BOOT0:通过 10kΩ 下拉电阻接 GND。可预留跳帽、焊盘或测试点,便于在需要时切换到系统存储器启动(ISP 下载)。
  • BOOT1:直接接 GND 或作为普通 GPIO 使用,在大多数应用中不参与启动模式切换。

该配置方式可以确保:默认情况下,上电 / 复位即从 主 Flash 启动,用户程序能够自动运行,同时保留通过系统 Bootloader 进行 ISP 下载或故障恢复的灵活性。

3.4 KeilMDK设置及下载流程

因为在设置中勾选了Reset and Run,程序下载完就直接运行了​​​​​。

注意:在Pack那个页面要将 Enable 这个选项取消掉,不然烧录完程序无法自动复位运行!!!


4.标准固件库工程模板创建

4.1 器件包的安装

安装分两种:

  • 在线安装:在线安装省去了去官网寻找的麻烦,但缺点很明显网络不好的时候会很慢。
  • 离线安装:离线安装需要去Keil官网下载,一步步找到所需的器件包,好处是网络几乎不会影响到你。

4.1.1 在线安装

首先打开Keil软件,点击小绿图标。

将弹出的窗口关掉。

选择STM,选择STM32F103C8这款。

等待安装........

如果出现错误,那大概率是网络出现了问题,可以直接换离线安装方式。

按钮出现这个信息就说明已经安装完成了,就可以关闭窗口了。

4.1.2 离线安装

官网下载地址:https://www.keil.arm.com/packs/stm32f1xx_dfp-keil/boards/

等待下载完成之后,双击安装。

直接 Next 下一步

无脑点击下一步,等待安装完成。

安装完成之后会在Keil软件的安装目录下有一个 STM32F1xx_DFP 的文件夹:

注意这个packs文件夹是我在安装Keil的时候自己设定的,我们其他不用管,只要这个文件夹存在就证明安装成功了。

4.2 标准固件库获取

官网下载地址:https://www.st.com/zh/embedded-software/stsw-stm32054.html

如果没有账号,可以注册一个。

4.3标准固件库目录介绍

4.3.1文件夹介绍

打开下载的STM32F10x标准固件库,里面的目录如图3-1-1所示。

  • Libraries:此文件夹里面有3个文件夹,包含CMSIS和标准外设库,存放官方封装的一些库函数,方便用户开发使用。
  • Project:此文件夹是工程模板文件夹和官方编写的示例代码,涉及芯片的大部分功能。
  • Utilities:此文件夹包含一些第三方组件和STM32配套的开发板文件。
4.3.1.1Project文件夹

STM32F10x_StdPeriph_Examples文件夹,内容如图所示。

STM32F10x_StdPeriph_Template文件夹,内容如图所示。

从上图中可以看出,这个文件夹里是各种外设资源的例程,每个文件夹里面还有更为详细的功能示例,对我们开发具有很高的参考价值,一定要多看,多读,多用。

4.3.1.2Libraries文件夹

打开Libraries文件夹之后,内容如图所示。

  • CMSIS:微控制器软件接口标准(CMSIS:Cortex Microcontroller Software Interface Standard) 是Cortex-M处理器系列的与供应商无关的硬件抽象层。
  • STM32F10x_StdPeriph_Driver: 从名称也可以看出,这个是STM32F10系列的标准外设库,存放一些封装了寄存器的库函数,我们后面编程也是依赖于这个库进行开发,覆盖STM32F10芯片的绝大部分功能。
4.3.1.3Utilities文件夹

这个是第三方组件文件夹,不必太关注。


4.4创建标准固件库工程模板

4.4.1前提条件

  • 已经安装好KeilMDK软件
  • 已经安装好STM32F1xx的Pack包
  • 已经下载好STM32F10x标准固件库

4.4.2新建工程目录

新建文件夹,命令为STM32F103C8T6_ProjectTemplate,用来保存新创建的工程。然后在该文件夹下新建6个文件夹,分别命名为:

  • project: 存放工程文件(目前只支持MDK5)。
  • libraries:存放各种库文件,CMSIS,芯片固件库,文件系统库,网络库等。
  • board:存放板子初始化文件和链接脚本。
  • app: 存放应用程序,由用户编写和放main函数。
  • bsp:存放和底层相关的支持包。
  • module:主要存放各种软件模块,比如软件定时器,PID,FIFO,状态机等。

然后在project文件夹下新建一个文件夹:MDK(V5)

在STM32F103C8T6_ProjectTemplate文件夹下新建一个README.md文件,如图所示。

4.4.3拷贝工程文件

找到固件库的下载目录,将STM32F10x_StdPeriph_Lib_V3.6.0\Libraries文件夹下的内容全部拷贝到新建目录的libraries下。

找到\STM32F10x_StdPeriph_Lib_V3.6.0\Project\STM32F10x_StdPeriph_Template下面的

  • stm32f10x_conf.h
  • stm32f10x_it.c
  • stm32f10x_it.h

复制到module文件夹下

我们将下方这个文件下载并且解压,拖到 STM32F103C8T6_ProjectTemplate 文件夹中替换。

包含延时函数:

包含串口1收发函数:

复制到STM32F103C8T6_ProjectTemplate 文件夹中

4.4.4新建Keil工程

在上面建立的project文件夹里面创建一个MDK(V5)文件夹用来存放keil MDK ARM V5版本的工程文件。 打开Keil uVision5创建一个新的工程: 单击->Project->New uVision Project ->选好芯片型号STM32F103C8。

4.4.5创建分组并添加源文件

右键Target->单击Manage Project Items 如下所示设计分组(双击可以修改名字):

STM32F103C8T6 属于中容量器件,因此启动文件应选择:startup_stm32f10x_md.s

将\libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm目录下的startup_stm32f10x_md.s(启动文件)添加到STARTUP组里(注意最后要点击对话框左下角的OK按钮才能生效)

启动文件使用汇编写的,他是上电或复位后执行的第一段代码:

  1. 设置堆栈大小和PC指针。
  2. 配置中断向量表:设置中断向量表的位置,这样当中断发生时,CPU能够找到相应的中断服务程序。
  3. 定义中断服务例程的默认处理函数:通常会定义一个默认的中断处理函数,当未定义特定的中断处理函数时,会调用这个默认函数。在某些启动文件中,都是了弱定义的中断处理函数,允许我们在其C代码中重定义。
  4. 系统时钟初始化。
  5. 跳转到c语言的main函数里面。

将\libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x目录下的system_stm32f10x.c添加到APP组里面:

 将\libraries\STM32F10x_StdPeriph_Driver\src目录下的所有文件添加到Driver组里面:

添加\libraries\CMSIS\CM3\CoreSupport目录下的com3文件到Driver组下面:

添加\board目录下的board.c 文件到Board组中去。

添加\bsp\uart目录下的bsp_uart.c 文件到BSP组中。

添加README.md文件到DOC目录下去。(注意:文件类型一定要选择 All files 才可以看得到.md后缀的文件)

然后我们新建一个main.c 文件,添加到APP目录中。

4.4.6添加头文件路径

4.4.7配置工程

点击魔法棒弹出页面

  • 勾选 Create HEX File

  • 使用微库

  • 添加全局宏定义:STM32F10X_MD,USE_STDPERIPH_DRIVER。

在 Keil 中点击“魔法棒”打开Options for Target,切换到C/C++选项卡,在Preprocessor Symbols → Define一栏中填写:

STM32F10X_MD,USE_STDPERIPH_DRIVER

两个宏之间使用英文逗号分隔,宏名区分大小写,必须与标准库要求一致。

(1)USE_STDPERIPH_DRIVER

USE_STDPERIPH_DRIVER 是使用 STM32F10x 标准外设库的条件编译宏。在 stm32f10x.h 中存在类似下面的代码:

#ifdef USE_STDPERIPH_DRIVER #include "stm32f10x_conf.h" #endif

因此,只要使用标准外设库提供的驱动函数,如 GPIO_Init()、RCC_APB2PeriphClockCmd()等,就必须定义 USE_STDPERIPH_DRIVER。定义后,stm32f10x.h 会自动包含 stm32f10x_conf.h,从而把工程需要的外设头文件包含进来。

如果不定义该宏,标准外设库的外设驱动头文件通常不会被自动包含,容易出现 GPIO_Init、GPIO_InitTypeDef 等未定义或隐式声明的问题。

(2)STM32F10X_MD

STM32F10X_MD 用于告诉 STM32F10x 标准外设库:当前目标芯片属于中容量 Medium-density器件。

在 stm32f10x.h 开头,标准库会检查用户是否定义了具体器件类型。如果没有定义 STM32F10X_LD、STM32F10X_MD、STM32F10X_HD 等宏之一,编译时会报错,例如:

Please select first the target STM32F10x device used in your application

在某些使用 RTE/Pack 的工程中,Keil 可能会通过器件配置或组件自动提供与芯片相关的预定义宏,但本教程是手动拷贝 STM32F10x 标准外设库来创建工程,不应假设 Keil 一定会自动定义 STM32F10X_MD。为了工程清晰、可移植、可复现,建议在 Define 中显式添加STM32F10X_MD。

这里的 md 与宏 STM32F10X_MD 对应。若以后更换为其他容量的 STM32F10x 芯片,需要同时修改:

  1. 全局宏定义,例如 STM32F10X_LD、STM32F10X_MD、STM32F10X_HD 等;

  2. 对应的启动文件,例如 startup_stm32f10x_ld.s、startup_stm32f10x_md.s、startup_stm32f10x_hd.s 等;

  3. 必要时调整 stm32f10x_conf.h 中使能的外设模块。


  • C语言使用C99标准

在C/C++选项卡中,设置 C 语言标准。

  • 如果使用 ARM Compiler 5,建议勾选:C99 Mode
  • 如果使用 ARM Compiler 6,可以在语言标准选项中选择 c99 或 gnu11 等兼容标准。

该设置与全局宏定义是两个独立配置:宏定义用于控制标准外设库的条件编译,C 语言标准用于控制编译器语法支持。

对于本工程模板,建议统一使用 C99 标准,以便后续编写代码时可以使用 C99 特性。

4.4.8 编译测试

将main.c中的代码替换为:

#include "stm32f10x.h" #include "board.h" #include "bsp_uart.h" #include "stdio.h" int main(void) { board_init(); uart1_init(115200); 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); GPIO_ResetBits(GPIOC,GPIO_Pin_13); while(1) { GPIO_SetBits(GPIOC, GPIO_Pin_13); printf("LED ON!\r\n"); delay_ms(500); GPIO_ResetBits(GPIOC, GPIO_Pin_13); printf("LED OFF!\r\n"); delay_ms(500); } }

 点击编译

发现0错误,0警告,这样我们的工程模板就完成了!


5. 寄存器点亮LED灯

5.1 LED灯原理介绍

5.1.1 LED灯基础知识

5.1.1.1 LED灯结构组成

LED灯,也称发光二极管,是一种能够将电能转化为可见光的固态的半导体器件,它可以直接把电转化为光。LED的内部是一个半导体的晶片,晶片的一端附在一个支架上,一端是负极,另一端连接电源的正极,整个晶片被环氧树脂封装起来。

5.1.1.2 LED灯发光原理

半导体晶片由两部分组成,一部分是P型半导体,另一端是N型半导体。这两种半导体连接起来的时候,它们之间就形成了一个P-N结。当电流通过导线作用于这个晶片的时候,电子就会被推向P区,在P区里电子跟空穴复合,然后就会以光子的形式发出能量,这就是LED灯发光的原理。

5.1.1.3 LED灯驱动原理

LED灯的驱动比较简单,只需要给将对应的正负极接到单片机的正负极即可驱动。需要注意的是LED灯的颜色不同,对应的电压也不同。

5.1.2 LED灯原理图

开发板关于LED灯的原理图如图2-1-1所示。LED引脚接到了PC13。

5.1.3 LED灯驱动流程

通过上面的原理图可以了解到,LED灯的负极接到了电源地,LED灯的正极连接到了单片机的GPIO口上,通过LED灯的驱动原理,只需要将GPIO配置为高电平即可点亮LED灯。对应用户LED就是使能PC13口输出高电平即可点亮用户LED。

5.2 配置流程

一般我们使用GPIO的端口,都需要有以下几个步骤。

  • 开启GPIO的端口时钟
  • 配置GPIO的模式
  • 配置GPIO的输出

从LED介绍那一章节我们了解到用户LED接的是单片机的PB2,下面我们就以用户LED接的PC13进行介绍。

5.2.1 开启GPIO的端口时钟

STM32的所有外设资源时钟默认都是关闭的,在配置外设之前需要先开 启对应的时钟。

要使能用户LED,就要先开启GPIOC的时钟,从数据手册的第11页我们了解到GPIOC挂载在APB2总线上,那操作GPIOC的时钟肯定要配置APB2使能寄存器。在中文参考手册的第115页,APB2使能寄存器如图1-1-1所示。

从图1-1-1可以看到地址偏移量为0x18,那这个基地址是多少呢?因为APB2使能寄存器是在RCC外设的地址范围内,所以RCC的外设基地址就是APB2使能寄存器的基地址,在中文参考手册的第28页有明确说明,RCC基地址:0x4002 1000。那我们RCC_APB2ENR寄存器的地址就是基地址加上偏移量,0x4002 1000 + 0x18 = 0x4002 1018。找到要操作的地址了,那我们怎么去配置这个值呢?我们继续看这个寄存器的说明,在中文参考手册的第95页有寄存器说明,如图1-1-2所示。

从图1-1-2可以了解到RCC_APB2ENR寄存器的第4位就是GPIOC端口时钟使能,我们要开启GPIOC端口时钟使能,就需要往RCC_AHB1ENR的第4位写1,然后为了保持其他位不变,我们可以使用一个或运算,也就是RCC_AHB1ENR|= 0x00000010,也就是相当于拉高RCC_AHB1EN寄存器的第4位,其他位保持不变。其实也可以写为RCC_AHB1ENR|=(1 << 4),是一样的效果,这里的4就是寄存器的第4位,如果是2就是寄存器的第2位,依次类推(位数从0开始)。

在STM32的头文件中已经将大部分的地址定义了,所以我们只需要调用这些地址的定义就可以了。

RCC->APB2ENR |= ( 0x01 << 4 );

5.2.2 配置GPIO模式&输出

要使能用户LED,自然是配置GPIO为输出模式,找到控制寄存器(GPIOx_CRH),如图1-2-1所示。

从图1-2-1可以看到GPIOx_MODER的地址偏移是0x00,我们要使能的是GPIOC的引脚,所以要加上GPIOC的基地址0x4002 0400,即GPIOx_MODER寄存器的地址为0x4002 0400 + 0x00。我们接着看这个寄存器的介绍说明,如图1-2-2所示。

从图1-2-2可以看到每一个引脚都由2位控制,我们要操作的是PC13引脚,也就是pin13,也就是GPIOx_CRH寄存器的第20位到第23位。

  • 将MODE13配置为:11
  • 将CNF13配置为:00

在STM32的头文件中已经将大部分的地址定义了,所以我们只需要调用这些地址的定义就可以了。

// 清空控制PC13的端口位 GPIOC->CRH &= ~(0xFF << (4 * (13 - 8))); // 注意PC13对应CRH中的第5个位置,因此是13-8 // 配置PC13为通用推挽输出,速度为10MHz GPIOC->CRH |= (0x1 << (4 * (13 - 8))); // 设置为通用推挽输出模式,速度10MHz

5.3 配置GPIO输出低电平

配置GPIO的输出需要配置端口输出寄存器GPIOx_ODR

端口输出模式寄存器如图所示。

从图可以看到端口输出模式寄存器的地址偏移为0x0C,那GPIOx_ODR寄存器的地址就是0x4002 0400 + 0x0C。我们接着往下看这个寄存器的介绍,如图所示。

从图来看,只需要往对应的寄存器写0即可。转化为代码为GPIOC_MODER &= (0x00 << 13)。

在STM32的头文件中已经将大部分的地址定义了,所以我们只需要调用这些地址的定义就可以了。

// PC13输出低电平 GPIOC->ODR |= (0<<13);

5.4 实验现象

如果用上面的代码需要注意一下,像RCC这种宏定义其实在STM32的头文件中已经定义了,可以直接使用原来定义的,这样我们就不用再编写,也可以把我们编写的名称稍微修改一下,比如在它们前面加上一个LED或者BSP等等都可。

关于这一章节的代码如下所示:

#include "stm32f10x.h" int main(void) { RCC->APB2ENR |= ( 0x01 << 4 ); GPIOC->CRH &= ~(0xFF << (4 * (13 - 8))); GPIOC->CRH |= (0x1 << (4 * (13 - 8))); GPIOC->ODR |= (0<<13); while(1) { } }

烧写我们的代码之后,可以看到开发板的LED这个灯将被点亮。

6. 基于标准外设库点亮 LED

本节将基于STM32 标准外设库(Standard Peripheral Library, SPL),以点亮 LED 为例,介绍 GPIO 外设的基本配置流程,并说明库函数在 STM32 开发中的作用与使用方法。

6.1 STM32 标准外设库概述

6.1.1 为什么要使用库函数

在上节中,通过直接操作寄存器来驱动 STM32 的外设。虽然这种方式能够最直观地反映硬件工作原理,但在实际开发中存在明显问题:

  • STM32 外设寄存器数量庞大,寄存器地址和位定义繁杂
  • 每次配置外设都需要反复查阅参考手册
  • 代码可读性差、可维护性低,开发效率不高

为了解决上述问题,ST 官方提供了 STM32 标准外设库。该库在底层寄存器操作的基础上,对各类外设进行了统一封装,提供了功能明确、接口规范的 API。

使用库函数的优势主要体现在:

  • 屏蔽底层寄存器细节,降低使用门槛
  • 提高开发效率,减少重复劳动
  • 增强代码可读性和可维护性
  • 有利于团队协作和工程规模扩大

需要注意的是:库函数并不是脱离硬件工作的,本质仍然是对寄存器的封装操作。

6.1.2 标准外设库的基本结构

前面章节中创建的工程模板里添加了STM32F1x的标准外设库,在基于标准外设库创建的 STM32 工程中,通常可以看到如下文件:

  • stm32f10x_gpio.h
  • stm32f10x_gpio.c
  • stm32f10x_rcc.h
  • stm32f10x_rcc.c

其中:

  • .h 文件:定义外设相关的宏、结构体和函数声明
  • .c 文件:实现具体的寄存器配置逻辑

以 GPIO 为例,所有 GPIO 相关的配置接口、模式定义和初始化函数,都集中在 stm32f10x_gpio.h/.c 文件中。可以理解为:标准外设库 = 在寄存器之上封装的一层功能接口,最终硬件仍然是通过寄存器完成配置和控制。

6.2 寄存器操作与库函数的区别

无论是直接操作寄存器,还是使用库函数,最终目的都是对指定的寄存器地址进行读写操作。二者的核心区别在于抽象层级和使用场景。

对比项寄存器操作标准外设库
抽象层级低较高
学习成本高较低
开发效率低高
可读性差好
灵活性最高略受限制

一般建议:

  • 学习阶段:采用寄存器操作方式,深入理解硬件原理
  • 工程开发:优先使用库函数,提升开发效率和代码可靠性

6.3 GPIO 配置的一般流程(基于库函数)

使用标准外设库配置 GPIO,通常遵循如下固定流程:

  1. 开启 GPIO 外设时钟
  2. 配置 GPIO 工作模式和参数
  3. 控制 GPIO 输出高/低电平

下面以连接在 GPIOC 的 PC13 引脚(示例) 的用户 LED 为例进行说明,其它 GPIO 引脚配置方式完全一致。

在使用库函数之前,需要了解到,STM32官方帮我们做好了这一套库函数(STM32F10x_StdPeriph_Driver),很详细,很全面。我们使用的时候只需要到对应的函数中去查找即可。

6.3.1 使能 GPIO 外设时钟

在 STM32 中,所有外设的时钟在复位后默认关闭。在对任何外设进行配置之前,必须先使能其对应的时钟,否则对寄存器的操作将不会生效。

既然要开启时钟,那我们就要去关于时钟的库函数里去查找,打开STM32f10x_rcc.h头文件,里面有很详细的关于时钟的函数的声明,到里面去查找我们需要的即可。GPIO 外设的时钟由 RCC(复位与时钟控制)模块管理。GPIOA~GPIOG 均挂载在 APB2 总线 上,因此需要使能 APB2 对应的时钟。在 stm32f10x_rcc.h 中可以找到相关函数:

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

下面还有很多其他的开启函数:

由此可知,这个函数的一个参数就是要使能的时钟,以开启 GPIOC 时钟为例:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

6.3.2 配置GPIO的输出参数

GPIO 的初始化通过结构体 GPIO_InitTypeDef 完成,主要包括以下参数:

  • 引脚号(GPIO_Pin)
  • 工作模式(GPIO_Mode)
  • 输出速度(GPIO_Speed,仅输出模式有效)

这配置的也是关于GPIO的操作,要到 STM32f10x_gpio.h 去查找对应的函数或结构体。

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)

这个函数 GPIO_Init 是用于初始化STM32系列微控制器的GPIO(通用输入输出)端口的。它根据提供的参数配置GPIO端口的工作模式、速度、输出类型等。

参数解释:

  • GPIOx: 这个参数指定了要初始化的GPIO端口。

  • GPIO_InitStruct: 这是一个指向 GPIO_InitTypeDef 结构的指针,该结构体包含了GPIO端口的配置信息。GPIO_InitTypeDef 结构体通常包括以下几个重要的成员:

    • Pin: 指定要配置的GPIO引脚。可以是一个引脚或多个引脚的组合。

    • GPIO_Mode: 设置引脚的工作模式,如输出模式、输入模式、浮空输入模式等

    • GPIO_Speed: 设置引脚的输出速度(低速、中速、高速、极高速)。

配置PC13为推挽输出,速度为50MHZ,只需要传入对应的参数即可。转换为代码为

GPIO_InitTypeDef GPIO_InitStructure; // 开启GPIOC的时钟 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; // 输出50Mhz GPIO_Init(GPIOC, &GPIO_InitStructure); // 初始化

到此,有关GPIO的配置就完成了。

6.4 控制 GPIO 输出电平(点亮 LED)

在 GPIO 初始化完成后,即可通过标准外设库函数控制 GPIO 引脚输出高电平或低电平,从而实现 LED 的点亮或熄灭。在本实验所使用的最小系统板上:

  • 用户 LED 连接在 PC13 引脚
  • LED 为低电平点亮、高电平熄灭

因此,点亮 LED 的本质操作为:使 PC13 输出低电平。

标准外设库为 GPIO 输出控制提供了多种接口,下面分别进行说明。

6.4.1 GPIO_SetBits

该函数用于将指定的 GPIO 引脚设置为高电平(逻辑 1)。

函数原型为:

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

参数说明:

  • GPIOx:GPIO 端口,例如 GPIOA、GPIOB、GPIOC;
  • GPIO_Pin:引脚号,可为 GPIO_Pin_x(x = 0~15),也可按位或组合多个引脚;

示例:

GPIO_SetBits(GPIOC, GPIO_Pin_13); // PC13 输出高电平(LED 熄灭)

该函数本质上是通过 GPIOx->BSRR 寄存器 置位实现,具有原子操作特性。

6.4.2 GPIO_ResetBits

该函数用于将指定的 GPIO 引脚设置为低电平(逻辑 0)。

函数原型为:

void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

参数说明:

  • GPIOx:GPIO 端口
  • GPIO_Pin:引脚号,可为单个或多个 GPIO_Pin 组合

示例:

GPIO_ResetBits(GPIOC, GPIO_Pin_13); // PC13 输出低电平(LED 点亮)

该函数同样基于 BSRR 寄存器的复位位实现,安全且高效,是最常用的输出控制方式之一。

6.4.3 GPIO_WriteBit

该函数用于直接向指定引脚写入电平值,既可以输出高电平,也可以输出低电平。

函数原型为:

void GPIO_WriteBit(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin,BitAction BitVal);

参数说明:

  • GPIOx:GPIO 端口
  • GPIO_Pin:指定引脚
  • BitVal:Bit_SET 输出高电平,Bit_RESET输出低电

示例:

GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET); // 高电平(LED 熄灭) GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET); // 低电平(LED 点亮)

该函数内部会判断 BitVal,并调用置位或复位逻辑,语义清晰,适合可读性要求较高的场景。

6.4.4 GPIO_Write

该函数用于一次性写入整个 GPIO 端口(16 位)的输出值。

函数原型为:

void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

参数说明:

  • GPIOx:GPIO 端口
  • PortVal:16 位数据,对应端口 16 个引脚的输出状态

例如,0x0001 → 第 0 位输出高电平,其余位为低电平

注意事项:

  • 该函数会直接覆盖整个端口的 ODR 寄存器
  • 若端口中存在多个已使用引脚,极易误改其它引脚状态
  • 调试和排错难度较高

因此,在实际工程中不推荐使用 GPIO_Write,除非明确掌控整个端口的用途。

6.5 实验现象

代码:

#include "stm32f10x.h" int main(void) { // 开启GPIOC的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; // 引脚号 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 输出50Mhz GPIO_Init(GPIOC, &GPIO_InitStructure); // 初始化 GPIO_ResetBits(GPIOC, GPIO_Pin_13); // PC13 输出低电平(LED 点亮) while(1) { } }

将程序烧写至 STM32 后:

  • 系统上电或复位
  • PC13 被配置为输出低电平
  • 开发板上的用户 LED 被点亮

实验现象与程序设计目标一致。

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

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

立即咨询