一、STM32 工程开发方式介绍
STM32 工程结构复杂,代码建立在工程结构之上。
STM32 开发方式主要有基于寄存器、基于**标准库(库函数) **和基于 HAL 库三种。
- 基于寄存器方式与五一单片机开发方式类似,直接配置寄存器,这种方式最底层、最直接、效率更高,但因 STM32 结构复杂、寄存器多,目前不推荐。
- 基于库函数方式使用官方封装好的函数间接配置寄存器,由于ST对于寄存器封装较好,所以这种方式既能满足对寄存器的配置,而且对开发人员友好,有利于提高开发效率,本文采用此方式。
- 基于 HAL 库方式可通过图形化界面快速配置 STM32,适合快速上手,但隐藏底层逻辑,不熟悉 STM32 时只能停留在较浅水平,推荐学过标准库后了解。
二、准备库函数压缩包
- 使用库函数方式需要准备 STM32 库函数压缩包,可在下方提供的资料链接中找到固件库文件。
- 解压后的库函数文件夹包含多个部分:
第一个文件夹有两个图片,用处不大。
第二个文件夹 “libraries” 是库函数文件,建工程时会用到。
第三个文件夹 “project” 是官方提供的工程示例和模板,可参考使用。
第四个文件夹 “utilities” 是 STM32 官方评估版的相关例程。
还有库函数的发布文档和使用手册。
三、新建基于标准库的工程
- ’建立存放工程的文件夹,如在桌面新建 “sm32 project” 文件夹。
- 打开 Keil 软件,点击 “Project - New μVision Project”,选择新建的文件夹,再新建一个文件夹存放本次工程,命名为 “2-1 stm32 工程模板”。
- 给工程文件起通用名字 “project”,点击保存。
- 选择器件型号为 “STM32F103C8”,点击 OK。
- 此时弹出新建工程小助手,暂不用可擦掉。工程建好后文件为空,需添加必要文件。
四、添加工程必要文件
- 打开固件库文件夹,找到 STM32 的启动文件所在路径(打开Libraries, CMSIS, CM3, DeviceSupport, STM32F10x,startup, arm),将启动文件复制到新建的工程文件夹中的 “start” 文件夹。
- 回到固件库文件夹,将 “STM32F10x.h(外设寄存器描述文件)”、两个 “system(用于配置时钟)” 开头的文件及内核计算器描述文件复制到 “start” 文件夹。
- 由于STM32是内核和内核外围的设备组成,并且内核的寄存器描述与外围设备的描述文件是不在一起的,所以还需要添加一个内核寄存器的描述文件。其中的cm3文件就是内核寄存器描述文件。
补充:
外设寄存器描述文件:作用与51单片机头文件REGX52.H一样,用于描述STM32有哪些寄存器和它对应的地址的。
system(用于配置时钟):STM32主频72MHz,就是该文件里的函数配置的。
内核寄存器描述文件:其中还带了一些内核配置函数,所以有一个.c文件,也需要一并复制到Stat文件夹中。
到此工程的必要文件的复制就完成了,后面需要回到Keil软件将复制的文件添加到工程中。
五、在工程中添加文件及设置头文件路径
-
在 Keil 软件中,将复制的文件添加到工程里。点击选中 “Source Group”,改名为 “start”,右键选择 “添加已经存在的文件到组里来”,添加启动文件及其他文件,这里的文件都是STM32中最基本的文件,是不需要我们修改的我们添加进来即可。
注意:文件中的小钥匙图标,表示该文件是只允许我们阅读,不允许我们修改的文件。 -
在工程选项中添加 “start” 文件夹的头文件路径,否则软件是找不到.h文件的,点击 “魔术棒按钮”,在 “C/C++” 里找到 “Include Paths” 栏,新建路径并添加 “start” 的路径。
六、新建 main 函数并测试工程
-
在工程文件夹中新建 “user” 文件夹,在 Keil 软件中右键添加组,命名为 “User”,然后添加新文件,选择 C 文件并命名为 “main”,放在 “User” 文件夹下(记得修改路径否则是放在User文件夹外面的)。
-
在 “main.c” 文件中插入头文件 “stm32f10x.h”,写一个 main 函数,包含一个 while 死循环,注意 main 函数的返回值和参数类型,文件最后一行必须为空行(不然会报警告)。
-
点击编译并建立工程,若提示零错误零警告,则工程没问题。此时工程还没有添加STM32的库函数,所以还是基于寄存器开发的工程,如果你想利用寄存器开始STM32,那么到这里就可以了。以下会演示配置寄存器实现点灯操作。
七、通过配置寄存器点灯
- 连接 STM32 最小系统板、ST-Link 和杜邦线,配置调试器。
其中配置调试器需要注意:
在 Keil 软件中,点击魔术棒按钮选择 “Debug”。此时调试器默认是 “unlink”,由于使用的是 ST-Link,所以选择 “ST-Link Debug”。
点击右边的设置按钮,在 “Flash Download” 这一项中,把 “Reset and run” 这一项勾上。勾上这一项之后,下载程序后会立马复位并执行,这样更加方便,否则每次下载之后还需要按一下板子上的复位按键才能执行程序。
配置好调试器之后点击确认 “OK”。重新编译工程,如果没有错误,点击 “Load” 按钮将程序下载到 STM32 里面。 - 通过配置三个寄存器来点灯:
配置 RCC 的一个寄存器使能 GPIOC 的时钟。
配置 PC13 口的模式为通用推挽输出模式,最大速度 50 兆赫兹。
给 PC13 口输出数据,低电平点亮灯。
注意:
这种通过配置寄存器来实现点灯操作,需要我们不断地查手册来了解寄存器的每一位都是干什么的,并且该方式也有弊端,就是我们把除了PC13之外的位都配置成了0,这样就会影响到其他端口的原有配置,如果要做到只配置PC13而不影响其他位那还需要&=和|=的操作,那这样就会更加麻烦。所以这种寄存器的操作方式虽然代码简洁,但是还是不太方便。所以下面会使用STM32标准库的方式进行开发,但是在使用标准库开发的前提是配置好相关的库函数文件。
八、添加库函数
- 在工程文件夹中新建 “library” 文件夹,存放库函数。
- 打开固件库文件夹,将库函数的源文件和头文件复制到 “library” 文件夹。
- 在 Keil 软件中添加组 “library”,将库函数文件添加进来(打开固件库的文件夹,打开Libraries,STM32标准外设驱动(STM32F10x_StdPeriph_Driver),src/inc),其中src中存放着库函数的源文件,inc存放着库函数的头文件,将他们添加进工程文件中,但是此时的库函数还不能直接使用。
- 复制固件库中的三个文件到工程的 “user” 目录下(打开固件库文件夹,打开Project,STM32Template,stm32f10x conf.h和两个it结尾的文件),并在 Keil 软件中添加到右侧组里。
这三个文件分别为:
stm32f10x_conf.h 文件
用途:用来配置库函数头文件的包含关系。
特点:这里面还有一个用来参数检查的函数定义。所有库函数都需要这个文件,它起到了协调和组织各个库函数头文件之间关系的作用。
两个以 it 结尾的文件
用途:用来存放中断函数。
特点:启动文件中定义了 STM32 所有的其他中断,当中断达到触发条件后就会自动执行,而这些中断函数的定义就在这两个以 it 结尾的文件里。同时,ST 公司也建议可以把自己定义的中断写在这个位置,当然也可以根据习惯写在其他地方使用中断的位置。
这三个文件对于构建基于库函数的 STM32 工程非常重要,确保了工程在中断处理和库函数配置方面的完整性和正确性。 - 在头文件中添加宏定义,打开工程选项(魔术棒),在 “C/C++” 的 “Define” 栏目粘贴字符串**“USE_STDPERIPH_DRIVER”,并添加头文件路径以确保软件能够找到头文件,通过以上步骤,就完成了添加头文件的操作,使得基于库函数的工程能够正确地包含所需的头文件,从而能够顺利地调用库函数进行开发**。
这里已经将库函数的文件已经配置完成,下面就可以利用这些库函数实现点灯的操作了。
九、用库函数实现点灯操作
用库函数也是间接配置寄存器实现点灯操作与寄存器方式步骤类似,但更方便。
步骤:
一、使能(开启)时钟
使用库函数开启时钟,找到函数
RCC_APB2PeriphClockCmd(外设时钟控制)
该函数有两个参数,第一个参数选择外设,可以通过右键跳到函数定义查看函数的简介和说明,找到APB2 外设 GPIO C这一项,复制作为第一个参数(即RCC_APB2Periph_GPIOC)。
第二个参数选择新的状态,查看参数说明可知newstate的值可以是ENABLE或者DISABLE,复制ENABLE放在第二个参数的位置。
完整的代码为:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
这样就配置好了GPIOC的外设时钟,并且不会影响寄存器的其他位。
补充:
在翻找函数简介时可以找到这函数的具体内容,内部还是配置了RCC_APB2ENR这个寄存器,但是在经过了函数的包装之后,就不需要再去查手册,来确认哪一位具体是干什么的了,并且在函数中也利用了**|=和&=帮助我们操作过了,所以此库函数的配置也不会影响到寄存器的其他位**,这就是库函数和寄存器的区别,虽然库函数的代码比寄存器更长,但是语义更加明确,也不需要再去查表计算寄存器的值了,只需要调用库函数,在按照函数提示填好参数即可。
二、配置端口模式
使用函数GPIO_Init来配置端口模式,该函数有两个参数。
第一个参数选择要配置的GPIO,因为是配置PC13口的LED,所以第一个参数写GPIOC。
第二个参数是一个参数的结构体,需要先定义一个结构体变量。
定义结构体变量的步骤如下:
- 首先在上面写下结构体的类型,然后给结构体起名为GPIO_InitStructure(根据官方推荐命名)。
复制粘贴结构体的名字,用点引出结构体的参数,该结构体有三个参数,分别是GPIO 模式、GPIO 端口、GPIO 速度。 - 再右键转到各个参数的定义去查找对应的配置值(参数中的定义需要使用Ctrl + F+Find Next查找):
对于GPIO 模式,选中参数按Ctrl + F搜索定义位置,找到发现是一个枚举,选择GPIO_Mode_Out_PP(通用推挽输出) 复制,然后在结构体参数中写上GPIO_Mode_Out_PP。
对于GPIO 端口,右键的方法发现其有多个定义,双击member这一项,再利用Ctrl + F的方法查找GPIO_ pins_define找到对应的宏定义列表,选择GPIO_Pin_13复制,在结构体参数中填上该值,表示配置PC13口。
对于GPIO 速度,找到 50 兆赫兹的速度对应的配置值复制,在结构体参数中填上该值。
最后,因为函数的第二个参数是一个指向结构体的指针,所以需要传递结构体的地址,复制结构体的名字粘贴到该位置,前面加上一个取地址符号,再加上右括号和分号,完整的代码为
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体
GPIO_Initstructure.GPIO_Mode = GPIO_Mode_outPP;
GPIO_Initstructure.GPIO_Pin = GPIO_Pin_13;
GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz
GPIO_Init(GPIOC, &GPIO_InitStructure); //声明函数
这样就完成了GPIOC端口模式的配置。
三、设置端口高低电平来点灯
设置端口为高电平:使用函数GPIO_SetBits(指定端口输出高电平的函数),参数说明可以通过右键查看。这里直接填参数,第一个参数是GPIOC,第二个参数是GPIO_Pin_13
即
GPIO_SetBits(GPIOC, GPIO_Pin_13);
这一句就可以将PC13号口置为高电平。
设置端口为低电平:使用函数GPIO_ResetBits(指定端口输出低电平的函数),参数同样是GPIOC和GPIO_Pin_13
即
GPIO_ResetBits(GPIOC, GPIO_Pin_13);
这一句就可以将PC13号口设为低电平。
通过以上步骤,就实现了使用库函数来点灯的操作。
使用库函数开启时钟,不会影响寄存器其他位。配置端口模式,使用结构体配置参数,代码逻辑较复杂但语义明确。设置端口高低电平来点灯。
十、补充内容
-
新建工程里启动文件(Strar文件夹中的文件(startup_stm32f10x_md.s))的选择根据芯片型号和 flash 大小确定。
-
总结新建工程的步骤:
第一步:建立工程文件夹,在 Keil 中新建工程选择型号。
第二步:建立工程文件夹中的子文件夹,复制固件库文件到子文件夹,为了添加工程文件为准备,建立子文件夹是文件较多,为了方便管理。其实需要使用的文件一定要复制到子文件夹中,不要添加到工程文件夹之外,否则外面的文件一旦挪动位置,工程就找不到该文件了。
第三步:在工程中建立与子文件夹同名的分组,添加文件,为了在keil中方便管理文件,因为keil没有办法直接添加文件夹,所以选择在keil中建立分组再把文件都添加进来。
第四步:在工程选项中声明包含你自己设置的头文件的文件夹的路径。
第五步:定义使用标准外设驱动的字符串,使用库函数的条件编译,使用库函数就必须定义这个。
第六步:选择对应调试器并勾选 “reset and run”。
-
介绍工程架构:
一、启动文件(start up)
启动文件是程序执行最基本的文件,在 Keil 中用汇编写成。
启动文件内定义了中断向量表和中断服务函数。
中断服务函数中有个复位中断,是整个程序的入口。当 STM32 上电复位或按下复位按键后,程序就会进入复位中断函数。
执行复位中断函数主要做两件事:一是调用system_init函数,二是调用main函数。
在 Keil 中可以看到复位中断函数的具体实现,先调用system_init,再调用main,然后程序进入main函数中的死循环,因为单片机程序实际上永远不会结束。
二、system_init函数
system_init函数定义在以 “system” 开头的.c文件里。
函数作用是设置微控制器的启动初始化,包括嵌入式闪存、接口锁、相环更新系统内核的时钟变量等。
函数简介和注释中说明了其作用以及仅在复位后需要调用。
三、中断部分
启动文件还定义了 STM32 所有的其他中断,当中断达到触发条件后就会自动执行。
中断函数的定义在STM32F10x_it文件里。
四、用户文件
可以自己定义一些用户文件来分装一些模块,供主函数和中断调用。
这样有利于程序结构的模块化,避免所有程序都堆在主函数里,使主函数过长。
五、STM32 资源
工程结构右边是 STM32 的资源,相当于被动执行的部分。
在主函数或中断函数里可以调用这些资源。
右上角的STM32F10x.h和内核文件是外设和内核的寄存器描述文件。如果直接调用这些寄存器来使用 STM32,就是寄存器的开发方式,但这种方式有弊端且比较麻烦。
ST 公司提供了库函数文件,每个外设都提供了一大堆函数,封装了寄存器的操作,提供更加人性化的函数调用方式。只要学会操作套路,配置一个外设就很简单,甚至连手册都不需要看一眼。
conf文件用来配置头文件的包含关系,在conf文件中include了所有的库函数头文件,同时在STM32F10x.h的最后又包含了conf文件,所以在使用库函数时,只需要包含STM32F10x.h这一个头文件,就相当于包含了所有的库函数头文件,这样就可以任意调用库函数了。