STM32模板工程创建(库函数模板创建、启动文件介绍)

目录

1. 固件库的获取

2. 创建库函数工程

3. 启动文件介绍

3.1 汇编指令

3.2 堆栈 

3.3 复位程序


1. 固件库的获取

固件库的获取以及模版工程的链接如下:

https://pan.baidu.com/s/11_vWqdOGvIW9Foknibk8Cg?pwd=sh2s 

要创建库函数工程模板,首先需要有固件库包,固件库的版本有很多,上面链接提供的是最新 V3.5 版,如果后面有新版本出来,大家可按照同样的方法创建库函数工程模板

固件库直接使用即可,不嫌麻烦自己也可以到 ST 官网 https://www.st.com.cn 上下载,也可通过百度搜索下载,但是一定要下载 STM32F1的固件库,如果下载的是其他系列固件库,后面使用该固件库创建的工程模板下载到开发板上时就会出问题。为了保证资料内的固件库包不被修改,建议备份一份

2. 创建库函数工程

提供给大家的链接里包含一份最新的固件库、一份模版工程以及新建工程的方法,大家下载后按pdf上的方法进行即可,里面写的很详细,这里就不在赘述

创建步骤主要可分为以下几步:

1、新建工程

2、选择CPU

3、给工程添加文件

4、配置魔术棒选项卡

3. 启动文件介绍

启动文件内部使用的都是汇编语言,这个文件的作用是负责执行微控制器从“复位”到“开始执行 main 函数” 中间这段时间(称为启动过程)所必须进行的工作。

它完成的具体工作有如下几点:

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

(2)初始化 PC 指针=Reset_Handler

(3)初始化中断向量表

(4)配置系统时钟

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

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 中断和异常向量”章节中找到

STM32中文参考手册 https://pan.baidu.com/s/1GDaMNNC3eKiT6KTV-NDs0w?pwd=je2x 

如下图所示,在那个函数名后面还有一个; 在汇编程序中 “;” 表示注释,和 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 指令,只需要选择收索,然后输入要收索的指令,最后选择“ 列出主题 ”即可。选择对应的指令,右侧就会显示指令的具体介绍和格式说明。具体操作步骤如下:

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 堆空间。堆中的内存一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收。分配方式类似于数据结构中的链表。堆和栈生长方式是相反的,堆是由低向高生长的,栈是由高向低生长。

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()函数

  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值