STM32 学习 —— 个人学习笔记2-2(新建工程)

声明

文中内容为观看 BiliBili 视频【STM32入门教程-2023版 细致讲解 中文字幕】后学习并扩展总结。

本文章为个人学习使用,版面观感若有不适请谅解,文中知识仅代表个人观点,若出现错误,欢迎各位批评指正。

一、开发范式的技术基底

STM32 作为基于 ARM Cortex-M 内核的 32 位微控制器系列,其开发本质是通过软硬件协同实现外设资源的可控调度,核心依赖寄存器操作抽象层封装两大技术逻辑。开发过程需遵循 "硬件资源映射 - 驱动逻辑实现 - 系统功能集成" 的三层架构,涉及时钟树配置、中断向量表设计、外设寄存器映射等底层技术要素,同时兼容裸机运行与操作系统承载两种部署模式。

二、主流开发方式分类及特征

2.1 寄存器直接开发

作为最底层的开发方式,该模式通过直接读写硬件寄存器实现外设控制,无需任何中间件封装。开发者需基于芯片数据手册(Datasheet)明确寄存器地址映射关系,通过位操作指令配置 GPIO、定时器、通信接口等外设。其核心优势在于极致资源利用率确定性实时响应,代码执行效率接近硬件极限,Flash 占用量与 RAM 消耗均达到最小;但存在显著局限性,包括开发复杂度高(需精通硬件架构)、可移植性差(与具体芯片型号强绑定)、调试周期长等问题,适用于 Bootloader 开发、资源极端受限场景及底层性能优化需求。

2.2 标准库(SPL)开发

STMicroelectronics 提供的标准外设库(Standard Peripheral Library)将寄存器操作封装为结构化函数接口,形成对外设功能的抽象封装。该方式通过标准化 API 屏蔽寄存器操作细节,开发者可通过调用库函数实现外设初始化、数据传输等功能,无需关注底层硬件差异。其技术特征表现为性能与便捷性的平衡:相比寄存器开发,开发效率显著提升,代码可读性增强;相较于高层抽象库,保留了较高的执行效率,支持同系列芯片间的适度移植。该模式曾广泛应用于 STM32F1/F4 等经典系列的工业控制项目,目前仍用于 legacy 系统维护与性能敏感型应用开发。

2.3 HAL 库开发

作为当前主流的开发范式,硬件抽象层(Hardware Abstraction Layer, HAL)库通过高度抽象化设计实现跨系列芯片的统一接口。该方式与 STM32CubeMX 图形化配置工具深度集成,支持通过可视化操作生成初始化代码,涵盖时钟配置、外设映射、中断管理等核心功能。其核心优势包括:跨平台兼容性 (支持 F0/F7/H7 等全系列芯片移植)、开发效率优化 (自动化代码生成降低重复劳动)、功能完整性 (集成 USB、文件系统、RTOS 适配等中间件)、标准化错误处理(通过 HAL_StatusTypeDef 实现状态机管理)。但抽象层引入的额外开销导致代码体积增大(相较于寄存器开发增加约 30%-50%),部分场景下中断响应延迟略高于前两种方式,适用于物联网设备、消费电子产品、快速原型开发等对开发周期要求较高的场景。

2.4 高级框架与 RTOS 集成开发

随着应用复杂度提升,基于实时操作系统(RTOS)的开发方式日益普及。该模式将 HAL 库与 FreeRTOS、RT-Thread 等嵌入式操作系统结合,实现多任务调度、资源管理与并发控制。开发者可通过框架化工具链实现模块化开发,支持图形界面(GUI)、网络协议栈(LwIP)、物联网协议(MQTT/NB-IoT)等复杂功能集成。近年来,该方式进一步拓展至边缘 AI 领域,STM32N6 等新型芯片通过 Neural-ART 加速器支持 YOLO Tiny 等轻量级 AI 模型部署,形成 "硬件抽象层 - 操作系统 - 算法框架" 的三层开发架构,适用于工业自动化、智能监控、自动驾驶辅助等高端应用场景。

三、开发方式选型逻辑

STM32 开发方式的选择需基于项目需求进行多维度权衡,核心决策因素包括:硬件资源约束(Flash/RAM 容量)、实时性要求、开发周期、可移植性需求及团队技术栈。学术研究与工程实践表明,寄存器开发适用于底层优化与资源受限场景,HAL 库开发主导快速原型与跨平台项目,而 RTOS 集成框架则面向复杂智能系统开发。三者形成互补性技术体系,共同支撑 STM32 从简单控制到智能感知的全场景应用需求。

本文将以教学视频中演示的标准库(SPL)开发为例,进行总结和展示。

四、基于标准库工程的建立

4.1 STM32 库函数准备

STM32 标准外设库(Standard Peripheral Library, SPL)是意法半导体(STMicroelectronics)为其 STM32 系列微控制器推出的第一代官方外设驱动封装库,作为寄存器直接操作与高层抽象库(HAL)之间的过渡性技术方案,是早期 STM32 嵌入式开发的核心工具集。可以通过访问 STMicroelectronics 官网资源中心 选择对应芯片系列下载完整的库包。

文件名称:STM32F10x_StdPeriph_Lib_V3.5.0

文件结构:|-------- STM32F10x_StdPeriph_Lib_V3.5.0    // 根目录

|-------- _htmresc    // 两张图片,无用

|-------- Libraries    // 库函数文件,新建工程使用

|-------- Project    // 官方提供的工程示例和模板,可作为库函数使用参考

|-------- Utilities    // 官方评估版的相关例程,测评程序

|-------- Release_Notes.html    // 库函数发布文档

|-------- stm32f10x_stdperiph_lib_um.chm    // 库函数使用手册

4.2 建立工程存放文件夹

在搭建 STM32 工程前预先创建独立的工程文件夹,是嵌入式开发的基础规范操作,核心是为工程相关的所有文件建立专属且独立的存储载体,既能对源码、库文件、编译配置、输出文件等各类资源进行结构化归类,避免文件散落混乱,又能有效隔离不同工程的编译依赖与配置参数,防止版本冲突和误修改,同时也为后续的工程迁移、备份、团队协作及 Git 等版本控制工具的使用奠定基础,大幅提升开发与维护效率。

  • 首先,选择一个合适的位置,预先创建独立的工程文件夹,并打开安装好的 Keil5 MDK 程序,依次点击菜单栏的 Project ----> New μVision Project...。
  • 选择预先创建好的独立的工程文件夹,点击打开。
  • 在独立的工程文件夹根目录下,再次创建存放本次工程所需要的文件夹(右击空白区域,新建文件夹即可),选择建立好的文件夹并点击打开。
  • 在下方文件名输入栏,输入工程文件的名字,点击保存。
  • 根据自己的硬件信息,选择对应的器件型号,本人使用芯片型号为 STM32F103C8T6 ,因此依次选择 STMicroelectronics ----> STM32F103 ----> STM32F103C8 并点击 OK 。
  • 弹出 Keil 软件的新建工程助手后,点击 OK 即可。
4.3 添加工程所需的必要文件
  • 1、STM32 的启动文件

预先下载好的 STM32F10x_StdPeriph_Lib_V3.5.0 固件库文件夹中下的

STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/arm/

文件夹中的所有文件(共 8 个 .s 结尾文件)

  • 2、STM32 的外设寄存器描述文件

预先下载好的 STM32F10x_StdPeriph_Lib_V3.5.0 固件库文件夹中下的

STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/

文件夹中的 stm32f10x.h

  • 3、STM32 的配置时钟文件

预先下载好的 STM32F10x_StdPeriph_Lib_V3.5.0 固件库文件夹中下的

STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/

文件夹中的 system_stm32f10x.c 和 system_stm32f10x.h

  • 4、STM32 的内核寄存器描述文件

预先下载好的 STM32F10x_StdPeriph_Lib_V3.5.0 固件库文件夹中下的

STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/CMSIS/CM3/CoreSupport/

文件夹中的 core_cm3.c 和 core_cm3.h

  • 5、将上述的 13 个文件,复制到本次工程建立的文件夹下,为方便管理和查看,可在本次工程文件夹下建立 Start 文件夹,并将上述文件存放其中。

  • 6、将本次工程文件夹下的 Source Group 1 文件夹重命名为 Start ,单击图标后,再次单击名字即可修改。

  • 7、鼠标右键点击该文件夹,并选择 Add Existing Files to Group 'Start'...。
    * 8、选择已经在工程文件夹下创建好的 Start 文件夹,首先添加后缀为 md.s 的启动文件(startup_stm32f10x_md.s),然后将文件夹下所有的 .c 和 .h 后缀的文件添加进来,添加后选择 Close 关闭窗口即可。

  • 9、添加文件夹的头文件路径:点击魔术棒按钮,打开工程选项,在 C/C++ 选项卡中的 Include Paths 栏填写 Start 的文件夹路径。

  • 10、新建 main 函数,验证工程是否可行:在本工程文件夹下,新建 User 文件夹,随后在 Keil5 软件中新建文件夹(右击 Target 1 文件夹,选择 Add Group...)并重命名为 User。

  • 11、右击 User 文件夹,选择 Add New Item to Group 'User'...,选择 C File 并填写文件名称 main,随后改变 Location 路径为 User 文件夹 ,点击 Add 即可。

  • 12、在 main.c 文件的空白处右击,选择 Insert 'include file' ----> stm32f10x.h,插入头文件。

  • 13、在 main.c 文件中编写 main 函数(注意最后一行需为空行,否则会报错),然后点击左上角工具栏的第二个按钮进行编译。

    #include "stm32f10x.h" // Device header

    int main(void){
    while(1){

    复制代码
      }

    }

  • 14、按教学视频的操作步骤进行到第 13 步时,发现软件提示有 4 个错误,22 个警告,部分信息为:Start/core_cm3.c(445): error: non-ASM statement in naked function is not supported,于是查询原因及解决方案,发现将共工程选项(魔术棒按钮)中 Target 下的 Code Generation 中 Compiler Version 从 6 修改为 5 即可。错误产生原因为老版本的 core_cm3.c 是为 ARMCC V5 编译器编写的,语法兼容度不足导致。
4.4 通过寄存器完成点灯操作(了解即可)
  • 1、首先将 STM32 的最小系统板按下列表格的对应关系和 ST-Link / DAP 连接,芯片中测试程序插电后默认状态为常亮状态,教学视频中为一个常亮另一个闪烁,但本人学习过程中实际情况为单灯常亮。
STM32 DAP mini ST - Link
GND GND GND
DCLK SWDCLK SWCLK
DIO SWDIO SWDIO
3.3 3.3V 3.3V
  • 2、在 Keil5 软件中配置调试器:点击魔术棒按钮,在 Debug 选项卡下的 Use 选择自己所使用的硬件,本人这里选择 CMSIS-DAP Debugger ,并点击 Settings 在 Flash Download 选项卡下勾选 Reset and Run,依次选择 OK 退出即可。通过这个设置,可以使每次下载程序后立马复位并执行,无需手按板子上的复位按键。

  • 3、调试器配置完成后,先点击编译按钮,再点击下载按钮,若无报错提示 Verify OK.,则代表配置成功。

  • 4、通过查询操作手册中关于 RCC 寄存器 APB2 外设时钟寄存器、GPIO 端口配置高寄存器以及 GPIO 端口输出数据寄存器的描述,进行参数配置,编译并配置可看见小灯亮起。其中 GPIOC->ODR = 0x00000000;表示亮灯,GPIOC->ODR = 0x00002000;表示灭灯。

    #include "stm32f10x.h" // Device header

    int main(void){
    RCC->APB2ENR = 0x00000010;
    GPIOC->CRH = 0x00300000;
    GPIOC->ODR = 0x00000000;
    while(1){

    复制代码
      }

    }

4.5 通过使用标准库完成点灯操作
  • 1、库函数源文件

预先下载好的 STM32F10x_StdPeriph_Lib_V3.5.0 固件库文件夹中下的

STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/STM32F10x_StdPeriph_Driver/src/

STM32F10x_StdPeriph_Lib_V3.5.0/Libraries/STM32F10x_StdPeriph_Driver/inc/

文件夹中的所有文件(共 46 个 .c 结尾文件)

  • 2、将上述的 46 个文件,复制到本次工程建立的文件夹下,为方便管理和查看,可在本次工程文件夹下建立 Library 文件夹,并将上述文件存放其中,与 Start 添加步骤一致,此处不做重复演示。
  • 3、库函数头文件包含关系的配置文件

预先下载好的 STM32F10x_StdPeriph_Lib_V3.5.0 固件库文件夹中下的

STM32F10x_StdPeriph_Lib_V3.5.0/Project/STM32F10x_StdPeriph_Template

文件夹中的 stm32f10x_conf.h 文件

  • 4、存放中断函数的文件

预先下载好的 STM32F10x_StdPeriph_Lib_V3.5.0 固件库文件夹中下的

STM32F10x_StdPeriph_Lib_V3.5.0/Project/STM32F10x_StdPeriph_Template

文件夹中的 stm32f10x_it.c 和 stm32f10x_it.h 文件

  • 5、将 3 和 4 中的三个文件粘贴到 User 目录下,并在 Keil5 软件中添加已存在文件。

  • 6、右键 main.c 文件中的头定义,选择 Open document 'stm32f10x.h',在第 8296 行左右找到如下语句,该语句含义为如果定义了 USE_STDPERIPH_DRIVER 字符串,#include "stm32f10x_conf.h" 该语句才有效。

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

  • 7、因此,为了使 #include "stm32f10x_conf.h" 该语句有效,打开工程设置选项将 USE_STDPERIPH_DRIVER 字符串添加到 C/C++ 选项卡下的 Preprocessor Symbols ----> Define 中,并将 Start、User 和 Library 文件夹路径添加进下方的 Include Paths 中。

  • 8、点击编译,耐心等待无报错后,即代表库函数的添加成功。

  • 9、使用库函数进行点灯操作,在依照教学视频按照以下方式进行编写并点击编译后,发现程序报错,错误信息为:User\main.c(5): error: #268: declaration may not appear after executable statement in block     GPIO_InitTypeDef GPIO_InitStructure;,意思为声明不允许出现在块中的可执行语句之后,因此将 GPIO_InitTypeDef GPIO_InitStructure; 语句上移一行,重新编译发现问题解决。

    #include "stm32f10x.h" // Device header

    int main(void){
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    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(GPIOC, &GPIO_InitStructure);
    // GPIO_SetBits(GPIOC, GPIO_Pin_13); // Off Power
    GPIO_ResetBits(GPIOC, GPIO_Pin_13); // On Power
    while(1){

    复制代码
      }

    }

  • 10、重新调整声明语句的位置,编译后" 0 Error(s), 0 Warning(s).,问题解决,由于点灯效果与通过寄存器完成点灯操作一致,此处不做重复展示。

    #include "stm32f10x.h" // Device header

    int main(void){
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    // GPIO_SetBits(GPIOC, GPIO_Pin_13); // Off Power
    GPIO_ResetBits(GPIOC, GPIO_Pin_13); // On Power
    while(1){

    复制代码
      }

    }

五、新建工程启动文件的选择

STM32 启动文件(以startup_stm32f10x_xx.s为典型命名)是基于 ARM Cortex-M 内核的汇编级初始化文件,是芯片上电后执行的第一段代码,其核心作用是初始化堆栈、中断向量表、系统时钟,并最终跳转到main()函数。启动文件与芯片型号强绑定,选择错误会直接导致程序无法启动、中断异常或硬件资源配置失效,选型的核心逻辑是匹配芯片的内核架构、Flash 容量、封装类型三大关键特征,具体参数如下表所示。(之前演示在 Keil5 中选择的 Start/startup_stm32f10x_md.s 启动文件的原因)

缩写 释义 Flash 容量 型号
LD_VL 小容量产品超值系列 16 ~ 32K STM32F100
MD_VL 中容量产品超值系列 64 ~ 128K STM32F100
HD_VL 大容量产品超值系列 256 ~ 512K STM32F100
LD 小容量产品 16 ~ 32K STM32F101 / 102 / 103
MD 中容量产品 64 ~ 128K STM32F101 / 102 / 103
HD 大容量产品 256 ~ 512K STM32F101 / 102 / 103
XL 加大容量产品 > 512K STM32F101 / 102 / 103
CL 互联型产品 - STM32F105 / 107

文中部分知识参考:B 站 ------ 江协科技;百度百科

相关推荐
tb_first2 小时前
万字超详细苍穹外卖学习笔记4
java·spring boot·笔记·学习·spring·mybatis
日更嵌入式的打工仔2 小时前
C内存布局
笔记
卡布叻_星星2 小时前
达梦数据库笔记之使用教程以及不推荐迁移选择小写
笔记
山岚的运维笔记2 小时前
SQL Server笔记 -- 第15章:INSERT INTO
java·数据库·笔记·sql·microsoft·sqlserver
孞㐑¥2 小时前
算法—队列+宽搜(bfs)+堆
开发语言·c++·经验分享·笔记·算法
代码游侠3 小时前
学习笔记——Linux字符设备驱动开发
linux·arm开发·驱动开发·单片机·嵌入式硬件·学习·算法
charlie1145141913 小时前
嵌入式C++教程——ETL(Embedded Template Library)
开发语言·c++·笔记·学习·嵌入式·etl
李小星同志3 小时前
VID2WORLD: CRAFTING VIDEO DIFFUSION MODELSTO INTERACTIVE WORLD MODELS论文学习
学习
TheNextByte13 小时前
如何在iPhone上恢复已删除的笔记的综合指南
笔记·ios·iphone