参考
STM32+PCB设计+物联网,手牵手带你从零开始画板子+写代码
https://www.bilibili.com/video/BV18arkYjEuL
STM32入门教程-2023版 细致讲解 中文字幕
https://www.bilibili.com/video/BV1th411z7sn
【已完结STM32】--自学江协科技笔记汇总
https://blog.csdn.net/2301_81011494/article/details/147069164
1.1课程简介
https://www.bilibili.com/video/BV1th411z7sn/
2-1软件开发
安装Keil5 MDK
找到软件,双击MDK536.EXE

下一步,选择路径,

我在本地创建了一个目录
D:\zhangjun\
目录名不要包含特殊字符和中文
然后继续下一步,会安软件大概3min钟
安装后,出现是否安装ULINK调试器

选择是,就安装完成了
自动弹Pack Installer窗口,可以用于在线安装支持库,这一步先不用,后期可以在Keil中重新弹出来这个界面

安装器件支持包
离线安装
新建 一个工程,

会提示选择设备

这里只有GD32F4和STM32F4系列的,没有STM32F1系列的,需要安装下
找到离线支持包,进行离线安装

双击Keil.STM32F1xx_DFP.2.2.0.pack

目标路径为自动选择我们安装时的目录,单击Next就安装完成了

在线安装
如果没有对应的离线支持包,可以在Keil中,自动弹Pack Installer窗口,可以用于在线安装支持库,

也可以 通过project--manage--pack installer进行在线安装

在左侧筛选栏选择芯片厂商(如 STMicroelectronics),找到目标系列(如 STM32F2xx),右侧会列出对应的 DFP 版本(如Keil.STM32F2xx_DFP
),点击 Install 即可自动下载并安装。
DFP 是 Device Family Pack(设备家族包) 的缩写。它是一种专门为特定微控制器系列提供支持的软件包,包含设备描述文件、启动代码、闪存算法、外设驱动及示例项目等核心资源,确保 Keil MDK(Microcontroller Development Kit)能够识别并适配目标芯片的硬件特性。

软件注册
右键Keil图标--以管理员身份运行,打开Keil
然后通过File--License Management

弹出的页面中复制CID,这里会发现我的版本已经注册了,可以使用到2032年

复制CID到注册机中,

把生成的License 复制到前一个图的 New License ID Code中,再单击Add LIC
安装STLINK驱动
把STLink插到电脑上
查看设备管理器

我的STLink驱动位置如下

双击DPinst_amd64.exe

选择

单击安装

安装完成后

在Segger中有JLink的驱动

一分钟制作STLink
https://www.bilibili.com/video/BV1ZA4m137e9/
安装USB转串口驱动-CH340
USB转串口工具连接电脑,查看设备管理器,如果出现感叹号,说明没有装驱动

打开本地的USB转串口CH340驱动

双击后,直接安装

安装完成后,会在端口处识别出USB-SERIAL

拓展
USB-SERIAL(通常称为"USB转串口工具")是一种硬件转换设备,核心功能是在USB接口 和串口(UART/RS232/RS485等) 之间建立通信桥梁,实现两种不同接口设备的数据交互。
主要用途
-
设备调试与开发
嵌入式设备(如单片机、PLC、传感器)通常采用串口(UART)作为调试接口,而现代电脑(PC/笔记本)普遍取消了物理串口,仅保留USB接口。通过USB-SERIAL工具,可将电脑的USB信号转换为串口信号,实现对嵌入式设备的程序下载、日志打印、参数配置等操作。
-
连接传统串口设备
一些老式设备(如工业仪表、路由器控制台、POS机)仍依赖串口通信,USB-SERIAL工具可让这些设备与现代电脑或智能设备(如通过USB扩展的嵌入式系统)连接,实现数据采集或控制。
-
数据传输扩展
在物联网或工业场景中,可通过USB-SERIAL将串口设备(如温湿度传感器、RFID读卡器)接入USB主机(如树莓派、工业电脑),扩展数据传输链路。
工作原理
USB-SERIAL的核心是"协议转换",通过硬件芯片和软件驱动协同实现信号转换,具体分为三个层面:
1. 硬件层面:信号转换
工具内部包含一块USB-to-UART转换芯片(如常见的CH340、PL2303、CP2102等),这是核心部件:
- 当数据从电脑USB发送到设备时:芯片接收USB总线上的差分信号(符合USB 2.0/3.0协议),将其解码为串口的TTL/RS232电平信号(如高低电平表示0/1),通过串口引脚(TX/RX)发送给目标设备。
- 当数据从串口设备发送到电脑时:芯片接收串口的电平信号,编码为USB协议要求的数据包格式,通过USB线传输给电脑。
此外,芯片还会处理电平匹配(如将USB的5V/3.3V转换为串口设备所需的3.3V/5V)和时序同步(确保两种接口的波特率、校验位等参数一致)。
2. 软件层面:驱动与虚拟串口
电脑需要安装对应转换芯片的驱动程序,其作用是:
- 将USB-SERIAL工具识别为"虚拟串口"(如Windows中的COM3、Linux中的/dev/ttyUSB0),让操作系统和应用程序(如串口调试助手、编程软件)将其视为物理串口,无需修改原有串口通信代码。
- 负责USB协议与串口协议的软件适配(如将USB的批量传输转换为串口的异步通信,处理数据帧拆分与拼接)。
3. 协议层面:数据格式转换
USB和串口的通信协议完全不同:
- USB采用主从式差分信号,基于数据包传输(包含地址、数据、校验等字段),支持热插拔和多种传输模式(控制、批量、中断、等时)。
- 串口(以UART为例)采用单端信号,基于帧传输(包含起始位、数据位、校验位、停止位),通信双方需提前约定波特率(如9600、115200)。
转换芯片会自动完成两种协议的格式转换:例如,将USB数据包中的有效数据提取出来,按照串口帧格式封装后发送;反之,将串口接收的帧数据打包成USB数据包,上传给电脑。
总结
USB-SERIAL本质是"协议翻译器",通过硬件芯片实现电信号和物理层转换,通过驱动程序实现操作系统层面的接口虚拟化,最终让USB设备和串口设备能够"无障碍对话"。它是现代电脑与传统串口设备、嵌入式系统交互的重要工具,在电子开发、工业控制、物联网等领域应用广泛。
2-2新建工程
1.新建文件夹
新建工程的文件夹,
d:\stm32pro\

打开Keil5软件-->选择Project--->New uVision Project -->选择d:\stm32pro\
然后选择刚刚我们新建的文件夹,在 新建文件夹 里面再建一个文件夹
d:\stm32pro\2-1STM32工程模板

进入
d:\stm32pro\2-1STM32工程模板
输入工程文件名Project,然后点击保存工程,

接下来就是选择芯片型号,我们的芯片型号是STM32F103C8T6。


用来存放本次工程。
点击OK,弹出来的是新建工程小助手,这个可以帮助我们快速新建工程,我们暂时用不到这个,直接选择叉掉。

这样就新建好一个工程,但是还不完整,还需要添加文件
此时的工程目录如下

2.start启动文件
添加文件到本地的Start目录下
工程建好之后 还需要添加一些库文件,首先在工程文件夹下新建一个Start启动文件

然后打开固件库文件夹
D:\BaiduNetdiskDownload\STM32入门教程资料\固件库\固件库\STM32F10x_StdPeriph_Lib_V3.5.0\STM32F10x_StdPeriph_Lib_V3.5.0\

找到启动文件,
D:\BaiduNetdiskDownload\STM32入门教程资料\固件库\固件库\STM32F10x_StdPeriph_Lib_V3.5.0\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm
(固件库文件来自b站江科大,可自行下载,见固件库的下载方式)

将arm里面的启动文件全部复制到 工程模板文件夹Start文件夹下


接着回到STM32F10x文件夹,找到STM32头文件和配置时钟文件,也复制到Star文件夹下

接着还需要添加内核寄存器的描述文件,打开文件路径:
D:\BaiduNetdiskDownload\STM32入门教程资料\固件库\固件库\STM32F10x_StdPeriph_Lib_V3.5.0\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\CoreSupport\
,同样复制到Start文件夹下
把本地Start目录下的文件添加到Keil5工程中
接着回到Kile5,把刚才复制到的文件添加到工程里,单击Source Group 1可将其改为Start

然后右键单击Start,选择AddExisting Files to Group 'Start'

弹出的页面默认只添加.c文件

选择文件类型中的所有类型,可以看到所有文件

这里只需要添加后缀为md.s的启动文件就可以了 ,选择add,然后单击close
然后把 本地Start目录中剩下的的.c和.h文件都要添加进来

3、添加头文件路径
最后还需要在工程选项添加头文件路径
首先确定ARM 的Compiler ,选择version5

右键Keil5中左侧,选择Options for Target 'Target1'

弹出的选项界面中,选择C/C++选项,然后选择Include Paths,选择新建new,选择三个点的图标,选择头文件的目录

按照要求选择目录即可


4、 测试工程
测试工程是否可行,在工程本地文件夹下新建一个User文件用来存放main函数

进来Kile5右击Target1添加组

并将其改名为User

,然后右击User添加新项目到组里

选择文件类型,设置文件名,文件的路径

单击确定即可
这样工程文件夹里面就可以看到有一个main文件了

插入头文件

并编写一个main函数,注意代码最后一行必须空行,否则会报警告
#include "stm32f10x.h" // Device header
int main(void)
{
while(1)
{
}
}
以上配置就可以用寄存器开发32单片机了。当然用寄存器开发STM32不太适合初学者,后面还要添加库函数。
现在进行build, 编译通过0错误0警告就证明工程建立成功

5、连接下载器ST-LINK(注意事项)
ST-LINK通过杜邦线链接最小系统版,注意接线

插上后,会出现一个灯亮表示电源,链接在PC13的灯默认是闪烁状态,这是芯片里的一个测试程序
插上下载器ST-LINK,点击魔术棒找到Debug,选择ST-Link ,然后选择Flash Download为自动复位

然后选择工程的build,编译成功后,单击load,如果load成功,Keil会提示,同时最小系统版的灯不再闪烁
因为load进去的代码没有操作
6、测试点灯
基于寄存器配置,测试点灯程序 ,最小系统板上的的灯是低电平点亮的,0x00002000是灯灭,0x00000000是灯亮
打开
D:\BaiduNetdiskDownload\STM32入门教程资料\参考文档\参考文档\STM32F10xxx参考手册(中文).pdf
7.3.7 APB2外设时钟使能寄存器(RCC_APB2ENR)
GPIO都是APB2的外设,在7.3.7 APB2外设时钟使能寄存器(RCC_APB2ENR)



00000000 00000000 00000000 00010000
00 00 00 10
0X00000010
#include "stm32f10x.h" // Device header
int main(void)
{
// 使能GPIOC
RCC->APB2ENR = 0X00000010;
while(1)
{
}
}
在8.2.2端口配置寄存器(GPIOx_CRH)(x=A...E)


这样如果给GPIOC的13设置为通用推挽 输出模式设置为50MHz,
21:20位为 11输出模式设置为50MHz 23:22位为00通用推挽模式
00000000 00110000 00000000 00000000
00 30 00 00
00300000
代码如下
#include "stm32f10x.h" // Device header
int main(void)
{
// 使能GPIOC
RCC->APB2ENR = 0X00000010;
// 设置GPIOC 推挽与50MHz
GPIOC->CRH=0X00300000;
while(1)
{
}
}
在8.2.4端口输出寄存器(GPIOx_ODR)(x=A...E)

可以看到,如果想给PC13上的灯点亮(低电平点亮),给ODR设置为0,可以都设置为0,即
GPIOC->ODR=0X00000000 表示灯亮
给GPIOC->ODR=0X00002000表示灯灭
二进制和十六进制为
00000000 00000000 00100000 00000000
00 00 20 00
00002000
00000000 00000000 00100000 00000000
00 00 20 00
00002000
#include "stm32f10x.h" // Device header
int main(void)
{
// 使能GPIOC
RCC->APB2ENR = 0X00000010;
// 设置GPIOC 推挽与50MHz
GPIOC->CRH=0X00300000;
// 设置灯亮
GPIOC->ODR=0X00000000;
// 设置灯灭
//GPIOC->ODR=0X00002000;
while(1)
{
}
}
实现闪烁
#include "stm32f10x.h" // Device header
int main(void){
RCC->APB2ENR = 0x00000010;
// 设置GPIOC 推挽与50MHz
GPIOC->CRH=0X00300000;
while(1){
uint32_t i, j; // 在循环外声明变量(兼容C90标准)
for(i = 0; i < 3000; i++)
for(j = 0; j < 3600; j++); // 大约1ms的延时
GPIOC->ODR=0X00000000 ;//表示灯亮
for(i = 0; i < 3000; i++)
for( j = 0; j < 3600; j++); // 大约1ms的延时
GPIOC->ODR=0X00002000 ;//表示灯灭
}
}
循环版本
#include "stm32f10x.h" // Device header
// 延时函数,大约延时ms毫秒
void Delay_ms(uint32_t ms)
{
uint32_t i, j;
// 简单的软件延时,具体值需要根据实际系统时钟校准
for(i = 0; i < ms; i++)
for(j = 0; j < 7200; j++); // 大约1ms的延时
}
int main(void){
//RCC->APB2ENR=0X00000010;
// 使能GPIOC
RCC->APB2ENR = 0X00000010;
// 设置GPIOC 推挽与50MHz
GPIOC->CRH=0X00300000;
while(1){
// 设置灯亮
GPIOC->ODR=0X00000000;
Delay_ms(3000); // 延时3秒
// 设置灯灭
GPIOC->ODR=0X00002000;
Delay_ms(3000); // 延时3秒
}
}
7.添加库函数
上面的方式需要经常查看手册的寄存器,采用库函数可以解决这个问题
打开工程文件夹,新建一个文件夹Library,用来存放库函数

打开固件库文件找内核库函数源文件,路径:
D:\BaiduNetdiskDownload\STM32入门教程资料\固件库\固件库\STM32F10x_StdPeriph_Lib_V3.5.0\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver\src

打开固件库文件找内核库函数头文件,路径:
D:\BaiduNetdiskDownload\STM32入门教程资料\固件库\固件库\STM32F10x_StdPeriph_Lib_V3.5.0\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver\inc
然后Ctrl+a全选复制刚刚新建的到工程文件夹下Library文件夹下

回到Kile5右击Target1添加组并命名为Library,右击Library添加存在的文件,将库函数源文件和头文件全部添加进来

至此库函数还不能直接使用,还需要再添加一个文件。接着打开固件库文件
D:\BaiduNetdiskDownload\STM32入门教程资料\固件库\固件库\STM32F10x_StdPeriph_Lib_V3.5.0\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template\
把
stm32f10x_it.h
stm32f10x_it.c
stm32f10x_conf.h
复制到User目录下

接着回到Kile5把刚刚的三个文件添加到User组里

最后我们还需要一个宏定义,跳转到头文件stm32f10x.h下滑到最后,找到 USE_STDPERIPH_DRIVER


然后选择魔法棒--选择C++,将USE_STDPERIPH_DRIVER复制到Define ,同时在include中添加User和Library目录

最后编译一下,0错误0警告说明工程建立成功

8.用库函数点灯
RCC_APB2PeriphClockCmd函数

通过右键RCC_APB2PeriphClockCmd()跳转到定义的位置

第一个参数选择 RCC_APB2Periph_GPIOC,第二个参数ENABLE
c
// #define RCC_APB2Periph_GPIOC ((uint32_t)0x00000010)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_Init函数

通过右键RCC_APB2PeriphClockCmd()跳转到定义的位置
第一个参数为GPIO_TypeDef* GPIOx, 第二个参数为GPIO_InitTypeDef* GPIO_InitStruct

参数需要一个结构体,就需要先定义一个结构体
// 定义GPIO的结构体,通过.设置结构体的属性
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode =
GPIO_InitStructure.GPIO_Pin =
GPIO_InitStructure.GPIO_Speed =

右键GPIO_Mode可以看到定义的位置,注释中的无法跳转,选择GPIOMode_TypeDef然后复制查找

可以看到GPIOMode_TypeDef是一个枚举,可以把GPIO_Mode_Out_PP设置给结构体
// 定义GPIO的结构体,通过.设置结构体的属性
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin =
GPIO_InitStructure.GPIO_Speed =
右键GPIO_Pin查看定义,出现左下角的多个定义,选择member,

跳转到

选择注释中的GPIO_pins_define,ctrl+f可以快速查找,选择Find Next

复制 GPIO_Pin_13 到GPIO_Pin
// 定义GPIO的结构体,通过.设置结构体的属性
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed =
右键跳转,在注释中ctrl+f查找,可以看到是一个枚举类型,选择GPIO_Speed_50MHz

复制GPIO_Speed_50MHz到 GPIO_InitStructure.GPIO_Speed如下:
// 定义GPIO的结构体,通过.设置结构体的属性
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
把结构体地址赋值给GPIO_Init函数
// 定义GPIO的结构体,通过.设置结构体的属性
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 初始化GPIOC
// 第一个参数为GPIO_TypeDef* GPIOx, 第二个参数为GPIO_InitTypeDef* GPIO_InitStruct
// 先在上面初始化一个GPIO_InitTypeDef结构体 变量名为 GPIO_InitStructure
GPIO_Init(GPIOC,&GPIO_InitStructure);
编译下,出现如下问题
问题13:编译时,出现错误declaration may not appear after executable statement in block
可以把GPIO_InitTypeDef GPIO_InitStructure;放在句首,也可以在c/c++选项卡在,勾选C99 Mode,就可以在 在函数的可执行语句之后定义变量了。
GPIO_SetBits和GPIO_ResetBits函数
// 给GPIOC的13端口设置高电平
//GPIO_SetBits(GPIOC,GPIO_Pin_13);
// 给GPIOC的13端口设置低电平
GPIO_ResetBits(GPIOC,GPIO_Pin_13);
编译下,然后load 就出现灯亮了
9.新建工程步骤
建立工程文件夹,Keil中新建工程,选择型号
工程文件夹里建立Start、Library、User等文件夹,复制固件库里面的文件到工程文件夹
工程里对应建立Start、Library、User等同名称的分组,然后将文件夹内的文件添加到工程分组里
工程选项,C/C++,Include Paths内声明所有包含头文件的文件夹
工程选项,C/C++,Define内定义USE_STDPERIPH_DRIVER
工程选项,Debug,下拉列表选择对应调试器,Settings,Flash Download里勾选Reset and Run
10.工程结构

所以复位键后,程序后重新启动

单片机的程序最后为一个死循环,永远不会结束
SystemInit的函数定义在system开头.c的里了

拓展:固件库的下载方式
进入官网
意法半导体官网 | ST官网 - STMicroelectronics
选择产品--微处理器

选择主流MCU,选择F1系列,选择STM32F103

选择工具与软件

选择嵌入式软件中的MCU and MPU embedded software,

选择
STM32 Standard Peripheral Libraries,选择STSW-STM32054

选择打开Open software page,选择对应的版本,这里选择的是3.5

选择版本后,谈成是否接受界面

单击接受后,进入登录或访客下载页面

登录或访客都可以,

进入版本选择并选择版本边上的下载按钮,即完成下载的功能

下载后的压缩包与提供的压缩包内容一致。
拓展:固件库的目录说明
STM32 标准外设库(StdPeriph Library)是 ST 官方为简化开发提供的工具包,其目录结构分工明确。以下结合典型目录(以 STM32F1 系列为例),解析核心目录的作用:
一、核心目录:Libraries(驱动库核心)
是外设库的底层支撑 ,包含 CMSIS 内核抽象层 和 STM32 外设驱动层,让开发者无需直接操作寄存器即可控制硬件。
1. CMSIS 文件夹(Cortex-M 软件接口标准)
- 作用:实现 Cortex-M 内核与芯片外设的解耦,让代码兼容不同厂商的 Cortex-M 芯片,同时简化内核相关操作(如中断、调试)。
- 子目录分解 :
-
Core_Support:
core_cm3.c/h
(对应 Cortex-M3 内核,M4/M7 内核则为core_cm4.c/h
等):
定义内核寄存器的操作函数(如配置 NVIC 中断优先级、系统滴答定时器),实现内核外设(如 SysTick、NVIC)的驱动。- 这些文件是 ARM 官方提供的通用代码,与 STM32 芯片无关,只负责内核层面的抽象。
-
Device/ST/STM32F1xx:
- 启动文件 (如
startup_stm32f10x_h/m/ld.s
,汇编编写):
芯片上电后第一步执行的代码 ,负责初始化栈指针、复制全局变量到 RAM、跳转到main
函数。不同容量的芯片(小/中/大容量)对应不同启动文件(如hd
代表大容量)。 system_stm32f10x.c/h
:
定义SystemInit()
函数,初始化系统时钟(配置 HSE、HSI、PLL 等,设置 AHB/APB 总线分频),是启动流程的关键步骤。 STM32主频是72MHz,就是这个文件中配置的stm32f10x.h
:
外设寄存器的映射文件 ,通过宏和结构体(如GPIO_TypeDef
)定义所有外设的寄存器地址(如GPIOA->CRL
对应 GPIOA 的配置寄存器),是库函数操作硬件的基础。类似Reg51.h ,描述有哪些寄存器和地址,有些类似域名与ip地址的关系
- 启动文件 (如
-
2. STM32F10x_StdPeriph_Driver 文件夹(外设驱动层)
- 作用 :为 STM32 的每个外设(GPIO、UART、SPI 等)提供封装好的库函数,开发者通过调用函数即可配置外设,无需深入寄存器细节。
- 子目录分解 :
-
inc 文件夹 :
存放外设的头文件 (如
stm32f10x_gpio.h
、stm32f10x_usart.h
):- 定义初始化结构体 (如
GPIO_InitTypeDef
,包含引脚模式、速度、上下拉等参数)。 - 声明库函数(如
GPIO_Init()
、USART_SendData()
)。 - 定义寄存器的位掩码 (如
GPIO_Mode_Out_PP
代表推挽输出模式)。
- 定义初始化结构体 (如
-
src 文件夹 :
存放外设的源文件 (如
stm32f10x_gpio.c
、stm32f10x_usart.c
):- 实现
inc
中声明的函数,内部通过操作stm32f10x.h
的寄存器完成功能(如GPIO_Init()
会修改GPIOx->CRL/CRH
寄存器)。 - 每个外设的功能(如 GPIO 翻转、UART 发送数据)都通过这里的函数封装。
- 实现
-
特殊文件:misc.c/h :
提供对**内核 NVIC(中断控制器)**的操作函数(如
NVIC_Init()
配置中断优先级、NVIC_EnableIRQ()
使能中断),因为 NVIC 属于内核外设,需与芯片外设的中断配合使用。
-
二、辅助目录(项目开发相关)
1. Project 文件夹
- 作用 :提供 官方示例工程 和 编译器模板,快速上手开发。
- 内容 :
STM32F10x_StdPeriph_Examples
:按外设分类的示例(如 GPIO 翻转、UART 通信、SPI 传输),可直接导入 Keil/IAR 编译,学习外设的典型用法。Templates
:针对不同编译器(Keil、IAR)的工程模板,包含最小化的文件结构(已关联库路径、包含必要头文件),开发者可在此基础上扩展功能。
2. Utilities 文件夹
- 作用 :存放 ST 官方评估板(如 STM32 Discovery、Eval 板)的 专用驱动和示例。
- 内容 :
- 评估板上的扩展硬件(如显示屏、传感器、按键)的驱动代码(如
stm32_eval_led.c
)。 - 若使用通用开发板,这部分可忽略,但其中的驱动设计思路(如分层封装、状态机)可借鉴。
- 评估板上的扩展硬件(如显示屏、传感器、按键)的驱动代码(如
3. 根目录文件
stm32f10x_stdperiph_lib_um.chm
:
库函数帮助文档 (CHM 格式),详细说明每个库函数的参数、功能、使用示例 (如USART_Config()
如何配置波特率、校验位),是开发时的核心参考。Release_Notes.html
:
版本说明,记录库的更新内容、支持的芯片型号、已知问题,用于确认库的兼容性。
三、目录协作关系与开发流程
- 底层支撑 :
Libraries/CMSIS
负责内核和芯片的基础初始化(启动、时钟、寄存器映射)。 - 外设控制 :
Libraries/STM32F10x_StdPeriph_Driver
提供外设的库函数,开发者通过调用这些函数配置硬件(如GPIO_Init()
设置引脚模式)。 - 快速验证 :
Project
中的示例和模板可直接复用,减少搭建工程的时间。 - 参考学习 :
Utilities
中的评估板代码和chm
文档辅助理解外设用法。
通过合理利用这些目录,开发者可跳过寄存器级的繁琐操作 ,专注于应用层逻辑,大幅提升开发效率。若需深入底层,也可通过 stm32f10x.h
和 core_cm3.h
查看寄存器定义,实现库函数的扩展或自定义。
拓展:GPIO 与 APB2
在STM32微控制器中,这句话描述的是GPIO(通用输入/输出端口)与系统总线架构中APB2总线的连接关系,以下从微控制器的总线架构、APB2总线特点、GPIO作为外设的属性这几个方面来深入理解:
微控制器的总线架构
STM32微控制器采用了先进的总线架构,以实现不同功能模块之间高效的数据传输和控制。其中,常见的总线有AHB(Advanced High-performance Bus,高级高性能总线)和APB(Advanced Peripheral Bus,高级外设总线) 。
- AHB总线:主要用于连接高性能、高数据传输速率的模块,如CPU内核、存储器、DMA控制器等。它能够支持高速的数据传输,满足对带宽要求较高的设备之间的数据交互。
- APB总线:则是用于连接低带宽的外设模块,为这些外设提供了一种相对简单且低功耗的通信方式。APB又分为APB1和APB2,它们的主要区别在于工作频率和所连接的外设类型。
APB2总线特点
APB2总线相较于APB1总线,通常具有更高的工作频率。这意味着连接到APB2总线上的外设可以在更快的时钟信号驱动下工作,从而具备更高的数据传输速率和响应速度。在STM32系列中,APB2总线通常用于连接那些对速度要求相对较高的外设。
GPIO作为APB2的外设
- GPIO功能简介:GPIO是STM32中非常重要且常用的外设,它允许用户通过软件灵活地配置引脚的功能,如设置为输入模式来读取外部信号(像按键状态),或者设置为输出模式来控制外部设备(如点亮LED灯)。
- 连接关系原因:GPIO在工作时,有时需要快速地响应外部信号的变化或者及时输出控制信号,比如在驱动高速通信接口的使能引脚,或者快速切换LED灯的亮灭状态来实现高频闪烁效果等场景下,就对数据传输和处理速度有一定要求。将GPIO连接到APB2总线上,利用APB2相对较高的工作频率,能够满足GPIO在这些快速操作场景下的性能需求。
不过需要注意,并不是所有STM32系列微控制器的GPIO都是APB2的外设 。不同型号的STM32,其外设与总线的连接情况会根据芯片的设计和定位有所差异。比如,一些低功耗或低成本的STM32型号,其GPIO可能会连接到APB1总线上,以平衡性能和功耗等方面的需求。
拓展:RCC_APB2ENR
在STM32微控制器中,RCC_APB2ENR
是一个寄存器,它是复位和时钟控制(Reset and Clock Control,RCC)模块的一部分,全称为APB2外设时钟使能寄存器(Advanced Peripheral Bus 2 Peripheral Clock Enable Register) ,主要用于控制连接在APB2总线上的外设时钟的开启和关闭。以下从寄存器基本概念、作用原理以及使用方式这几个方面来理解它:
寄存器基本概念
寄存器是微控制器内部的一组存储单元,每个寄存器都被分配了特定的地址。通过对这些地址进行读写操作,就可以访问和修改寄存器中的值,进而实现对微控制器不同功能模块的配置和控制。RCC_APB2ENR
寄存器也是如此,它在内存中占据特定的地址空间,用户可以通过编程来读写这个寄存器。
作用原理
- 时钟控制机制:在STM32中,为了降低功耗以及对不同外设进行灵活管理,并不是所有外设的时钟在系统上电后都处于开启状态。每个外设都有对应的时钟使能位,当该位被设置为1时,相应外设的时钟才会被打开,外设才能正常工作;如果设置为0 ,外设时钟被关闭,此时外设处于低功耗状态,无法执行数据处理等操作。
- 与APB2总线外设关联 :APB2总线上连接着一些对速度要求相对较高的外设,比如GPIO端口(部分STM32型号)、ADC(模拟数字转换器)、SPI1(串行外设接口1)等。
RCC_APB2ENR
寄存器中的每一位都对应着一个连接在APB2总线上的外设。例如,在STM32F103系列中,RCC_APB2ENR
寄存器的第2位对应着IOPAEN
(GPIOA时钟使能位),第3位对应着IOPBEN
(GPIOB时钟使能位)等。当要使用某个APB2外设时,就需要将该外设对应的时钟使能位置1 ,为其提供时钟信号,外设才能按照设定的功能运行。
具体使用方式(以STM32F103为例,基于标准外设库)
- 头文件包含 :在使用与
RCC_APB2ENR
相关的功能之前,需要在代码中包含对应的头文件,如stm32f10x.h
,该头文件中定义了RCC_APB2ENR
寄存器以及相关的宏定义。
c
#include "stm32f10x.h"
- 使能外设时钟:比如要使用GPIOA端口,就需要使能GPIOA的时钟,示例代码如下:
c
// 使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
在上述代码中,RCC_APB2PeriphClockCmd
是标准外设库中提供的函数,它的第一个参数指定要使能时钟的外设(这里是RCC_APB2Periph_GPIOA
,对应RCC_APB2ENR
寄存器中的IOPAEN
位),第二个参数ENABLE
表示使能该外设时钟,其本质就是对RCC_APB2ENR
寄存器中对应的位进行置1操作。
- 关闭外设时钟:当某个外设不再使用时,为了降低功耗,可以关闭其时钟,示例代码如下:
c
// 关闭GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, DISABLE);
这里的DISABLE
参数会将RCC_APB2ENR
寄存器中IOPAEN
位清0,从而关闭GPIOA的时钟。
总之,RCC_APB2ENR
寄存器是STM32中管理APB2总线上外设时钟的关键部件,合理地配置它能够有效地控制外设的工作状态,实现对系统功耗和性能的优化。
拓展:GPIOx_CRH寄存器
在STM32微控制器中,配置完RCC_APB2ENR
使能GPIO时钟后,还需要配置端口配置寄存器GPIOx_CRH
(x = A..E
),这是因为两者承担着不同且互补的功能,具体如下:
时钟使能是基础前提
RCC_APB2ENR
寄存器用于使能连接在APB2总线上的外设时钟,对于GPIO端口而言,使能其时钟是必不可少的准备工作。只有当GPIO对应的时钟使能位(例如RCC_APB2ENR
中与GPIOA
对应的IOPAEN
位)被置1 ,GPIO端口才能正常工作。
这就好比给一个工厂供电,只有先接通电源,工厂里的机器(GPIO外设)才有可能运转起来, 但仅接通电源,机器并不会自动按照我们期望的方式工作,还需要对其进行功能设定。
GPIOx_CRH决定端口具体功能
GPIOx_CRH
寄存器是GPIO端口的配置寄存器(高8位配置寄存器,对应引脚GPIOx_8 - GPIOx_15
,低8位配置寄存器是GPIOx_CRL
,对应引脚GPIOx_0 - GPIOx_7
),它的作用是详细设定GPIO引脚的工作模式和输出速度等关键参数,具体如下:
1. 配置引脚工作模式
GPIO引脚可以配置为多种工作模式,以满足不同的应用需求,GPIOx_CRH
中的相关位用于选择这些模式:
- 输入模式 :
- 浮空输入(IN_FLOATING):引脚的电平状态完全由外部输入决定,内部没有上拉或下拉电阻,常用于读取外部信号,如按键输入。
- 上拉输入(IPU):引脚内部连接了上拉电阻,默认情况下引脚被拉高为高电平,当外部连接低电平信号时,引脚电平被拉低,适用于按键等外部信号常态为高电平的场景。
- 下拉输入(IPD):与上拉输入相反,引脚内部连接下拉电阻,默认被拉低为低电平,用于外部信号常态为低电平的情况。
- 模拟输入(AIN):引脚用于连接模拟信号源,关闭施密特触发器(一种用于波形整形的电路),直接将外部模拟信号接入内部ADC(模拟数字转换器),用于模拟量采集。
- 输出模式 :
- 通用推挽输出(GPIO_Mode_Out_PP):引脚可以输出高电平或低电平,驱动能力较强,常用于驱动LED灯、继电器等需要较大电流的负载。
- 通用开漏输出(GPIO_Mode_Out_OD):引脚只能输出低电平,输出高电平时需要外部上拉电阻将引脚拉高,适用于需要"线与"逻辑(多个开漏输出引脚连接在一起,只要有一个输出低电平,整体就为低电平)的场景,或者用于电平转换。
- 复用推挽输出(GPIO_Mode_AF_PP):引脚用于复用功能,如SPI、USART等外设的引脚,由外设模块控制引脚的输出信号,同样具有较强的驱动能力。
- 复用开漏输出(GPIO_Mode_AF_OD):与复用推挽输出类似,但输出高电平时需要外部上拉电阻,常用于I2C等需要"线与"逻辑的复用功能场景。
2. 配置输出速度
在输出模式下,还可以通过GPIOx_CRH
设置引脚的输出速度,常见的有2MHz、10MHz、50MHz等选项。 输出速度决定了引脚电平翻转的速率,合理选择输出速度可以避免信号干扰和电磁兼容性(EMC)问题。例如,驱动LED灯时,较低的输出速度(如2MHz)即可满足要求;而在高速通信接口(如SPI)中,可能就需要选择较高的输出速度(如50MHz)以满足数据传输速率的要求。
两者配合实现完整功能
配置RCC_APB2ENR
打开GPIO时钟,是让GPIO模块能够正常工作;而配置GPIOx_CRH
(和GPIOx_CRL
)则是告诉GPIO模块具体要做什么,比如某个引脚是用于输入按键信号,还是用于输出驱动LED的信号,以及以什么样的速度和方式进行输入输出操作。
只有这两步配置都正确完成,GPIO引脚才能按照开发者的预期在电路中发挥作用,实现与外部设备的有效交互。
拓展:stm32f10x_it.h
和 stm32f10x_it.c
和stm32f10x_conf.h
作用
在STM32F10x系列的标准外设库项目中,stm32f10x_it.h
、stm32f10x_it.c
和stm32f10x_conf.h
是三个具有特定功能的核心文件,分别承担中断服务程序管理和库配置的作用,具体解析如下:
1. stm32f10x_it.h
和 stm32f10x_it.c
:中断服务程序(ISR)的集中管理
这两个文件是中断服务程序的专用存放地,用于定义和实现STM32的所有中断处理函数,是系统响应外部事件(如GPIO触发、定时器溢出、串口接收等)的核心逻辑载体。
核心作用:
-
中断函数声明与实现分离:
stm32f10x_it.h
:声明所有中断服务函数的原型(如EXTI0_IRQHandler()
、TIM2_IRQHandler()
),供其他模块调用或引用。stm32f10x_it.c
:实现这些中断服务函数的具体逻辑,例如在外部中断触发时读取GPIO状态、在定时器中断中更新变量等。
-
统一管理中断向量表映射 :
STM32的中断向量表(启动文件中定义)会将硬件中断信号映射到
stm32f10x_it.c
中对应的函数。例如,当外部中断线0(EXTI0)触发时,系统会自动跳转到EXTI0_IRQHandler()
函数执行。 -
避免中断函数命名冲突 :
中断服务函数的名称是固定的(由芯片硬件决定,如
USART1_IRQHandler
对应串口1中断),将其集中放在这两个文件中,可避免与其他模块的函数命名冲突,规范代码结构。
示例场景:
当需要处理按键触发的外部中断(如PA0引脚连接按键,触发EXTI0中断)时,会在stm32f10x_it.c
中实现:
c
void EXTI0_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
// 处理中断逻辑(如点亮LED、更新标志位)
GPIO_SetBits(GPIOC, GPIO_Pin_13);
// 清除中断标志位(必须操作,否则会持续触发中断)
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
而stm32f10x_it.h
中会声明该函数:
c
void EXTI0_IRQHandler(void);
2. stm32f10x_conf.h
:外设库的配置开关
该文件是标准外设库的"配置中心",通过宏定义控制是否启用特定外设的驱动文件、中断向量和调试信息,用于裁剪项目代码、减少编译体积。
核心作用:
-
选择性包含外设驱动头文件 :
文件中通过
#define
宏控制是否包含某个外设的头文件。例如:c#define USE_STDPERIPH_DRIVER // 启用标准外设库 #include "stm32f10x_gpio.h" // 包含GPIO驱动 #include "stm32f10x_usart.h" // 包含USART驱动 // #include "stm32f10x_adc.h" // 注释掉则不启用ADC驱动
未被包含的外设驱动不会被编译,从而减少固件体积。
-
控制中断向量的使能 :
部分外设的中断向量需要通过该文件间接控制(结合
stm32f10x_it.c
)。例如,若不启用ADC,则可注释掉#include "stm32f10x_adc.h"
,避免ADC相关的中断函数被编译。 -
定义库函数的调试输出开关 :
部分库函数(如
assert_param()
参数检查)的调试信息输出,可通过该文件中的宏(如USE_FULL_ASSERT
)控制是否启用,便于开发阶段调试和量产阶段关闭冗余输出。
典型配置示例:
c
// 启用参数检查(开发阶段用,量产可关闭)
#define USE_FULL_ASSERT
// 包含需要使用的外设驱动
#include "stm32f10x_rcc.h" // 时钟控制
#include "stm32f10x_gpio.h" // GPIO控制
#include "stm32f10x_tim.h" // 定时器控制
// 不包含SPI和I2C驱动(项目中用不到)
// #include "stm32f10x_spi.h"
// #include "stm32f10x_i2c.h"
三者的协作关系
stm32f10x_conf.h
决定项目中启用哪些外设驱动,为中断处理和其他功能提供基础支持。- 当外设触发中断时,中断向量表会引导系统执行
stm32f10x_it.c
中对应的中断服务函数,而这些函数的声明在stm32f10x_it.h
中。 - 三者共同构成了STM32中断系统和外设管理的核心框架,确保中断响应的规范性和代码的可维护性。
void);
#### 2. `stm32f10x_conf.h`:外设库的配置开关
该文件是**标准外设库的"配置中心"**,通过宏定义控制是否启用特定外设的驱动文件、中断向量和调试信息,用于裁剪项目代码、减少编译体积。
##### 核心作用:
- **选择性包含外设驱动头文件**:
文件中通过`#define`宏控制是否包含某个外设的头文件。例如:
```c
#define USE_STDPERIPH_DRIVER // 启用标准外设库
#include "stm32f10x_gpio.h" // 包含GPIO驱动
#include "stm32f10x_usart.h" // 包含USART驱动
// #include "stm32f10x_adc.h" // 注释掉则不启用ADC驱动
未被包含的外设驱动不会被编译,从而减少固件体积。
-
控制中断向量的使能 :
部分外设的中断向量需要通过该文件间接控制(结合
stm32f10x_it.c
)。例如,若不启用ADC,则可注释掉#include "stm32f10x_adc.h"
,避免ADC相关的中断函数被编译。 -
定义库函数的调试输出开关 :
部分库函数(如
assert_param()
参数检查)的调试信息输出,可通过该文件中的宏(如USE_FULL_ASSERT
)控制是否启用,便于开发阶段调试和量产阶段关闭冗余输出。
典型配置示例:
c
// 启用参数检查(开发阶段用,量产可关闭)
#define USE_FULL_ASSERT
// 包含需要使用的外设驱动
#include "stm32f10x_rcc.h" // 时钟控制
#include "stm32f10x_gpio.h" // GPIO控制
#include "stm32f10x_tim.h" // 定时器控制
// 不包含SPI和I2C驱动(项目中用不到)
// #include "stm32f10x_spi.h"
// #include "stm32f10x_i2c.h"
三者的协作关系
stm32f10x_conf.h
决定项目中启用哪些外设驱动,为中断处理和其他功能提供基础支持。- 当外设触发中断时,中断向量表会引导系统执行
stm32f10x_it.c
中对应的中断服务函数,而这些函数的声明在stm32f10x_it.h
中。 - 三者共同构成了STM32中断系统和外设管理的核心框架,确保中断响应的规范性和代码的可维护性。
通过合理配置这三个文件,开发者可以高效管理中断逻辑、裁剪项目代码,适配不同场景的需求(如资源受限的嵌入式设备需最小化固件体积)。