26.2.28 ZD-EP22 [Keil/寄存器] 新建寄存器版本工程流程
新建工程好复杂,但是养成习惯应该会很好
1 新建工程文件夹
首先在新建的工程文件夹的根目录下创建五个文件夹,分别是Drivers,Middlewares,Output,Projects,User。其用处如下:
| 文件夹名称 | 作用 |
| Drivers | 存放与硬件相关的驱动层文件 |
| Middlewares | 存放正点原子提供的中间层组件文件和第三方中间层文件 |
| Output | 存放工程编译输出文件 |
| Projects | 存放 MDK 工程文件 |
| User | 存放用户编写的代码,如 main.c |
接下来进入到Drivers文件夹,再创建三个文件夹,分别是BSP,CMSIS,SYSTEM。其作用如下:
| 文件夹名称 | 作用 |
| BSP | 存放开发板板级支持包驱动代码,如各种外设驱动 |
| CMSIS | 存放 CMSIS 底层代码,如启动文件(.s 文件)、stm32f4xx.h 等 |
| SYSTEM | 存放正点原子系统级核心驱动代码,如 sys.c、delay.c 和 usart.c 等 |
这三个文件夹内的文件大多都由正点/ST/Arm直接提供,这里就按照开发指南创建了一个范例(寄存器版本):
再来到Projects文件夹,新建一个MDK-ARM文件夹来存放工程文件
2 新建工程框架
进入到Keil,开始新建工程
首先点击Project->New uVision Project,引导到MDK-ARM文件夹,修改工程名称(这里参照我个人的范例修改为lan_f407即ID_单片机型号)确认后在弹出的界面选择自己对应的芯片即可
之后在MDK-ARM目录下会生成三个文件夹:Debugconfig,Listings,Objects。其作用如下:
| 文件夹名称 | 作用 |
| DebugConfig | 用于存放调试设置信息文件(.dbgconf),不可删除! |
| Listings | 用于存放编译过程产生的链接列表等文件 |
| Objects | 用于存放编译过程产生的调试信息、.hex、预览、.lib 文件等 |
为了统一管理,方便使用,我们会把输出在 Listings 和 Objects 文件夹的内容,统一改为输出到 Output 文件夹(通过魔术棒设置),我们先把 MDK 自动生成的这两个文件夹(Listings 和 Objects)删除。
3 创建分组 添加文件
找到Manage Project Items(红绿白方块),进入界面后设置工程名称(Project Targets)为Template,并设置四个分组(Groups):Startup(存放启动文件)、User(存放 main.c 等用户代码)、Drivers/SYSTEM(存放系统级驱动代码)、Readme(存放工程说明文件),如下图所示:

注意:为了让工程结构清晰,我们会尽量让 MDK 的工程分组和我们前面新建的工程文件夹对应起来,由于 MDK 分组不支持多级目录,因此我们将路径也带入分组命名里面,以便区分。如:User 分组对应 User 文件夹里面的源码,Drivers/SYSTEM 分组,对应 Drivers/SYSTEM文件夹里面的源码,Drivers/BSP 分组对应 Drivers/BSP 文件夹里面的源码等。
添加启动文件(此处直接搬自开发指南)
启动文件(.s 文件)包含 STM32 的启动代码,其主要作用包括:1、堆栈(SP)的初始化;2、初始化程序计数器(PC);3、设置向量表异常事件的入口地址;4、调用 main 函数等,是每个工程必不可少的一个文件,我们在本书第九章会有详细介绍。
该文件由 ST 官方提供,对于 STM32F407 来说只有一个启动文件:startup_stm32f407xx.s。
启动文件存放在 STM32CubeF4 软件包的:Drivers→CMSIS→Device→ST→STM32F4xx→Source→Templates→arm 文件夹下。为了节省空间,在精简版 CMSIS 文件夹里面我们把其他启动文件都删了。而且,为了更好的匹配寄存器版本代码,我们对 startup_stm32f407xx.s 做了 2 处修改:
1,我们用不到编译器的内存管理函数,为节省内存,将 Heap_Size 改成 0,源码如下:
;未用到编译器自带的内存管理(malloc,free 等),设置 Heap_Szie 为 0Heap_Size EQU 0x000000002,寄存器代码不需要调用 SystemInit 函数,因此修改 Reset_Handler 函数,去掉 SystemInit调用,源码如下:
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
LDR R0, =0xE000ED88 ;使能浮点运算 CP10,CP11
LDR R1,[R0]
ORR R1,R1,#(0xF << 20)
STR R1,[R0]
;寄存器版本代码,因为没有用到 SystemInit 函数,所以注释掉以下代码为防止报错!
;HAL 库版本代码,建议加上这里(外部必须实现 SystemInit 函数),以初始化
;stm32 时钟等.
;IMPORT SystemInit
;LDR R0, =SystemInit
;BLX R0
LDR R0, =__main
BX R0
ENDP这段代码,我们主要加入了开启 STM32F407 硬件 FPU 的代码,以使能 STM32F407 的浮点运算单元,并关闭硬件自动压栈。其中,0xE000ED88 就是协处理器控制寄存器(CPACR)的地址,该寄存器的第 20~23 位用来控制是否支持浮点运算,这里我们全设置为 1,以支持硬件浮点运算。关于 CPACR 寄存器的详细描述,见《STM32F3 与 F4 内核编程手册.pdf》第 4.6.1节。另外,寄存器版本,我们还屏蔽了 SystemInit 函数的调用,如果是 HAL 库版本,可以取消这个函数的注释,并在外部实现 SystemInit 函数。
特别注意:我们在汇编代码里面使能了 FPU(浮点运算),所以在 MDK 里面,我们也要设置使用 FPU,否则可能代码会无法运行,设置方法如下:选择 Options for Target ‘Target1’,打开 Target 选项卡,在 Code Generation 里面,选择 Double Precision,如图 6.1.3.4 所示:

关于启动文件的说明,我们就介绍这么多,接下来我们看如何添加启动文件到工程里面。我们有两种方法给 MDK 的分组添加文件:1,双击 Project 下的分组名添加。2,进入工程管理界面添加。
这里我们使用方法 1 添加(路径:实验 0-2,新建工程实验-寄存器版本\Drivers\CMSIS\Device\ST\STM32F4xx\Source\Templates\arm),如图 6.1.3.5 所示:

上图中,我们也可以点击 Add 按钮进行文件添加。添加完后,点击 Close,完成启动文件添加,得到工程分组如图 6.1.3.6 所示:

总而言之,我们要做的就是双击Startup分组,在CMSIS中找到对应的启动文件夹并添加即可,这里直接搬自开发指南一方面是我懒,另一方面是介绍启动文件的作用,不过这里只针对寄存器版本,后续进行HAL库版本时会有变动。
添加 SYSTEM 源码
参考启动文件,在Drivers/SYSTEM分组下加入各自的源码,最终效果如下:

4 魔术棒设置
这里真的太复杂了,我依旧直接搬开发指南。。
Target

由于 AC5 对中文支持比较好,且兼容性相对好一点,为了避免不必要的麻烦,我们推荐大家使用 AC5 编译器。为了让大家自由选择,我们正点原子的源码,绝大部分也是支持 AC6编译器的,不过在选项卡设置上稍有差异,具体差异如表 6.1.4.2 所示:
若使用AC6,应在Misc Controls 设置:-Wno-invalid-source-encoding以关闭对汉字的错误警告Output

注意,我们勾选:Browse Information,用于输出浏览信息,这样就可以使用 go to definition查看函数/变量的定义,对我们后续调试代码比较有帮助,如果不需要调试代码,则可以去掉这个勾选,以提高编译速度。
Listing

经过 Output 和 Listing 这两步设置,原来存储在 Objects 和 Listings 文件夹的内容(中间文件)就都改为输出到 Output 文件夹了。
☆ C/C++

在②处设置了全局宏定义:STM32F407xx,用于定义所用 STM32 型号,在 stm32f4xx.h 里面会用到该宏定义。
在③处设置了优化等级为-O0,可以得到最好的调试效果,当然为了提高优化效果提升性能并降低代码量,可以设置-O1~-O3,数字越大效果越明显,不过也越容易出问题。注意:当使用AC6 编译器的时候,这里推荐默认使用-O1 优化。
在④处勾选 C99 模式,即使用 C99 C 语言标准。
在⑤处,我们可以进行头文件包含路径设置,点击此按钮,进行如图 6.1.4.5 所示设置:

上图中我们设置了 4 个头文件包含路径,其中 3 个在 Drivers 文件夹下,一个在 User 文件夹下。为避免频繁设置头文件包含路径,正点原子最新源码的 include 全部使用相对路径,也就是我们只需要在头文件包含路径里面指定一个文件夹,那么该文件夹下的其他文件夹里面的源码,如果全部是使用相对路径,则无需再设置头文件包含路径了,直接在 include 里面就指明了头文件所在。
关于相对路径,这里大家记住 3 点:
1,默认路径就是指 MDK 工程所在的路径,即.uvprojx 文件所在路径(文件夹)
2,“./”表示当前目录(相对当前路径,也可以写做“.\”)
3,“../”表示当前目录的上一层目录(也可以写做“..\”)
举例来说,上图中:..\..\Drivers\CMSIS\Device\ST\STM32F4xx\Include,前面两个“..\”,表示 Drivers 文件夹在当前 MDK 工程所在文件夹(MDK-ARM)的上 2 级目录下,具体解释如图 6.1.4.6 所示:

上图表示根据头文件包含路径:..\..\Drivers\CMSIS\Device\ST\STM32F4xx\Include,编译器可以找到⑥处所包含的这些头文件,即代码里面可以直接 include 这些头文件使用。
再举个例子,在完成如图 6.1.4.5 所示的头文件包含路径设置以后,我们在代码里面编写:
#include "./SYSTEM/sys/sys.h"即表示当前头文件包含路径所指示的 4 个文件夹里面,肯定有某一个文件夹包含了:SYSTEM/sys/sys.h 的路径,实际上就是在 Drivers 文件夹下面,两者结合起来就相当于:
#include "../../Drivers/SYSTEM/sys/sys.h"这就是相对路径。它既可以减少头文件包含路径设置(即减少 MDK 配置步骤,免去频繁设置头文件包含路径的麻烦),同时又可以很方便的知道头文件具体在哪个文件夹,因此我们推荐在编写代码的时候使用相对路径。
Debug

开发指南使用的是DAP,实际上使用的是ST-Link,补充说明内容如下:
关于速度,与 ST-Link 固件版本有关。所以这里大家只需要选择一个合适的速度即可(一般为 1-5M 之间)。 当然这里大家也可以修改为 SWD 方式,修改方法非常简单,配置如下:

在最新版Keil中去除了Max的,改用了Req(请求)和Selected(选择),豆包解释如下:
- Req (Requested): 你向 ST-LINK 调试器 “请求” 的目标工作频率。
- Selected: ST-LINK 调试器根据硬件能力,实际 “选择” 并工作的频率(ST-LINK/V2 的频率是固定档位,不是连续可调,因此会自动匹配最接近
Req的可用值)。
豆包建议如下:
STM32F407 的具体设置建议
根据你的使用场景选择即可:
- 日常稳定调试(推荐)
- 将
Req设置为 4 MHz 或 10 MHz。 - 只要连接稳定(线材短、无强干扰),
Selected通常会直接跟随Req变为相同的值。这两个档位是 ST-LINK/V2 最常用且最稳定的档位。
- 将
- 连接不稳定 / 长线调试
- 如果使用杜邦线连接、线较长(>10cm)或周围有电机等干扰源,建议将
Req降低到 1 MHz。 - 降低频率能大幅提高 SWD 通信的抗干扰能力,虽然下载速度稍慢,但能避免
No target connected或下载校验失败。
- 如果使用杜邦线连接、线较长(>10cm)或周围有电机等干扰源,建议将
- 追求极速下载
- ST-LINK/V2 (硬件版本 V2) 通常最高支持 10 MHz。
- 你可以尝试将
Req设为10 MHz,如果Selected能稳定显示10 MHz且下载不报错,即可使用此档位以获得最快的下载速度。
至于为什么明明区别怎么大我还要把不一样的,老版本的内容放在这里,,我也不知道
Utilities

图中⑥处下载算法,是 MDK 默认添加的,针对 STM32F4xx 大容量系列产品(FLASH 容量为 1M)。一般我们用这个即可。如果⑥处没有下载算法,则点击⑦处按钮,执行添加一下下载算法即可(名字和⑥处的算法名字一样)。
5 添加main.c 开始编写代码
先按 New按钮(一个纸张一样的按钮)来新建一个文件,并按 Save按钮 重命名为main.c并保存到User文件夹下,再将该文件添加到User分组中
输入示例代码:
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
int main(void)
{
uint8_t t = 0;
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
usart_init(84, 115200); /* 串口初始化为 115200 */
while (1)
{
printf("t:%d\r\n", t);
delay_ms(500);
t++;
}
}编译后通过串口工具来验证是否成功下载
注意:若编译出现错误,请检查 C/C++选项卡 是否成功设置,由于Keil一坨屎一样的适配,很容易崩溃导致设置失效需要重新设置,若成功设置还是有问题,在 C/C++选项卡 下把 GNU extensions(非标准语法支持) 选项取消勾选再进行尝试
写是真的难写,但是做起来应该还算OK
26.3.21补充 使用VSCode进行开发:使用VSCode搭建Keil开发仿真环境