Keil STM32工程各文件作用
一、Keil 工程文件
使用Keil MDK5新建工程,会立即在工程指定目录下生成.uvprojx
和.uvoptx
两个文件,关闭Keil时,工程目录下还会再生成一个.uvguix.username
的文件。
其中.uvprojx
是Keil工程的核心文件,存储了工程的主要配置信息,包括项目结构:源文件、头文件、库文件的路径和分组;编译选项:编译器、汇编器、连接器设置;调试工具配置:J-Link、ST-Link;以及输出文件格式:HEX、AXF;MCU型号等内容。删除该文件等于删除了整个工程。.uvprojx
全称是uVision Project File(读作MicroVision Project File),uv = uVision,是Keil MDK中的集成开发环境,简单理解为界面部分。MDK(Microcontroller Development Kit)是完整的工具链:Keil MDK = uVision IDE + ARM编译器 + 调试工具 + 设备支持包。proj
表示工程,Project。x
表示XML格式,Keil MDK5及之后的版本使用XML格式工程文件,取代以前的二进制工程文件.uvproj
。.uvoptx
是工程选项文件,保存了用户对工程的个性化配置,包括编辑器窗口布局、断点位置、书签等;还包括调试会话的配置,如外设寄存器视图、变量监控列表等;还有最近打开的文件记录等等。此文件可以删除,重启Keil后会重新生成一个新的该文件,之前的个性化配置会丢失。.uvoptx
全称是uVision Option File,其中opt
表示配置选项,Option。x
表示XML格式。XML格式有很多好处,比如可读性好,可以通过文本编辑器查看,比旧的二进制.uvopt
更容易实现版本管理。.uvguix.username
是用户本地配置文件,保存了用户个性化的使用习惯,包括窗口位置、工具栏布局、编辑器字体和颜色主题、工作区中展开/折叠的文件夹结构、用户特定的快捷键配置等。文件名中的username
是当前操作系统用户名。该文件可以删除。.uvguix
全称是uVision GUI eXternal User Settings File,其中gui
表示图形用户界面,Graphical User Interface。
通常将.uvprojx
和.uvoptx
两个文件纳入版本控制(如Git),而将含用户本地配置的.uvguix.username
忽略。
二、CMSIS标准库文件
2.1 ARM内核库
在基于ARM Cortex-M内核的单片机开发中,CMSIS(Cortex Microcontroller Software Interface Standard)提供了一组核心头文件,用于标准化内核访问。接下来分别介绍各Cortex-M内核该组头文件的功能。
(1)ARM Cortex-M3内核
core_cm3.h
···
(2) ARM Cortex-M4内核
core_cm4.h
core_cm4_simd.h
core_cmFunc.h
core_cmInstr.h
在基于ARM Cortex-M4内核的单片机开发中,CMSIS提供了以上4个核心头文件,用于标准化内核访问。
core_cm4.h文件最为重要,给出了 M4 内核的寄存器定义等,即与内核硬件直接交互的宏和结构体;并且它还包含了另外3个头文件,属于内核级库文件总的对外接口。
core_cm4_simd.h文件提供了单指令多数据指令集支持,SIMD,Single Instruction, Multiple Data。
core_cmFunc.h文件提供了内核资源访问函数,这些函数基本上都是内联汇编实现的。如获取控制寄存器 __get_CONTROL()、使能中断 __enable_irq()等。
core_cmInstr.h文件提供汇编指令的宏定义的封装或内联汇编函数封装,如#define __NOP __nop
、void __NOP(void) {__ASM volatile ("nop");}
。
2.2 MCU外设库
gd32f30x.h
system_gd32f30x.h
system_gd32f30x.c
GD厂商提供了以上3个MCU外设文件,常用于标准外设库或HAL库开发。
gd32f30x.h文件提供了片上外设的寄存器定义、外设中断号定义等,同时还包含了core_cm4.h文件和system_gd32f30x.h文件,属于MCU级库文件总的对外接口。芯片容量宏、内外晶振赫兹数宏也定义在该文件中,这俩宏需要开发人员根据实际情况修改。
system_gd32f30x.h文件比较废柴(但人家是CMSIS定义的标准),仅声明了系统初始化函数 SystemInit() 和时钟配置函数 SystemCoreClockUpdate()。
system_gd32f30x.c文件中提供了 SystemInit() 和 SystemCoreClockUpdate() 两个函数的实现。启动文件会在调用main函数之前调用SystemInit(),当然用户在main函数的开始也可以再手动调用一次 SystemInit(),以确保用户代码执行之前系统时钟已正确配置。至于 SystemCoreClockUpdate() 函数就比较鸡肋了,主要作用是更新全局变量SystemCoreClock
,SystemInit() 函数已经初始化了SystemCoreClock
,如果后续不需要更新系统时钟的话,这个 SystemCoreClockUpdate() 函数就没用了。 另外,给SystemCoreClock
变量赋值的系统时钟速率宏就定义在该文件内,供开发人员根据需要解注释选择。该文件仅包含了gd32f30x.h这一个头文件。
startup_gd32f30x_cl.s
startup_gd32f30x_hd.s
startup_gd32f30x_xd.s
除C文件外,GD厂商还会提供以上3个汇编启动文件,此处以Keil为例(IAR因使用自研编译器,启动文件内容与Keil的会略有差异),文件后缀表示片上Flash容量或芯片类型,开发人员可根据手头芯片的《用户手册》和《芯片数据手册》来定位使用哪个启动文件,如下两图的举例。
Reset_Handler PROCEXPORT Reset_Handler [WEAK]IMPORT SystemInitIMPORT __mainLDR R0, =SystemInitBLX R0LDR R0, =__mainBX R0ENDP
启动文件中一定能得到执行代码就上面这几行,Reset_Handler
为函数名,表示芯片上电或复位后第一个执行的代码。PROC
是ARM汇编语法,表示过程的开始(Procedure),类似于C语言的函数声明;ENDP
表示过程(函数)的结束,与PROC
配对使用。EXPORT
声明该符号(函数名)为全局可见,允许其他文件(如C代码)调用或链接到它。[WEAK]
表示这是一个弱定义,如果用户在代码中重新实现了Reset_Handler
(例如在C文件中),则链接器会优先使用用户自定义的版本,否则使用此默认实现,此函数作为上电第一个执行的函数一般不自己实现。IMPORT
声明需要从外部引入的符号(函数名),这里引入的是 SystemInit() 和 __main()。注意这个__main() 不直接指向用户的主函数 main(),而是由C库提供的初始化函数,__main() 会将Flash中的全局变量的初始化值复制RAM中的 .data 段,并清零RAM .bss段中的未初始化全局变量,完成运行库的初始化之后,最后才调用用户主函数 main()。LDR R0, =SystemInit
,加载SystemInit函数的地址到寄存器R0中。LDR(Load Register)指令用于从内存加载数据到寄存器中,=SystemInit 前的等于号不能省略,表示取变量地址。如果不带等于号表示取变量值。BLX R0
,跳转到R0中的地址执行,即调用SystemInit函数。BLX(Branch with Link and eXchange)带链接和状态切换的分支跳转指令。
三、用户自定义文件
gd32f30x_it.h
gd32f30x_it.c
gd32f30x_libopt.h
上述3个文件属于方便开发的用户自定义文件,不在MCU厂商的固件库中。但这3个文件也是由MCU厂商提供的,存在于MCU厂商提供的工程模版中,由于太过经典,开发人员自定义工程目录时也可以将这三个文件放进工程的固件库路径下。
第二章介绍过启动代码中用[WEAK]
修饰的函数,如果在其他文件中有定义,则弃用启动代码中的函数,采用用户自定义的函数。启动代码中的中断处理函数都在文件 gd32f30x_it.h 和 gd32f30x_it.c 中做了外部实现,如 SysTick_Handler()、USART1_IRQHandler()等。另外,要想使用标准库开发,还需要使用到GD工程模版中的 gd32f30x_libopt.h 文件(与STM32工程模版中的stm32f30x_conf.h作用相同)。文件包含关系:gd32f30x_libopt.h 含于 gd32f30x.h 含于 gd32f30x_it.h,因此用户的 main.c 只需包含 gd32f30x_it.h 即可。
注意,在 gd32f30x.h 中,包含 gd32f30x_libopt.h 是有条件的,需前置定义宏USE_STDPERIPH_DRIVER
,即 Use standard peripheral driver 使用标准外设驱动,这也就是当年跟着教程敲进Keil中但不知为何意的那串大写字母,如下图,