【普中STM32精灵开发攻略】--第 7 章 库函数模板创建

发布于:2025-08-07 ⋅ 阅读:(14) ⋅ 点赞:(0)

实验平台:
普中STM32精灵开发板https://item.taobao.com/item.htm?id=739076227953


        本章将向大家介绍如何创建库函数工程模板,为后面基于库函数程序的开发提供方便。学习本章可以参考“STM32 固件库介绍”章节内容。本章分为如下几部分内容:

 

7.1 固件库的获取

7.2 创建库函数工程

7.2.1 新建工程

7.2.2 选择 CPU

7.2.3 给工程添加文件

7.2.4 配置魔术棒选项卡

7.3 启动文件介绍

7.3.1 汇编指令

7.3.2 堆栈

7.3.3 复位程序


7.1 固件库的获取

        要创建库函数工程模板,首先需要有固件库包,固件库的版本有很多,我们提供给大家的是最新 V3.5 版,如果后面有新版本出来,大家可按照同样的方法创建库函数工程模板。固件库包存放在资料“8--STM32 相关资料\STM32 最新固件库 v3.5”内,大家直接使用即可。如果朋友不嫌麻烦自己也可以到ST 官网上下载,也可通过百度搜索下载,但是一定要下载 STM32F1 的固件库,如果下载的是其他系列固件库,后面使用该固件库创建的工程模板下载到开发板上时就会出问题。为了保证资料内的固件库包不被修改,自己可备份一份:

7.2 创建库函数工程

7.2.1 新建工程

        固件库包获取以后下面就正式进入工程模板的创建,我们在电脑任意位置创建一个文件夹,命名为“库函数模板创建”,然后在其下面新建3 个文件夹,如下:(文件夹命名可任意,但不能使用中文或特殊符号,我们这里根据文件用途命名

        Obj 文件夹:用于存放编译产生的 c/汇编/链接的列表清单、调试信息、hex文件、预览信息、封装库等文件。

        User 文件夹:用于存放用户编写的 main.c、stm32f10x.h 头文件、stm32f10x_conf.h配置文件、stm32f10x_it.c和stm32f10x_it.h中断函数文件。

        Libraries 文件夹:用于存放 CMSIS 标准和 STM32 外设驱动文件。

        在 Libraries 文件夹下新建 2 个文件夹命名为 CMSIS 和STM32F10x_StdPeriph_Driver,其实这些文件夹命名都是直接复制固件库相应的文件夹名,如下:

        CMSIS 文件夹用于存放一些 CMSIS 标准文件和启动文件;STM32F10x_StdPeriph_Driver 文件夹用于存放 STM32 外设驱动文件。具体的CMSIS 标准文件、外设文件以及它们的路径等内容可以在“STM32 固件库介绍”章节中了解,里面有详细的操作步骤:

        CMSIS 文件夹内文件如下:

        我们从这里面抽取工程模板 CMSIS 所需的几个文件出来,如下:(这些文件在前面介绍固件库已做讲解,这里就不再重复它们的路径是哪里)

        STM32F10x_StdPeriph_Driver 文件夹内文件如下:

        其实这个文件夹都是直接从固件库“STM32 最新固件库v3.5\Libraries\STM32F10x_StdPeriph_Driver”目录下复制,无需修改。里面存放的就是 STM32 标准外设驱动文件,src 目录存放的是外设驱动的源文件,inc目录存放的是对应的头文件。如下:

        User 文件夹内文件如下:

        里面的这些文件也是从固件库内复制而来。

        到这里就已经将创建库函数模板所需的固件库文件复制过来了,下一步打开KEIL5 软件,新建一个工程,工程名根据喜好命名,但是要注意使用英文,如果使用中文名可能会出现一些奇怪的错误,这里我们命名为Template,直接保存在最开始创建的 “库函数模板创建”文件夹下。具体步骤如下:

7.2.2 选择 CPU

        这个根据你开发板使用的 CPU 具体的型号来选择,我们开发板采用的是STM32F103C8T6 芯片。如果这里没有出现你想要的 CPU 型号,或者一个型号都没有,那么肯定是在安装 KEIL5 软件的时候没有添加芯片包,KEIL5 不像KEIL4那样自带了很多 MCU 的型号,KEIL5 需要自己添加,可以参考我们视频KEIL5软件的安装。具体操作如下:

        选择完 CPU 点击 OK 后会弹出在线添加固件库文件的界面,如下:

        我们这里手动进行添加,不需要此步,所以直接关闭即可。

7.2.3 给工程添加文件

        在新建的工程中添加文件,文件从“库函数模板创建”文件夹下获取,双击Group 文件夹就会出现添加文件的路径,然后选择文件即可。如果我们将“库函数模板创建”目录下的文件都添加到 Group 这个默认组中,显然是非常乱的,对于我们查找工程文件和工程维护极其不方便。因此这里我们需要根据文件类型来构建新的工程组。操作步骤如下:

        可以选择快捷图标 1 来进行创建工程组,也可以在工程列表下右键“Target1”选择“Manage Project Items...”,如下:

        为了工程目录更加清晰及方便文件查找,新建 User、Startup、StdPeriph_Driver 和 CMSIS 工程组。

        ser 组用于存放 User 文件夹下的源文件,Startup 组用于存放STM32 的启动文件,StdPeriph_Driver 组用于存放 STM32 外设的驱动源文件、CMSIS 组用于存放 CMSIS 标准文件,比如系统总线时钟等初始化源文件。

        创建好的工程目录组如下所示:

        接下来就需要将对应的一些文件添加到工程目录组中,这样才能进行程序的开发。添加工程文件步骤如下:

        同样是点击图标 1,弹出界面后,在图标 2 中选择好要添加的工程组,点击图标 3 添加文件,会弹出添加文件对话框,在图标 4 中选择对应文件夹内的文件(细心的朋友会发现前面我们只创建了 User、Obj、Libraries 文件夹,却多出了另外 2 个文件夹 Objects 和 Listings,这是因为使用KEIL5 创建工程时默认会产生这两个文件夹用于存放程序编译后的列表文件及HEX 等文件,这里我们可以将这两个文件夹剪切到我们创建的 OBJ 文件夹内,也可以删除它),注意图标5 中文件类型的选择,默认是.c 文件,在 Startup 工程组中我们需要添加STM32的启动文件,而启动文件类型是.s,所以要将文件类型选择为“All files(*.*)”才能看到,点击图标 6 即可添加,将所有文件添加至对应工程组后,如下所示:

        在 StdPeriph_Driver 工程组中我们只添加了 2 个源文件,对于STM32 程序开发,通常这 2 个文件都是需要的,其他的外设源文件根据是否使用外设而添加,如果大家把所有的源文件都加进来也是没有问题的,只不过工程在编译的时候会比较慢,所以我们的原则是使用到哪个外设就添加哪个外设的源文件。

7.2.4 配置魔术棒选项卡

        这一步的配置工作非常重要,很多人编写程序编译后发现找不到HEX 文件,还有的人后面做 printf 实验时打印不出信息,这些问题都是在这个地方没有配置好导致的。

        (1)Target 中选中微库“Use MicroLib”,主要是为了后面printf 重定向输出使用,不然会出现各种奇怪的现象。其他的设置保持默认即可,配置如下:

        (2)Output 选项卡中把输出文件夹定位到我们工程目录下的Obj 文件夹,如果想在编译的过程中生成 hex 文件,那么 Create HEX File 选项勾上。配置如下:

        (3)Listing 选项卡中把输出文件夹也定位到我们工程目录下的Obj 文件夹。其他设置默认,配置如下:  

        (4)C/C++选项卡配置

        因为创建的是库函数模板,所以需要在对处理器类型和库进行宏定义,在Define 这一栏中复制这两个宏:USE_STDPERIPH_DRIVER,STM32F10X_MD

        注意它们之间有一个英文符的逗号。通过这两个宏就可以对STM32F10x 系列芯片进行库开发,因为在库源码内支持很多 F1 系列芯片,通过这个宏就可以选择到底是用种芯片的库驱动。同理 USE_STDPERIPH_DRIVER 这个宏也是类似的原理。这里我们就不深究,大家只要复制到 Define 栏中即可。

        设置好了宏,还需要将我们前面添加到工程组中的文件路径包括进来,同样还是在 C/C++选项卡中,具体步骤如下:

        首先点击图标 1,弹出一个添加头文件路径的对话框,然后点击图标2,新建一个空路径列表,再点击图标 3,会弹出浏览文件夹对话框,在图标4 中选择对应的头文件路径即可,这个头文件路径就是你工程组中那些文件的头文件路径,选择好后点击图标 5 确定,然后回到图标 2 重新建立一个空路径列表,再次添加路径,直到所有头文件路径添加完成点击图标 6 即可。

        最后添加好的头文件路径如下:

        (5)ARM 仿真器配置

        只有当你的仿真器安装好驱动并且连接了电脑和板子,那么就可以开始配置(前提是需要有普中的 ARM 仿真器)。如果使用普中公司的ARM 仿真器可以直接插上电脑,电脑会自动安装驱动无需手动安装,而且将ARM 仿真器的USB一端连接电脑,侧面另一端连接开发板上的 SWD 接口,开发板即可供电,只要在KEIL 软件内进行相应的配置即可下载程序和仿真。具体的连接和配置如下(我们已经将 ARM 仿真器连接开发板):

        选择好 CMSIS-DAP Debugger 型号后点击 Settings,会弹出如下界面,如果你的型号没有选择错误的话,自动会识别你的 ARM 仿真器ID 了,然后即可设置SW 模式以及复位的方式。具体设置步骤如下:

        (6)芯片型号选择

        同样在上一步的那个 Debug 选项卡内进行设置,点击Settings,选择FlashDownload 选项卡,我们使用的芯片是 STM32F103C8T6,其Flash 为64KB。在框3中的 Reset and Run 选项中,如果勾上的话,当程序下载进去后自动复位运行,如果不勾上,程序下载进去后需按下开发板上复位键才能运行。通常我们选择勾上。具体配置如下:

        最后点击 OK 按钮即可。然后双击工程组中的 main.c 文件会发现里面有很多代码,这个是我们直接从 ST 公司提供的模板上复制过来的,所以我们把main.c文件内的所有内容删除,将下面这段程序复制到 main.c 中,我们输入如下内容:

#include "stm32f10x.h"
int main()
{
    while(1)
    {
        
    }
}

        然后我们编译一下工程,编译后结果 0 错误 0 警告,表明我们创建的库函数模板完全正确。如下:

        

        图标 1 中第一个编译是对单个文件编译,第二个编译是对工程中修改过的文件进行编译,第三个编译是对工程中所有文件进行编译。通常我们使用的是中间那个编译,效率比较高。

        至此,我们就成功创建好一个库函数工程模板。在以后的实验中,我们都将以此工程模板为基础来编写实验程序,减少反复创建库函数模板的麻烦

7.3 启动文件介绍

        在“寄存器模板创建”中就已经提到过启动文件,它内部使用的都是汇编语言。这个文件的作用是负责执行微控制器从“复位”到“开始执行main 函数”中间这段时间(称为启动过程)所必须进行的工作。那么它完成的具体工作有如下几点:

        (1)初始化堆栈指针 SP=_initial_sp

        (2)初始化 PC 指针=Reset_Handler

        (3)初始化中断向量表

        (4)配置系统时钟

        调用 C 库函数_main 初始化用户堆栈,从而转向我们用户应用程序的main。

7.3.1 汇编指令

        打开 STM32 的启动文件会发现,里面全部都是汇编语句,对于汇编指令不了解的朋友来说可能一头雾水,没关系,下面我们按照启动文件内指令出现的顺序来介绍,通过这几个指令的讲解,相信可以了解大概的一个情况。

        (1)EQU:给数字常量取一个符号名,相当于 C 语言中的预处理命令define。常用格式如下:

Stack_Size        EQU         0x00000400

        表示将 0x00000400 这个数值,用 Stack_Size 名代替。  

        (2)AREA:汇编一个新的代码段或者数据段。常用格式如下:  

AREA         STACK, NOINIT, READWRITE, ALIGN=3    

        表示汇编一个数据段,名字是 STACK,NOINIT 表示不初始化,READWRITE表示可读可写,ALIGN 表示字节对齐,通常后面会赋一个立即数,比如ALIGN=3表示的就是 2^3 字节对齐,即 8 字节对齐。

        (3)SPACE:分配一定大小的内存空间,单位为字节。常用格式如下:

Stack_Mem        SPACE        Stack_Size

        表示给 Stack_Mem 分配一个 Stack_Size 大小的内存空间。通常它后面还会跟随一个__initial_sp 语句,表示栈的结束地址,即栈顶地址,因为栈是由高向低生长的。

        (4)PRESERVE8:当前文件堆栈需按照 8 字节对齐。格式:直接写此指令即可。

        (5)THUMB:表示后面指令兼容 THUMB 指令。在ARM 以前的指令集中有16位的 THUMBM 指令,现在 Cortex-M 系列使用的都是 THUMB-2 指令集,THUMB-2是32 位的,兼容 16 位和 32 位的指令,是 THUMB 的超级版。格式:直接写此指令即可。

        (6)EXPORT:声明一个具有全局属性的标号,可被外部文件使用。常用格式如下:

EXPORT         __Vectors

        表示__Vectors 标号具有全局属性,外部文件可以调用它。

        (7)DCD:以字为单位分配内存,要求 4 字节对齐,并要求初始化这些内存。常用格式如下:

DCD         Reset_Handler         ; Reset Handler

        表示给 Reset_Handler 名称的地址分配内存并初始化这些它,这个名称地址可以在“STM32F1xx 中文参考手册”-“9.1.2 中断和异常向量”章节中找到。如下图所示。在那个函数名后面还有一个; 在汇编程序中“;”即注释,和C语言中的//类似效果。在后面的学习中会接触很多的中断函数,这些中断函数名都可在 DCD 这部分找到。

        (8)PROC:定义子程序。常用格式:

; Reset handler

Reset_Handler        PROC

                                EXPORT        Reset_Handler        [WEAK]

                IMPORT  SystemInit

                IMPORT  __main

                                LDR         R0, =SystemInit

                                BLX         R0

                                LDR         R0, =__main

                                BX           R0

                                ENDP

        表示定义一个全局的子程序 Reset_Handler,需与ENDP 成对使用,表示子程序结束。在

EXPORT        Reset_Handler        [WEAK]

        后面有一个 WEAK,这个是弱定义,如果外部文件声明了一个标号,则优先使用外部文件定义的标号,如果外部文件没有定义也不出错。要注意的是:这个并不是 ARM 的指令,是编译器的。

        (9)LDR:从存储器中加载字到一个寄存器中。常用格式:

LDR         R0,  =SystemInit

        (10)BLX:跳转到由寄存器给出的地址,并根据寄存器的LSE 确定处理器的状态,还要把跳转前的下条指令地址保存到 LR。常用格式:

BLX         R0

        (11)BX:跳转到由寄存器/标号给出的地址,不用返回。常用格式:

BX        R0

        (12)IMPORT:声明标号来自外部文件,和 C 语言中的extern 关键字类似。上述代码中

IMPORT   SystemInit

IMPORT   __main

        表示声明 SystemInit 和 main 为外部文件,在创建寄存器模板的时候写一个SystemInit()空函数,就是这个原因,如果没有定义该函数,编译将会报错。如果朋友们想修改 main.c 文件中的 main 函数名,在这个地方就可以改动,然后后面LDR R0, =__main 中的 main 也需要改动。不过建议大家还是不要改,以免增添不必要的烦恼。

        (13)B:跳转到一个标号。常用格式如下:

B        .

        B 后面有一个“.”,汇编中表示循环。这句话的意思就是说跳进了循环。

        (14)IF,ELSE,ENDIF:汇编条件分支语句,与 C 语言的if else 类似。常用格式:

IF         :DEF:__MICROLIB

                        EXPORT    __initial_sp

                        EXPORT    __heap_base

                        EXPORT    __heap_limit

                        ELSE

                        IMPORT     __use_two_region_memory

                        EXPORT    __user_initial_stackheap

ENDIF

        (15)END:到达文件的末尾,文件结束。

        到这里启动文件指令就介绍完了,有的朋友在学习过程中还会遇到其他的汇编指令,那么怎么查找它们的功能和用法呢?其实很简单,KEIL5 软件内已经给我们提供了帮组文档,打开 Help 选项就会弹出帮助文档,如下:

        假如我们要查找 AREA 指令,只需要选择收索,然后输入要收索的指令,最后选择列出主题即可。选择对应的指令,右侧就会显示指令的具体介绍和格式说明。具体操作步骤如下:

7.3.2 堆栈

        想必学习过 C 语言的朋友对堆栈这一词非常熟悉,在启动文件开始处就定义了一堆栈的大小,代码如下:

;栈空间的开辟

Stack_Size         EQU         0x00000400

                           AREA       STACK, NOINIT, READWRITE, ALIGN=3

Stack_Mem        SPACE     Stack_Size

__initial_sp         ;栈的结束地址

;堆空间的开辟

Heap_Size         EQU         0x00000200

                           AREA         HEAP, NOINIT, READWRITE, ALIGN=3

__heap_base

Heap_Mem        SPACE       Heap_Size

__heap_limit      ;堆的结束地址

        在程序开头开辟了一个 0x00000400 即 1KB 的 Stack_Size 栈空间。栈主要用于存放函数的参数值、局部变量的值等,其操作方式类似于数据结构中的栈。栈的大小不能超过内部 RAM 的大小。假如你在开发的程序占用的RAM 比较大,局部变量使用的比较多,那么可以在启动文件内修改这个Stack_Size 值。如果你的程序出现莫名奇怪的错误,并进入了硬 fault 的时候,这时你就要考虑下是不是栈不够大,溢出了。通常我们修改最多的还是栈值。

        紧接着又开辟了一个 0x00000200 即 512 字节的 Heap_Size 堆空间。堆中的内存一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收。分配方式类似于数据结构中的链表。堆和栈生长方式是相反的,堆是由低向高生长的,栈是由高向低生长。如不了解生长方式可以百度下。

7.3.3 复位程序

        通过前面部分的讲解相信大家对启动文件已经大致了解,那么我们再来看看系统在复位过程中做了哪些工作。复位程序如下:

; Reset handler

Reset_Handler        PROC

                                EXPORT    Reset_Handler         [WEAK]

                IMPORT SystemInit

                IMPORT __main

                                LDR        R0,   =SystemInit

                                BLX        R0

                                LDR        R0,   =__main

                                BX         R0

                                ENDP

        在复位程序内,声明了外部文件标号 SystemInit 和__main(__main 是一个标准的 C 库函数,用于初始化用户堆栈,最终还是进入main),因此需要在外部文件中按照这个标号名写出对应的函数,否则编译器将报错。在库文件system_stm32f10x.c 内就写了 SystemInit()函数;在main.c 文件内也写了一个main()函数。所以这也就是为什么我们主函数是 main 这个名字了。

        系统复位后进入复位函数,首先调用 SystemInit()函数,初始化STM32 系统时钟,然后再进入我们熟悉的 main()函数。


网站公告

今日签到

点亮在社区的每一天
去签到