STM32 固件库简介、工程模板创建保姆级教程

目录

一、由寄存器配置向库函数配置过渡

(1)用结构体来写库函数

(2)编写GPIO端口置位复位函数

(3)头文件条件编译的宏

(4)总结:初始化I/O口的步骤:

(5)官方固件库里的初始化顺序:

二、初识固件库

固件库文件分析:

三、新建工程—库函数版 保姆级教程

(1)准备工作

(2)创建工程

(3)添加编译路径

(4)定义宏
​​​​​​​​​​​​​​


一、由寄存器配置向库函数配置过渡

  我们在一开始创建工程的时候,创建了一个.h文件,用于存放寄存器映射的代码,相当于51的<reg52.h>。现在就写这个.h文件,以方便使用中直接引用赋值。

还是以GPIO口为例。

/*用于存放寄存器映射的代码,相当于51的reg52.h*/

// peripheral 外设

// 所有的外设的基地址  最基础的地址

#define PERIPH_BASE   ((unsigned int)0X40000000 )
	
// 总线基地址

#define APB1PERIPH_BASE    PERIPH_BASE
#define APB2PERIPH_BASE    (PERIPH_BASE + 0X00010000)
#define AHBPERIPH_BASE     (PERIPH_BASE + 0X00018000)

// GPIO外设基地址

#define GPIOA_BASE    (APB2PERIPH_BASE + 0X0800)
#define GPIOB_BASE    (APB2PERIPH_BASE + 0X0C00)
#define GPIOC_BASE    (APB2PERIPH_BASE + 0X1000)
#define GPIOD_BASE    (APB2PERIPH_BASE + 0X1400)

//寄存器基地址 以 GPIOC 为例

#define GPIOC_CRL     *(unsigned int *)(GPIOC_BASE+0X00)
#define GPIOC_CRH     *(unsigned int *)(GPIOC_BASE+0X04)
#define GPIOC_IDR     *(unsigned int *)(GPIOC_BASE+0X08)
#define GPIOC_ODR     *(unsigned int *)(GPIOC_BASE+0X0C)
#define GPIOC_BSRR    *(unsigned int *)(GPIOC_BASE+0X10)
#define GPIOC_BRR     *(unsigned int *)(GPIOC_BASE+0X14)
#define GPIOC_LCKR    *(unsigned int *)(GPIOC_BASE+0X18)

(1)用结构体来写库函数

这时,如果GPIOA、B、C、D都要用到的话,寄存器的基地址又要写很多,所以我们用结构体来写会更简洁欸。GPIO_TypeDef代表着struct这个结构体,里面有CRL,CRH,等寄存器。然后把后面的数转换为struct指针类型,编译器就会认为这串数是个地址。让GPIOA/B/C/D代表四个具体的寄存器。所有外设的基地址、总线的基地址、外设基地址都是按照之前的方法所写。4个寄存器的基地址用结构体来写。

typedef unsigned int uint32_t;
typedef unsigned short uint16_t;

typedef struct 
{
	uint32_t CRL;
	uint32_t CRH;
	uint32_t IDR;
	uint32_t ODR;
	uint32_t BSRR;
	uint32_t BRR;
	uint32_t LCKR;
}GPIO_TypeDef;
//定义一个寄存器结构体类型
// GPIO_TypeDef 里面都是 struct 类型的

             /*结构体类型的指针*/
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)//对 GPIO_BASE 进行强制类型转换。
#define GPIOB ((GPIO_TypeDef *)GPIOB_BASE)//对 GPIO_BASE 进行强制类型转换。
#define GPIOC ((GPIO_TypeDef *)GPIOC_BASE)//对 GPIO_BASE 进行强制类型转换。
#define GPIOD ((GPIO_TypeDef *)GPIOD_BASE)//对 GPIO_BASE 进行强制类型转换。
//现在,GPIOX 是一个结构体类型的指针(地址)

(2)编写GPIO端口置位复位函数

创建一个寄存器的置位复位c文件,存放置位复位函数:#include "stm32f10x_gpio.h"。置位函数:用到BSRR寄存器,函数定义两个形参,结构体类型的指针和对具体I/O口的操作。

/*置位函数与复位函数*/
//写在对应寄存器的程序里

#include "stm32f10x_gpio.h"

//置位:BSRR寄存器  某一位置1
void GPIO_SetBits( GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
	GPIOx->BSRR = GPIO_Pin;
}
//对该函数形参的解释:
//第一个形参表示 GPIOX 寄存器的地址,
//由于在头文件中,GPIOX 就是一个结构体类型的指针(地址),
//所以在使用的时候,把前面的前缀去掉即可。
//第二个形参代表 如何对该寄存器的 BSRR 寄存器进行操作。
//也就是对具体的I/O口进行操作,比如将 1左移2位,直接就写(1<<2)即可

//复位:BRR寄存器  某一位置0
void GPIO_RetsetBits( GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
	GPIOx->BRR = GPIO_Pin;
}
//对该函数形参的解释:
//第一个形参表示 GPIOX 寄存器的地址,
//由于在头文件中,GPIOX 就是一个结构体类型的指针(地址),
//所以在使用的时候,把前面的前缀去掉即可。
//第二个形参代表 如何对该寄存器的 BRR 寄存器进行操作。
//也就是对具体的I/O口进行操作,比如将 1左移2位,直接就写(1<<2)即可

(3)头文件条件编译的宏

当一个头文件被多个文件多次调用的时候,应该加上条件编译的宏,每写一个头文件都要加上条件编译的宏。为了防止系统会出现“重复定义”的报错

#ifndef __STM32F10X_H
#define __STM32F10X_H 
//头文件的条件编译
//作用在于当一个头文件被多个文件同时调用的时候,会出现重复编译的情况
//这样就不会出现

/*用于存放寄存器映射的代码,相当于51的reg52.h*/

// peripheral 外设

// 所有的外设的基地址  最基础的地址

#define PERIPH_BASE   ((unsigned int)0X40000000 )

// 总线基地址

#define APB1PERIPH_BASE    PERIPH_BASE
#define APB2PERIPH_BASE    (PERIPH_BASE + 0X00010000)
#define AHBPERIPH_BASE     (PERIPH_BASE + 0X00018000)

// GPIO外设基地址

#define GPIOA_BASE    (APB2PERIPH_BASE + 0X0800)
#define GPIOB_BASE    (APB2PERIPH_BASE + 0X0C00)
#define GPIOC_BASE    (APB2PERIPH_BASE + 0X1000)
#define GPIOD_BASE    (APB2PERIPH_BASE + 0X1400)

//寄存器基地址 以 GPIOB 为例

//#define GPIOC_CRL     *(unsigned int *)(GPIOC_BASE+0X00)
//#define GPIOC_CRH     *(unsigned int *)(GPIOC_BASE+0X04)
//#define GPIOC_IDR     *(unsigned int *)(GPIOC_BASE+0X08)
//#define GPIOC_ODR     *(unsigned int *)(GPIOC_BASE+0X0C)
//#define GPIOC_BSRR    *(unsigned int *)(GPIOC_BASE+0X10)
//#define GPIOC_BRR     *(unsigned int *)(GPIOC_BASE+0X14)
//#define GPIOC_LCKR    *(unsigned int *)(GPIOC_BASE+0X18)

typedef unsigned int uint32_t;
typedef unsigned short uint16_t;

typedef struct 
{
	uint32_t CRL;
	uint32_t CRH;
	uint32_t IDR;
	uint32_t ODR;
	uint32_t BSRR;
	uint32_t BRR;
	uint32_t LCKR;
}GPIO_TypeDef;
//定义一个寄存器结构体类型
// GPIO_TypeDef 里面都是 struct 类型的

             /*结构体类型的指针*/
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)//对 GPIO_BASE 进行强制类型转换。
#define GPIOB ((GPIO_TypeDef *)GPIOB_BASE)//对 GPIO_BASE 进行强制类型转换。
#define GPIOC ((GPIO_TypeDef *)GPIOC_BASE)//对 GPIO_BASE 进行强制类型转换。
#define GPIOD ((GPIO_TypeDef *)GPIOD_BASE)//对 GPIO_BASE 进行强制类型转换。
//现在,GPIOX 是一个结构体类型的指针(地址)

#endif /*__STM32F10X*/

在头文件的开头,使用“#ifndef”关键字,判断标号“__STM32F10X_H”是否被定义,若没有被定义,则从“#ifndef”至“#endif”关键字之间的内容都有效,也就是说,这个头文件若被其它文件“#include”,头文件就会被包含到这个文件中了,且头文件中紧接着使用“#define”关键字定义上面判断的标号“__STM32F10X_H”。当这个头文件被同一个文件第二次“#include”包含时,因为存在第一次包含中的“#define __STM32F10X_H”定义,这时再判断“__STM32F10X_H”,判断的结果就是该文件已经包含过头文件了,从“#ifndef”至#endif”之间的内容都无效,从而防止了同一个头文件被包含多次,编译时就不会出现“redefine(重复定义)”的错误了。

至于为什么要用两个下划线来定义“__STM32F10X_H”标号,其实这只是防止它与其它普通宏定义重复了。

(4)总结:初始化I/O口的步骤:

   1、打开外设(GPIO)的时钟

   2、选定I/O口

   3、配置输入还是输出(配置CRL/CRH寄存器)

   4、如果是输出,配置输出速率(也是在CRL/CRH寄存器)

   5、调用外设(GPIO)初始化函数,把配置好的结构体成员写到寄存器里面

    注:2、3、4步其实就是我们在用“寄存器点亮LED”那篇文章中提到的配置GPIO初始化结构体的步骤。

(5)官方固件库里的初始化顺序:

①定义GPIO初始化结构体

②枚举定义速度和工作模式

上面定义的结构体很直接,美中不足的是在对结构体中各个成员赋值实现某个功能时还需要查询手册的寄存器说明,我们可以使用 C语言中的枚举定义功能,根据手册把每个成员的所有取值都定义好。 GPIO_Speed和GPIO_Mode 这两个成员对应的寄存器是CRL和CRH 这两个端口配置寄存器。

bit4 用来区分端口是输入还是输出,0表示输入,1表示输出。bit2和bit3 对应寄存器的CNFY[1:0]位(控制输入输出模式),是我们真正要写入到CRL和CRH这两个端口控制寄存器中的值。bit0和 bit1 对应寄存器的MODEY[1:0]位(控制输入输出的速度),这里我们暂不初始化,在GPIO_Init() 初始化函数中用来跟 GPIOSpeed 的值相加即可实现速率的配置。有关具体的代码分析见 GPIO_Init()库函数。其中在下拉输入和上拉输入中我们设置 bit5 和 bit6 的值为 01 和 10来以示区别。

GPIO支持4种输入模式(浮空输入、上拉输入、下拉输入、模拟输入)4种输出模式(开漏输出、开漏复用输出、推挽输出、推挽复用输出)

初始化一个其他的外设:在驱动头文件里面有一个初始化结构体,结构体里面有很多成员,而且在头文件里面有这些成员的枚举(或者宏定义)。

二、初识固件库

固件库也就是函数库,调用这些函数就可以驱动外设来工作。STM32F10x standard peripheral library(这个就是标准库)

固件库文件:链接:https://pan.baidu.com/s/1hMK_uJEGyXtGo14Po2pyuA?pwd=gx9s

 提取码:gx9s

  打开固件库文件夹,“_htmresc”里面是图片不重要,重要的是“Libraries”和“Project”。打开工程:“Template”是模板,所有芯片你生产厂家通用的。“Example”是stm32对于所有外设的全部例程。

固件库文件分析:

启动文件:系统启动之后第一个需要执行的文件。在固件库--libraries--CMSIS--CM3--DeviceSupport--ST--STM32F10X--startup--arm代表着keil,其他的是我们用到的其他的编写软件。

1-汇编编写的启动文件(STARTUP)

startup_stm32f10x_md.s:设置堆栈指针、设置PC指针、初始化中断向量表、配置系统时钟、对用C库函数_main最终去到C的世界


外设相关:


2-时钟配置文件(CMSIS)

system_stm32f10x.c:把外部时钟HSE=8M晶振,经过PLL倍频为72M(系统晶振)。

system_stm32f10x.h

3-外设相关的(FWLB)

xxx:GPIO、USRAT、I2C、SPI、FSMC

stm32f10x_xxx.c:外设的驱动函数库文件

stm32f10x_xxx.h:存放外设的初始化结构体,外设初始化结构体成员的参数列表,外设固件库函数的声明

4- 所有外设的寄存器映射头文件  (CMSIS)

stm32f10x.h:实现了内核之外的外设的寄存器映射 

#include "stm32f10x.h"   相当于51单片机中的  #include <reg51.h>


内核相关


5-内核相关的(CMSIS)

CMSIS - Cortex 微控制器软件接口标准

core_cm3.h:实现了内核里面外设的寄存器映射

core_cm3.c:内核外设的驱动固件库

6-NVIC(嵌套向量中断控制器)、SysTick(系统滴答定时器)(FWLB)

misc.h

misc.c


用户相关


7-用户相关(USER)

     main.c

     int main(void)

     {

    // 来到这里的时候,系统的时钟已经被配置成72M。

     }

   专门存放中断服务函数的C文件

     stm32f10x_it.c

     stm32f10x_it.h

中断服务函数你可以随意放在其他的地方,并不是一定要放在stm32f10x_it.c

8-头文件的配置文件

stm32f10x_conf.h:头文件的头文件

//stm32f10x_usart.h

//stm32f10x_i2c.h

//stm32f10x_spi.h

//stm32f10x_adc.h

//stm32f10x_fsmc.h

......

三、新建工程—库函数版 保姆级教程

使用库建立一个空的工程,作为工程模板。以后直接复制一份工程模板,在它之上进行开发。

(1)准备工作

  创建固件库模板文件夹—创建DOC文件夹—创建程序说明txt文件。

  创建Libraries文件夹—把固件库Libraries文件夹里的内容全部复制过去—把CMSIS—CM3—DeviceSupport文件夹里面的启动文件、.c、.h文件复制到CMSIS文件夹下(不要让这些文件的路径太深)--把DeviceSupport文件夹删除掉--把CMSIS—CM3—CoreSupport文件夹里面的.c、.h文件复制到CMSIS文件夹下--把CoreSupport文件夹删除掉—把CM3删除掉—打开startup文件夹,只保留arm,其他删除—把arm里面的全部文件复制到startup文件夹下,删除arm文件夹

  创建Listing文件夹  创建Output文件夹(可以下载到开发板里的可执行文件)(仿真器下载,后缀是hif;串口下载,后缀是hex)

  创建Project文件夹  创建User文件夹—把头文件的头文件、专门存放中断服务函数的.c、.h文件复制过去。

(2)创建工程

  打开keil—创建新工程在Project文件夹下,工程名要用英文—芯片型号选择STM32103RC—左侧工程栏内右击“目标文件夹”—add group—添加STARTUP、CMSIS、FWLIB、USER、DOC这几个文件夹—STARTUP在Libraries—CMSIS—startup文件夹里面,选择hd.s的文件;CMISIS要把CMSIS文件夹里面的全部添加进去(除startup文件夹);FWLIB要把Libraries—STM32F10X…--src和inc—全部添加进去;USER创建C文件夹,添加头文件的头文件、专门存放中断服务函数的.c、.h文件;DOC添加txt文件

(3)添加编译路径

需要给所有的头文件添加编译路径。头文件的头文件“stm32f10x_conf.h”在USER文件夹下,要添加它的编译路径,魔法棒—C/C++--include paths后面的三个点—点击小方框—点击三个点—选择USER文件夹。其他的头文件依次类推,把所有含头文件的上一级文件夹都添加进去

(4)定义宏

在“stm32f10x.h”头文件中,有所有单片机型号的宏,mini单片机的宏是“STM32F10X_HD”,把这个宏复制到—魔法棒—C/C++--Preprocessor Symbols—Define中,后面加一个英文状态下的逗号

除此之外,还有一个头文件的头文件的条件宏需要定义,如果需要包含这个头文件,就需要定义这个宏。

3、4步一定不要忘记!!!

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值