GPIO 外设讲解

通过博主上一篇博客,相信大家已经初步了解到了对于 STM32 的控制原理,但是 STM32 到外设 / 引脚有这么多,是要一个一个的去记住它的地址?或者每一次使用一个外设都要看一下数据手册?这样也太麻烦了吧,所以 STM32 便有固件库来方便大家使用,就不用去记那么多的外设地址了。
固件库

固件库原理是什么?
固件库怎么使用?
首先,固件库的原理其实是和上一篇文章讲到的原理是一样的。

在这里插入图片描述

上图可以看到:

其实,固件库就是将基地址宏定义成一个比较好记的名字,比如 GPIOB 的基地址就直接使用 GPIOB 这个宏就可以使用了,固件库将所有的地址全部定义了,省去了用户自己定义的时间,用户移植固件库后可以直接使用宏。

由此看来,固件库确实好操作,好理解了许多。

下面,我就开始讲解 STM32 固件库GPIO 外设的使用吧

GPIO 配置

我们以 GPIOB 为例子(其他 GPIO 外设操作一样)
首先,我们可以打开关于 GPIO 的固件库(stm32f10x_gpio.h 文件)。

在这里插入图片描述

里面,我们可以看到有一个关于 GPIO 的初始化结构体,我们只需要配置这个结构体就可以对 GPIO 进行引脚配置。

首先,我们可以看到结构体中有:1. 引脚;2. 模式;3. 速度;
接下来我们就开始分析三种配置;

1.引脚
每一个 GPIO 外设都有 16 个引脚,这儿就可以配置使用的引脚是哪个引脚。

在这里插入图片描述

这儿就有所有引脚的定义,我们只需要往里面填这些值就行:

#define GPIO_PIN    GPIO_Pin_3                        //使用宏定义,方面改写,下同。
void GPIO_Config(void)                                //这儿我配置的GPIO初始化函数。
{
    GPIO_InitTypeDef GPIO_InitStruct;                //定义一个GPIO初始化变量。
//接下来配置GPIO结构体的值。
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;            //这儿将GPIO初始化的引脚定义成了3引脚。
//接下来的配置在下面会提到。
}

2.模式
GPIO 引脚的模式比较多;

在这里插入图片描述

这儿可以看到, GPIO 一共有 8 种模式,每一种模式都是什么?怎么用?我们可以在数据手册里面查看。

在这里插入图片描述

这儿我们可以看到这 8 种模式的名称,可是,在什么情况用什么模式呢?接下来我会讲解,不过,其实在数据手册里面可以看到它对每一种外设的模式有配置。

在这里插入图片描述

这儿就可以看到每一种外设的不同引脚有专用的模式;
因为我们这儿只是使用普通的 GPIO 输出高电平或者低电平,所以我们可以配置通用推挽输出模式。(关于每种模式详解,文章以后更新会讲到,因为小编对于 8 种模式还只是模糊理解,所以不敢定论,待小编深度学习后,会回来这篇文章末尾补充,这儿不做详解。)

//这个代码插入上述GPIO_Config()函数中,下同。
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;

3.速度
接下来就配置 GPIO 引脚的速度(并不是每个引脚都需要速度,输出才需要配置速度,输入不需要。)。
因为上述我们使用的是通用推挽输出,所以要配置速度。

在这里插入图片描述

这儿我们可以看到,对于速度我们有 3 种可以选择,因为下图

在这里插入图片描述

可以看到, GPIO 的外设是挂载到 APB2 上的,而固件库是将时钟配置成了 72MHz,所以 AHB 是 72MHz ,APB2 也是 72MHz , APB1 是 36MHz ,所以引脚速度直接配置最高的 50MHz 是没有问题的。

    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

GPIO 时钟

这样,我们把 GPIO 配置好了,但是还不能用,博主上一篇博客讲了要使用外设必须打开时钟,所以,我们要打开 GPIO 外设的时钟。
如何使用固件库打开 GPIO 的时钟?我们可以打开 stm32f10x_rcc.h 来看。
因为 GPIO 是挂载在 APB2 上面的,在 stm32f10x_rcc.h 里面我们可以找到这样一个函数:
在这里插入图片描述

这个函数就是可以打开 APB2 的时钟,这个时钟打开了,那么 GPIO 的时钟是不是也就打开了。
可是,怎么配置这个函数呢?
我们进入这个函数看看。

在这里插入图片描述

有注释来帮助我们看能够填写的变量。
因为我们是需要使用 GPIOB 这个外设,所以可以很清楚的看到里面有 RCC_APB2Periph_GPIOB 这个宏,而后面那个变量有两种选择:ENABLE(使能) or DISABLE(不使能).
所以,我们可以这样配置:

/*
这个定义在函数外。
*/
#define GPIO_PORT RCC_APB2Periph_GPIOB
#define GPIO_CLK RCC_APB2PeriphClockCmd
/*
下面定义在初始化函数内。
*/
    GPIO_CLK(GPIO_PORT, ENABLE);    //这样就打开了GPIOB外设的时钟。

最后,再初始化结构体就可以了。

在 gpio 固件库中可以看到这样的函数

在这里插入图片描述

里面有个 void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct); 的函数可以用来对 GPIO 的初始化。

怎么配置?

进入函数内部,可以看到:

在这里插入图片描述

还是看注释,第一个参数可以填 GPIOx ,什么是 GPIOx ?
就是 GPIOA / GPIOB / GPIOC ……
所以我们可以填 GPIOB ;
第二个参数可以看到,就是填写我们所定义的结构体指针,所以

/*
这个定义在函数外。
*/
#define GPIOx GPIOB
/*
下面定义在初始化函数内。
*/
    GPIO_Init(GPIOx, &GPIO_InitStruct);    //注意这儿是指针,所以要用&。

这样,GPIOB 就可以使用了!

可是, GPIOB 可以使用,该怎么使用呢?
还记得刚刚 gpio 固件库函数,里面是不是有很多函数?对!我们就是使用这些函数来操作这些 GPIOB 的引脚输出电平!
其实,输出高低电平只有两个函数就可以操作了,所以,我们目前只需要掌握两种函数。

在这里插入图片描述

这两个函数就可以设置或者清除 GPIOB 引脚,可以发现两个函数的输入参数是一样的,所以我们只需要看其中一个函数的参数就可以了。

在这里插入图片描述

通过这张图,我们就可以发现,填写的参数就是 GPIOx ,和 引脚号了。

    GPIO_SetBits(GPIOx, GPIO_PIN);        //设置IO口,使IO口置1。
    GPIO_ResetBits(GPIOx, GPIO_PIN);    //清除IO口,使IO口置0

对于上面函数是不是很分散,所以我总结了一下:

//宏定义,建议放在.h里面。
#define GPIO_PIN        GPIO_Pin_3
#define GPIO_PORT         RCC_APB2Periph_GPIOB
#define GPIO_CLK        RCC_APB2PeriphClockCmd
#define GPIOx            GPIOB
//函数放在.c文件。
void GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    GPIO_CLK(GPIO_PORT, ENABLE);    
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOx, &GPIO_InitStruct);
}

对于 GPIO 就配置好并可以使用了,接下来详解寄存器

其实,出了使用函数操作 IO 口还可以操作寄存器。
因为我们知道所有寄存器在固件库中都被定义。
所以我们可以直接操作寄存器。
首先我们要了解 GPIO 的寄存器。

在这里插入图片描述

这是关于 GPIO 所有的寄存器,我们接下来逐个分析。

CRL 寄存器

在这里插入图片描述

已知这个图很明显看出是配置引脚的输出模式和速度,但是这个只是配置的 0 ~ 7 这几个引脚,具体配置图中有。
所以相对的。

CRH 寄存器

在这里插入图片描述

这个是 8 ~ 15 引脚配置。与 CRL 寄存器作用相同。

IDR 寄存器

在这里插入图片描述

它的高 16 为是保留,就是没有使用,所以它只有低 16 为有效(能够使用低 16 位)。
它的作用就是可以读取 IO 口的状态,这个很好理解吧 IO 是高电平还是低电平。

ODR 寄存器

在这里插入图片描述

这个也是低 16 位有效,可以设置 16 个 IO 的输出;

BSRR 寄存器

在这里插入图片描述

这个寄存器可以对 IO 口进行设置和清除(也就是置位 0 / 1 )。
高 16 位置 1 就是对 IO 口清除,低 16 置 1 就是对 IO 口置 1 ,当这些位写 0 时不影响,这个就很保险。

BRR 寄存器

在这里插入图片描述

这个寄存器跟上述 BSRR 寄存器功能有些相似。
它是高 16 位保留,只有低 16 位有效,低 16 位之 0 时无影响, 置 1 时可以清除(也就是置 0)。

LCKR 寄存器

在这里插入图片描述

这个一共有 17 个位可以使用,最高位 16 位是用来激活这个寄存器功能。
置 1 -> 置 0 -> 置 1 -> 读取 0 -> 读取 1 这样就可以使寄存器激活。
低 16 位就是使 IO 口锁定,置 1 则 IO 口将不能再改变,置 0 则 IO 口可以再改变。

至此,小编的分享已经完了,谢谢各位的支持,如果有不对或者欠缺考虑的地方还请各位大佬多多指点。